Linux内核源代码情景分析-进程间通信-命名管道
建立命名管道,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; else printk(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->writer if ((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->reader wait_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; }
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。