網絡編程IO -- 同步,異步,阻塞,非阻塞

0. IO基礎

0.1 計算機IO控制方式

  • 詢問方式(CPU和IO設備串行工作)
a. 查詢指令,用來查詢設備是否就緒
b. 傳送指令,設備就緒時,執行數據交換
c. 轉移指令,設備未就緒時,執行轉移指令轉向查詢指令繼續查詢
缺點:CPU的反覆查詢過程,浪費時間;且CPU需參與數據的傳送過程
  • 中斷方式(CPU和IO設備可實現部分並行)
a. 外圍設備僅當操作正常或異常結束時,才中斷中央處理機
b. CPU不再查詢I/O設備是否就緒,僅在響應了I/O中斷請求後,轉至中斷處理程序執行
缺點:輸入輸出操作由CPU控制,每傳送一個字符或一個字,都會發生一次中斷;因此只是實現了CPU和I/O的部分並行
  • DMA方式(I/O設備直接與主存交換數據,而不佔用CPU)
a. CPU計算文件地址 -> 委派DMA讀取文件 -> DMA接管總線 -> DMA讀完文件後通知CPU(中斷異常)
b. 相比中斷方式:CPUIO的干預從字爲單位減少到以數據塊爲單位;且不需要數據拷貝
c. 缺點:每發出一條IO指令,只能讀寫一個數據塊;當需讀寫多個離散的數據塊,並將其傳送到不同的主存區域時,需要多條IO指令
  • 通道方式(具有特殊功能的處理器)

0.2 用戶態與內核態

a. 用戶態運行的上層應用程序 系統調用 內核態資源
b. 系統調用是操作系統最小功能單位,fork/clone/wait/

1. 同步,異步,阻塞,非阻塞

  • 根據消息通知機制:同步/異步

    同步:等待

    異步:註冊回調

  • 根據等待消息通知的狀態:阻塞/非阻塞

    阻塞:當前線程掛起等待

    非阻塞:執行其他消息處理

    考慮CPU利用率 與 系統線程切換成本

    類型 線程狀態 線程獲取IO操作結果方式
    同步阻塞 掛起 等待
    同步非阻塞 可執行其他 詢問
    異步阻塞 掛起 通知或回調
    異步非阻塞 可執行其他 通知或回調

2. 網絡IO —— 5種io模型

阻塞的兩個場景:
a. 連接過程,connect()
客戶端發起connect請求,服務端accept時,其他客戶端的請求會被阻塞  
b. IO過程
IO執行的兩個階段:等待數據和拷貝數據

2.1 阻塞IO


用戶空間的應用程序執行一個系統調用,會導致應用程序阻塞,直到數據準備好,並且將數據從內核複製到用戶進程,最後進程再處理數據,在等待數據到處理數據的兩個階段,整個進程都被阻塞。
調用應用程序處於一種不再消費 CPU 而只是簡單等待響應的狀態,請求延遲短

請求連接量增加,線程池/連接池
存在問題:大規模請求

2.2 非阻塞IO

  • 系統調用後,進程並沒有被阻塞,內核立即返回給進程,如果數據沒有準備好,返回error
  • 重複請求(輪詢),將等待數據過程中整段時間的阻塞變爲小的阻塞
  • 可以在輪詢的間隙執行其他任務,相應的任務完成的響應延遲會增大
  • 輪詢檢查對CPU的消耗

2.3 多路複用IO/事件驅動IO

  • select, poll, epoll
  • select可同時對多個IO端口監聽,當某個socket有數據到達時,通知用戶進程

存在問題:  
select需要探測的句柄值較大
某個事件響應過長時,對事件探測的影響

* epoll,更高效的實現

2.4 信號驅動IO

  • 內核數據準備好後,使用信號通知用戶進程
  • 拷貝數據過程仍是阻塞的

2.5 異步IO(內核2.6以上支持)

異步IO

  • 內核接收到異步read後,會立刻返回,不會產生阻塞;當數據準備完成並拷貝到用戶內存後,內核給用戶進程發一個signal或一個基於線程的回調函數
  • 從kernel的角度,當它受到一個asynchronous read之後,首先它會立刻返回,所以
  • aio_read函數會指定通知方式

3. 多路複用IO,epoll

3.1 等待隊列

等待隊列實現了在事件上的條件等待: 希望等待特定事件的進程把自己放進合適的等待隊列,並放棄控制權
等待隊列即一組睡眠的進程,當某一條件爲真時,由內核喚醒它們

3.2 select/poll

  • select支持的文件描述符默認是1024
  • 每次調用select,都需要把fd集合從用戶態拷貝到內核態,這個開銷在fd很多時會很大
  • 每次調用select,都需要在內核遍歷傳遞進來的所有fd,這個開銷在fd很多時也很大(如服務器有10w連接,某一時間只有一個連接向服務器發送數據,仍會循環10w次,浪費CPU資源)
  • 每次調用開始時,要把current進程放入各個文件描述符的等待隊列。在調用結束後,又把進程從各個等待隊列中刪除
select源碼分析:http://zhangyafeikimi.iteye.com/blog/248815

3.3 epoll

可以維持無限數量的連接,無需輪詢;select和poll都只提供了一個函數,select或者poll函數。而epoll提供了三個函數,epoll_create,epoll_ctl和epoll_wait
epoll具體實現:

(1). int epoll_create(int size)
    創建一個epoll句柄
    param size: 監聽fd的數目
    return: 創建epoll實例的文件描述符
(2). int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
    註冊要監聽的事件類型
    param epfd:create的返回值
    param op: 動作(註冊,修改,刪除fd監聽事件)
    param fd: 監聽fd
    param event: 需要監聽的事件
(3). int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
    等待事件的產生
    return :準備好的文件fd
    檢測到事件,就將所有就緒的事件從內核事件表中複製到events指向的數組中
  • epoll_ctl時把current在各個文件描述符的等待隊列裏掛一遍,併爲每個fd指定一個回調函數;當設備就緒,喚醒等待隊列上的等待者時,就會調用這個回調函數,而這個回調函數會把就緒的fd加入一個就緒鏈表
  • epoll_wait只需要判斷就緒鏈表是否爲空,不需遍歷所有fd,不爲空則從內核態拷貝少量句柄到用戶態
  • epoll註冊時(epoll_ctl),一次拷貝

4. swoole

4.1 reactor模型

  • 事件驅動模型
  • 主要操作
a. add 添加socket到reactor
b. set 修改事件監聽,可設置監聽類型。如對於listen socket監聽新連接進行accept
c. del 從reactor移除,不再監聽
d. callback事件發生後的處理邏輯
  • 一個或多個併發輸入源 - service handler - request handler(處理事件請求)
  • reactor模型:epoll 與 線程池

  • todo

swoole簡單實踐:
http://blog.jobbole.com/98986/
https://github.com/swoole/swoole-src
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章