linux文件主要有4种:普通文件、目录文件、设备文件(字符设备文件/块设备文件)、管道文件
管道文件(p表示管道文件)
[redhat@localhost tmp]$ mkfifo fifo
[redhat@localhost tmp]$ ll fifo
prw-rw-r--. 1 redhat redhat 0 7月 1 22:06 fifo
文件系统(ext4/ext3/nfs)中存在管道文件,所以就通过文件名称来标识管道文件及其关联的管道FIFO;所以有名管道就可以在非亲子关系的进程间实现通信,一个进程通过文件名打开管道的写端,另一个进程通过文件名打开管道的读端,两个进程就可以通过该FIFO实现通信。
I.def_fifo_fops
def_fifo_fops表示管道文件的操作集,定义如下:
/* fs/fifo.c */
148 /*
149 * Dummy default file-operations: the only thing this does
150 * is contain the open that then fills in the correct operations
151 * depending on the access mode of the file...
152 */
153 const struct file_operations def_fifo_fops = {
154 .open = fifo_open, /* will set read_ or write_pipefifo_fops */
155 };
当打开管道文件时会触发def_fifo_fops中的open方法fifo_open,代码跟踪如下:
文件方法集:
open->do_sys_open->do_filp_open->path_lookup_open->do_path_lookup->path_walk->link_path_walk->__link_path_walk->do_lookup->real_lookup
ext4为例:
ext4_lookup->ext4_iget->init_special_inode->def_fifo_fops
open回调:
open->do_sys_open->do_filp_open->nameidata_to_filp->__dentry_open->fifo_open
II.fifo_open
18 static void wait_for_partner(struct inode* inode, unsigned int *cnt)
19 {
20 int cur = *cnt;
21
22 while (cur == *cnt) {
23 pipe_wait(inode->i_pipe);
24 if (signal_pending(current))
25 break;
26 }
27 }
28
29 static void wake_up_partner(struct inode* inode)
30 {
31 wake_up_interruptible(&inode->i_pipe->wait);
32 }
33
34 static int fifo_open(struct inode *inode, struct file *filp)
35 {
36 struct pipe_inode_info *pipe;
37 int ret;
38
39 mutex_lock(&inode->i_mutex);
40 pipe = inode->i_pipe;
41 if (!pipe) {
42 ret = -ENOMEM;
43 pipe = alloc_pipe_info(inode);
44 if (!pipe)
45 goto err_nocleanup;
46 inode->i_pipe = pipe;
47 }
48 filp->f_version = 0;
49
50 /* We can only do regular read/write on fifos */
51 filp->f_mode &= (FMODE_READ | FMODE_WRITE);
52
53 switch (filp->f_mode) {
54 case FMODE_READ:
55 /*
56 * O_RDONLY
57 * POSIX.1 says that O_NONBLOCK means return with the FIFO
58 * opened, even when there is no process writing the FIFO.
59 */
60 filp->f_op = &read_pipefifo_fops;
61 pipe->r_counter++;
62 if (pipe->readers++ == 0)
63 wake_up_partner(inode);
64
65 if (!pipe->writers) {
66 if ((filp->f_flags & O_NONBLOCK)) {
67 /* suppress POLLHUP until we have
68 * seen a writer */
69 filp->f_version = pipe->w_counter;
70 } else
71 {
72 wait_for_partner(inode, &pipe->w_counter);
73 if(signal_pending(current))
74 goto err_rd;
75 }
76 }
77 break;
78
79 case FMODE_WRITE:
80 /*
81 * O_WRONLY
82 * POSIX.1 says that O_NONBLOCK means return -1 with
83 * errno=ENXIO when there is no process reading the FIFO.
84 */
85 ret = -ENXIO;
86 if ((filp->f_flags & O_NONBLOCK) && !pipe->readers)
87 goto err;
88
89 filp->f_op = &write_pipefifo_fops;
90 pipe->w_counter++;
91 if (!pipe->writers++)
92 wake_up_partner(inode);
93
94 if (!pipe->readers) {
95 wait_for_partner(inode, &pipe->r_counter);
96 if (signal_pending(current))
97 goto err_wr;
98 }
99 break;
100
101 case FMODE_READ | FMODE_WRITE:
102 /*
103 * O_RDWR
104 * POSIX.1 leaves this case "undefined" when O_NONBLOCK is set.
105 * This implementation will NEVER block on a O_RDWR open, since
106 * the process can at least talk to itself.
107 */
108 filp->f_op = &rdwr_pipefifo_fops;
109
110 pipe->readers++;
111 pipe->writers++;
112 pipe->r_counter++;
113 pipe->w_counter++;
114 if (pipe->readers == 1 || pipe->writers == 1)
115 wake_up_partner(inode);
116 break;
117
118 default:
119 ret = -EINVAL;
120 goto err;
121 }
122
123 /* Ok! */
124 mutex_unlock(&inode->i_mutex);
125 return 0;
126
127 err_rd:
128 if (!--pipe->readers)
124 mutex_unlock(&inode->i_mutex);
125 return 0;
126
127 err_rd:
128 if (!--pipe->readers)
129 wake_up_interruptible(&pipe->wait);
130 ret = -ERESTARTSYS;
131 goto err;
132
133 err_wr:
134 if (!--pipe->writers)
135 wake_up_interruptible(&pipe->wait);
136 ret = -ERESTARTSYS;
137 goto err;
138
139 err:
140 if (!pipe->readers && !pipe->writers)
141 free_pipe_info(inode);
142
143 err_nocleanup:
144 mutex_unlock(&inode->i_mutex);
145 return ret;
146 }
147
1.获取管道互斥锁,进入管道操作临界区
2.如果文件未与管道关联,则创建管道并将管道文件与管道关联起来
3.判断文件的打开方式:
*如果是只读方式打开管道文件
A.将管道文件操作赋为read_pipefifo_fops,即只读管道不能有写管道操作,否则报错EBADF
B.管道读者计数器加1
C.管道当前读者计数器加1,如果以前没有读者,则尝试唤醒被阻塞的写者进程
D.如果管道没有写者进程
a.如果文件以NONBLOCK方式打开,则同步f_version与w_counter,用于抑制POLLHUP直到有写者出现
b.否则阻塞读者进程,直到有写者进程出现或有信号产生;如果是产生信号而被唤醒,则管道当前读者计数器减1,如果没有读者了则唤醒管道中被阻塞的进程,并返回ERESTARTSYS,内核处理完信号后再自动重启open;如果是有写者进程出现而被唤醒,则正常返回
*如果是只写方式打开管道文件
A.如果以NONBLOCK方式打开管道文件,且管道没有读者,则返回ENXIO错误
B.将管道文件操作赋为write_pipefifo_fops,即只写管道不能有读管道操作,否则报错EBADF
C.管道写者计数器加1
D.管道当前写者计数器加1,如果以前没有写者,则尝试唤醒被阻塞的读者进程
E.如果管道没有读者进程,阻塞写者进程,直到有读者进程出现或有信号产生;如果是产生信号而被唤醒,则管道当前写者计数器减1,如果没有写者了则唤醒管道中被阻塞的进程,并返回ERESTARTSYS,内核处理完信号后再自动重启open;如果是有读者进程出现而被唤醒,则正常返回
*如果是读写方式打开管道文件
B.将管道文件操作赋为rdwr_pipefifo_fops,表示管道既能写也能读
B.将管道当前读者计数器,管道当前写者计数器,管道读者计数器,管道写者计数器分别加1
C.如果以前没有读者/写者,尝试唤醒被阻塞的写者/读者进程
4.释放管道互斥锁,即出管道操作临界区
管道的read,write,poll,fcntl,ioctl操作见linux pipe文件系统(pipefs)