linux 有名管道FIFO

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)

你可能感兴趣的:(linux 有名管道FIFO)