同步和异步、阻塞和非阻塞

1 同步和异步

同步和异步指的是在进行I/O操作完成之前,是否允许其他处理步骤继续执行。
计算机中的I/O操作相对于数据处理操作时十分耗时的。

一个简单的I/O操作方式就是启动连接并等待操作完成,但是这样的操作(同步阻塞I/O)在通信过程中会阻塞进程的处理进度。
相应的,可以在启动通信的同时进行其他的处理,并不需要等待I/O操作的完成,这样的操作就被称作是异步I/O。那些依赖于I/O操作执行完成的任务会阻塞等待I/O操作的完成,其他不依赖与I/O操作的任务能够继续执行。

同步模型常用的函数接口: read , write , send , recv
异步模型常用的函数接口: aio_write , aio_read

1.1 POSIX AIO

在头文件 aio.h 中定义,链接时使用 -lrt

函数接口
异步写操作

int aio_read(struct aiocb* aiocbp);

异步读操作

int aio_write(struct aiocb* aiocbp);

获取异步操作结果

int aio_return(struct aiocb* aiocbp);

获取异步操作中的错误

int aio_error(struct aiocb* aiocbp);

示例代码: github gist

1.2 Linux AIO

在头文件 libaio.h 中定义,链接时使用 -laio

函数接口
需要注意的是aio的函数接口需要借助 syscall 进行调用。
创建aio context对象

int io_setup(unsigned nr, aio_context_t* ctxp);

销毁aio context对象

int io_destroy(aio_context_t ctx);

提交异步操作

int io_submit(aio_context_t ctx, long nr, struct iocb** iocbpp);

获取异步操作结果

int io_getevents(aio_context_t ctx, long min_nr, long max_nr,
		 io_event* events, struct timespec* timeout);

示例代码:

1.3 POSIX AIO与Linux AIO的区别

摘自 stackoverflow.com

On linux, the two AIO implementations are fundamentally different.
The POSIX AIO is a user-level implementation that performs normal blocking I/O in multiple threads, hence giving the illusion that the I/Os are asynchronous. The main reason to do this is that:

  • it works with any filesystem
  • it works (essentially) on any operating system (keep in mind that gnu's libc is portable)
  • it works on files with buffering enabled (i.e. no ODIRECT flag set)

The main drawback is that your queue depth (i.e. the number of outstanding operations you can have in practice) is limited by the number of threads you choose to have, which also means that a slow operation on one disk may block an operation going to a different disk. It also affects which I/Os (or how many) is seen by the kernel and the disk scheduler as well.
The kernel AIO (i.e. iosubmit() et.al.) is kernel support for asynchronous I/O operations, where the io requests are actually queued up in the kernel, sorted by whatever disk scheduler you have, presumably some of them are forwarded (in somewhat optimal order one would hope) to the actual disk as asynchronous operations (using TCQ or NCQ). The main restriction with this approach is that not all filesystems work that well or at all with async I/O (and may fall back to blocking semantics), files have to be opened with ODIRECT which comes with a whole lot of other restrictions on the I/O requests. If you fail to open your files with ODIRECT, it may still "work", as in you get the right data back, but it probably isn't done asynchronously, but is falling back to blocking semantics.
Also keep in mind that iosubmit() can actually block on the disk under certain circumstances.

在Linux上两种AIO是完全不同的;
POSIX AIO实现在用户层,实际上进行的操作是普通的多线程阻塞操作,表现为I/O操作是异步的,这种AIO的优点是兼容性和可移植性好,缺点是操作队列长度受限于最大线程数量。
Linux AIO是内核提供的AIO函数接口,I/O操作请求的队列在内核中维护,这种AIO的缺点是并不支持所有的文件系统,Linux AIO在某些情况下的磁盘操作是会阻塞的。

2 阻塞和非阻塞

阻塞与非阻塞的概念针对的是函数是否会立即返回。
非阻塞模型常与IO复用技术组合使用。
可以通过函数将IO设备设置为非阻塞模式。

3 如何理解阻塞非阻塞与同步异步的区别

在处理 IO 的时候,阻塞和非阻塞都是同步 IO。
只有使用了特殊的 API 才是异步 IO。

Comments

Comments powered by Disqus