资源描述
操作系统
班级:2011211302
学号:2011211168
姓名:康雅微
目录
实验5.1 进程通信观察实验
实验6.3 I/O系统编程实验
实验7.1 文件管理系统管理实验代码分析
实验5.1 观察实验
1、实验目的与内容
在Linux下,用ipcs()命令观察进程通信情况,了解Linux基本通信机制。
2、实验原理
Linux IPC继承了Unix System V及DSD等,共有6种机制: 信号(signal)、管道(pipe和命名管道(named piped)、消息队列(message queues)、共享内存(shared memory segments)、信号量(semaphore)、套接字(socket)。
本实验中用到的几种进程间通信方式:
(1)共享内存段(shared memory segments)方式
– 将2个进程的虚拟地址映射到同一内存物理地址,实现内存共享
– 对共享内存的访问同步需由用户进程自身或其它IPC机制实现(如信号量)
– 用户空间内实现,访问速度最快。
– Linux利用shmid_ds结构描述所有的共享内存对象。
(2)信号量(semaphore)方式
– 实现进程间的同步与互斥
– P/V操作, Signal/wait操作
– Linux利用semid_ds结构表示IPC信号量
(3)消息队列(message queues)方式
– 消息组成的链表,进程可从中读写消息。
– Linux维护消息队列向量表msgque,向量表中的每个元素都有一个指向msqid_ds结构的指针,每个msqid_ds结构完整描述一个消息队列
LINUX系统提供的IPC函数有:
l msgget(关键字,方式):创建或打开一个消息队列
l msgsnd(消息队列标志符,消息体指针,消息体大小,消息类型): 向队列传递消息
l msgrcv(消息队列标志符,消息体指针,消息体大小,消息类型): 从队列中取消息
l msgctl(消息队列标志符,获取/设置/删除,maqid_ds缓冲区指针): 获取或设置某个队列信息,或删除某消息队列
Linux系统中,内核,I/O任务,服务器进程和用户进程之间采用消息队列方式,许多微内核OS中,内核和各组件间的基本通信也采用消息队列方式.
3、实验结果
实验6.3 编程实验
1、实验目的
编写一个daemon进程,该进程定时执行 ps命令,然后将该命令的输出写至文件F1尾部。通过此实验,掌握Linux I/O系统相关内容。
2、实验原理
在这个程序中,首先fork一个子程序,然后,关闭父进程,这样,新生成的子进程被交给init进程接管,并在后台执行。
新生成的子进程里,使用system系统调用,将ps的输出重定向,输入到f1.txt里面。
3、实验步骤
编写daemon.c
代码如下:
#include<stdio.h>
#include<stdlib.h>
int main(int argc,char* argv[])
{
int i,p;
p = fork();
if(p > 0){
exit(0);
}
else if(p == 0){
for(i = 0; i < 100; i++){
sleep(100);
system("ps > f1.txt");
}
}
else{
perror("Create new process!");
}
return 1;
}
}
编译程序
# gcc -o daemon daemon.c
执行程序
# ./daemon
5、实验结果及分析
程序sleep(100)后会在当前目录生成一个文件f1.txt,内容如下:
PID TTY TIME CMD
1258 pts/0 00:00:00 bash
2729 pts/0 00:00:00 daemon
2801 pts/0 00:00:00 sh
2802 pts/0 00:00:00 ps
再sleep(100),此文件会更新。重复执行100次。
实验7.1 代码分析
1、实验目的
了解与文件管理有关的Linux内核模块的代码结构。
2、实验内容
阅读 Linux/Minix中有关文件模块的调用主线,并写出分析报告,包括
l 文件建立模块,即系统调用create()
l 文件删除模块,即系统调用rm()
l 读/写模块,即 read/write
3、分析报告示例
A. 创建文件模块分析
5780 /*creat system call */
5781 Creat()
5782 {
5783 resister *ip;
5784 extern uchar;
5785
5786 ip = namei(&uchar,1);
5787 if(ip == NULL){
5788 if(u.u_error)
5789 return;
5790 ip = maknode(u.u_arg[1]&07777&(~ISVTX));
5791 if (ip == NULL)
5792 return;
5793 open1(ip,FWRITE,2);
5794 }else
5795 open1(ip,FWRITE,1);
5796 }
第5 7 8 6:“namei”( 7 5 1 8 )将一路径名变换成一个“inode”指针。“uchar”是一个过程的名字,它从用户程序数据区一个字符一个字符地取得文件路径名。
5 7 8 7:一个空“inode”指针表示出了一个错,或者并没有具有给定路径名的文件存在。
5 7 8 8:对于出错的各种条件,请见U P M的C R E AT ( I I )。
5 7 9 0:“maknode”( 7 4 5 5 )调用“ialloc”创建一内存“ inode”,然后对其赋初值,并使其进入适当的目录。注意,显式地清除了“粘住”位( I S V T X )。
B. 删除文件rm模块分析
3510 unlink()
3511 {
3512 resister *ip,*pp;
3513 extern uchar;
3514
3515 pp = namei(&uchar,2);
3516 if (pp ==NULL)
3517 return;
3518 prele(pp);
3519 ip = iset(pp ->dev,u.u_dent.u_ino);
3520 if (ip == NULL)
3521 panic (*unlink – iset *);
3522 if ((ip ->i_mode%IFMT) == IFDIR && !suser())
3523 goto out;
3524 u.u_offset[1] = - DIRSIZ+2;
3525 u.ubase = &u.u_dent;
3526 u.ucount = DIRSIZE +2;
3527 u.u_dent.u_ino = 0;
3528 writei(pp);
3529 ip ->i_nlink--;
3530 ip->i_flag =! IUPD;
3531
3532 out:
3533 iput(pp);
3534 iput(ip);
3535 }
新文件作为永久文件自动进入文件目录。关闭文件不会自动地造成文件被删除。当内存“ inode”项中的“ i _ nlink”字段值为0并且相应文件未被打开时,将删除该文件。在创建文件时,该字段由“ m a k n o d e”赋初值为1。系统调用“ link”( 5 9 4 1 )可将其值加1,系统调用“unlink”( 3 5 2 9 )则可将其值减1。创建临时“工作文件”的程序应当在其终止前执行“ unlink”系统调用将这些文件删除。
注意,“unlink”系统调用本身并没有删除文件。当引用计数( i _ count )被减为0时( 7 3 5 0、7 3 6 2 ),才删除该文件。
为了减少在程序或系统崩溃时遗留下来的临时文件所带来的问题,程序员应当遵守下列约定:
(1) 在打开临时文件后立即对其执行“ unlink”操作。
(2) 应在“tmp”目录下创建临时文件。在文件名中包括进程标识数就可构成一惟一文件名
C 读/写模块,即 read/write 分析
/*
* linux/fs/minix/file.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
* minix regular file handling primitives
*/
#include <asm/segment.h>
#include <asm/system.h>
#include <linux/sched.h>
#include <linux/minix_fs.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/locks.h>
#define NBUF 32
#define MIN(a,b) (((a)(b))?(a):(b))
#include
#include
static int ext2_file_read (struct inode *, struct file *, char *, int);
static int ext2_file_write (struct inode *, struct file *, char *, int);
static void ext2_release_file (struct inode *, struct file *);
/*
* We have mostly NULL's here: the current defaults are ok for
* the ext2 filesystem.
*/
static struct file_operations ext2_file_operations = {
NULL, /* lseek - default */
ext2_file_read, /* read */
ext2_file_write, /* write */
NULL, /* readdir - bad */
NULL, /* select - default */
ext2_ioctl, /* ioctl */
generic_mmap, /* mmap */
NULL, /* no special open is needed */
ext2_release_file, /* release */
ext2_sync_file /* fsync */
};
struct inode_operations ext2_file_inode_operations = {
&ext2_file_operations,/* default file operations */
NULL, /* create */
NULL, /* lookup */
NULL, /* link */
NULL, /* unlink */
NULL, /* symlink */
NULL, /* mkdir */
NULL, /* rmdir */
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
ext2_bmap, /* bmap */
ext2_truncate, /* truncate */
ext2_permission /* permission */
};
static int ext2_file_read (struct inode * inode, struct file * filp,
char * buf, int count)
{
int read, left, chars;
int block, blocks, offset;
int bhrequest, uptodate;
struct buffer_head ** bhb, ** bhe;
struct buffer_head * bhreq[NBUF];
struct buffer_head * buflist[NBUF];
struct super_block * sb;
unsigned int size;
int err;
if (!inode) {
printk ("ext2_file_read: inode = NULL\n");
return -EINVAL;
}
sb = inode->i_sb;
if (!S_ISREG(inode->i_mode)) {
ext2_warning (sb, "ext2_file_read", "mode = %07o",
inode->i_mode);
return -EINVAL;
}
offset = filp->f_pos;
size = inode->i_size;
if (offset > size)
left = 0;
else
left = size - offset;
if (left > count)
left = count;
if (left > EXT2_BLOCK_SIZE_BITS(sb);
offset &= (sb->s_blocksize - 1);
size = (size + sb->s_blocksize - 1) >> EXT2_BLOCK_SIZE_BITS(sb);
blocks = (left + offset + sb->s_blocksize - 1) >> EXT2_BLOCK_SIZE_BITS(sb);
bhb = bhe = buflist;
if (filp->f_reada) {
// 37 /* This specifies how many sectors to read ahead on the disk. */
// 39 int read_ahead[MAX_BLKDEV] = {0, };
blocks += read_ahead[MAJOR(inode->i_dev)] >>
(EXT2_BLOCK_SIZE_BITS(sb) - 9);
if (block + blocks > size)
blocks = size - block;
}
/*
* We do this in a two stage process. We first try and request
* as many blocks as we can, then we wait for the first one to
* complete, and then we try and wrap up as many as are actually
* done. This routine is rather generic, in that it can be used
* in a filesystem by substituting the appropriate function in
* for getblk
*
* This routine is optimized to make maximum use of the various
* buffers and caches.
*/
do {
bhrequest = 0;
uptodate = 1;
while (blocks) {
--blocks;
*bhb = ext2_getblk (inode, block++, 0, &err);
if (*bhb && !(*bhb)->b_uptodate) {
uptodate = 0;
bhreq[bhrequest++] = *bhb;
}
if (++bhb == &buflist[NBUF])
bhb = buflist;
/*
* If the block we have on hand is uptodate, go ahead
* and complete processing
*/
if (uptodate)
break;
if (bhb == bhe)
break;
}
/*
* Now request them all
*/
if (bhrequest)
ll_rw_block (READ, bhrequest, bhreq);
do {
/*
* Finish off all I/O that has actually completed
*/
if (*bhe) {
wait_on_buffer (*bhe);
if (!(*bhe)->b_uptodate) { /* read error? */
brelse(*bhe);
if (++bhe == &buflist[NBUF])
bhe = buflist;
left = 0;
break;
}
}
if (left s_blocksize - offset)
chars = left;
else
chars = sb->s_blocksize - offset;
filp->f_pos += chars;
left -= chars;
read += chars;
if (*bhe) {
memcpy_tofs (buf, offset + (*bhe)->b_data,
chars);
brelse (*bhe);
buf += chars;
} else {
while (chars-- > 0)
put_fs_byte (0, buf++);
}
offset = 0;
if (++bhe == &buflist[NBUF])
bhe = buflist;
} while (left > 0 && bhe != bhb && (!*bhe || !(*bhe)->b_lock));
} while (left > 0);
/*
* Release the read-ahead blocks
*/
while (bhe != bhb) {
brelse (*bhe);
if (++bhe == &buflist[NBUF])
bhe = buflist;
}
if (!read)
return -EIO;
filp->f_reada = 1;
if (!IS_RDONLY(inode)) {
inode->i_atime = CURRENT_TIME;
inode->i_dirt = 1;
}
return read;
}
static int ext2_file_write (struct inode * inode, struct file * filp,
char * buf, int count)
{
off_t pos;
int written, c;
struct buffer_head * bh;
char * p;
struct super_block * sb;
int err;
if (!inode) {
printk("ext2_file_write: inode = NULL\n");
return -EINVAL;
}
sb = inode->i_sb;
if (sb->s_flags & MS_RDONLY)
/*
* This fs has been automatically remounted ro because of errors
*/
return -ENOSPC;
if (!S_ISREG(inode->i_mode)) {
ext2_warning (sb, "ext2_file_write", "mode = %07o\n",
inode->i_mode);
return -EINVAL;
}
/*
* ok, append may not work when many processes are writing at the same time
* but so what. That way leads to madness anyway.
*/
if (filp->f_flags & O_APPEND)
pos = inode->i_size;
else
pos = filp->f_pos;
written = 0;
while (written s_blocksize, 1, &err);
if (!bh) {
if (!written)
written = err;
break;
}
c = sb->s_blocksize - (pos % sb->s_blocksize);
if (c > count-written)
c = count - written;
if (c != sb->s_blocksize && !bh->b_uptodate) {
ll_rw_block (READ, 1, &bh);
wait_on_buffer (bh);
if (!bh->b_uptodate) {
brelse (bh);
if (!written)
written = -EIO;
break;
}
}
p = (pos % sb->s_blocksize) + bh->b_data;
pos += c;
if (pos > inode->i_size) {
inode->i_size = pos;
inode->i_dirt = 1;
}
written += c;
memcpy_fromfs (p, buf, c); //写入到缓冲块中
buf += c;
bh->b_uptodate = 1;
bh->b_dirt = 1;
brelse (bh);
}
inode->i_ctime = inode->i_mtime = CURRENT_TIME;
filp->f_pos = pos;
inode->i_dirt = 1;
return written;
}
分析:
对于整个文件的索引操作担,都使用块计算的。
整块读取时,调用底层的读取函数。只要得到了更新块就跳出去处理,否则就记录要读取的块
整块的写入时很好办的,只要整体为脏就可以了.但是对于不能整块写入的,必须写入的块原先是更新的.
源码行号 注释:
86 left = size - offset; //剩余可读数
88 left = count; //left就是最终要读取的数据
92 //下面的size和blocks是对于整个文件的索引操作担,都使用块计算的
93 size = (size + sb->s_blocksize - 1) >> EXT2_BLOCK_SIZE_BITS(sb); //整个文件的块数
94 blocks = (left + offset + sb->s_blocksize - 1) >> EXT2_BLOCK_SIZE_BITS(sb); //要读取的块数
95 bhb = bhe = buflist;
97 //进行预读
98 if (filp->f_reada) {
99
100 // 37 /* This specifies how many sectors to read ahead on the disk. */
101 // 39 int read_ahead[MAX_BLKDEV] = {0, };
102 blocks += read_ahead[MAJOR(inode->i_dev)] >>
103 (EXT2_BLOCK_SIZE_BITS(sb) - 9); //可能都是使用512标示的,这里进行校正
104 if (block + blocks > size)
105 blocks = size - block; //校正实际能读取的块数
106 }
126 bhreq[bhrequest++] = *bhb; //同时bhrequest记录了需要重新读取的块数
128 if (++bhb == &buflist[NBUF]) //回卷到头部
134 if (uptodate) //只要得到了更新块就跳出去处理,否则就记录要读取的块
136 if (bhb == bhe) //满了,要读取NBUF块
142 if (bhrequest) //调用底层的读取函数
150 if (!(*bhe)->b_uptodate) { /* read error? */
//这里当做错误处理的,left=0,跳出大循环了
154 bhe = buflist; //不是跳出去就完了,这里的bhe将来还是有用的
184 while (bhe != bhb) { //预读的块释放掉
191 filp->f_reada = 1; //标志已经预读了
226 if (filp->f_flags & O_APPEND) //是否追加,更新文件指针
237 c = sb->s_blocksize - (pos % sb->s_blocksize); //c记录本次可以读取的字符个数
240 if (c != sb->s_blocksize && !bh->b_uptodate) { //整块的写入时很好办的,只要整体为脏就可以了.但是对于不能整块写入的,必须写入的块原先是更新的.
257 memcpy_fromfs (p, buf, c); //写入到缓冲块中
展开阅读全文