关注了就能看到更多这么棒的文章哦~
Two new ways to read a file quickly
By Jonathan Corbet
March 6, 2020
原文来自:https://lwn.net/Articles/813827/
Linux的系统调用通常开销不大,尽管近来为了防御speculative-execution漏洞已经给系统调用额外增加了不少工作。不过,再轻量级的系统调用,如果要被调用非常非常多次的话,也会是个问题。因此开发者们一直在想办法来避免系统调用。近期正在讨论的就是为了减少读取文件内容这个操作的系统调用次数的两种方法,其中一个要比另外一个更加简单。
readfile()
LWN最近介绍过大家提议增加的fsinfo()系统调用,主要用于把mount上来的文件系统的相关信息返回给应用程序。大家讨论的时候又引申出一个话题,就是哪些信息其实应该通过sysfs来暴露给user space,讨论中有人担心读取许多小文件的开销会不会太大。Miklos Szeredi认为不算大,但是他建议如果大家担心这点的话,不如引入一个新的系统调用来读取文件内容,从而进一步减少系统调用的调用次数。
ssize_t readfile(int dfd, const char *path, char *buf, size_t bufsize,
int flags);
dfd和path这两个参数就指定了要操作的文件,跟其他类似的系统调用含义一样。如果readfile()成功了,则会把文件内容读入buf,最多可以读取bufsize数量的字节,返回值是真正读出的字节数。表面来看,readfile()没有增加什么新东西,应用程序完全可以通过openat(), read(), close()等调用来得到相同的结果。不过它可以把所需调用的系统调用数量从3个变成1个,这样对某些Linux用户可能很有吸引力。
util-linux project的maintainer Karel Zak就非常赞成实现这么一个readfile() API,愿意请作者喝啤酒喝个够。util-linux里面的许多工具(例如ps和top这种)都花了许多时间从/proc和sysfs里的小文件读取信息,如果能有readfile()调用,就可以让这些工具更加高效。
经常有人抱怨kernel开发者们不愿意关注他报出的问题,其实这主要是他没有使用一个重要技巧。Greg Kroah-Hartman就很快非常兴奋地回应:“为了一个21行的kernel patch就啤酒管够?放着让我来!”他很快实现了第一个版本,也补充说这个系统调用其实确实有其合理性。当然,随着更多细节需要考虑进来,21行肯定是不够的,此外还要写一个man page。不过看起来很快就能看到readfile()被提交到mainline了。
当然,已经有人开始讨论是否需要writefile()了。
readfile() on the ring
随着讨论的进行,Jann Horn指出那些开发io_uring的开发者也有兴趣增加一个readfile()类似的功能。io_uring的主要目的是能用异步调用的方式来完成系统调用,并且不需要真正调用到kernel里面去,所以io_uring可能确实比较适合这种应用场景。他也说,要在io_uring里面真正支持这个功能还是有点复杂的,因为没有现成的方法能把openat()返回的文件描述符来传递给ring队列中后续的read()操作。没有这个机制的话,read()调用就必须等到文件open操作完成之后才能送到ring队列中,这样就完全失去了我们的初衷。
不过,其实整个io_uring方案都是“有点复杂”的。类似ps这类工具的开发者可能不会愿意花这么多功夫来建立io_uring instance、把它映射进address space、queue一些操作进去、然后发起这些操作。这么多动作只是为了避免对/proc下面的一些系统调用,他们不一定愿意。不过其他一些更复杂的应用程序的开发者则可能很希望利用这种功能。
很快,io_uring的maintainer Jens Axboe就提交了一个patch来弥补了这个缺陷,当然他没有掺和到那个“啤酒管够”的讨论里面去,而是单独发出来。这个patch可以记住上一个openat()调用返回的文件描述符,供队列中后续操作所用。为了实现readfile()的功能,应用程序可以直接建立一个包含3个操作的io_uring队列,分别对应openat(), read(), close()调用。对后面两个来说,通常的文件描述符这个参数则会通过一个特殊常量IOSQE_FD_LAST_OPEN来提供,会在执行此操作的时候替换为最后一次open的文件。
这个方案切实可行,就是实现的接口有点复杂,也要偷偷地对文件描述符进行替换。Josh Triplett有个不同的注意,就是他此前1月份在LWN上发布的评论里提到的方式:由application来指定在open文件的时候使用哪个文件描述符。在3月份的时候他就提交了一组patch,为openat2()增加了一个O_SPECIFIC_FD flag。这个功能不依赖io_uring,比如application如果想打开某个文件并且使用42号文件描述符来管理这个打开的文件,那么就直接使用这个新增的flag即可。
这个patch set还增加了一个prctl()操作,可以设置一个最小号的文件描述符,这样在application没有明确指定某个文件描述符的时候就可以用这个。缺省值设置为0,保留了Unix一直以来的“使用最小的可用描述符”的语义。如果开发者想控制使用哪些文件描述符,就可以设置一下这个最小值,kernel就不会使用比这个描述符更小的,除非是有明确指定要求使用某个描述符。
Axboe只花了3个小时,就提出了一组新patch,跟这个功能集成在了一起。主要是把检查文件描述符是否有效的动作尽量往后放,避免这个文件描述符被真正提供之前就检查它是否有效。大家的共识是这个方案要比那个偷偷替换文件描述符的方式更好,因此这个版本看起来很可能被大多数人接受。
目前来说,这个改动还只是在io_uring的mailing list里面讨论,参与讨论的人比较局限于一个小范围。Axboe计划是能近期能发出来,期望能合入到Linux 5.7版本中。所以,很可能不久之后就有两种方式可以让application只用最少数的系统调用就读出文件内容了,所以Karel Zak可能要买许多许多许多啤酒了。
全文完
LWN文章遵循CC BY-SA 4.0许可协议。
欢迎分享、转载及基于现有协议再创作~
长按下面二维码关注,关注LWN深度文章以及开源社区的各种新近言论~