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);
posted @ 2025-04-24 09:42  cockpunctual  阅读(32)  评论(0)    收藏  举报