struct file_operations rdwr_fifo_fops = {
llseek: pipe_lseek,
read: pipe_read,
write: pipe_write,
poll: fifo_poll,
ioctl: pipe_ioctl,
open: pipe_rdwr_open,
release:pipe_rdwr_release,
};
从上面代码,对照一下用于普通管道的数据结构read_pipe_fops,write_pipe_fops和rdwr_pipe_fops就可以看出它们几乎是完全一样的。fifo_poll和pipe_poll()都用于select()系统调用,与通信机制本身没有多大关系,这里我们并不关心。所不同的是,对于普通管道虽然也定义了相当于open()的操作pipe_read_open(),pipe_write和pipe_rdwr_open(),但是这些函数实际上在典型的应用中是不使用的。普通管道是通过do_pipe()建立,通过fork()的过程延伸到两个进程之间的。对于父进程,在系统调用fork()以后就已经打开,而对于子进程来说则是与生俱来的,所以都不需要再打开。然而,命名管道就不同了参加通信的进程确实要调用这些函数来“打开通向已经建立在文件系统中的FIFO文件的通道。
既然普通管道和命名管道的不同之处仅仅在于“”打开“”的过程,那么我们来分析一下,一个进程是怎样通过open()系统调用来建立与一个已经创建的FIFO文件之间的联系的。在文件系统中,进程在内核中由sys_open()进入file_open(),然后在open_namei()中调用一个函数path_walk(),根据文件的路径名在文件系统中找到代表这个文件的inode,在将磁盘上inode读入内存时,要根据文件的类型(FIFO文件的S_FIFO标志位1),将inode中的i_op和i_fop指针设置成指向相应的inode_operations数据结构和file_operations数据结构,但是对于FIFO这样的特殊文件则调用init_special_inode()来加以初始化。
6.3对FIFO文件打开的三种模式
FIFO文件可以按照三种不同的模式打开,就是“只读”,“只写”,“读写”。同时,在系统调用open()中还有个参数flags。如果flags的标志位O_NONBLOCK为1,就表示在打开的过程中即使某些条件得不到满足也不需要等待,而应立即返回。
在典型的应用中,就相对普通管道一样,一个进程按照只读模式打开命名通道,成为“消费者”;另一个进程按照只写模式打开命名管道,成为“生产者”。可是在普通管道的情况下,管道的两端是由同一进程do_pipe()中同时打开的,而在命名管道的情况下则管道的两端通常分别由两个进程先后打开,这就有了个“同步”的问题。除此之外,还有个不同,就是普通管道既然是“无名”,“无形”的,一般就不会由另一个进程也来打开这个管道。而在命名管道的情况下,任意一个进程都可以通过相同的路径名打开同一个FIFO文件。这些因素都使建立命名管道的过程比建立普通管道的过程要复杂一些。
先来看命名管道的“读端”,也就是按照“只读”模式打开一个FIFO文件时的几种情况:
1)如果管道的写端已经打开,那么现在读端的打开就完成了命名管道的建立过程,在这种情况下,写端的进程,也就是“生产者”进程一般都是正在睡眠中,等待着命名管道建立过程的完成,所以要将其唤醒。然后,两个进程差不多同时返回各自的用户空间,然后就可以通过这个命名管道进行通信了。
2)如果命名管道的写端尚未打开,而flags中的O_NONBLOCK标志位为1,表示不应该等待。此时读端虽已打开,但是命名管道只是部分建立了(写端未打开)。而标志的使用又要求系统调用不加等待立即返回,所以不做等待。
3)如果命名管道的写端尚未打开,而flags中的O_NONBLOCK标志位为0,。在这种情况下,读端的打开只是完成了命名管道建立过程的一半,所以消费者进程要通过wait_for_parter()进入睡眠,等待某个生产者进程来打开命名管道的写端已完成其建立过程。
相应的,命名管道的写端的打开也有以下几种不同的情况(只写):
1)如果命名管道的读端已经打开,那么写端的打开就完成了命名管道的建立过程。在这种情况下,命名管道读端的进程(“消费者进程”)有可能正在睡眠中等待,所以,如果当前进程是第一次打开该管道写端的进程,就要负责将其唤醒。
2)如果命名管道的读端尚未打开,而flags中的O_NONBLOCKED标志位为0。在这种情况下,生产者进程要睡眠等待至消费者进程打开命名管道的读端才可以返回。
3)如果命名管道的读端尚未打开,而flags中的O_NONBLOCKED标志位为1。此时对命名管道写端打开失败,所以要释放已经分配的各种资源并且返回-1.
对命名管道文件“读写”打开