I/O 多路復用之select

轉載:http://blog.csdn.net/u012432778/article/details/47347133

概述

Linux提供了三種 I/O 多路復用方案:select,poll和epoll。在這一篇博客里先討論select, poll 在將下一篇中介紹,epoll是Linux特有的高級解決方案,將在接下來中介紹。

select()

select()系統調用提供了一種實現同步 I/O 多路復用的機制:

? ? ? ? ?#include <sys/select.h>

? ? ? ? ?int select (int n,
? ? ? ? ? ? ? ? ? ? ? ? ??fd_set *readfds,
? ? ? ? ? ? ? ? ? ? ? ? ??fd_set *writefds,
? ? ? ? ? ? ? ? ? ? ? ? ??fd_set *exceptfds,
? ? ? ? ? ? ? ? ? ? ? ? ??struct timeval *timeout);

? ? ? ? ?FD_CLR(int fd, fd_set *set);
? ? ? ? ?FD_ISSET(int fd, fd_set *set);
? ? ? ? ?FD_SET(int fd, fd_set *set);
? ? ? ? ?FD_ZERO(fd_set *set);

在給定的文件描述符 I/O 就緒之前并且還沒有超出指定的時間限制,select()調用就會阻塞。

監視的文件描述符可以分為3類,分別等待不同的事件。對于 readfds 集中的文件描述符,監視是否有數據可讀(即某個讀操作是否可以無阻塞完成);對于 writefds 集中的文件描述符,監視是否有某個寫操作可以無阻塞完成;對于 exceptfds 中的文件描述符,監視是否有發生異常,或者出現帶外 (out-of-band) 數據(這些場景只適用于socket)。指定的集合可能是NULL,在這種情況下,select() 不會監視該事件。

成功返回時,每個集合都修改成只包含相應類型的 I/O 就緒的文件描述符。舉個例子,假定 readfds 集中有兩個文件描述符 7 和 9。當調用返回時,如果描述符 7 還在集合中,它在 I/O 讀取時不會阻塞。如果描述符 9 不在集合中,它在讀取時很可能會發生阻塞。(這里說的是“很可能”是因為在調用完成后,數據可能已經就緒了。在這種場景下,下一次調用 select() 就會返回描述符可用。)

第一個參數 n,其值等于所有集合中文件描述符的最大值加 1 。因此,select() 調用負責檢查哪個文件描述符值最大,將該最大值 加 1 后傳給第一個參數。

參數 timeout 是指向 timeval 結構體的指針,定義如下:

? ? ? ? #include <sys/time.h>
? ? ? ? struct timeval {
? ? ? ? ? ? ? ?long tv_sec; ? ? ? ? ? ??/* seconds */
? ? ? ? ? ? ? ?long tv_usec; ? ? ? ? ??/* microseconds */
? ? ? ? };

如果該參數不是NULL,在 tv_sec 秒 tv_usec 微妙后。select() 調用會返回,即使沒有一個文件描述符處于 I/O 就緒狀態。返回時,在不同的UNIX系統中,該結構體是未定義的,因此每次調用必須(和文件描述符集一起)重新初始化。實際上,當前Linux版本會自動修改該參數,把值修改成剩余的時間。因此,如果超時設置是 5 秒,在文件描述符可用之前已逝去了 3 秒,那么在調用返回時,tv.tv_sec 的值就是 2。

如果超時值都是設置成 0,調用會立即返回,調用時報告所有事件都掛起,而不會等待任何后續事件。

不是直接操作文件描述符集,而是通過輔助宏來管理。通過這種方式,UNIX系統可以按照所希望的方式來實現。不過,大多數系統把集合實現成位數組。

FD_ZERO 從指定集合中刪除所有的文件描述符。每次調用 select() 之前,都應該調用該宏。

? ? ? ?fd_set writefds;
? ? ? ?FD_ZERO(&writefds);

FD_SET 向指定集中添加一個文件描述符,而 FD_CLR 則從指定集中刪除一個文件描述符。

? ? ? ?FD_SET(fd, &writefds); ? ? ? ? ? /* add 'fd' to the set */
? ? ? ?FD_CLR(fd, &writefds); ? ? ? ? ? /* oops, remove 'fd' from the set */

設計良好的代碼應該不需要使用FD_CLR,極少使用該宏。

FD_ISSET 檢查一個文件描述符是否在給定集合中。如果在,則返回非0值,否則返回 0。當select() 調用返回時,會通過 FD_ISSET 來檢查文件描述符是否就緒:

? ? ? ?if (FD_ISSET(fd, &readfds))?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?/* 'fd' is readable without blocking */

由于文件描述符集是靜態建立的,所以文件描述符數存在上限值,而且存在最大文件描述符值,這兩個值都是由 FD_SETSIZE 設置。在Linux,該值是1024。

返回值和錯誤碼

select() 調用成功時,返回三個集合中 I/O 就緒的文件描述符總數。如果給出了超時設置,返回值可能是 0 。出錯時。返回 -1 ,并把 errno 值設置成如下值之一:

EBADF ? ? ? ?某個集合中存在非法文件描述符。
EINTR ? ? ? ?等待時捕獲了一個信號,可以重新發起調用。
EINVAL ? ? ?參數 n 是負數,或者設置的超時時間值非法。
ENOMEN ? ?沒有足夠的內存來完成該請求

select() 示例

[cpp]?view plain?copy
  1. #include?<stdio.h>??
  2. #include?<sys/time.h>??
  3. #include?<sys/types.h>??
  4. #include?<unistd.h>??
  5. ??
  6. #define?TIMEOUT???5????????????/*?select?timeout?to?seconds?*/??
  7. #define?BUF_LEN???1024?????????/*?read?buffer?in?bytes?*/??
  8. ??
  9. int?main(void)???
  10. {??
  11. ????struct?timeval?tv;??
  12. ????fd_set?readfds;??
  13. ????int????ret;??
  14. ??
  15. ????/*?Wait?on?stdin?for?input?*/??
  16. ????FD_ZERO(&readfds);??
  17. ????FD_SET(STDIN_FILENO,?&readfds);??
  18. ??
  19. ????/*?Wait?up?to?five?seconds?*/??
  20. ????tv.tv_sec?=?TIMEOUT;??
  21. ????tv.tv_usec?=?0;??
  22. ??
  23. ????/*?All?right,?now?block?*/??
  24. ????ret?=?select(STDIN_FILENO?+?1,??
  25. ?????????????????&readfds,??
  26. ?????????????????NULL,??
  27. ?????????????????NULL,??
  28. ?????????????????&tv);??
  29. ????if?(ret?==?-1)?{??
  30. ????????perror("select");??
  31. ????????return?1;??
  32. ????}?else?if(!ret){??
  33. ????????printf("%d?seconds?eclapsed.\n",?TIMEOUT);??
  34. ????}??
  35. ??
  36. ????/*??
  37. ?????*?Is?our?file?descriptor?ready?to?read??
  38. ?????*?(It?must?be,?as?it?was?the?only?fd?that?
  39. ?????*??we?provied?and?the?call?returned?
  40. ?????*??nonzero,?but?we?will?humor?ourselves.)?
  41. ?????*/??
  42. ????if?(FD_ISSET(STDIN_FILENO,?&readfds))?{??
  43. ????????char?buf[BUF_LEN];??
  44. ????????int?len;??
  45. ????????/*?guaranteed?to?not?block?*/??
  46. ????????len?=?read(STDIN_FILENO,?buf,?BUF_LEN);??
  47. ????????if?(len?==?-1)?{??
  48. ????????????perror("read");??
  49. ????????????return?1;??
  50. ????????}??
  51. ??
  52. ????????if?(len)?{??
  53. ????????????buf[len]?=?'\0';??
  54. ????????????printf("read:?%s\n",?buf);??
  55. ????????}??
  56. ????????return?0;??
  57. ????}??
  58. ??
  59. ????fprintf(stderr,?"This?should?not?happen!\n");??
  60. ????return?1;??
  61. }??

用select() 實現可移植的sleep功能

在各個Unix系統中,相比微秒級的sleep功能,對select() 的實現更普遍,因此select() 調用常常被作為可移植的sleep實現機制:把所有三個集都設置成NULL,超時值設置為非NULL。如下:

? ? ? ?struct timeval tv;
? ? ? ?tv.tv_sec = 0;
? ? ? ?tv.tv_usec = 500;

? ? ? ?/* sleep for 500 microseconds */
? ? ? ?select(0, NULL, NULL, NULL, &tv);
Linux提供了更高精度的sleep機制。

pselect()

select()系統調用很流行,它最初是在 4.2BSD 中引入的,但是 POSIX 標準在 POSIX 1003.1g-2000 和后來的 POSIX 1003.1-2001中定義了自己的 pselect() 方法:

? ? ? ?#define _XOPEN_SOURCE 600
? ? ? ?#include <sys/select.h>

? ? ? ?int pselect(int n,
? ? ? ? ? ? ? ? ? ? ? ? ?fd_set *readfds,
? ? ? ? ? ? ? ? ? ? ? ? ?fd_set *writefds,
? ? ? ? ? ? ? ? ? ? ? ? ?fd_set *exceptfds,
? ? ? ? ? ? ? ? ? ? ? ? ?const struct timespec *timeout,
? ? ? ? ? ? ? ? ? ? ? ? ?const sigset_t *sigmask);

? ? ? ?/* these are the same as those used by select() *
? ? ? ?FD_CLR(int fd, fd_set *set);
? ? ? ?FD_ISSET(int fd, fd_set *set);
? ? ? ?FD_SET(int fd, fd_set *set);
? ? ? ?FD_ZERO(fd_set *set);

pselect() 和 select() 存在三點區別:
  • pselect() 的timeout 參數使用了timespec 結構體,而不是 timeval 結構體。timespec 結構體使用秒和納秒,而不是毫秒,從理論上講更精確些。但是實際上,這兩個結構體在毫秒精度上已經不可靠了。
  • pselect() 調用不會修改 timeout 參數。因此,在后續調用中,不需要重新初始化該參數。
  • select() 系統調用沒有sigmask參數。當這個參數設置為NULL時,pselect() 的行為和select() 相同。
timespec 結構體定義如下:

? ? ? #include <sys/time.h>
? ? ? struct timespec {
? ? ? ? ? ? ?long tv_sec; ? ? ? ? /* seconds */
? ? ? ? ? ? ?long tv_nsec; ? ? ? /* nanoseconds */
? ? ? };

把pselect() 添加到UNIX工具箱主要原因是為了增加 sigmask 參數,該參數是為了解決文件描述符和信號之間等待出現競爭條件。假設信號處理程序設置了全局標志位(大部分如此),進程每次調用 select() 之前會檢查該標志位。現在,假定在檢查標志位和調用之間收到信號,應用可能會一直阻塞,永遠都不會響應該信號。pselect() 提供了一組可阻塞信號,應用在調用時可以設置這些信號來解決這個問題。阻塞的信號要等到解除阻塞才會處理。一旦 pselect() 返回,內核會恢復老的信號掩碼。

在Linux內核2.6.16之前,pselect() 還不是系統調用,而是由 glibc 提供的對 select() 調用的簡單封裝。該封裝對出現競爭的風險最小化,但是沒有完全的消除競爭。當真正引入了新的系統調用pselect() 之后,才徹底解決了這個競爭問題。

雖然和 select() 相比,pselect() 有一定的改進,但大多數應用還是使用 select(),有的是出于習慣,有的是為了更好的可移植性。


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/384349.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/384349.shtml
英文地址,請注明出處:http://en.pswp.cn/news/384349.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

leetcode(283)移動零

283. 移動零 給定一個數組 nums&#xff0c;編寫一個函數將所有 0 移動到數組的末尾&#xff0c;同時保持非零元素的相對順序。 示例: 輸入: [0,1,0,3,12] 輸出: [1,3,12,0,0] 說明: 必須在原數組上操作&#xff0c;不能拷貝額外的數組。 盡量減少操作次數。 方法一&#xff1…

exec函數族實例解析

轉載&#xff1a;http://www.cnblogs.com/blankqdb/archive/2012/08/23/2652386.html fork()函數通過系統調用創建一個與原來進程(父進程)幾乎完全相同的進程(子進程是父進程的副本&#xff0c;它將獲得父進程數據空間、堆、棧等資源的副本。注意&#xff0c;子進程持有的是上述…

leetcode(167)兩數之和 II - 輸入有序數組

兩數之和 II - 輸入有序數組 給定一個已按照 升序排列 的整數數組 numbers &#xff0c;請你從數組中找出兩個數滿足相加之和等于目標數 target 。 函數應該以長度為 2 的整數數組的形式返回這兩個數的下標值。numbers 的下標 從 1 開始計數 &#xff0c;所以答案數組應當滿足 …

常量指針與指針常量的區別(轉帖)

轉載&#xff1a;http://www.cnblogs.com/witty/archive/2012/04/06/2435311.html 三個名詞雖然非常繞嘴&#xff0c;不過說的非常準確。用中國話的語義分析就可以很方便地把三個概念區分開。 一) 常量指針。 常量是形容詞&#xff0c;指針是名詞&#xff0c;以指針為中心的一個…

c/c++錯題總結

1.類名 對象名 默認調用“對象名()”這個構造函數&#xff0c;在棧內存中存在對象名&#xff0c;在堆內存中存在實際對象&#xff1b; 2.類名 對象名(一個或以上個參數) 默認調用相應的構造函數&#xff0c;在棧內存中存在對象名&#xff0c;在堆內存中也是存在實際對象的&a…

智能指針學習筆記

轉載&#xff1a;http://www.cnblogs.com/wuchanming/p/4411878.html 1. 介紹 本文介紹智能指針的使用。智能指針是c 中管理資源的一種方式&#xff0c;用智能指針管理資源&#xff0c;不必擔心資源泄露&#xff0c;將c 程序員 從指針和內存管理中解脫出來&#xff0c;再者&…

c++程序編譯過程

c程序編譯分成四個過程&#xff1a;編譯預處理&#xff0c;編譯&#xff0c;匯編&#xff0c;鏈接 編譯預處理&#xff1a;處理以#為開頭 編譯&#xff1a;將.cpp文件翻譯成.s匯編文件 匯編&#xff1a;將.s匯編文件翻譯成機器指令.o文件 鏈接&#xff1a;匯編生產的目標文件.o…

仿函數(函數對象)

轉載&#xff1a;http://www.cnblogs.com/wuchanming/p/4411867.html 本文乃作者學習《C標準程序庫》的學習筆記&#xff0c;首先介紹了仿函數&#xff08;函數對象&#xff09;和函數適配器&#xff08;配接器&#xff09;的概念&#xff0c;然后列出STL中所有的仿函數&#x…

C++ template —— 動多態與靜多態(六)

轉載&#xff1a;http://www.cnblogs.com/yyxt/p/5157517.html 前面的幾篇博文介紹了模板的基礎知識&#xff0c;并且也深入的講解了模板的特性。接下來的博文中&#xff0c;將會針對模板與設計進行相關的介紹。 ------------------------------------------------------------…

變量之間的區別

全局變量、局部變量、靜態全局變量、靜態局部變量的區別 c變量根據定義具有不同的生命周期&#xff0c;會有不同的作用域&#xff0c;主要有六個作用域&#xff1a;全局作用域&#xff0c;局部作用域&#xff0c;文件作用域&#xff0c;類作用域&#xff0c;語句作用域&#xf…

計算機的網絡體系以及參考模型

計算機的網絡體系以及參考模型一、OSI七層模型二、TCP/IP參考模型三、TCP/IP 五層參考模型四、OSI 模型和 TCP/IP 模型異同比較五、OSI 和 TCP/IP 協議之間的對應關系六、為什么 TCP/IP 去除了表示層和會話層&#xff1f;七、數據如何在各層之間傳輸&#xff08;數據的封裝過程…

C++ 模板詳解(二)

轉載&#xff1a;http://www.cnblogs.com/gw811/archive/2012/10/25/2736224.html 四、類模板的默認模板類型形參 1、可以為類模板的類型形參提供默認值&#xff0c;但不能為函數模板的類型形參提供默認值。函數模板和類模板都可以為模板的非類型形參提供默認值。 2、類模板的類…

c++類對象的創建方式

對象創建限制在堆或棧 c類對象的創建方式對象創建限制在堆或棧C 中的類的對象的建立模式如何將類限制在堆上呢&#xff1f;C 中的類的對象的建立模式 C 中的類的對象的建立模式分為兩張&#xff1a;靜態建立&#xff0c;動態建立 靜態建立&#xff1a;由編譯器為對象在棧空間…

C++ 模板詳解(一)

轉載&#xff1a;http://www.cnblogs.com/gw811/archive/2012/10/25/2738929.html C模板 模板是C支持參數化多態的工具&#xff0c;使用模板可以使用戶為類或者函數聲明一種一般模式&#xff0c;使得類中的某些數據成員或者成員函數的參數、返回值取得任意類型。 模板是一種對類…

劍指Offer09. 用兩個棧實現隊列

class CQueue { public:stack<int> stack1,stack2;CQueue() {//初始化棧while(!stack1.empty()){stack1.pop();}while(!stack2.empty()){stack2.pop();}}void appendTail(int value) {stack1.push(value);}int deleteHead() {if(stack2.empty()){while(!stack1.empty()){…

rk3588 之啟動

目錄 uboot版本配置修改編譯 linux版本配置修改編譯 啟動sd卡啟動制作spi 燒錄 參考 uboot 版本 v2024.01-rc2 https://github.com/u-boot/u-boot https://github.com/rockchip-linux/rkbin 配置修改 使用這兩個配置即可&#xff1a; orangepi-5-plus-rk3588_defconfig r…

C++引用詳解

轉載&#xff1a;http://www.cnblogs.com/gw811/archive/2012/10/20/2732687.html 引用的概念 引用&#xff1a;就是某一變量&#xff08;目標&#xff09;的一個別名&#xff0c;對引用的操作與對變量直接操作完全一樣。 引用的聲明方法&#xff1a;類型標識符 &引用名目標…

劍指Offer03.數組中重復的數字

找出數組中重復的數字。 在一個長度為 n 的數組 nums 里的所有數字都在 0&#xff5e;n-1 的范圍內。數組中某些數字是重復的&#xff0c;但不知道有幾個數字重復了&#xff0c;也不知道每個數字重復了幾次。請找出數組中任意一個重復的數字。 示例 1&#xff1a; 輸入&…

C++ 模板全特化中的函數特化

轉載&#xff1a;http://blog.csdn.net/rain_qingtian/article/details/15815251 [cpp] view plaincopy print?#include <iostream> using namespace std; template<typename T> bool isLess(T x, T y) { cout << "general version\n&q…

c++面向對象總結

c面向對象總結什么是面向對象&#xff1f;面向對象的三大特性重寫和重載的區別隱藏和重寫&#xff0c;重載的區別什么是多態&#xff1f;多態如何實現什么是面向對象&#xff1f;面向對象的三大特性 面向對象&#xff1a;對象是指具體的某一個事物&#xff0c;這些事物的抽象就…