
摘要:Linux操作系統在軟件工程中的應用比較廣泛,各種商業發行版本廣泛地存在于PC桌面、后端服務器和個人移動終端中。而Linux中數據輸入輸出的效率直接決定了操作系統的應用執行效率。就硬件的讀寫速度來看,CPU的數據處理速度遠大于磁盤IO的數據處理速度。因此在Linux中,IO性能的優化一直是Linux效率優化的重點。在Linux操作系統中一共存在非阻塞、阻塞、多路復用、信號驅動和異步5種IO模型。這5種IO模型優缺點各不相同,分別對應著不同的應用場景。
關鍵詞:Linux操作系統;IO模型;多路復用;信號驅動
中圖分類號:TP31
文獻標志碼:A
0 引言
Linux是一種應用廣泛、執行高效的操作系統。要成為一種高效的操作系統就必須包含一整套完備的文件IO模型。Linux為了操作系統的整體運行安全,劃分了用戶空間和內核空間。當發生IO操作的時候,就會經歷用戶空間的數據等待和內核空間的數據拷貝2個階段,而IO數據處理就發生在這2個階段。在編寫高性能用戶應用的時候需要重點考慮不同IO模型的選擇。
1 網絡IO過程簡介
Linux中的5種IO模型一般應用在2個方面,一方面是文件IO,另一方面是網絡IO。在Linux中一切均是文件,所以文件IO和網絡IO本質上是一樣的。下面主要以網絡IO為例,描述一次完整的網絡IO的基本過程。
首先,需要2臺計算機通過TCP/IP協議建立網絡連接,在成功建立連接之后,客戶端主機向目標服務器發送網絡請求。然后,服務器端DMA芯片在內核程序的操控下將磁盤中的數據拷貝到內存。CPU擅長數據處理,所以這一過程由DMA替代CPU完成數據拷貝的任務,這個步驟決定了網絡IO是否阻塞。最后,由內核空間和用戶空間通過mmap完成同一塊物理內存的映射,從而完成數據從內核緩沖區拷貝到用戶空間的過程。由用戶空間的Web服務進程完成客戶端網絡請求的響應,這一步主要決定了IO是否同步。
Linux IO模型是一種針對數據讀寫過程的優化方案。套接字是一個典型的系統調用接口,它的通信效率直接影響了用戶空間應用程序的執行效率。不同的IO模型為用戶空間進程提供了不同的優化策略,例如非阻塞IO模型會通過輪詢的過程來完成數據的傳輸;當數據還沒有準備好的時候,阻塞IO模型會阻塞用戶空間進程;IO復用模型提供了用戶空間進程同時處理多個套接字的方式;異步IO模型通過注冊回調函數來提醒用戶空間進程數據傳輸的完成[3];信號驅動IO模型通過信號來提醒用戶空間進程當前數據傳輸的完成[4]。
2 同步阻塞IO
同步阻塞IO模型是一種簡單、實用的IO模型。在這種模型中,如果內核空間緩沖區中沒有具體的數據,那么read函數就會一直阻塞下去。在阻塞的這段時間內用戶空間將無法進行其他操作。由于用戶空間進程一直占用CPU時間片,所以阻塞IO會導致應用程序的可伸縮性受到影響。阻塞IO通常適用于串行、單線程以及同步的場景,例如文件傳輸程序、打印機程序等。雖然阻塞程序在這些場景中比較實用,但是在大規模、高并發的場景下,阻塞IO的性能會受到較大的影響。
在Linux操作系統中,所有socket函數都是默認阻塞的,同時listen()、send()、recv()函數也都是阻塞型IO函數,使用這些接口可以很方便地構建客戶機-服務器模型。這些接口在網絡編程的時候會帶來一個問題,就是在執行函數的時候,線程將無法執行其他運算和網絡請求。通??梢酝ㄟ^多線程或者是多進程的方式來改進這種模型,這樣任何一個阻塞的連接都不會影響其他的連接。使用進程還是線程需要根據具體的情況來看,如果需要為多個客戶機提供服務,則推薦使用pthread_create ()創建多線程;如果單個任務體消耗資源過多或者需要進行大規模、長時間的數據運算,則推薦使用fork()創建多進程。
3 同步非阻塞IO
非阻塞IO是一種不會被阻塞同時會等待結果返回的IO模型,這種非阻塞IO會查詢內核緩沖區中數據是否準備好,內核會立即給予反饋。如果數據尚未準備好,則非阻塞IO將進行輪詢,重復步驟直到內核緩沖區中的數據準備好為止。非阻塞IO還可以通過事件驅動的方式來實現數據IO操作,通過提前注冊事件回調函數,當IO操作完成的時候,回調函數將會被調用,從而實現非阻塞IO的數據提取過程。在使用非阻塞IO的過程中需要特別關注競爭條件和線程安全問題,來保障非阻塞IO程序的穩定性和準確性。
相較于阻塞接口,非阻塞接口在被函數調用之后,會立即返回結果。線程通過循環調用非阻塞接口,可以實現所有關注的連接數據的接收過程。但是這樣的工作過程不建議在實際開發中使用,因為非阻塞接口的調用過程將顯著地提高CPU的占用率。推薦使用select()多路復用的模式來替代輪詢的過程,可以同時檢測多個連接的活躍性。
4 IO多路復用
相較于NIO,多路復用IO會在內核空間中進行多個socket文件句柄的輪詢工作[5]。多路復用IO將輪詢所有socket的工作交給內核來完成,可以大大減少內核態和用戶態之間切換的次數。當某些socket有數據到達時,內核會返回socket文件句柄給用戶空間進程,此時內核會把數據拷貝到用戶空間,進程就可以直接讀取數據了。
在執行相關的系統調用函數之后,多路復用IO就處于阻塞狀態,所以多路復用IO本質上還是同步阻塞IO,只不過將輪詢的過程放入內核中來實現,大大提高了IO的處理速度,進而也減少了系統切換的開銷。同時,多路復用IO可以做到僅使用一個線程就可以處理多個socket的IO請求,相較于BIO更節省系統資源。多路復用一般有select、epoll和poll 3種實現方法。其主要區別在套接字文件句柄的獲取方式上。
5 信號驅動式IO
信號驅動IO本質上是一種多路復用IO技術,通過指定一個事件和描述符,目標進程可以使用sigaction注冊一個信號處理函數,該函數會在IO處于就緒態的時候被內核調用。當內核檢測到描述符上發生的指定事件的時候,會向用戶空間進程發送特定的信號。最終,用戶空間進程就可以通過捕捉指定的信號來執行相應的操作,實現相關的異步IO過程。
信號驅動IO有很多優點,其中最重要的一個優點是可以在處理多個描述符的時候避免阻塞目標進程,從而提高應用程序的響應和并發能力,避免輪詢機制帶來的巨大系統開銷。同時信號驅動IO也帶來了一些缺點,在Linux操作系統中,信號的捕獲、解析、處理都需要一定的時間,所以信號驅動IO一般情況下不太適合高速的IO數據處理過程。而且Linux操作系統中信號也不太可靠,意味著信號在傳輸過程中容易丟失。最關鍵的一點是在信號驅動IO編程模型中,需要特別注意競爭條件和死鎖問題,這就需要較高的編程技巧去處理問題。
6 異步IO模型
異步IO模型是一種不同于同步IO的數據處理模型[6-7]。在等待IO處理完成的時候,異步IO模型允許進程執行其他任務而不必阻塞等待IO完成。操作系統提供相應的通知機制來配合IO操作,完成異步處理的過程。異步IO一般分為3個主要過程。首先由應用程序發起異步IO的請求,其中包含數據長度、緩沖區的地址和相關的描述符參數。發起異步請求之后,應用程序仍然可以執行其他任務。其次內核將異步IO請求放入IO隊列,并將請求的數據保存到內核空間。最后在IO操作完成后,內核發送通知,應用程序處理相關IO數據。在Linux操作系統中,通知機制一般采用事件通知、信號或回調函數的方式。
從本質上看,非阻塞IO和異步IO都是阻塞的。在應用程序等待IO處理數據的時候,它們都可以執行其他相關任務,但它們的區別主要集中在以下3個方面。從編程模型上看,非阻塞IO需要由用戶主動編寫輪詢方案來查看數據的IO準備過程;異步IO一般使用回調函數來處理IO事件。從實現接口上看,非阻塞IO一般將相關的系統調用設置為非阻塞模式[8-10],設置之后可以立刻得到IO操作的返回值;異步IO一般使用Linux操作系統提供的aio_write()和aio_read()等系統調用,同時應用程序在發起系統調用之后立刻返回。從實現方式上看,非阻塞IO通過應用程序發起IO請求,通過輪詢的方式來查看IO請求是否已經完成;異步IO主要通過Linux內核處理相關的IO請求,使用Linux的通知機制完成IO數據處理的結果發送過程。
7 網絡IO中數據的詳細接收流程
上文詳細描述了5種網絡IO模型的主要工作流程。如圖1所示,顯示了在網絡IO中網絡數據的接收過程。當計算機網卡收到網絡數據幀時,網絡數據幀會通過DMA直接放入RingBuffer。RingBuffer的主要作用是用來存儲網絡數據幀,當RingBuffer中的數據存滿,通過網卡收到的新數據幀將會被丟棄。DMA操作完成之后,CPU會收到網卡發出的一個硬中斷,然后調用硬中斷響應驅動程序。內核數據結構sk_buffer將被硬中斷響應驅動程序創建,同時網絡數據幀也會被拷貝到sk_buffer數據結構中。網卡中斷程序將發起軟中斷請求,通知內核有新的網絡數據幀到達。ksoftirqd線程收到軟中斷請求后,將調用poll函數。poll函數將網絡數據包送到ip_rcv函數中,從而完成軟中斷的響應過程。數據包到達網絡層之后,將被去除IP頭,如果此時數據包是發送給本機的,就會去除TCP或UDP協議類型;如果用的是TCP協議,數據包會被tcp_rcv函數進行處理,在函數tcp_rcv中去除TCP的頭部。在查找到對應的socket后,網絡數據包中的數據會被拷貝到socket的接收緩沖區中。從應用層的角度來看,當使用read系統調用來讀取socket接收緩沖區中的數據的時候,如果發現沒有數據,那么用戶空間的應用程序就會被阻塞。當接收緩沖區中出現數據的時候,內核會將接收緩沖區的數據拷貝到用戶空間,最終應用程序就會讀取到數據。
8 結語
非阻塞IO在讀取數據的時候雖然不需要阻塞等待數據,但是為了查詢數據的到達狀態,仍然需要執行低效率的輪詢操作。阻塞IO在實現性能上表現不夠好,但是在編程實現上比較簡單。對于時效性要求比較高的IO操作,信號IO不太適用,原因是信號IO在信號準備、發送和處理上需要一定的時間。但是信號IO編程模型比較簡單易懂。Linux操作系統提供了多種方式的多路復用IO實現,分別是select、epoll和poll。其中poll和select均采用輪詢方式,這2種方式不能夠監聽太多數量的文件描述符,使用上有限制;epoll由于使用了紅黑樹,對文件描述的增、刪、改的效率比較高,同時epoll采用了觸發機制來實現對IO的反饋,具有較高的執行效率。Linux操作系統還提供了異步IO方式,與同步IO不同的是異步IO的編程模型更加復雜,且需要Linux操作系統的通知機制來完成IO事件的處理過程。在異步IO編程過程中需要特別注意死鎖問題和競爭條件的設置問題。
參考文獻
[1]趙夢茹.移動終端Linux存儲IO性能分析與優化[D].西安:西安電子科技大學,2014.
[2]段鍇,李永進,郝鵬,等.基于System V消息隊列的I/O多路復用方法[J].數字技術與應用,2023(10):40-42.
[3]劉露.基于異步I/O的緩存框架研究與實現[D].綿陽:西南科技大學,2023.
[4]賀慶.Linux實時信號驅動I/O的改進[D].成都:電子科技大學,2005.
[5]溫冰.HP-UNIX下IO多路復用網絡服務器的設計方案[J].計算機光盤軟件與應用,2014(15):139,141.
[6]劉華鎣,劉華煜,聶永丹.LINUX2.5下的異步I/O在FTP服務器上的應用[J].大慶石油學院學報,2005(1):72-74,124.
[7]黃可杰,馬莉.Linux環境下I/O重定向與異步通信的實現[J].計算機應用與軟件,2011(3):57-60.
[8]許會元,何利力.NodeJS的異步非阻塞I/O研究[J].工業控制計算機,2015(3):127-129.
[9]趙思遠.Java NIO與IO性能對比分析[J].軟件導刊,2021(4):214-219.
[10]蔡宇昂.分布式文件系統I/O擁塞控制研究[J].綠色科技,2018(24):184-186.
(編輯 沈 強)
Exploration of multiple IO mechanisms and applications in Linux
Wang" Min
(Management Office of Song Qingling Cemetery, Honorary President of the People’s Republic of China, Shanghai 200237, China)
Abstract: The application of Linux operating system in software engineering is relatively rich, and various commercial distribution versions of Linux are widely present in PC desktops, backend servers, and personal mobile terminals. The efficiency of data input and output in Linux directly determines the application execution efficiency of the operating system. In terms of hardware read and write speed, the data processing speed of the CPU is much faster than the data processing speed of the disk IO. Therefore, in Linux, optimizing IO performance has always been the focus of Linux efficiency optimization. There are five IO models in the Linux operating system: non blocking, blocking, multiplexing , signal driven, and asynchronous. These five IO models have different advantages and disadvantages, corresponding to different application scenarios.
Key words:Linux operating system; IO model; multiplexing; signal driven