在direct io模式下,非同步是非常有必要的(因為繞過了pagecache,直接和磁碟互動)。linux native aio正是基於這種場景設計的,具體的介紹見:kernel
asynchronousi/
o(aio)
support
forlinux。下面我們就來分析一下aio程式設計的相關知識。
阻塞模式下的io過程如下:
int fd = open(const char *pathname, int flags, mode_t mode);
ssize_t pread(int fd, void *buf, size_t count, off_t offset);
ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
int close(int fd);
因為整個過程會等待read/write的返回,所以不需要任何額外的資料結構。但非同步io的思想是:應用程式不能阻塞在昂貴的系統呼叫上讓cpu睡大覺,而是將io操作抽象成乙個個的任務單元提交給核心,核心完成io任務後將結果放在應用程式可以取到的地方。這樣在底層做i/o的這段時間內,cpu可以去幹其他的計算任務。但非同步的io任務批量的提交和完成,必須有自身可描述的結構,最重要的兩個就是iocb和io_event。
struct iocb u;
};
struct io_iocb_common ;
iocb是提交io任務時用到的,可以完整地描述乙個io請求:
data是留給用來自定義的指標:可以設定為io完成後的callback函式;
aio_lio_opcode表示操作的型別:io_cmd_pwrite | io_cmd_pread;
aio_fildes是要操作的檔案:fd;
io_iocb_common中的buf, nbytes, offset分別記錄的io請求的mem buffer,大小和偏移。
struct io_event ;
io_event是用來描述返回結果的:
obj就是之前提交io任務時的iocb;
res和res2來表示io任務完成的狀態。
libaio提供的api有:io_setup, io_submit, io_getevents, io_destroy。
1. 建立io任務
int io_setup (int maxevents, io_context_t *ctxp);
io_context_t對應核心中乙個結構,為非同步io請求提供上下文環境。注意在setup前必須將io_context_t初始化為0。
當然,這裡也需要open需要操作的檔案,注意設定o_direct標誌。
2.提交io任務
long io_submit (aio_context_t ctx_id, long nr, struct iocb **iocbpp);
提交任務之前必須先填充iocb結構體,libaio提供的包裝函式說明了需要完成的工作:
void io_prep_pread(struct iocb *iocb, int fd, void *buf, size_t count, long long offset)
void io_prep_pwrite(struct iocb *iocb, int fd, void *buf, size_t count, long long offset)
這裡注意讀寫的buf都必須是按扇區對齊的,可以用posix_memalign來分配。
3.獲取完成的io
long io_getevents (aio_context_t ctx_id, long min_nr, long nr, struct io_event *events, struct timespec *timeout);
這裡最重要的就是提供乙個io_event陣列給核心來copy完成的io請求到這裡,陣列的大小是io_setup時指定的maxevents。
timeout是指等待io完成的超時時間,設定為null表示一直等待所有到io的完成。
4.銷毀io任務
int io_destroy (io_context_t ctx);
在非同步程式設計中,任何乙個環節的阻塞都會導致整個程式的阻塞,所以一定要避免在io_getevents呼叫時阻塞式的等待。還記得io_iocb_common中的flags和resfd嗎?看看libaio是如何提供io_getevents和事件迴圈的結合:
void io_set_eventfd(struct iocb *iocb, int eventfd)
這裡的resfd是通過系統呼叫eventfd生成的。
int eventfd(unsigned int initval, int flags);
eventfd是linux 2.6.22核心之後加進來的syscall,作用是核心用來通知應用程式發生的事件的數量,從而使應用程式不用頻繁地去輪詢核心是否有時間發生,而是由核心將發生事件的數量寫入到該fd,應用程式發現fd可讀後,從fd讀取該數值,並馬上去核心讀取。
有了eventfd,就可以很好地將libaio和epoll事件迴圈結合起來:
1. 建立乙個eventfd
efd = eventfd(0, efd_nonblock | efd_cloexec);
2. 將eventfd設定到iocb中
io_set_eventfd(iocb, efd);
3. 交接aio請求
io_submit(ctx, num_events, iocb);
4. 建立乙個epollfd,並將eventfd加到epoll中
epfd = epoll_create(1);
epoll_ctl(epfd, epoll_ctl_add, efd, &epevent);
epoll_wait(epfd, &epevent, 1, -1);
5. 當eventfd可讀時,從eventfd讀出完成io請求的數量,並呼叫io_getevents獲取這些io
以上就是linux 非同步io程式設計的一些基礎知識,希望對感興趣的同學或多或少有些幫忙,謝謝。
linux非同步IO程式設計例項分析
在direct io模式下,非同步是非常有必要的 因為繞過了pagecache,直接和磁碟互動 linux native aio正是基於這種場景設計的,具體的介紹見 kernel asynchronousi o aio support forlinux。下面我們就來分析一下aio程式設計的相關知識。...
網路程式設計之IO模型 非同步IO
linux下的asynchronous io其實用得不多,從核心2.6版本才開始引入。先看一下它的流程 使用者程序發起read操作之後,立刻就可以開始去做其它的事。而另一方面,從kernel的角度,當它受到乙個asynchronous read之後,首先它會立刻返回,所以不會對使用者程序產生任何bl...
Linux 核心101 非同步IO
posix 非同步 io inte ce aio 定義了允許程序建立乙個或多個非同步的 io 操作的介面。程序可以在 io 操作完成之後得到作業系統的通知,手段包括 不通知 訊號 例項化thread。注意 這只是 posix the portable operating system inte ce...