您的位置:首页 > 大数据 > 人工智能

『阿男的工程世界』*03 POSIX AIO vs. Native AIO*

2017-01-03 00:00 295 查看
『阿男的工程世界』*03 POSIX AIO vs. Native AIO*

POSIX AIO是标准的C语言库提供的一个异步式IO的框架。因为它是libc里面提供的,所以它是运行在USER SPACE里面。USER SPACE里面实现AIO可以有多种方式,而POSIX AIO是用
pthread
来实现的。

我们可以看一下libc里面的aio实现,以
aio_write
为例^1

int
aio_write64 (aiocbp)
struct aiocb64 *aiocbp;
{
return (__aio_enqueue_request ((aiocb_union *) aiocbp, LIO_WRITE64) == NULL
? -1 : 0);
}

从上面的代码中,我们可以看到,libc的"异步写入"其实是
__aio_enqueue_request
,我们来看一下这个函数的实现^2

/* The main function of the async I/O handling.  It enqueues requests
and if necessary starts and handles threads.  */
struct requestlist *
internal_function
__aio_enqueue_request (aiocb_union *aiocbp, int operation)
{
int result = 0;
int policy, prio;
struct sched_param param;
struct requestlist *last, *runp, *newp;
int running = no;
...
/* Compute priority for this request.  */
pthread_getschedparam (pthread_self (), &policy, ¶m);
prio = param.sched_priority - aiocbp->aiocb.aio_reqprio;

/* Get the mutex.  */
pthread_mutex_lock (&__aio_requests_mutex);
...
/* Release the mutex.  */
pthread_mutex_unlock (&__aio_requests_mutex);
}
while (runp != NULL);

return NULL;

可以看到实现的方式是使用
pthread
,也就是说libc的AIO实际上是"多线程",这个实现是比较蛋疼的。结论:这个libc的AIO并不是真正的异步式IO,不要使用它。

真正的AIO框架是Linux自己的Native AIO,它是内核级别的,由内核管控IO context和events,所以是真正的非阻塞式IO,所以要用就用Linux自己提供的Native AIO的System calls。Linux的AIO接口其实非常干净整洁,就是5个system calls:

int io_setup(unsigned nr_events, aio_context_t *ctxp);
int io_destroy(aio_context_t ctx);
int io_submit(aio_context_t ctx, long nr, struct iocb *cbp[]);
int io_cancel(aio_context_t ctx, struct iocb *, struct io_event *result);
int io_getevents(aio_context_t ctx, long min_nr, long nr,
struct io_event *events, struct timespec *timeout);

这五个接口是内核级别的真正的非阻塞接口。关于Linux Native AIO的使用方法,阿男后续给大家写文章介绍。

最后我们分析一下各个语言平台下面的异步式IO是怎么实现的。首先是Java:

tb13:jdk7u-jdk weli$ find . | grep "/nio" | grep -v test | grep -v demo | grep c$

阿男自己的电脑里面有openjdk 1.7的源代码,所以就执行上面的命令,把JDK的源代码里面,NIO相关的文件过滤出来(NIO是Java的异步式IO接口),去掉测试代码和演示用的代码,并且只看C代码。因为Java最重和操作系统打交道的部分是c语言实现的代码,所以我只看c代码就好。

接下来我们可以看一下上面命令的执行结果。阿男把输出拆分成两部分并进行精简,找出重要的内容给大家。首先是第一部分:

./src/macosx/native/sun/nio/ch/KQueueArrayWrapper.c

不用分析源代码,只需要看文件名,我们就可以看到Java针对macos的NIO实现使用了
KQueue
KQueue
是FreeBSD内核的内核级别的AIO框架,因为MacOS使用的是Darwin内核,Darwin内核很大一部分实现来自于FreeBSD内核,因此使用KQueue很正常。

接下来是输出的第二部分:

./src/solaris/native/sun/nio/ch/EPoll.c
./src/solaris/native/sun/nio/ch/EPollArrayWrapper.c
./src/solaris/native/sun/nio/ch/EPollPort.c

从上面的代码文件名当中,我们可以看到
EPoll
,可以看到Solaris平台下是使用的
epoll
机制来实现的aio。

Linux下也有
epoll
,它们的实现和
aio
在内核里面有所不同,也有设计上的共通之处。这个作为文章外的课题交给大家自己研究,后续阿男在另外一个专讲内核的专栏里面会给大家慢慢讲。

最后我们可以看看JDK有没有用到Linux的AIO,使用下面代码查找JDK的代码里面有没有AIO相关的接口调用:

$ grep -rl 'io_setup' *
share/demo/jvmti/hprof/hprof_init.c
share/demo/jvmti/hprof/hprof_io.c
share/demo/jvmti/hprof/hprof_io.h

阿男在JDK 1.7这个版本的源代码里面没有找到对于AIO的应用,上面的结果其实是
Java Virtual Machine Tools Interface
^3的一个demo展示,就是给大家展示JVMTI如何调用AIO接口。也许后续更新版本的JDK对Native AIO会有更多应用,阿男也会关注。

阿男在这篇文章的最后想说,学习操作系统是很重要的事情,可以帮助我们理解各种语言在各种操作系统上面的特定实现,从中可以学到很多知识,会有很多收获。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  阿男的工程世界