2007-12-17   

1.概述
fs/fifo.c fs/pipe.c 组成了一个模块,一个文件系统, pipe. 也是一个例子,可以从中窥得,一个文件系统应该实现的基本接口.
和VFS系统如何挂接.

pipe和fifo都是一个先进先出的队列,用于进程间通讯. pipe对于文件系统来说,就是一个"不完整实现的文件系统"而已. 也不能通
过open打开一个pipe. 只能通过pipe系统调用来获取两个fd. 这两个打开的"文件", 属于"pipefs".
asmlinkage int sys_pipe(unsigned long * fildes)
{
int fd[2];
int error;

error = do_pipe(fd);
if (!error) {
if (copy_to_user(fildes, fd, 2*sizeof(int)))
error = -EFAULT;
}
return error;
}

pipe的这个特点决定了pipe只能用于有亲属关系的两个进程之间. 两个不相干的进程之间就有问题:先是这个fd如何传递到另外一个
进程然后是,这个fd并没有安装到另外一个进程的文件表中.....
然后(上帝说要有名字),然后就有了fifo,命名个管道. 所谓命名的的管道,就是说你能够通过字符串open一个pipe, 然后pipe就可
以在两个进程之间作为传递消息的通道了.

2.fifo
而fifo的使用上也有些特殊:首先用mkfifo或者mknode创建一个文件,这个文件指明了S_IFIFO属性....., 然后你open一个文件的
时候,我们又看到了一个熟悉的东西:
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; /*通过这个open,我们就可以打开一个pipe了....*/
else if (S_ISSOCK(mode))
inode->i_fop = &bad_sock_fops;
else
printk(KERN_DEBUG "init_special_inode: bogus imode (%o)\n", mode);
}


具体的流程是这样的:
1) mknode/mkfifo 创建了S_IFIFO的fifo文件
2) open 这个fifo
a)sys_open->filp_open->open_namei->path_walk->real_lookup->dir->i_op->lookup->ext2_lookup(or other)->
iget->iget4->get_new_inode->sb->s_op->read_inode(inode);//ext2_read_inode ->init_special_inode
b)现在inode->i_fop是def_fifo_fops->fifo_open
c)sys_open->filp_open->dentry_open-> f->f_op = fops_get(inode->i_fop); f->f_op->open(inode,f)
->
fifo_open
d)fifo_open会替换掉f->f_op为以下之一:read_fifo_fops write_fifo_fops rdwr_fifo_fops
3)奇怪的是之后,不会再调用这个已经打开的文件的f_op->open了,相应的,以上函数接口表中相关的open函数也是没
有作用的.(fix me)


3. 从pipe看特殊文件系统

在分析shmem.c的时候已经分析过tempfs,而这里的pipefs更为简单.声明文件系统类型有两个宏,一个是
#define DECLARE_FSTYPE_DEV(var,type,read) \
DECLARE_FSTYPE(var,type,read,FS_REQUIRES_DEV)
这个标记在do_mount的时候用于区分是否是一个普通fs还是像proc/tempfs/pipefs这样的特殊文件系统. 根据

FS_SINGLE有可以有两种类型的FS.指定了single的有pipefs和procfs.这样的文件系统在安装到用户文件namespace的
时候拥有在这之前内核所建立起的一切结构. proc正是这样一种目的. 而这里的pipefs则是无法在用户的namespace里
安装的.

1) 实现文件系统的第一步是声明一个fs type:
static DECLARE_FSTYPE(pipe_fs_type, "pipefs", pipefs_read_super,
FS_NOMOUNT|FS_SINGLE);
FS_NOMOUNT: 不准对其进行mount操作, 见do_mount. 只能kernel mount了.用户无法通过用户接口看到个个文件
系统.完全是"fly"在内核空间的一个安装.
FS_SINGLE: mount的时候就是用 fs_type->kern_mnt->mnt_sb那个sb,就是多次安装使用一个sb.(都不能安装了
这个标记的作用也就基本没有了.

2) 然后初始化的时候注册文件系统,进行kernel mount
static int __init init_pipe_fs(void)
{
int err = register_filesystem(&pipe_fs_type);
if (!err) {
pipe_mnt = kern_mount(&pipe_fs_type);
err = PTR_ERR(pipe_mnt);
if (IS_ERR(pipe_mnt))
unregister_filesystem(&pipe_fs_type);
else
err = 0;
}
return err;
}
kern_mount在分析shmem.c的时候已经详细说过.kern mount建立了一个以fs_type->kern_mnt->mnt_sb为根的文件系统树.
但是用户却不可达(ls/open都不行,就是pathwalk找不到的).
文件系统本身就是这颗树的所有操作,当内核实现pipe_fs的时候,总要适应各个模块直接的接口: inode, super block,dentry, mnt
都是以这颗树为假象的情景. 不建立这颗树实难进行各种操作.比如 do_pipe,需要d_alloc这个接口, 如要为此准备参数... 还有,通
过系统的open/ls/read/write写文件的时候,到处都是对这个棵树的各种操作, 故而,需要这个kern_mount,需要有一棵这样的树存在.

static struct super_block * pipefs_read_super(struct super_block *sb, void *data, int silent)

3)pipe文件系统特性分析
但是,具体到pipe,情景是这样的:
1)通过kern_mount建立了一颗这样的树, 但是不能mount,也就不能为用户所见
2)只有sb->statfs, f_ops->read, write, poll,close,这几个有效操作.
3)创建方式通过
sys_pipe这个系统调用,秘密的为用户crate并open一个pipefs的文件,安装到用户的fd数组中.
4)以上种种,仅仅是为了适应文件系统的要求,最后只是在inode上建立了一个页面为缓冲区的buffer,通过read write进行通讯.
这个缓冲区描述符inode->i_pipe确实是建立在pipefs这个kern mount的文件系统上的:即inode是pipefs的inode.

4)pipe 的read/write以及其他操作
如果理解了pipe和文件系统的接口,剩下的代码的阅读实......

int do_pipe(int *fd) /*create 和open的一揽子操作,并且创建两个fd(dup)*/
{
..... //创建连个file 结构
f1 = get_empty_filp();
f2 = get_empty_filp();
.... //分配一个inode
inode = get_pipe_inode();
... //找两个fd
error = get_unused_fd();
error = get_unused_fd();

//分配一个dentry
error = -ENOMEM;
sprintf(name, "[%lu]", inode->i_ino);
....
dentry = d_alloc(pipe_mnt->mnt_sb->s_root, &this);
...
dentry->d_op = &pipefs_dentry_operations; /*假操作,只有一个假的delete*/
/*建立文件树的种种联系....*/
d_add(dentry, inode);
f1->f_vfsmnt = f2->f_vfsmnt = mntget(mntget(pipe_mnt));
f1->f_dentry = f2->f_dentry = dget(dentry);

/* read file */
f1->f_pos = f2->f_pos = 0;
f1->f_flags = O_RDONLY;
f1->f_op = &read_pipe_fops;
f1->f_mode = 1;
f1->f_version = 0;

/* write file */
f2->f_flags = O_WRONLY;
fd_install(i, f1);
fd_install(j, f2);
fd[0] = i;
fd[1] = j;
return 0;
...........
}

struct inode* pipe_new(struct inode* inode) /*分配并初始化inode上的i_pipe*/


static ssize_t
pipe_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
{
.......
if (down_interruptible(PIPE_SEM(*inode)))
goto out_nolock;

if (PIPE_EMPTY(*inode)) { //如果缓冲区为空,就要等待写进程,除非为nonblock调用
do_more_read:
ret = 0;
....
if (filp->f_flags & O_NONBLOCK)
goto out;

for (;;) {
PIPE_WAITING_READERS(*inode)++;
pipe_wait(inode);
PIPE_WAITING_READERS(*inode)--;
ret = -ERESTARTSYS;
if (signal_pending(current)) //内核到处有check signal的地方
goto out;
ret = 0;
if (!PIPE_EMPTY(*inode))
break;
if (!PIPE_WRITERS(*inode))
goto out;
}
}

/* Read what data is available. */ /*不多说了*/
.............
}

5) pipe poll 和select
需要多说两句的是这个,poll调用.
static unsigned int pipe_poll(struct file *filp, poll_table *wait)
{
unsigned int mask;
struct inode *inode = filp->f_dentry->d_inode;

poll_wait(filp, PIPE_WAIT(*inode), wait); /*先不看这个函数*/

/* Reading only -- no need for acquiring the semaphore. */
mask = POLLIN | POLLRDNORM; /*数据可读出*/
if (PIPE_EMPTY(*inode))
mask = POLLOUT | POLLWRNORM; /*可以做写操作*/
if (!PIPE_WRITERS(*inode) && filp->f_version != PIPE_WCOUNTER(*inode))
mask |= POLLHUP; /*写操作hang up,给fifo准备的,pipe的writers总是1*/
if (!PIPE_READERS(*inode))
mask |= POLLERR; /*无读取者,出错-:)*/

return mask;
}
f_ops->poll是为select系统调用和 poll系统调用准备的. 这里简单说下select 和这个 pipe poll的关系:
1) pipe里有个wait queue, 当需要时唤醒对应进程
2) select 通过调用各个文件的poll函数:
a)把自己加入指定的唤醒队列,这里就是pipe的一个wait queue,
b)通过poll来的状态,决定是否可以返回(有可读写的fd,或者出错)
3)如果select 决定等待,有相关事件的时候,就会有相应的通知来唤醒select.


想的参考资料:
man poll

POLLIN There is data to read.

POLLPRI
There is urgent data to read (e.g., out-of-band data on TCP socket;
pseudo-terminal master in packet mode has seen state change in
slave).

POLLOUT
Writing now will not block.

POLLRDHUP (since Linux 2.6.17)
Stream socket peer closed connection, or shut down writing half of
connection. The _GNU_SOURCE feature test macro must be defined in
order to obtain this definition.

POLLERR
Error condition (output only).

POLLHUP
Hang up (output only).

POLLNVAL
Invalid request: fd not open (output only).

When compiling with _XOPEN_SOURCE defined, one also has the following, which convey
no further information beyond the bits listed above:
POLLRDNORM
Equivalent to POLLIN.

POLLRDBAND
Priority band data can be read (generally unused on Linux).

POLLWRNORM
Equivalent to POLLOUT.

POLLWRBAND
Priority data may be written.

iBCS2:
Work has been progressing on an emulator for Microsoft Windows
binaries. ("Can Linux Run Microsoft Windows Programs?")

iBCS2 (Intel Binary Compatibility Standard) emulator code for SVR4 ELF
and SVR3.2 COFF binaries can be included in the kernel as a
compile-time option. There is information at
ftp://tsx-11.mit.edu/pub/linux/BETA/ibcs2/.


4)从fifo看pipefs

比起pipefs, fifo就不是一个文件系统了. 并且和pipefs也基本没有关系:(当然和pipe有关系了)
1) fifo 文件不在pipefs中
fifo使用过程是在一个文件系统使用mknode创建一个特殊文件,指明这是一个pipe.所以打开的时候调用的是fifo_open, 和pipefs是
没有关系的.
2)fifo仅仅是一个文件,他要依附于另一个文件系统,只是一中特殊的文件,special的inode.



5) 其他说明
至于具体的队列操作,并不是很难. 但是有些问题:
1)PIPE_READERS 和 PIPE_RCOUNTER(*inode) 啥区别?
搜索知道,readers是真正的打开这这个fifo的文件的用户数, 而r_counter则只增不减.即便是打开后又马上关闭.
static int fifo_open(struct inode *inode, struct file *filp)
{ ......
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)++;
if (PIPE_READERS(*inode)++ == 0)
wake_up_partner(inode);

if (!PIPE_WRITERS(*inode)) {
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));/*w_count变化后才被唤醒*/
if(signal_pending(current))
goto err_rd;
}
}
break;
.........
}
考虑这样一种情况:打开fifo又马上关闭,这个等待的进程就会永远的wait下去(不考虑signal), 这样才需要一个r_counter,只是readers是
不够的.
2)PIPE_WRITERS 和 PIPE_WCOUNTER(*inode)
同上.
3)pipe_rdwr_open / pipe_read_open这些open好像不可达....