pipe_read函数分析
pipe_read函数逻辑 相对简单,这里只关注内部实现,暂时忽略copy_page_to_iter函数实现:
/* 对管道上锁,避免竞态 */
__pipe_lock(pipe);
for (;;) {
/* 管道当前可读的数据缓冲区数量;这里的for循环就是需要把缓冲区的数据全部读取出来存放到用户指定的buf */
int bufs = pipe->nrbufs;
if (bufs) {
/* 读取到第几个数据缓冲区 */
int curbuf = pipe->curbuf;
/* 获取第curbuf个缓冲区的地址 */
struct pipe_buffer *buf = pipe->bufs + curbuf;
size_t chars = buf->len;
size_t written;
int error;
/* 如果缓冲区数据长度大于用户缓冲区的长度,就把读取的长度设置为用户缓冲区的长度 */
if (chars > total_len)
chars = total_len;
/* 确认管道数据是否可读 */
error = pipe_buf_confirm(pipe, buf);
if (error) {
if (!ret)
ret = error;
break;
}
/* 将数据拷贝到用户空间缓冲区 */
written = copy_page_to_iter(buf->page, buf->offset, chars, to);
if (unlikely(written < chars)) {
if (!ret)
ret = -EFAULT;
break;
}
ret += chars;
/* 读取成功后,修改buf的偏移和剩余长度 */
buf->offset += chars;
buf->len -= chars;
/* Was it a packet buffer? Clean up and exit */
if (buf->flags & PIPE_BUF_FLAG_PACKET) {
total_len = chars;
buf->len = 0;
}
if (!buf->len) {
/* 如果buf数据都读取完毕,先释放内存 */
pipe_buf_release(pipe, buf);
/* (curbuf + 1) & (pipe->buffers - 1)目的是为了防止溢出!!! */
curbuf = (curbuf + 1) & (pipe->buffers - 1);
/* 更新curbuf和nrbufs的实际值 */
pipe->curbuf = curbuf;
pipe->nrbufs = --bufs;
/* 数据读取完毕,可以唤醒等待队列中的其他进程 */
do_wakeup = 1;
}
total_len -= chars;
if (!total_len)
break; /* common path: read succeeded */
}
/* 如果还有未读完的数据缓冲区,就继续读取;因为后面是唤醒和等待新的数据 */
if (bufs) /* More to do? */
continue;
/* 如果没有写端,可以直接退出循环了 */
if (!pipe->writers)
break;
if (!pipe->waiting_writers) {
/* syscall merging: Usually we must not sleep
* if O_NONBLOCK is set, or if we got some data.
* But if a writer sleeps in kernel space, then
* we can wait for that data without violating POSIX.
*/
if (ret)
break;
if (filp->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
break;
}
}
if (signal_pending(current)) {
if (!ret)
ret = -ERESTARTSYS;
break;
}
/* 唤醒等待队列的进程 */
if (do_wakeup) {
wake_up_interruptible_sync_poll(&pipe->wait, EPOLLOUT | EPOLLWRNORM);
kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT);
}
/* 等待新的数据到来,被唤醒 */
pipe_wait(pipe);
}
__pipe_unlock(pipe);

浙公网安备 33010602011771号