什麼是IO多路複用,理解IO多路複用

1、什麼是socket?

咱們都知道unix(like)世界裏,一切皆文件,而文件是什麼呢?文件就是一串二進制流而已,無論socket,仍是FIFO、管道、終端,對咱們來講,一切都是文件,一切都是流。在信息 交換的過程當中,咱們都是對這些流進行數據的收發操做,簡稱爲I/O操做(input and output),往流中讀出數據,系統調用read,寫入數據,系統調用write。不過話說回來了 ,計算機裏有這麼多的流,我怎麼知道要操做哪一個流呢?對,就是文件描述符,即一般所說的fd,一個fd就是一個整數,因此,對這個整數的操做,就是對這個文件(流)的操做。咱們建立一個socket,經過系統調用會返回一個文件描述符,那麼剩下對socket的操做就會轉化爲對這個描述符的操做。不能不說這又是一種分層和抽象的思想vim

2、阻塞?

什麼是程序的阻塞呢?想象這種情形,好比你等快遞,但快遞一直沒來,你會怎麼作?有兩種方式:網絡

  • 快遞沒來,我能夠先去睡覺,而後快遞來了給我打電話叫我去取就好了。
  • 快遞沒來,我就不停的給快遞打電話說:擦,怎麼還沒來,給老子快點,直到快遞來。

很顯然,你沒法忍受第二種方式,不只耽擱本身的時間,也會讓快遞很想打你。
而在計算機世界,這兩種情形就對應阻塞和非阻塞忙輪詢。socket

  • 非阻塞忙輪詢:數據沒來,進程就不停的去檢測數據,直到數據來。
  • 阻塞:數據沒來,啥都不作,直到數據來了,才進行下一步的處理。

先說說阻塞,由於一個線程只能處理一個套接字的I/O事件,若是想同時處理多個,能夠利用非阻塞忙輪詢的方式,僞代碼以下:函數

while true
{
    for i in stream[]
    {
        if i has data
        read until unavailable
    }
}

咱們只要把全部流從頭至尾查詢一遍,就能夠處理多個流了,但這樣作很很差,由於若是全部的流都沒有I/O事件,白白浪費CPU時間片。正若有一位科學家所說, 計算機全部的問題均可以增長一箇中間層來解決,一樣,爲了不這裏cpu的空轉,咱們不讓這個線程親自去檢查流中是否有事件,而是引進了一個代理(一開始是select,後來是poll),這個代理很牛,它能夠同時觀察許多流的I/O事件,若是沒有事件,代理就阻塞,線程就不會挨個挨個去輪詢了,僞代碼以下:

while true
{
    select(streams[]) //這一步死在這裏,知道有一個流有I/O事件時,才往下執行
    for i in streams[]
    {
        if i has data
        read until unavailable
    }
}

可是依然有個問題,咱們從select那裏僅僅知道了,有I/O事件發生了,卻並不知道是哪那幾個流(可能有一個,多個,甚至所有),咱們只能無差異輪詢全部流,找出能讀出數據,或者寫入數據的流,對他們進行操做。因此select具備O(n)的無差異輪詢複雜度,同時處理的流越多,無差異輪詢時間就越長。spa

epoll能夠理解爲event poll,不一樣於忙輪詢和無差異輪詢,epoll會把哪一個流發生了怎樣的I/O事件通知咱們。因此咱們說epoll其實是事件驅動(每一個事件關聯上fd)的,此時咱們對這些流的操做都是有意義的。(複雜度下降到了O(1))僞代碼以下:線程

while true
{
    active_stream[] = epoll_wait(epollfd)
    for i in active_stream[]
    {
        read or write till
    }
}

能夠看到,select和epoll最大的區別就是:select只是告訴你必定數目的流有事件了,至於哪一個流有事件,還得你一個一個地去輪詢,而epoll會把發生的事件告訴你,經過發生的事件,就天然而然定位到哪一個流了。不能不說epoll跟select相比,是質的飛躍,我以爲這也是一種犧牲空間,換取時間的思想,畢竟如今硬件愈來愈便宜了。代理

3、I/O多路複用

好了,咱們講了這麼多,再來總結一下,到底什麼是I/O多路複用。
先講一下I/O模型:
首先,輸入操做通常包含兩個步驟:unix

  1. 等待數據準備好(waiting for data to be ready)。對於一個套接口上的操做,這一步驟關係到數據從網絡到達,並將其複製到內核的某個緩衝區。
  2. 將數據從內核緩衝區複製到進程緩衝區(copying the data from the kernel to the process)。

其次瞭解一下經常使用的3種I/O模型:code

一、阻塞I/O模型

最普遍的模型是阻塞I/O模型,默認狀況下,全部套接口都是阻塞的。 進程調用recvfrom系統調用,整個過程是阻塞的,直到數據複製到進程緩衝區時才返回(固然,系統調用被中斷也會返回)。接口

1

二、非阻塞I/O模型

當咱們把一個套接口設置爲非阻塞時,就是在告訴內核,當請求的I/O操做沒法完成時,不要將進程睡眠,而是返回一個錯誤。當數據沒有準備好時,內核當即返回EWOULDBLOCK錯誤,第四次調用系統調用時,數據已經存在,這時將數據複製到進程緩衝區中。這其中有一個操做時輪詢(polling)。

2

三、I/O複用模型

此模型用到select和poll函數,這兩個函數也會使進程阻塞,select先阻塞,有活動套接字才返回,可是和阻塞I/O不一樣的是,這兩個函數能夠同時阻塞多個I/O操做,並且能夠同時對多個讀操做,多個寫操做的I/O函數進行檢測,直到有數據可讀或可寫(就是監聽多個socket)。select被調用後,進程會被阻塞,內核監視全部select負責的socket,當有任何一個socket的數據準備好了,select就會返回套接字可讀,咱們就能夠調用recvfrom處理數據。
正由於阻塞I/O只能阻塞一個I/O操做,而I/O複用模型可以阻塞多個I/O操做,因此才叫作多路複用

3