pipe_open源码分析
在使用open函数打开管道时,会对管道的inode节点进行内存分配:
/* 防止并发,上锁保护 */
spin_lock(&inode->i_lock);
/* 首次打开管道,需要申请内存,这里判断是否已经分配内存 */
if (inode->i_pipe) {
pipe = inode->i_pipe;
/* 每打开一次管道files就会++ */
pipe->files++;
spin_unlock(&inode->i_lock);
} else {
spin_unlock(&inode->i_lock);
/* 给管道分配内存 */
pipe = alloc_pipe_info();
if (!pipe)
return -ENOMEM;
pipe->files = 1;
spin_lock(&inode->i_lock);
if (unlikely(inode->i_pipe)) {
inode->i_pipe->files++;
spin_unlock(&inode->i_lock);
free_pipe_info(pipe);
pipe = inode->i_pipe;
} else {
inode->i_pipe = pipe;
spin_unlock(&inode->i_lock);
}
}
1.1 只读方式打开
打开fifo后,如果当前没有writer,那么会进入休眠状态,等待writer写入数据后,唤醒等待队列中的reader(以只读方式打开):
case FMODE_READ:
/*
* O_RDONLY
* POSIX.1 says that O_NONBLOCK means return with the FIFO
* opened, even when there is no process writing the FIFO.
*/
pipe->r_counter++;
/* 如果当前管道上的reader为0,唤醒writer端,让对端先写数据。 */
if (pipe->readers++ == 0)
wake_up_partner(pipe);
/* 如果writer为0,那么需要等待writer写入信息 */
if (!is_pipe && !pipe->writers) {
if ((filp->f_flags & O_NONBLOCK)) {
/* suppress EPOLLHUP until we have
* seen a writer */
filp->f_version = pipe->w_counter;
} else {
/* 如果没有writer会休眠等待 */
if (wait_for_partner(pipe, &pipe->w_counter))
goto err_rd;
}
}
break;
因此阻塞可以分为两种:
-
当打开fifo并且发现没有writer的时候,此时是没有数据的,进程会进入休眠模式等待被唤醒
-
如果有writer但是没有数据,也会进入休眠模式,等待writer写入数据后被唤醒
static void wake_up_partner(struct pipe_inode_info *pipe)
{
/* 如果pipe的wait等待队列为空,直接返回 */
wake_up_interruptible(&pipe->wait);
}
wait_for_partner函数实际上这里就是等待writer,在创建fifo的写端时,是会给pipe->w_counter进行自增操作,所以可以根据w_couter的值来判断是否有写端被创建:
static int wait_for_partner(struct pipe_inode_info *pipe, unsigned int *cnt)
{
int cur = *cnt;
/* 这里有个trick,传入的cnt是个指针(指向pipe->w_counter或者pipe->r_counter),
pipe->w_counter(pipe->r_counter)在打开管道时会根据
读写模式来修改这个值。如果没有新增的对端就会一直等待。 */
while (cur == *cnt) {
pipe_wait(pipe);
if (signal_pending(current))
break;
}
return cur == *cnt ? -ERESTARTSYS : 0;
}
1.2 只写方式打开
case FMODE_WRITE:
/*
* O_WRONLY
* POSIX.1 says that O_NONBLOCK means return -1 with
* errno=ENXIO when there is no process reading the FIFO.
*/
ret = -ENXIO;
/* 在没有reader,以只写且非阻塞打开fifo的情况下,直接报错 */
if (!is_pipe && (filp->f_flags & O_NONBLOCK) && !pipe->readers)
goto err;
pipe->w_counter++;
/* 唤醒之前没有写端而导致阻塞的读进程 */
if (!pipe->writers++)
wake_up_partner(pipe);
/* 没有reader则一直休眠等待 */
if (!is_pipe && !pipe->readers) {
if (wait_for_partner(pipe, &pipe->r_counter))
goto err_wr;
}
break;
如果以只写方式打开,那么如果没有reader,这种情况下直接报错。
1.3 读写方式打开
case FMODE_READ | FMODE_WRITE:
/*
* 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.
*/
pipe->readers++;
pipe->writers++;
pipe->r_counter++;
pipe->w_counter++;
/* 当以读写方式打开管道,reader和writer的值都为1
需要另外程序打开对端管道 */
if (pipe->readers == 1 || pipe->writers == 1)
/* 唤醒阻塞的对端 */
wake_up_partner(pipe);
break;

浙公网安备 33010602011771号