建立命名管道,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;
}