Linux内核源代码情景分析-进程间通信-命名管道

来源:互联网 发布:霸气业务网站源码 编辑:程序博客网 时间:2024/06/06 02:33

    建立命名管道,mknod mypipe p。命名管道存在硬盘上,而管道不是。

    通过open打开这个命名管道,在内核中通过sys_open()实现,filename是"***/mypipe "。

    相关部分,请参考Linux内核源代码情景分析-文件的打开。

    sys_open进入filp_open,然后在open_namei中调用一个函数path_walk(),根据文件的路径名在文件系统中找到代表这个文件的inode。在将磁盘上的inode读入内存时,要根据文件的类型(FIFO文件的S_IFIFO标志位为1),将inode中的i_op指针和i_fop指针设置成指向相应的inode_operations数据结构和file_operations数据结构,但是对于像FIFO这样的特殊文件则调用init_special_inode()来加以初始化。这段代码在ext2_read_inode()中,如下:

if (inode->i_ino == EXT2_ACL_IDX_INO ||    inode->i_ino == EXT2_ACL_DATA_INO)/* Nothing to do */ ;else if (S_ISREG(inode->i_mode)) {inode->i_op = &ext2_file_inode_operations;inode->i_fop = &ext2_file_operations;inode->i_mapping->a_ops = &ext2_aops;} else if (S_ISDIR(inode->i_mode)) {inode->i_op = &ext2_dir_inode_operations;inode->i_fop = &ext2_dir_operations;} else if (S_ISLNK(inode->i_mode)) {if (!inode->i_blocks)inode->i_op = &ext2_fast_symlink_inode_operations;else {inode->i_op = &page_symlink_inode_operations;inode->i_mapping->a_ops = &ext2_aops;}} else init_special_inode(inode, inode->i_mode,   le32_to_cpu(raw_inode->i_block[0]));
    只要文件的类型不是ACL索引活数据,不是普通文件,不是目录,不是符号连接,就属于特殊文件,就要通过init_special_inode()来初始化其inode结构:

    sys_open()>filp_open()>open_namei()>path_walk()>real_lookup()>ext2_lookup()>iget()>get_new_inode() >ext2_read_inode()>init_special_inode()

void init_special_inode(struct inode *inode, umode_t mode, int rdev){inode->i_mode = mode;if (S_ISCHR(mode)) {inode->i_fop = &def_chr_fops;inode->i_rdev = to_kdev_t(rdev);} else if (S_ISBLK(mode)) {inode->i_fop = &def_blk_fops;inode->i_rdev = to_kdev_t(rdev);inode->i_bdev = bdget(rdev);} else if (S_ISFIFO(mode))inode->i_fop = &def_fifo_fops;else if (S_ISSOCK(mode))inode->i_fop = &bad_sock_fops;elseprintk(KERN_DEBUG "init_special_inode: bogus imode (%o)\n", mode);}

    显然,对于FIFO文件,其inode结构中的inode_operations结构指针i_op为0,而file_operations结构指针i_fop则指向def_fifo_fops,定义如下:

struct file_operations def_fifo_fops = {open:fifo_open,/* will set read or write pipe_fops */};
    与管道pipe文件的inode结构作一比较,就可以看出对于pipe文件中的inode结构并没有走过这么一个过程,与init_special_inode()也豪无关系。这是因为pipe文件的inode结构不是通过ext2_read_inode()从磁盘上读入,而是临时生成出来的(因而是无名的、无形的)。

    随后,在dentry_open中将inode结构中的这个file_operations结构指针复制到file数据结构中。这样,对于命名管道,在打开文件时经由数据结构def_fifo_fops,就可以得到函数指针fifo_open,从而进入函数fifo_open(),代码如下:

    f->f_op = fops_get(inode->i_fop);//f->f_op被赋值为inode_i_fop      if (inode->i_sb)          file_move(f, &inode->i_sb->s_files);//将其从中间队列脱链而挂入该文件所在设备的super_block结构中的file结构队列s_files      if (f->f_op && f->f_op->open) {          error = f->f_op->open(inode,f);          if (error)              goto cleanup_all;      } 
    f->f_op->open(inode,f)对应fifo_open,代码如下:
static int fifo_open(struct inode *inode, struct file *filp){int ret;ret = -ERESTARTSYS;lock_kernel();if (down_interruptible(PIPE_SEM(*inode)))goto err_nolock_nocleanup;if (!inode->i_pipe) {ret = -ENOMEM;if(!pipe_new(inode))//当首次打开这个FIFO文件的进程来到fifo_open时,该管道的缓冲页面尚未分配,所以通过pipe_new分配所需的pipe_inode_info数据结构和缓冲页面,以后再打开同一FIFO文件的进程就会跳过这一段goto err_nocleanup;}filp->f_version = 0;switch (filp->f_mode) {case 1://读端/* *  O_RDONLY *  POSIX.1 says that O_NONBLOCK means return with the FIFO *  opened, even when there is no process writing the FIFO. */filp->f_op = &read_fifo_fops;//和写端不同PIPE_RCOUNTER(*inode)++;//(inode).i_pipe->r_counter++if (PIPE_READERS(*inode)++ == 0)//(inode).i_pipe->readers++wake_up_partner(inode);//如果写端已经打开,那么读端的打开就完成了命名管道的建立过程。在这种情况下写端的进程,也就是"生产者"进程,一般都是正在睡眠中,等待着命名管道建立过程的完成,所以要把它唤醒if (!PIPE_WRITERS(*inode)) {//(inode).i_pipe->writerif ((filp->f_flags & O_NONBLOCK)) {/* suppress POLLHUP until we have * seen a writer */filp->f_version = PIPE_WCOUNTER(*inode);} else {wait_for_partner(inode, &PIPE_WCOUNTER(*inode));//如果命名管道的写端尚未打开,读端的打开只是完成了命名管道建立过程的一半,所以"消费者"进程要通过wait_for_partner()进入睡眠,等待某个"生产者"进程来打开命名管道的写端以完成其建立过程if(signal_pending(current))goto err_rd;}}break;case 2://写端/* *  O_WRONLY *  POSIX.1 says that O_NONBLOCK means return -1 with *  errno=ENXIO when there is no process reading the FIFO. */ret = -ENXIO;if ((filp->f_flags & O_NONBLOCK) && !PIPE_READERS(*inode))goto err;filp->f_op = &write_fifo_fops;//和读端不同PIPE_WCOUNTER(*inode)++;//(inode).i_pipe->w_counter++if (!PIPE_WRITERS(*inode)++)//(inode).i_pipe->writer++wake_up_partner(inode);//如果读端已经打开,那么写端的打开就完成了命名管道的建立过程。在这种情况下读端的进程,也就是"消费者"进程,一般都是正在睡眠中,等待着命名管道建立过程的完成,所以要把它唤醒if (!PIPE_READERS(*inode)) {//(inode).i_pipe->readerwait_for_partner(inode, &PIPE_RCOUNTER(*inode));//如果命名管道的读端尚未打开,写端的打开只是完成了命名管道建立过程的一半,所以"生产者"进程要通过wait_for_partner()进入睡眠,等待某个"消费者"进程来打开命名管道的读端以完成其建立过程if (signal_pending(current))goto err_wr;}break;case 3:/* *  O_RDWR *  POSIX.1 leaves this case "undefined" when O_NONBLOCK is set. *  This implementation will NEVER block on a O_RDWR open, since *  the process can at least talk to itself. */filp->f_op = &rdwr_fifo_fops;PIPE_READERS(*inode)++;PIPE_WRITERS(*inode)++;PIPE_RCOUNTER(*inode)++;PIPE_WCOUNTER(*inode)++;if (PIPE_READERS(*inode) == 1 || PIPE_WRITERS(*inode) == 1)wake_up_partner(inode);break;default:ret = -EINVAL;goto err;}/* Ok! */up(PIPE_SEM(*inode));unlock_kernel();return 0;err_rd:if (!--PIPE_READERS(*inode))wake_up_interruptible(PIPE_WAIT(*inode));ret = -ERESTARTSYS;goto err;err_wr:if (!--PIPE_WRITERS(*inode))wake_up_interruptible(PIPE_WAIT(*inode));ret = -ERESTARTSYS;goto err;err:if (!PIPE_READERS(*inode) && !PIPE_WRITERS(*inode)) {struct pipe_inode_info *info = inode->i_pipe;inode->i_pipe = NULL;free_page((unsigned long)info->base);kfree(info);}err_nocleanup:up(PIPE_SEM(*inode));err_nolock_nocleanup:unlock_kernel();return ret;}

0 1
原创粉丝点击