Linux中IO多路復用

2021-09-08 03:29:00 字數 3761 閱讀 3756

本篇部落格將說明linux中的阻塞io、非阻塞io以及這兩種進行io操作的困境,最後說明解決這種困境的方法,也就是io多路復用

1、阻塞式io

阻塞式io是指在執行裝置操作時,若不能獲得資源,則掛起程序直到滿足可操作的條件後在進行操作。被掛起的程序進入睡眠狀態,被從排程執行器的執行佇列中移走,直到等待的條件滿足。核心中預設的都是阻塞式的實現方式,因為這種實現方式效率比較高,被阻塞時,該程序並不占用cpu,而是將cpu交出,執行其他的程序。

2、非阻塞式io

非阻塞式io是指當前程序不能夠進行裝置操作的時候,並不會掛起,它要麼放棄,要麼不停的查詢,直到可以進行操作為止。這種方式看似效率很高,但是當長時間得不到資源的時候那麼,該程序將會長時間占用cpu,從而拉低整個系統的效率。非阻塞式訪問有兩種方法,一種時當檔案open的時候使用o_nonblock,以另一種是對已經開啟的問檔案進行屬性修改,使用fctl()。如下:

//開啟是設定

fd = open("/dev/input/mouse1", o_rdonly | o_nonblock); //非阻塞方式訪問 滑鼠

// 把0號檔案描述符(stdin)變成非阻塞式的

flag = fcntl(0, f_getfl); // 先獲取原來的flag

flag |= o_nonblock; // 新增非阻塞屬性

fcntl(0, f_setfl, flag); // 更新flag

// 這3步之後,0就變成了非阻塞式的了

3、阻塞與非阻塞io的困境

就現在來看,將系統中的所有io操作都使用阻塞式的訪問不就行了嗎?但是,事實並不是想象中的那麼簡單,在linux核心中存在著各種我們想不到的情況,都需要兼顧得到。下面舉乙個例子說明阻塞與非阻塞的困境。

在乙個程序中要進行3個io操作,分別是a、b、c。他們都是以阻塞的方式開啟進行訪問的,並且執行順序是a、b、c。現在有個情況是a因為條件不滿足而被阻塞,所以就執行不了b、c,但是b或者c的執行條件都是滿足的,這樣就導致了b、c不能執行的現象,顯然這不是我們想看的的結果。

阻塞式io在自己進行單個io操作的時候,是沒有問題的。但是像上面這種進行多路io操作的時候,就會出現問題。這裡用乙個簡單的程式例項來驗證這種說法。該程式是進行滑鼠與鍵盤的讀操作,並將讀到的資訊輸出到控制台。

#include #include #include #include #include #include int main(void)

memset(buf, 0, sizeof(buf));

printf("before 滑鼠 read.\n");

read(fd, buf, 50);

printf("滑鼠讀出的內容是:[%s].\n", buf);

// 讀鍵盤

memset(buf, 0, sizeof(buf)); //鍵盤就是標準輸入 對應的檔案描述符是0

printf("before 鍵盤 read.\n");

read(0, buf, 5);

printf("鍵盤讀出的內容是:[%s].\n", buf);

return 0;

}

程式最終的現象是,程式開始阻塞在讀滑鼠內容的位置,移動滑鼠或者單擊滑鼠,控制台有資料的列印,然後阻塞在讀鍵盤的位置,鍵盤輸入內容,然後控制台有資料列印,程式執行結束。這樣看好像沒有什麼問題,但是這是我們知道程式的原理的前提下進行的操作。當我們先敲擊鍵再單擊機滑鼠的時候,是沒有任何現象的。這樣顯然是不合理的,因為,這樣對於使用者來說這根本就是致命的bug的存在。

4、多路io復用

對於像這種多路io同時進行操作的問題,我們有很多種方法解決。例如將滑鼠與鍵盤都設定成非阻塞的方式,但是要將鍵盤與滑鼠的讀放在乙個while迴圈中。或者使用非同步io的方式,將滑鼠或者鍵盤其中的乙個做為主函式,另乙個作為類似於中斷的函式,當有資料可讀時就掛起主程式去執行「中斷」函式。等等吧。這裡我要詳細介紹的就是使用io多路復用的方式實現多路io同時進行操作的時候問題。

復用的意思時不用每個程序/執行緒來操控單獨的乙個io,只需乙個程序/執行緒來操控多個io。使用非阻塞io的用用程式通常會使用select()和poll()系統呼叫查詢是否可以對裝置進行無阻塞的訪問(select和poll系統呼叫最終都會使得裝置驅動中的pull函式被執行,這個關於驅動的知識放到以後在講述),其實select與pull系統呼叫本質是一樣的。在應用程式中最廣泛的是select()系統呼叫,其函式原型如下:

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
struct timeval ;
下面的一些操作是用來設定、清除、判斷檔案描述符集合:

void fd_clr(int fd, fd_set *set);    //將乙個檔案描述符從檔案描述符集合中清除

int fd_isset(int fd, fd_set *set); //判斷檔案描述符是否被置位

void fd_set(int fd, fd_set *set); //將乙個檔案描述符加入檔案描述符集合中

void fd_zero(fd_set *set); //清除乙個檔案描述符集合

用乙個例項進行說明select()函式的的使用

/*

使用select函式進行對滑鼠與鍵盤的監聽

*/#include #include #include #include #include #include #include #include int main(void)

// 當前有2個fd,一共是fd 乙個是0

// 處理myset

fd_zero(&myset);

fd_set(fd, &myset);

fd_set(0, &myset);

tm.tv_sec = 10; //等待時間

tm.tv_usec = 0;

ret = select(fd+1, &myset, null, null, &tm);

if (ret < 0)

else if (ret == 0)

else

if (fd_isset(fd, &myset))

}return 0;

}

使用poll()函式實現io多路復用

/*

使用select函式進行對滑鼠與鍵盤的監聽

*/#include #include #include #include #include #include #include int main(void);

fd = open("/dev/input/mouse1", o_rdonly);

if (fd < 0)

// 初始化我們的pollfd

myfds[0].fd = 0; // 鍵盤

myfds[0].events = pollin; // 等待讀操作

myfds[1].fd = fd; // 滑鼠

myfds[1].events = pollin; // 等待讀操作

ret = poll(myfds, fd+1, 10000);

if (ret < 0)

else if (ret == 0)

else

if (myfds[1].events == myfds[1].revents)

}return 0;

}

I O多路復用

一 五種i o模型 1 阻塞i o模型 最流行的i o模型是阻塞i o模型,預設情形下,所有套介面都是阻塞的。我們以資料報套介面為例來講解此模型 我們使用udp而不是tcp作為例子的原因在於就udp而言,資料準備好讀取的概念比較簡單 要麼整個資料報已經收到,要麼還沒有。然而對於tcp來說,諸如套介面...

i o多路復用

最常見的i o多路復用就是 select poll epoll了,下面說說他們的一些特點和區別吧。select 可讀 可寫 異常三種檔案描述符集的申明和初始化。fd set readfds,writefds,exceptionfds fd zero readfds fd zero writefds ...

I O多路復用

我們都知道unix like 世界裡,一切皆檔案,而檔案是什麼呢?檔案就是一串二進位製流而已,不管socket,還是fifo 管道 終端,對我們來說,一切都是檔案,一切都是流。在資訊 交換的過程中,我們都是對這些流進行資料的收發操作,簡稱為i o操作 input and output 往流中讀出資料...