splice 和管道

splice API

splice 是一种在内核内部移动数据块的方法,无需在内核和用户空间之间不断传输数据。

ssize_t splice_to_pipe(struct pipe_inode_info *pipe, struct splice_pipe_desc *spd)

将传递的数据填充到管道中

参数

struct pipe_inode_info *pipe

要填充的管道

struct splice_pipe_desc *spd

要填充的数据

描述

spd 包含页面映射和 len/offset 元组,以及与这些页面关联的 struct pipe_buf_operations。此函数会将这些数据链接到管道。

ssize_t copy_splice_read(struct file *in, loff_t *ppos, struct pipe_inode_info *pipe, size_t len, unsigned int flags)

从文件中复制数据并将副本拼接到管道中

参数

struct file *in

要读取的文件

loff_t *ppos

指向要读取的文件位置的指针

struct pipe_inode_info *pipe

要拼接到的管道

size_t len

要拼接的数量

unsigned int flags

SPLICE_F_* 标志

描述

此函数分配足够的页面以保存请求的数据量(但受剩余管道容量的限制),将其传递给文件的 ->read_iter() 以读取到其中,然后将使用的页面拼接进管道。

返回

成功时,将返回读取的字节数,如果适用,则会更新 *ppos;如果没有更多数据要读取,则返回 0;如果管道没有空间,则返回 -EAGAIN,如果发生错误,则返回其他负错误代码。如果管道空间不足、到达数据末尾或遇到空洞,则可能发生短读。

int splice_from_pipe_feed(struct pipe_inode_info *pipe, struct splice_desc *sd, splice_actor *actor)

将管道中的可用数据馈送到文件

参数

struct pipe_inode_info *pipe

要拼接的管道

struct splice_desc *sd

有关 actor 的信息

splice_actor *actor

拼接数据的处理程序

描述

此函数循环遍历管道并调用 actor 来实际移动单个 struct pipe_buffer 到目标位置。当管道中没有更多缓冲区或已复制请求的字节数(sd->total_len)时,它将返回。如果管道需要填充更多数据,则返回正数(1),如果已复制所需的字节数,则返回零,如果发生错误,则返回 -errno。

此函数与 splice_from_pipe_{begin,end,next} 一起,可用于在需要围绕将管道缓冲区复制到目标位置进行锁定时,实现 __splice_from_pipe() 的功能。

int splice_from_pipe_next(struct pipe_inode_info *pipe, struct splice_desc *sd)

等待一些数据进行拼接

参数

struct pipe_inode_info *pipe

要拼接的管道

struct splice_desc *sd

有关拼接操作的信息

描述

如果管道缓冲区可用,此函数将等待一些数据并返回正值(1)。如果没有更多数据需要拼接,它将返回零或 -errno。

void splice_from_pipe_begin(struct splice_desc *sd)

开始从管道拼接

参数

struct splice_desc *sd

有关拼接操作的信息

描述

此函数应在包含 splice_from_pipe_next()splice_from_pipe_feed() 的循环之前调用,以初始化 sd 的必要字段。

void splice_from_pipe_end(struct pipe_inode_info *pipe, struct splice_desc *sd)

完成从管道的拼接

参数

struct pipe_inode_info *pipe

要拼接的管道

struct splice_desc *sd

有关拼接操作的信息

描述

如果需要,此函数将唤醒管道写入器。它应在包含 splice_from_pipe_next()splice_from_pipe_feed() 的循环之后调用。

ssize_t __splice_from_pipe(struct pipe_inode_info *pipe, struct splice_desc *sd, splice_actor *actor)

将数据从管道拼接到给定的 actor

参数

struct pipe_inode_info *pipe

要拼接的管道

struct splice_desc *sd

有关 actor 的信息

splice_actor *actor

拼接数据的处理程序

描述

此函数所做的只是循环遍历管道并调用 actor 以实际移动单个 struct pipe_buffer 到目标位置。请参阅 pipe_to_file、pipe_to_sendmsg 或 pipe_to_user。

ssize_t splice_from_pipe(struct pipe_inode_info *pipe, struct file *out, loff_t *ppos, size_t len, unsigned int flags, splice_actor *actor)

将数据从管道拼接到文件

参数

struct pipe_inode_info *pipe

要拼接的管道

struct file *out

要拼接到的文件

loff_t *ppos

out 中的位置

size_t len

要拼接的字节数

unsigned int flags

拼接修饰符标志

splice_actor *actor

拼接数据的处理程序

描述

请参阅 __splice_from_pipe。此函数会锁定管道 inode,否则它与 __splice_from_pipe() 相同。

ssize_t iter_file_splice_write(struct pipe_inode_info *pipe, struct file *out, loff_t *ppos, size_t len, unsigned int flags)

将数据从管道拼接到文件

参数

struct pipe_inode_info *pipe

管道信息

struct file *out

要写入的文件

loff_t *ppos

out 中的位置

size_t len

要拼接的字节数

unsigned int flags

拼接修饰符标志

描述

根据 flags 选项的设置,将给定管道 inode 中的页面移动或复制到给定文件中。此函数基于 ->write_iter。

ssize_t splice_to_socket(struct pipe_inode_info *pipe, struct file *out, loff_t *ppos, size_t len, unsigned int flags)

将管道中的数据拼接至套接字

参数

struct pipe_inode_info *pipe

要拼接的管道

struct file *out

要写入的套接字

loff_t *ppos

out 中的位置

size_t len

要拼接的字节数

unsigned int flags

拼接修饰符标志

描述

将管道中的 len 个字节发送到网络套接字。不涉及数据复制。

ssize_t vfs_splice_read(struct file *in, loff_t *ppos, struct pipe_inode_info *pipe, size_t len, unsigned int flags)

从文件中读取数据并将其拼接至管道

参数

struct file *in

要从中拼接的文件

loff_t *ppos

输入文件偏移量

struct pipe_inode_info *pipe

要拼接至的管道

size_t len

要拼接的字节数

unsigned int flags

拼接修饰符标志 (SPLICE_F_*)

描述

将请求的数据量从输入文件拼接至管道。这是同步操作,因为调用者必须在整个操作过程中持有管道锁。

如果成功,则返回拼接的数据量;如果到达 EOF 或空洞,则返回 0;否则返回负错误代码。

ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd, splice_direct_actor *actor)

在两个非管道之间直接拼接数据

参数

struct file *in

要从中拼接的文件

struct splice_desc *sd

有关拼接位置的 actor 信息

splice_direct_actor *actor

处理数据拼接

描述

这是一个特殊的辅助函数,用于在两个点之间直接拼接数据,而无需显式管道。内部会在进程中缓存一个已分配的管道,并在该进程的生命周期内重复使用。

ssize_t do_splice_direct(struct file *in, loff_t *ppos, struct file *out, loff_t *opos, size_t len, unsigned int flags)

在两个文件之间直接拼接数据

参数

struct file *in

要从中拼接的文件

loff_t *ppos

输入文件偏移量

struct file *out

要拼接到的文件

loff_t *opos

输出文件偏移量

size_t len

要拼接的字节数

unsigned int flags

拼接修饰符标志

描述

供 do_sendfile() 使用。splice 可以轻松模拟 sendfile,但在应用程序中执行此操作会产生额外的系统调用(splice in + splice out,而 sendfile() 只有一个系统调用)。因此,此辅助函数可以直接通过进程私有管道进行拼接。

调用者已在整个范围内调用了 rw_verify_area()。

ssize_t splice_file_range(struct file *in, loff_t *ppos, struct file *out, loff_t *opos, size_t len)

在两个文件之间拼接数据,用于 copy_file_range()

参数

struct file *in

要从中拼接的文件

loff_t *ppos

输入文件偏移量

struct file *out

要拼接到的文件

loff_t *opos

输出文件偏移量

size_t len

要拼接的字节数

描述

供 ->copy_file_range() 方法使用。与 do_splice_direct() 类似,但 vfs_copy_file_range() 已经在 out 文件上持有 start_file_write()。

调用者已在整个范围内调用了 rw_verify_area()。

管道 API

管道接口全部供内核内部(内置镜像)使用。它们不导出供模块使用。

struct pipe_buffer

一个 Linux 内核管道缓冲区

定义:

struct pipe_buffer {
    struct page *page;
    unsigned int offset, len;
    const struct pipe_buf_operations *ops;
    unsigned int flags;
    unsigned long private;
};

成员

page

包含管道缓冲区数据的页面

offset

page 内数据的偏移量

len

page 内数据的长度

ops

与此缓冲区关联的操作。请参见 pipe_buf_operations

flags

管道缓冲区标志。请参见上面。

private

ops 拥有的私有数据。

struct pipe_inode_info

一个 Linux 内核管道

定义:

struct pipe_inode_info {
    struct mutex mutex;
    wait_queue_head_t rd_wait, wr_wait;
    unsigned int head;
    unsigned int tail;
    unsigned int max_usage;
    unsigned int ring_size;
    unsigned int nr_accounted;
    unsigned int readers;
    unsigned int writers;
    unsigned int files;
    unsigned int r_counter;
    unsigned int w_counter;
    bool poll_usage;
#ifdef CONFIG_WATCH_QUEUE;
    bool note_loss;
#endif;
    struct page *tmp_page;
    struct fasync_struct *fasync_readers;
    struct fasync_struct *fasync_writers;
    struct pipe_buffer *bufs;
    struct user_struct *user;
#ifdef CONFIG_WATCH_QUEUE;
    struct watch_queue *watch_queue;
#endif;
};

成员

mutex

保护整个管道的互斥锁

rd_wait

管道为空时的读取器等待点

wr_wait

管道满时的写入器等待点

head

缓冲区生产点

tail

缓冲区消费点

max_usage

环中可以使用的最大槽位数

ring_size

缓冲区总数(应为 2 的幂)

nr_accounted

此管道在 user->pipe_bufs 中占用的量

readers

此管道的当前读取器数量

writers

此管道的当前写入器数量

files

引用此管道的 struct file 数量(受 ->i_lock 保护)

r_counter

读取器计数器

w_counter

写入器计数器

poll_usage

此管道是否用于 epoll(具有疯狂的唤醒)?

note_loss

下一次 read() 应该插入一条数据丢失消息

tmp_page

缓存的已释放页面

fasync_readers

读取器端的 fasync

fasync_writers

写入器端的 fasync

bufs

管道缓冲区的循环数组

user

创建此管道的用户

watch_queue

如果此管道是 watch_queue,则这是用于该队列的东西

bool pipe_has_watch_queue(const struct pipe_inode_info *pipe)

检查管道是否为 watch_queue,即是否使用 O_NOTIFICATION_PIPE 创建。

参数

const struct pipe_inode_info *pipe

要检查的管道。

返回

如果管道是 watch queue,则为 true,否则为 false。

bool pipe_empty(unsigned int head, unsigned int tail)

如果管道为空,则返回 true。

参数

unsigned int head

管道环的头部指针。

unsigned int tail

管道环的尾部指针。

unsigned int pipe_occupancy(unsigned int head, unsigned int tail)

返回管道中已使用的槽位数量。

参数

unsigned int head

管道环的头部指针。

unsigned int tail

管道环的尾部指针。

bool pipe_full(unsigned int head, unsigned int tail, unsigned int limit)

如果管道已满,则返回 true。

参数

unsigned int head

管道环的头部指针。

unsigned int tail

管道环的尾部指针。

unsigned int limit

可用槽位的最大数量。

struct pipe_buffer *pipe_buf(const struct pipe_inode_info *pipe, unsigned int slot)

返回管道环中指定槽位的管道缓冲区。

参数

const struct pipe_inode_info *pipe

要访问的管道。

unsigned int slot

感兴趣的槽位。

struct pipe_buffer *pipe_head_buf(const struct pipe_inode_info *pipe)

返回管道环头部的管道缓冲区。

参数

const struct pipe_inode_info *pipe

要访问的管道。

bool pipe_buf_get(struct pipe_inode_info *pipe, struct pipe_buffer *buf)

获取对 pipe_buffer 的引用。

参数

struct pipe_inode_info *pipe

缓冲区所属的管道。

struct pipe_buffer *buf

要获取引用的缓冲区。

返回

如果成功获取引用,则为 true

void pipe_buf_release(struct pipe_inode_info *pipe, struct pipe_buffer *buf)

释放对 pipe_buffer 的引用。

参数

struct pipe_inode_info *pipe

缓冲区所属的管道。

struct pipe_buffer *buf

要释放引用的缓冲区。

int pipe_buf_confirm(struct pipe_inode_info *pipe, struct pipe_buffer *buf)

验证管道缓冲区的内容。

参数

struct pipe_inode_info *pipe

缓冲区所属的管道。

struct pipe_buffer *buf

要确认的缓冲区。

bool pipe_buf_try_steal(struct pipe_inode_info *pipe, struct pipe_buffer *buf)

尝试获取 pipe_buffer 的所有权。

参数

struct pipe_inode_info *pipe

缓冲区所属的管道。

struct pipe_buffer *buf

要尝试窃取的缓冲区。

bool generic_pipe_buf_try_steal(struct pipe_inode_info *pipe, struct pipe_buffer *buf)

尝试获取 pipe_buffer 的所有权。

参数

struct pipe_inode_info *pipe

缓冲区所属的管道。

struct pipe_buffer *buf

要尝试窃取的缓冲区。

描述

此函数尝试窃取附加到 bufstruct page。如果成功,此函数返回 0 并返回锁定的页面。调用者可以随后将页面重用于他想要的任何目的;典型的用途是将其插入到不同的文件页面缓存中。

bool generic_pipe_buf_get(struct pipe_inode_info *pipe, struct pipe_buffer *buf)

获取对 struct pipe_buffer 的引用。

参数

struct pipe_inode_info *pipe

缓冲区所属的管道。

struct pipe_buffer *buf

要获取引用的缓冲区。

描述

当我们将一个管道中的缓冲区复制到另一个管道中时,此函数用于 tee() 系统调用中,以抓取 buf 的额外引用。

void generic_pipe_buf_release(struct pipe_inode_info *pipe, struct pipe_buffer *buf)

释放对 struct pipe_buffer 的引用。

参数

struct pipe_inode_info *pipe

缓冲区所属的管道。

struct pipe_buffer *buf

要释放引用的缓冲区。

描述

此函数释放对 buf 的引用。