📄 041_fs_fifo_c_fs_pipe_c.html
字号:
top: 0; } div.google_footer { flow: static(footer); } /* always consider this element at the start of the doc */ div#google_footer { flow: static(footer, start); } span.google_pagenumber { content: counter(page); } span.google_pagecount { content: counter(pages); } } @page { @top { content: flow(header); } @bottom { content: flow(footer); } } /* end default print css */ /* custom css *//* end custom css */ /* ui edited css */ body { font-family: Verdana; font-size: 10.0pt; line-height: normal; background-color: #ffffff; } .documentBG { background-color: #ffffff; } /* end ui edited css */</style> </head> <body revision="dcbsxfpf_293gmsqdn7:199"> <div align=center>
<table align=center border=0 cellpadding=0 cellspacing=0 height=5716 width=802>
<tbody>
<tr>
<td height=5716 valign=top width=802>
<pre>2007-12-17 <br><br><font size=5><b>1.概述</b></font><br>fs/fifo.c fs/pipe.c 组成了一个模块,一个文件系统, pipe. 也是一个例子,可以从中窥得,一个文件系统应该实现的基本接口.<br>和VFS系统如何挂接.<br><br>pipe和fifo都是一个先进先出的队列,用于进程间通讯. pipe对于文件系统来说,就是一个"不完整实现的文件系统"而已. 也不能通<br>过open打开一个pipe. 只能通过pipe系统调用来获取两个fd. 这两个打开的"文件", 属于"pipefs".<br>asmlinkage int <font color=#000099><b>sys_pipe</b></font>(unsigned long * fildes)<br>{<br> int fd[2];<br> int error;<br><br> error = <font color=#3333ff><b>do_pipe</b></font>(fd);<br> if (!error) {<br> if (copy_to_user(fildes, fd, 2*sizeof(int)))<br> error = -EFAULT;<br> }<br> return error;<br>}<br><br>pipe的这个特点决定了pipe只能用于有亲属关系的两个进程之间. 两个不相干的进程之间就有问题:先是这个fd如何传递到另外一个<br>进程然后是,这个fd并没有安装到另外一个进程的文件表中.....<br> 然后(上帝说要有名字),然后就有了fifo,命名个管道. 所谓命名的的管道,就是说你能够通过字符串open一个pipe, 然后pipe就可<br>以在两个进程之间作为传递消息的通道了.<br><br><font size=5><b>2.fifo</b></font><br> 而fifo的使用上也有些特殊:首先用mkfifo或者mknode创建一个文件,这个文件指明了S_IFIFO属性....., 然后你open一个文件的<br>时候,我们又看到了一个熟悉的东西:<br>void <font color=#000099><b>init_special_inode</b></font>(struct inode *inode, umode_t mode, int rdev)<br>{<br> inode->i_mode = mode;<br> if (S_ISCHR(mode)) {<br> inode->i_fop = &def_chr_fops;<br> inode->i_rdev = to_kdev_t(rdev);<br> } else if (S_ISBLK(mode)) {<br> inode->i_fop = &def_blk_fops;<br> inode->i_rdev = to_kdev_t(rdev);<br> inode->i_bdev = bdget(rdev);<br> } else if (<font color=#cc0000>S_ISFIFO(mode)</font>)<br> <font color=#3333ff>inode->i_fop </font>= &<font color=#3333ff><b>def_fifo_fops</b></font>; <font color=#3333ff>/*通过这个open,我们就可以打开一个pipe了....*/</font><br> else if (S_ISSOCK(mode))<br> inode->i_fop = &bad_sock_fops;<br> else<br> printk(KERN_DEBUG "init_special_inode: bogus imode (%o)\n", mode);<br>}<br><br><br>具体的流程是这样的:<br>1) mknode/mkfifo 创建了S_IFIFO的fifo文件<br>2) open 这个fifo<br> a)sys_open->filp_open->open_namei->path_walk->real_lookup->dir->i_op->lookup->ext2_lookup(or other)-><br> iget->iget4->get_new_inode->sb->s_op->read_inode(inode);//ext2_read_inode<b> ->init_special_inode</b> <br> b)现在inode->i_fop是<font color=#3333ff><b>def_fifo_fops->fifo_open</b></font><br> c)sys_open->filp_open-><font color=#3333ff><b>dentry_open</b><font color=#000000><b>-> </b></font><font color=#000000>f->f_op = fops_get(inode->i_fop)</font><font color=#000000><b>; </b></font><font color=#000000>f->f_op->open(inode,f)<br><b> -></b></font></font><font color=#3333ff><font color=#000099><b>fifo_open</b></font><br> <font color=#000000> d)fifo_open会替换掉f->f_op为以下之一:<font color=#000099><b>read_fifo_fops</b> <b>write_fifo_fops</b> <b>rdwr_fifo_fops<br></b><font color=#000000>3)奇怪的是之后,不会再调用这个已经打开的文件的f_op->open了,相应的,以上函数接口表中相关的open函数也是没<br>有作用的.(fix me)<br><br><br><font size=5><b>3. 从pipe看特殊文件系统<br></b></font></font></font></font></font><font color=#3333ff><font color=#000000><font color=#000099><font color=#000000><font color=#3333ff><br><font color=#000000>在分析shmem.c的时候已经分析过tempfs,而这里的pipefs更为简单.声明文件系统类型有两个宏,一个是<br>#define DECLARE_FSTYPE_DEV(var,type,read) \<br> DECLARE_FSTYPE(var,type,read,<font color=#990000><b>FS_REQUIRES_DEV</b></font>)<br>这个标记在do_mount的时候用于区分是否是一个普通fs还是像proc/tempfs/pipefs这样的特殊文件系统. 根据</font></font></font></font></font></font><br><font color=#3333ff><font color=#000000><font color=#000099><font color=#000000>FS_SINGLE有可以有两种类型的FS.指定了single的有pipefs和procfs.这样的文件系统在安装到用户文件namespace的<br>时候拥有在这之前内核所建立起的一切结构. proc正是这样一种目的. 而这里的pipefs则是无法在用户的namespace里<br>安装的.<br><font size=3><br></font></font></font></font></font><font color=#3333ff><font color=#000000><font color=#000099><font color=#000000><font color=#3333ff><font size=3>1) 实现文件系统的第一步是声明一个fs type:</font><br></font>static DECLARE_FSTYPE(pipe_fs_type, "pipefs", pipefs_read_super,<br> FS_NOMOUNT|FS_SINGLE);<br>FS_NOMOUNT: 不准对其进行mount操作, 见do_mount. 只能kernel mount了.用户无法通过用户接口看到个个文件<br>系统.完全是"fly"在内核空间的一个安装.<br>FS_SINGLE: mount的时候就是用 fs_type->kern_mnt->mnt_sb那个sb,就是多次安装使用一个sb.(都不能安装了<br>这个标记的作用也就基本没有了.<br><br><font color=#3333ff size=3>2) 然后初始化的时候注册文件系统,进行kernel mount</font><br>static int __init init_pipe_fs(void)<br>{<br> int err = register_filesystem(&pipe_fs_type);<br> if (!err) {<br> pipe_mnt = kern_mount(&pipe_fs_type);<br> err = PTR_ERR(pipe_mnt);<br> if (IS_ERR(pipe_mnt))<br> unregister_filesystem(&pipe_fs_type);<br> else<br> err = 0;<br> }<br> return err;<br>}<br>kern_mount在分析shmem.c的时候已经详细说过.kern mount建立了一个以fs_type->kern_mnt->mnt_sb为根的文件系统树. <br>但是用户却不可达(ls/open都不行,就是pathwalk找不到的). <br>文件系统本身就是这颗树的所有操作,当内核实现pipe_fs的时候,总要适应各个模块直接的接口: inode, super block,dentry, mnt<br>都是以这颗树为假象的情景. 不建立这颗树实难进行各种操作.比如 do_pipe,需要d_alloc这个接口, 如要为此准备参数... 还有,通<br>过系统的open/ls/read/write写文件的时候,到处都是对这个棵树的各种操作, 故而,需要这个kern_mount,需要有一棵这样的树存在.<br><br>static struct super_block * <font color=#3333ff><b>pipefs_read_super</b></font>(struct super_block *sb, void *data, int silent)<br><font size=3><br></font><font color=#cc0000 size=3>3)pipe文件系统特性分析</font><br>但是,具体到pipe,情景是这样的:<br> 1)通过kern_mount建立了一颗这样的树, 但是不能mount,也就不能为用户所见<br> 2)只有sb->statfs, f_ops->read, write, poll,close,这几个有效操作.<br> 3)创建方式通过</font></font></font></font><font color=#000099><b>sys_pipe</b></font><font color=#3333ff><font color=#000000><font color=#000099><font color=#000000>这个系统调用,秘密的为用户crate并open一个pipefs的文件,安装到用户的fd数组中. <br> 4)以上种种,仅仅是为了适应文件系统的要求,最后只是在inode上建立了一个页面为缓冲区的buffer,通过read write进行通讯.<br> 这个缓冲区描述符inode->i_pipe确实是建立在pipefs这个kern mount的文件系统上的:即inode是pipefs的inode.<br></font></font></font></font><font color=#3333ff><font color=#000000><font color=#000099><font color=#000000><br><font color=#3333ff size=3>4)pipe 的read/write以及其他操作</font><br> 如果理解了pipe和文件系统的接口,剩下的代码的阅读实......<br><br>int do_pipe(int *fd) <font color=#3333ff>/*create 和open的一揽子操作,并且创建两个fd(dup)*/</font><br>{<br> ..... <font color=#339999>//创建连个file 结构</font><br> f1 = get_empty_filp();<br> f2 = get_empty_filp();<br> .... <font color=#339999> //分配一个inode</font><br> inode = get_pipe_inode();<br> ... <font color=#339999>//找两个fd</font><br> error = get_unused_fd();<br> error = get_unused_fd();<br> <br> <font color=#339999> //分配一个dentry</font><br> error = -ENOMEM;<br> sprintf(name, "[%lu]", inode->i_ino);<br> ....<br> dentry = d_alloc(pipe_mnt->mnt_sb->s_root, &this);<br> ...<br> dentry->d_op = &<font color=#663366><b>pipefs_dentry_operations</b></font>; <font color=#3333ff>/*假操作,只有一个假的delete*/</font><br> /*建立文件树的种种联系....*/<br> d_add(dentry, inode);<br> f1->f_vfsmnt = f2->f_vfsmnt = mntget(mntget(pipe_mnt));<br> f1->f_dentry = f2->f_dentry = dget(dentry);<br><br> /* read file */<br> f1->f_pos = f2->f_pos = 0;<br> f1->f_flags = O_RDONLY;<br> f1->f_op = &read_pipe_fops;<br> f1->f_mode = 1;<br> f1->f_version = 0;<br><br> /* write file */<br> f2->f_flags = O_WRONLY;<br> fd_install(i, f1);<br> fd_install(j, f2);<br> fd[0] = i;<br> fd[1] = j;<br> return 0;<br> ...........<br>}<br><br>struct inode* pipe_new(struct inode* inode) <font color=#3333ff>/*分配并初始化inode上的i_pipe*/</font><br><br><br>static ssize_t<br><font color=#000099><b>pipe_read</b></font>(struct file *filp, char *buf, size_t count, loff_t *ppos)<br>{<br> .......<br> if (down_interruptible(PIPE_SEM(*inode)))<br> goto out_nolock;<br><br> if (PIPE_EMPTY(*inode)) { <font color=#3333ff>//如果缓冲区为空,就要等待写进程,除非为nonblock调用</font><br>do_more_read:<br> ret = 0;<br> ....<br> if (filp->f_flags & O_NONBLOCK)<br> goto out;<br><br> for (;;) {<br> PIPE_WAITING_READERS(*inode)++;<br> pipe_wait(inode);<br> PIPE_WAITING_READERS(*inode)--;<br> ret = -ERESTARTSYS;<br> if (<font color=#3333ff><b>signal_pending</b></font>(current)) <font color=#3333ff>//内核到处有check signal的地方</font><br> goto out;<br> ret = 0;<br> if (!PIPE_EMPTY(*inode))<br> break;<br> if (!PIPE_WRITERS(*inode))<br> goto out;<br> }<br> }<br><br> <font color=#3333ff>/* Read what data is available. */ /*不多说了*/</font><br> .............<br>}<br><br><font color=#3333ff size=3><b>5) pipe poll 和select</b></font><br>需要多说两句的是这个,poll调用.<br></font>static unsigned int <b>pipe_poll</b>(struct file *filp, poll_table *wait)<br>{<br> <font color=#000000>unsigned int mask;<br> struct inode *inode = filp->f_dentry->d_inode;<br><br> poll_wait(filp, PIPE_WAIT(*inode), wait); /*先不看这个函数*/<br><br> /* Reading only -- no need for acquiring the semaphore. */<br> mask = POLLIN | POLLRDNORM;<font color=#3333ff> /*数据可读出*/</font><br> if (PIPE_EMPTY(*inode))<br> mask = POLLOUT | POLLWRNORM; <font color=#3333ff>/*可以做写操作*/</font><br> if (!PIPE_WRITERS(*inode) && filp->f_version != PIPE_WCOUNTER(*inode))<br> mask |= POLLHUP; <font color=#3333ff>/*写操作hang up,给fifo准备的,pipe的writers总是1*/</font><br> if (!PIPE_READERS(*inode))<br> mask |= POLLERR; <font color=#3333ff>/*无读取者,出错-:)*/</font><br><br> return mask;<br>}<br>f_ops->poll是为select系统调用和 poll系统调用准备的. 这里简单说下select 和这个 pipe poll的关系:<br>1) pipe里有个wait queue, 当需要时唤醒对应进程<br>2) select 通过调用各个文件的poll函数: <br> a)把自己加入指定的唤醒队列,这里就是pipe的一个wait queue,<br> b)通过poll来的状态,决定是否可以返回(有可读写的fd,或者出错)<br>3)如果select 决定等待,有相关事件的时候,就会有相应的通知来唤醒select.<br> <br><br>想的参考资料:<br>man poll<br><br> POLLIN There is data to read.<br><br> POLLPRI<br> There is urgent data to read (e.g., out-of-band data on TCP socket;<br> pseudo-terminal master in packet mode has seen state change in<br> slave).<br><br> POLLOUT<br> Writing now will not block.<br><br> POLLRDHUP (since Linux 2.6.17)<br> Stream socket peer closed connection, or shut down writing half of<br> connection. The _GNU_SOURCE feature test macro must be defined in<br> order to obtain this definition.<br><br> POLLERR<br> Error condition (output only).<br><br> POLLHUP<br> Hang up (output only).<br><br> POLLNVAL<br> Invalid request: fd not open (output only).<br><br>When compiling with _XOPEN_SOURCE defined, one also has the following, which convey<br>no further information beyond the bits listed above:<br> POLLRDNORM<br> Equivalent to POLLIN.<br><br> POLLRDBAND<br> Priority band data can be read (generally unused on Linux).<br><br> POLLWRNORM<br> Equivalent to POLLOUT.<br><br> POLLWRBAND<br> Priority data may be written.<br><br>iBCS2:<br></font></font></font></font>Work has been progressing on an emulator for Microsoft Windows<br>binaries. ("Can Linux Run Microsoft Windows Programs?")<br><br>iBCS2 (Intel Binary Compatibility Standard) emulator code for SVR4 ELF<br>and SVR3.2 COFF binaries can be included in the kernel as a<br>compile-time option. There is information at<br>ftp://tsx-11.mit.edu/pub/linux/BETA/ibcs2/.<br><br><br><font size=5><b>4)从fifo看pipefs</b></font><br> <br> 比起pipefs, fifo就不是一个文件系统了. 并且和pipefs也基本没有关系:(当然和pipe有关系了) <br>1) fifo 文件不在pipefs中<br> fifo使用过程是在一个文件系统使用mknode创建一个特殊文件,指明这是一个pipe.所以打开的时候调用的是fifo_open, 和pipefs是<br>没有关系的.<br>2)fifo仅仅是一个文件,他要依附于另一个文件系统,只是一中特殊的文件,special的inode.<br><br><br><br><font size=5><b>5) 其他说明</b></font><br> 至于具体的队列操作,并不是很难. 但是有些问题:<br>1)PIPE_READERS 和 PIPE_RCOUNTER(*inode) 啥区别?<br> 搜索知道,readers是真正的打开这这个fifo的文件的用户数, 而r_counter则只增不减.即便是打开后又马上关闭.<br>static int fifo_open(struct inode *inode, struct file *filp)<br>{ ......<br> switch (filp->f_mode) {<br> case 1:<br> /*<br> * O_RDONLY<br> * POSIX.1 says that O_NONBLOCK means return with the FIFO<br> * opened, even when there is no process writing the FIFO.<br> */<br> filp->f_op = &read_fifo_fops;<br> <b>PIPE_RCOUNTER(*inode)++;</b><br> if (PIPE_READERS(*inode)++ == 0)<br> wake_up_partner(inode);<br><br> if (!PIPE_WRITERS(*inode)) {<br> if ((filp->f_flags & O_NONBLOCK)) {<br> /* suppress POLLHUP until we have<br> * seen a writer */<br> filp->f_version = PIPE_WCOUNTER(*inode);<br> } else <br> {<br> <font color=#3333ff><b>wait_for_partner(inode, &PIPE_WCOUNTER(*inode));/*w_count变化后才被唤醒*/</b></font><br> if(signal_pending(current))<br> goto err_rd;<br> }<br> }<br> break;<br> .........<br>}<br>考虑这样一种情况:打开fifo又马上关闭,这个等待的进程就会永远的wait下去(不考虑signal), 这样才需要一个r_counter,只是readers是<br>不够的.<br>2)PIPE_WRITERS 和 PIPE_WCOUNTER(*inode)<br> 同上.<br>3)pipe_rdwr_open / pipe_read_open这些open好像不可达....<br><br></pre>
</td>
</tr>
<tr>
<td valign=top>
<br>
</td>
</tr>
<tr>
<td valign=top>
<br>
</td>
</tr>
</tbody>
</table>
</div></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -