FIFO文件的操作方法只有open方法(具体实现在fs/fifo.c)。但是,这并不是fifo文件真正的操作方法,其真正的读写方法是根据不同的打开方式而决定的。
FIFO文件的打开操作
第一次打开fifo文件的进程调用fifo_open时,该命名管道的缓冲页面还没有分配,
因此43行中alloc_pipe_info()函数会被执行。
分配所需要的pipe_inode_info数据结构和缓冲页面。以后打开该文件的进程会跳过该部分。
Fifo可以以“只读”、“只写”、“读写”三种方式打开。另外,open系统调用中有flag参数,如果调用fifo_open的进程开始时设置了flag中的O_NONBLOCK参数为1,则在打开的过程中无论是否可以正常打开,进程都不能进入睡眠,必须立即返回。下面具体分析每个打开方式的不同操作。
◆ 以“只读”方式打开。即命名管道的读端的几种情况:
a) 如果命名管道的写端已经打开,那么管道的创建就完成了。这时,一般写端(生产者)一般都在睡眠,因此要调用wake_up_partner()将其唤醒。
b) 如果写端没有打开,而且设置了O_NONBLOCK标志,此时尽管读端已经打开,但是没有完成管道的打开,由于进程要求不能等待,因此必须立即返回。
c) 写读没有打开,但是没有设置O_NONBLOCK标志,读进程调用wait_for_partner()函数,进入睡眠状态等待写读打开后将其唤醒。
◆ 以“只写”方式打开。即打开命名管道写端的几种情况:
a) 如果命名管道的读端没有打开,并且设置了O_NONBLOCK标志,写端进程就要直接跳转到err处(判断是否有读进程或写进程在睡眠,如果没有就释放pipe_inode_info)执行。否则,让filp的f_op指向write_pipefifo_fops方法。然后管道的写进程计数加1
b) 如果命名管道的读端已经打开,那么写端就完成了命名管道的打开。此时,读端一般都在睡眠等待,应该调用wake_up_partner()将其唤醒。
c) 如果命名管道读端没有打开,那么写端就要调用wait_for_partner()进入睡眠等待,直到读端打开,将其唤醒之后才能返回。
◆ 以“读写”方式打开
读写的方式打开命名管道,相当于同一个进程打开了命名管道的两端,因此不需要等待。但是,也有可能已经有进程已经打开了某一端正在睡眠等待,因此,任意一端第一次打开,就唤醒了正在睡眠的进程。这种打开方式下,真正的操作方法是rdwr_pipefifo_fops。
命名管道一旦建立,以后的读写以及关闭都与普通管道相同。尽管FIFO文件的inode节点是在磁盘上,但是数据只是存在于内存缓存中,与普通管道相同。
分析完FIFO文件的不同打开方式之后,接下来分析各自对应的操作方法。具体实现在
Fs/pipe.c文件中。
首先,我们可以看到在三个操作方法中llseek都调用的是no_llseek。
从代码中,可以发现no_llseek并没有做任何事情,只是在被调用的时候返回错误代码。也就是在FIFO文件中是不能使用seek方法的,对文件的读写只能根据先进先出的顺序进行访问。
接下来,在read_pipefifo_fops和write_pipefifo_fops中分别调用了bad_pipe_w和bad_pipe_r函数:
从代码中,可以看到它们也只是返回错误码,也就是说在pipe中读端写操作是禁止的,在写端读操作同样也是禁止的。