Nginx 使用 Linux-native aio 需要 Linux 内核支持

Nginx 性能优异在于善于利用操作系统内核的各种特性,比如 aio/epoll/sendfile (Linux), kqueue (FreeBSD) 等。对于使用 VPS 做图片站的站长来说,使用 nginx 的 aio 特性会大大提高性能,图片站的特点是大量的读 io 操作,nginx aio 不用等待每次 io 的结果有助于并发处理大量 io 和提高 nginx 处理效率。

前段时间有位客户要用 nginx aio,他用 rpm 升级安装 nginx 到 0.8.5 版本时候发现 rpm 包里的 nginx 没有包含 Linux-native aio (asynchronous I/O) 的支持,所以需要下载 nginx 源代码并带上参数 –with-file-aio 编译。除了要带参数外,Linux 内核还必须有支持这一特性的 api,eventfd(),否则在 nginx 错误日志(/var/log/nginx/error.log)里会看到类似的报错:

eventfd() failed (38: Function not implemented)
worker process 1858 exited with fatal code 2 and can not be respawn

要让 nginx 使用 aio 特性还需要修改 nginx 配置文件:

# vi /etc/nginx/nginx.conf
...
location / {
aio on;
directio 1;
output_buffers 1 128k;
}
...

昨天另一客户遇到 nginx 启动配置都正确却无法看到网页的情况,VPSee 刚开始怀疑是他自己的 nginx 配置有误或者防火墙屏蔽了80端口,后来登录到他的 VPS 上发现 nginx 配置的确没问题,能正常启动,可以 telnet 80 端口,但是不能 get 到网页。打开 nginx 日志也发现 eventfd() failed 的错误提示。后来检查他的 Linux VPS 内核版本是 2.6.18,查了一下 man 帮助发现只有 2.6.22 以后版本才支持 eventfd,解决方法很简单,升级内核就可以了。

Linux-native aio 比传统的 POSIX aio 功能更丰富一些,重要的一点是能通过内核加速提供高性能。直接用 Linux-native aio API 比较晦涩,为了方便使用和开发 Linux-native aio 应用程序我们可以用 libaio/libaio-devel 库。不过 nginx 的作者没有用这些库,因为 nginx 需要 eventfd(),而 libaio 库里只有 0.3.107 版本起才支持 eventfd;nginx 也没有用 glibc,因为 glibc 要到 2.8 版本才支持 eventfd(),为了减少对库的依赖性,nginx 干脆直接用 Linux-native aio API (system calls).

$ vi nginx-0.9.1/src/event/modules/ngx_epoll_module.c
...
#if (NGX_HAVE_FILE_AIO)

/*
 * We call io_setup(), io_destroy() io_submit(), and io_getevents() directly
 * as syscalls instead of libaio usage, because the library header file
 * supports eventfd() since 0.3.107 version only.
 *
 * Also we do not use eventfd() in glibc, because glibc supports it
 * since 2.8 version and glibc maps two syscalls eventfd() and eventfd2()
 * into single eventfd() function with different number of parameters.
 */
...

这里说到了 eventfd(),eventfd 是 Linux-native aio 其中的一个 API,用来生成 file descriptors,这些 file descriptors 可为应用程序提供更高效 “等待/通知” 的事件机制。和 pipe 作用相似,但比 pipe 更好,一方面它只用到一个 file descriptor(pipe 要用两个),节省了内核资源;另一方面,eventfd 的缓冲区管理要简单得多,pipe 需要不定长的缓冲区,而 eventfd 全部缓冲只有定长 8 bytes.

评论 (8 Comments)

  1. 偶添加了这段代码,但nginx提示错误“unknown directive “aio””。。。0.8x的

  2. 那是因为你的nginx ./configure 参数没有–with-file-aio

  3. linux aio 现在支持socket pipe等没有?

  4. Linux VPS 内核版本是 2.6.18 就无法使用nginx 的 aio了?
    一定要升级内核?

  5. @Cat
    嗯,内核里面必须有 eventfd() 这个系统调用,因为 nginx 要用到这个,eventfd 是新特性只有新内核才有。

  6. @vpsee 写个教程吧…xen vps 升级内核

  7. 呵呵,xen vps 上最好不要自己动内核,要编译很麻烦,可以参考这篇:在 ArchLinux VPS 上编译内核

  8. 为什么我的没有升级内核也没有出现你说的错误啊

发表评论