亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 操作系統 > 正文

高級I/O之I/O多路轉接——pool、select

2024-06-28 13:27:59
字體:
來源:轉載
供稿:網友
高級I/O之I/O多路轉接——pool、select

當從一個描述符讀,然后又寫到另一個描述符時,可以在下列形式的循環中使用阻塞I/O:

while ((n = read(STDIN_FILENO, buf, BUFSIZ)) > 0)    if (write(STDOUT_FILENO, buf, n) != n)        err_sys("write error");

這種形式的阻塞I/O到處可見。但是如果必須從兩個描述符讀,又將如何呢?如果仍舊使用阻塞I/O,那么就可能長時間阻塞在一個描述符上,而另一個描述符雖有很多數據卻不能得到及時處理。所以為了處理這種情況顯然需要另一種不同的技術。

讓我們觀察telnet(1)命令的結構。該程序讀終端(標準輸入),將所得數據寫到網絡連接上;同時讀網絡連接,將所得數據寫到終端(標準輸出)。在網絡連接的另一端,telnetd守護進程讀用戶在終端上所鍵入的內容,并將其送給shell,這如同用戶登錄在遠程機器上一樣。telnetd守護進程將執行用戶鍵入命令,而產生的輸出通過telnet命令回送給用戶,并顯示在用戶終端上。圖14-6顯示這種工作情景。

未命名

圖14-6 telnet程序概觀

telnet進程有兩個輸入、兩個輸出。對于這兩個輸入中的任一個都不能使用阻塞read,因為我們永遠不知道哪一個輸入有我們需要的數據。

(參考方法一)處理這種特殊問題的一種方法是,用fork將一個進程變成兩個進程,每個進程處理一條數據通路。圖14-7顯示了這種安排。

未命名

圖14-7 使用兩個進程實現telnet程序

如果使用兩個進程,則可使每個進程都執行阻塞read。但是這也產生了問題:操作什么時候終止?如果子進程接收到文件結束符,telnetd守護進程使網絡連接斷開,那么該子進程終止,然后父進程接收到SIGCHILD信號。但是,如若父進程終止(用戶在終端上鍵入了文件結束符),那么父進程應通知子進程停止。為此可以使用一個信號(例如SIGUSR1),但這使程序變得更加復雜。

(參考方法二)我們可以不使用兩個進程,而是用一個進程中的兩個線程。這避免了終止的復雜性,但卻要求處理線程之間的同步,在減少復雜性方面可能會是得不償失。

(參考方法三)另一個方法是仍舊使用一個進程執行該程序,但使用非阻塞I/O讀取數據。基本方法是將兩個輸入描述符都設置為非阻塞的,對第一個描述符發一個read。如果該輸入上有數據,則讀數據并處理它;如果無數據可讀,則read立即返回。然后對第二個描述符作同樣的處理。在此之后,等待若干秒,然后再讀第一個描述符(這里用一個無限循環)。這種形式的循環稱為輪詢(polling)。這種方法的不足之處是浪費CPU時間。因為大多數時間實際上是無數據可讀的,但是仍花費時間不斷反復執行read系統調用。在每次循環后要等多長時間再執行下一輪循環也很難確定。雖然輪詢技術在支持非阻塞I/O的系統上都可使用,但是在多任務系統中應當避免使用這種方法。

(參考方法四)還有一種技術稱之為異步I/O(asynchronous I/O)。其基本思想是進程告訴內核,當一個描述符已準備好可以進行I/O時,用一個信號通知它。這種技術有兩個問題。第一,并非所有系統都支持這種機制(在Single UNIX Specification中這是一個可選擇的設施)。系統V為此技術提供了SIGPOLL信號,但是僅當描述符引用STREAMS設備時,此信號才能工作。BSD有一個類似的信號SIGIO,但也有類似的限制,僅當描述符引用終端設備或網絡時才能工作。其次,這種信號對每個進程而言只有1一個(SIGPOLL或SIGIO)。如果使該信號對兩個描述符都起作用,那么在接到此信號時進程無法判別是哪一個描述符已準備好可以進行I/O。為了確定是哪一個,仍需將這兩個描述符都設置為非阻塞的,并順序試執行I/O。

(有效方法)一種比較好的技術是使用I/O多路轉接(I/O multiplexing)。先構造一張有關描述符的列表,然后調用一個函數,直到這些描述符中的一個已準備好進行I/O時,該函數才返回。在返回時,它告訴進程哪些描述符已準備好可以進行I/O。

poll、pselect和select這三個函數使我們能夠執行I/O多路轉接。注意基本POSIX.1標準定義了select函數,而poll則是對該基本部分的XSI擴展。

POSIX指定,為了在程序中使用select,必須包括<sys/select.h>。但是歷史上,為了在程序中使用select,還要包括另外三個頭文件,而且某些實現至今還落后在標準之后。為此,要查看select手冊頁,弄清楚你所用的系統對它支持到何種程度。較老的系統要求在程序中包括<sys/types.h>、<sys/time.h>和<unistd.h>。

1、select和pselect函數

在所有依從POSIX的平臺上,select函數使我們可以執行I/O多路轉接。傳向select的參數告訴內核:

  • 我們所關心的描述符。
  • 對于每個描述符我們所關心的狀態。(是否讀一個給定的描述符?是否想寫一個給定的描述符?是否關心一個描述符的異常狀態?)
  • 愿意等待多長時間(可以永遠等待,等待一個固定量時間,或完全不等待)。

從select返回時,內核告訴我們:

  • 已準備好的描述符的數量。
  • 對于讀、寫或異常這三個狀態中的每一個,哪些描述符已準備好。

使用這些返回信息,就可調用相應的I/O函數(一般是read或write),并且確知該函數不會阻塞。

#include <sys/select.h>int select(int maxfdpl, fd_set *restrict readfds,          fd_set *restrict writefds, fd_set *restrict exceptfds,          struct timeval *restrict tvptr);返回值:準備就緒的描述符數,若超時則返回0,若出錯則返回-1

先說明最后一個參數,它指定愿意等待的時間:

struct timeval {    long tv_sec;     /* seconds */    long tv_usec;    /* and microseconds */};

有三種情況:

tvptr==NULL

永遠等待。如果捕捉到一個信號則中斷此無限期等待。當所指定的描述符中的一個已準備好或捕捉到一個信號則返回。如果捕捉到一個信號,則select返回-1,errno設置為EINTR。

tvptr->tv_sec==0 && tvptr->tv_usec==0

完全不等待。測試所有指定的描述符并立即返回。這是得到多個描述符的狀態而不阻塞select函數的輪詢方法。

tvptr->tv_sec!=0 || tvptr->tv_usec!=0

等待指定的秒數和微秒數。當指定的描述符之一已準備好,或當指定的時間值已經超過時立即返回。如果在超時時還沒有一個描述符準備好,則返回值是0(如果系統不提供微秒分辨率,則tvptr->tv_usec值取整到最近的支持值)。與第一種情況一樣,這種等待可被捕捉到的信號中斷。

POSIX.1允許在實現中修改timeval結構中的值,所以在select返回后,你不能指望該結構仍舊保持調用select之前它所包含的值。

中間的三個參數readfds、writefds和exceptfds是指向描述符集的指針。這三個描述符集說明了我們關心的可讀、可寫或處于異常條件的各個描述符。每個描述符集存放在一個fd_set數據類型中。這種數據類型為每一可能的描述符保持了一位,其實現可如圖14-8中所示。

未命名

圖14-8 對select指定讀、寫和異常條件描述符

對fd_set數據類型可以進行的處理是:分配一個這種類型的變量;將這種類型的一個變量值賦予同類型的另一個變量;或對于這種類型的變量使用下列四個函數中的一個。

#include <sys/select.h>int FD_ISSET(int fd, fd_set *fdset);返回值:若fd在描述符集中則返回非0值,否則返回0void FD_CLR(int fd, fd_set *fdset);void FD_SET(int fd, fd_set *fdset);void FD_ZERO(fd_set *fdset);

這些接口可實現為宏或函數。調用FD_ZERO將一個指定的fd_set變量的所有位設置為0。調用FD_SET設置一個fd_set變量的指定位。調用FD_CLR則將一指定位清除。最后,調用FD_ISSET測試一指定位是否設置。

聲明了一個描述符集后,必須用FD_ZERO清除其所有位,然后在其中設置我們關心的各個位。這種操作序列如下所示:

fd_set     rset;int        fd;FD_ZERO(&rset);FD_SET(fd, &rset);FD_SET(STDIN_FILENO, &rset);

從select返回時,用FD_ISSET測試該集中的一個給定位是否仍舊設置:

if (FD_ISSET(fd, &rset)){    ...}

select的中間三個參數(指向描述符集的指針)中的任意一個或全部都可以是空指針,這表示對相應狀態并不關心。如果所有三個指針都是空指針,則select提供了較sleep更精確的計時器。(回憶http://www.CUOXin.com/nufangrensheng/p/3517365.html中,sleep等待整數秒,而對于select,其等待的時間可以小于1s;其實際分辨率取決于系統時鐘。)

select的第一個參數maxfdpl的意思是“最大描述符加1”。在三個描述符集中找出最大描述符編號值,然后加1,這就是第一個參數值。也可將第一個參數設置為FD_SETSIZE,這是<sys/select.h>中的一個常量,它說明了最大的描述符數(經常是1024)。但是對大多數應用程序而言,此值太大了,多數應用程序只使用3-10個描述符。(某些應用程序使用更多的描述符,但這種UNIX程序并不具代表性。) 如果將第一個參數設置為我們所關注的最大描述符編號值加1,內核就只需在此范圍內尋址打開的位,而不必在三個描述符集中的百位內搜索。

例如,若編寫如下代碼:

fd_set    readset, writeset;FD_ZERO(&readset);FD_ZERO(&writeset);FD_SET(0, &readset);FD_SET(3, &readset);FD_SET(1, &writeset);FD_SET(2, &writeset);select(4, &readset, &writeset, NULL, NULL);

那么,下圖顯示了這兩個描述符集的情況。

20130715141645921

因為描述符編號從0開始,所以要在最大描述符編號值上加1。第一個參數實際上是要檢查的描述符數(從描述符0開始)。

select有三個可能的返回值

(1)返回值-1表示出錯。出錯是有可能的,例如在指定的描述符都沒有準備好時捕捉到一個信號。在此種情況下,將不修改其中任何描述符集。

(2)返回值0表示沒有描述符準備好。若指定的描述符都沒有準備好,而且指定的時間已經超過,則發生這種情況。此時,所有描述符集皆被清0。

(3)正返回值表示已經準備好的描述符數,該值是三個描述符集中已準備好的描述符數之和,所以如果同一描述符已準備好讀和寫,那么在返回值中將其記為2。在這種情況下,三個描述符集仍舊打開的位對應于已準備好的描述符

對于“準備好”的意思要作一些更具體的說明:

  • 若對讀集(readfds)中的一個描述符的read操作將不會阻塞,則此描述符是準備好的。
  • 若對寫集(writefds)中的一個描述符的write操作將不會阻塞,則此描述符是準備好的。
  • 若異常狀態集(exceptfds)中的一個描述符有一個未決異常狀態,則此描述符是準備好的。

現在,異常狀態包括(a)在網絡連接上到達的帶外數據(http://blog.chinaunix.net/uid-27164517-id-3275870.html),或者(b)在處于數據包模式的偽終端上發生了某些狀態。

  • 對于讀、寫和異常狀態,普通文件描述符總是返回準備好。

應當理解,一個描述符阻塞與否并不影響select是否阻塞。也就是說,如果希望讀一個非阻塞描述符,并且以超時值為5s調用select,則select最多阻塞5s。相類似地,如果指定一個無限超時值,則在該描述符數據準備好或捕捉到一個信號之前,select一直阻塞。

如果在一個描述符上碰到了文件結尾處,則select認為該描述符是可讀的。然后調用read,它返回0,這是UNIX系統指示到達文件結尾處的方法。(很多人錯誤地認為,當到達文件結尾處時,select會指示一個異常狀態。)

POSIX.1也定義了一個select的變體,它被稱為pselect。

#include <sys/select.h>int pselect(int maxffdpl, fd_set *restrict readfds,            fd_set *restrict writefds, fd_set *restrict exceptfds,            const struct timespec *restrict tsptr,            const sigset_t *restrict sigmask);返回值:準備就緒的描述符數,若超時則返回0,若出錯則返回-1

除下列幾點外,pselect與select相同:

select的超時值用timeval結構指定,但pselect使用timespec結構。(回憶http://www.CUOXin.com/nufangrensheng/p/3521654.html中timespec結構的定義。) timespec結構以秒和納秒表示超時值,而非秒和微秒。如果平臺支持這樣精細的粒度,那么timespec就提供了更精準的超時時間。

pselect的超時值被聲明為const,這保證了調用pselect不會改變此值。

對于pselect可使用一可選擇的信號屏蔽字。若sigmask為空,那么在與信號有關的方面,pselect的運行狀況和select相同。否則,sigmask指向一信號屏蔽字,在調用pselect時,以原子操作的方式安裝該信號屏蔽字。在返回時回復以前的信號屏蔽字。

2、poll函數

poll函數類似于select,但是其程序員接口則有所不同。我們將會看到,雖然poll函數可用于任何類型的文件描述符,但它起源于系統V,所以poll與STREAMS系統僅僅相關。

#include <poll.h>int poll(struct pollfd fdarray[], nfds_t nfds, int timeout);返回值:準備就緒的描述符數,若超時則返回0,若出錯則返回-1

與select不同,poll不是為每個狀態(可讀性、可寫性和異常狀態)構造一個描述符集,而是構造一個pollfd結構數組,每個數組元素指定一個描述符編號以及其所關心的狀態。

struct pollfd {    int       fd;         /* file descriptor to check, or <0 to ignore */    short     events;     /* events of interest on fd */    short     revents;    /* events that occurred on fd */};

fdarray數組中的元素數由nfds說明。

應將每個數組元素的events成員設置為表14-6中所示的值。通過這些值告訴內核我們對該描述符關心的是什么。返回時,內核設置revents成員,以說明對于該描述符已經發生了什么事件。(注意,poll沒有更改events成員,這與select不同,select修改其參數以指示哪一個描述符已準備好了。)

表14-6 poll的events和revents標志

未命名

表14-6中頭四行測試可讀性,接著三行測試可寫性,最后三行則是測試異常狀態。最后三行是由內核在返回時設置的。即使在events字段中沒有指定這三個值,如果相應條件發生,則在revents中也會返回它們。

當一個描述符被掛斷(POLLHUP)后,就不能在寫向該描述符。但是仍可能從該描述符讀取到數據。

poll的最后一個參數說明我們愿意等待多少時間。如同select一樣,有三種不同的情形:

timeout == –1 永遠等待。(某些系統在<stropts.h>中定義了常量INFTIM,其值通常是-1。)當所指定的描述符中的一個已準備好,或捕捉到一個信號時則返回。如果捕捉到一個信號,則poll返回-1,errno設置為EINTR。

timeout == 0 不等待。測試所有描述符并立即返回。這是得到很多個描述符的狀態而不阻塞poll函數的輪詢方法。

timeout > 0 等待timeout毫秒。當指定的描述符之一已經準備好,或指定的時間值已超過時立即返回。如果已超過但是還沒有一個描述符準備好,則返回值是0.(如果系統不提供毫秒分辨率,則timeout值取整到最近的支持值。)

應當理解文件結束與掛斷之間的區別。如果正從終端輸入數據,并鍵入文件結束字符,POLLIN被打開,于是就可讀文件結束指示(read返回0)。POLLHUP在revents中沒有打開。如果正在讀調制解調器,并且電話已掛斷,則在revents中將接到POLLHUP通知。

與select一樣,不論一個描述符是否阻塞,都不影響poll是否阻塞。

select和poll的可中斷性

中斷的系統調用的自動再啟動是由4.2BSD引進的(見http://www.CUOXin.com/nufangrensheng/p/3515035.html) ,但當時select函數是不再啟動的。這種特性在大多數系統中一直延續了下來,即使指定了SA_RESTART也是如此。但是,在SVR4之下,如果指定了SA_RESTART,那么select和poll也是自動再啟動的。為了在將軟件移植到SVR4派生的系統上時防止這一點,如果信號可能終端對select或poll的調用,則總是使用signal_intr函數(見http://www.CUOXin.com/nufangrensheng/p/3515945.html中的程序清單10-13)。

本篇博文內容摘自《UNIX環境高級編程》(第二版),僅作個人學習記錄所用。關于本書可參考:http://www.apuebook.com/。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
中文字幕亚洲综合久久筱田步美| 精品福利在线看| 亚洲精品av在线播放| 日韩亚洲国产中文字幕| 欧美成人免费全部观看天天性色| 最近2019年好看中文字幕视频| 国产欧美一区二区白浆黑人| 亚洲成人1234| 欧美日韩一二三四五区| 色哟哟入口国产精品| 亚洲欧美在线x视频| 亚洲理论电影网| 欧美亚洲免费电影| 国产成人精品久久二区二区| 中文字幕日韩电影| 91精品成人久久| 三级精品视频久久久久| 黑人狂躁日本妞一区二区三区| 国产精品久久久久久av| 国产九九精品视频| 中文字幕亚洲综合久久筱田步美| 亚洲国产精品va在线观看黑人| 在线播放精品一区二区三区| 91亚洲精品视频| 亚洲第一区在线观看| 国产精品成人久久久久| 欧美成人黑人xx视频免费观看| 日韩a**站在线观看| 久久精品国产96久久久香蕉| 成人免费在线网址| 国产欧美在线观看| 日韩有码在线播放| 国产精品9999| 国产精品视频免费在线| 久久久综合免费视频| 日韩成人在线免费观看| 久久人人爽国产| 欧美疯狂做受xxxx高潮| 欧美日韩国产丝袜另类| 热门国产精品亚洲第一区在线| 亚洲欧洲日本专区| 亚洲高清av在线| 疯狂欧美牲乱大交777| 国产aaa精品| 久久综合九色九九| 国产精品激情av电影在线观看| 亚洲天堂免费视频| 欧美精品在线免费播放| 亚洲精品自拍偷拍| 欧美成人免费播放| 国产成人精品综合| 精品久久久久久久久久| 国产福利视频一区二区| 亚洲精品www久久久| 91色在线视频| 亚洲国产毛片完整版| 欧美在线性视频| 色综合五月天导航| 亚洲精品v天堂中文字幕| 亚洲人a成www在线影院| 色中色综合影院手机版在线观看| 日本久久久久久久| 欧美日韩国产在线播放| 日韩成人中文电影| 在线观看日韩视频| 欧美日韩国产综合新一区| 久久精品视频中文字幕| 国产精品一区=区| 国产精品久久久久aaaa九色| 中文字幕欧美视频在线| 亚洲欧洲国产伦综合| 精品亚洲一区二区三区在线播放| 欧美在线播放视频| 神马久久久久久| 国产91网红主播在线观看| 亚洲精品综合久久中文字幕| 精品免费在线视频| 国产一级揄自揄精品视频| 久久777国产线看观看精品| 国语自产精品视频在线看一大j8| 亚洲精品福利在线| 97碰碰碰免费色视频| 精品毛片网大全| 欧洲永久精品大片ww免费漫画| 在线观看不卡av| 亚洲iv一区二区三区| 97在线观看免费高清| 欧美高清无遮挡| 久久久久久久久国产精品| 国产精品日韩欧美综合| 福利微拍一区二区| 粉嫩老牛aⅴ一区二区三区| 国产精品第1页| 欧美日韩亚洲一区二| 亚洲色图25p| 国产一区二区三区18| 亚洲视频免费一区| 中文字幕国产精品久久| 日韩欧美在线观看视频| 插插插亚洲综合网| 国产视频精品va久久久久久| 九九精品视频在线观看| 国产网站欧美日韩免费精品在线观看| 亚洲欧美综合区自拍另类| 精品亚洲一区二区三区| 国产成人一区三区| 97**国产露脸精品国产| 一区二区av在线| 精品视频中文字幕| 日韩精品欧美激情| 国产欧美中文字幕| 国产不卡视频在线| 精品国产91久久久久久老师| 成年人精品视频| 欧美巨乳美女视频| 国产精品美女呻吟| 亚洲视屏在线播放| 久久精品国产成人| 日韩亚洲欧美成人| 伊人青青综合网站| 国产精品福利在线| 欧美激情高清视频| 国产精品精品国产| 国产91在线播放九色快色| 亚洲国内精品视频| 另类少妇人与禽zozz0性伦| 精品久久久香蕉免费精品视频| 狠狠干狠狠久久| 97超碰蝌蚪网人人做人人爽| 最好看的2019的中文字幕视频| 中文字幕欧美日韩精品| 国产欧美日韩中文| 欧美老少配视频| 一本久久综合亚洲鲁鲁| 日韩精品在线第一页| 伊人久久男人天堂| 亚洲精品电影网在线观看| 亚洲男人天堂网站| 欧美中文字幕在线播放| 亚洲深夜福利视频| 亚洲精品国精品久久99热| 日韩中文字幕免费视频| 91精品国产高清自在线看超| 最新国产成人av网站网址麻豆| 77777亚洲午夜久久多人| 精品国产一区二区三区久久久狼| 成人欧美一区二区三区黑人| 国产免费观看久久黄| 国产69久久精品成人看| 中文字幕欧美日韩| 美女视频黄免费的亚洲男人天堂| 欧美日韩亚洲精品一区二区三区| 欧美日本啪啪无遮挡网站| 欧美综合在线第二页| 亚洲成人精品久久久| 欧美性猛交xxxx免费看漫画| 欧美成人全部免费| 亚洲乱亚洲乱妇无码| 亚洲欧美一区二区三区久久| 久久久久久久网站| 最近2019中文字幕在线高清| 亚洲一区亚洲二区亚洲三区| 亚洲欧美日韩一区二区在线| 久久噜噜噜精品国产亚洲综合|