所谓管道么,想想家里的水管吧——自来水厂那头给我们送水,我们这头打开水龙头自来水就哗哗的流出来了,水怎么从自来水厂输送过来的?答案就是管道,当然我们这里通常称它为水管。
所以由上面定义,什么叫管道?就是从一个进程里要传东西给另一个进程,类似我们的输送自来水了,我们就借助一"根"管道,把要传的东西传了过去。
来举个小例子吧:
比如
$ ls -l | grep mbox
上
面的命令,将 ls -l的输入作为grep的输入,这种方式称为管道。这个命令原意是列出当前的信息(ls -l),然后用grep
命令在列出的当前信息中检索出含有mbox的信息。也就是说ls -l 我们通过这个命令得到的信息,然后传入到grep
mbox命令中,作为输入信息。Linux提供了很多功能强大的小命令,但使用管道将这些命令组合起来,就形成了非常强大的工具组合,能完成非常复杂的工
作。
上节说了我对Linux管道的一点儿小见解,这节来看看内核中关于管道的实现代码。
以下代码摘自 Linux/include/fs.h
#define PIPE_HEAD(inode) ((inode).i_zone[0]) //这里定义了管道头
#define PIPE_TAIL(inode) ((inode).i_zone[1]) //呃,这里定义的管道尾
管道的头尾我们都定义了,大家回头来看看管道的size 的定义:
#define PIPE_SIZE(inode) ((PIPE_HEAD(inode)-PIPE_TAIL(inode))&(PAGE_SIZE-1))
这里用 (管道头-管道尾)%pagesize 取余数 从而取得了管道大小,关于这个取余数的问题,我在《求余不用模》 中已经详细阐释了这种方法的数学原理。但是这段代码最值得探讨的倒还不是这块内容。我们来看看管道的抽象图:
假设管道大小是4,(请注意管道大小必须要选择2n这种形式),我们看看上图如果管头指针指向3,而管尾指针指向的是1,那么管道的大小按照上面的算法,我们得出管道大小为2 (即是(3-1)&3),那么我们有理由说上图管道头的指针指向的3号元素其实是空的!而我们不妨再假设如果是管头指向的是1,而管尾指向的是3,那么按照上面的做法(1-3)&3,最后得出的是2,注意这里虽然是负数但是用这种办法仍然奏效。
说了这么多,其实可能有人早就注意到了管道是个队列的数组结构,而且还是队列的一种特殊形式——循环队列。
对于这种队列,我们通常空出了一个元素位置来表达队列是否已满。比如如果rear==front
则表示管道为空,如果为满的话就不能还用这个表示了,(不然我们如何区分究竟谁是满的谁是空的),
通常是在队头空出一个位置,比如这里我们就是用rear==front+1 表示已满!
回到内核代码中,看看Linus怎么定义的:
#define PIPE_EMPTY(inode) (PIPE_HEAD(inode)==PIPE_TAIL(inode))
#define PIPE_FULL(inode) (PIPE_SIZE(inode)==(PAGE_SIZE-1))
跟我们推理的一样,为空则头和尾相等,为满则rear=
front+1,也就是(PIPE_SIZE(inode)==(PAGE_SIZE-1)
阅读(669) | 评论(0) | 转发(0) |