Linux下IO多路复用之select函数的使用
select函數(shù)的作用:
? ? 如果我們的程序里有兩個需要阻塞的地方,例如要從服務(wù)器讀數(shù)據(jù),同時還要從鍵盤上讀數(shù)據(jù)(若不采用阻塞而用查詢的方式則大量占用系統(tǒng)資源)。這個時候我們就有兩處阻塞,你當(dāng)然可以用多線程或多進程的方法去解決,但我們今天介紹另外一個方法:I/O多路復(fù)用,如select,poll等機制,select它能夠監(jiān)視我們需要監(jiān)視的文件描述符的變化情況——讀寫或是異常。
頭文件: #include <sys/select.h>函數(shù)原型: int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);參數(shù):
1.int maxfdp是一個整數(shù)值,是指集合中所有文件描述符的范圍,即所有文件描述符的最大值加1,不能錯!在Windows中這個? ? ? ?參數(shù)值無所謂,可以設(shè)置不正確。
2.fd_set* readfds是指向fd_set結(jié)構(gòu)的指針,這個集合中應(yīng)該包括文件描述符,我們是要監(jiān)視這些文件描述符的讀變化的,即我? ? 們關(guān)心是否可以從這些文件中讀取數(shù)據(jù)了,如果這個集合中有一個文件可讀,select就會返回一個大于0的值,表示有文件可讀,如果沒有可讀的文件,則根據(jù)timeout參數(shù)再判斷是否超時,若超出timeout的時間,select返回0,若發(fā)生錯誤返回負值。可以入NULL值,表示不關(guān)心任何文件的讀變化。
3.fd_set* writefds是指向fd_set結(jié)構(gòu)的指針,這個集合中應(yīng)該包括文件描述符,我們是要監(jiān)視這些文件描述符的寫變化的,即我們關(guān)心是否可以向這些文件中寫入數(shù)據(jù)了,如果這個集合中有一個文件可寫,select就會返回一個大于0的值,表示有文件可寫,如果沒有可寫的文件,則根據(jù)timeout再判斷是否超時,若超出timeout的時間,select返回0,若發(fā)生錯誤返回負值。可以傳入NULL值,表示不關(guān)心任何文件的寫變化。
4.fe_set* errorfds同上面兩個參數(shù)的意圖,用來監(jiān)視文件錯誤異常。
5.struct timeval* timeout是select的超時時間,這個參數(shù)至關(guān)重要,它可以使select處于三種狀態(tài)。
???? ?第一:若將NULL以形參傳入,即不傳入時間結(jié)構(gòu),就是將select置于阻塞狀態(tài),一定等到監(jiān)視文件描述符集合中某個文件描述符發(fā)生變化為止;
???? ?第二:若將時間值設(shè)為0秒0毫秒,就變成一個純粹的非阻塞函數(shù),不管文件描述符是否有變化,都立刻返回繼續(xù)執(zhí)行,文件無變化返回0,有變化返回一個正值;
???? ?第三:timeout的值大于0,這就是等待的超時時間,即select在timeout時間內(nèi)阻塞,超時時間之內(nèi)有事件到來就返回了,否則在超時后不管怎樣一定返回,返回值同上述。
返回值:
? 負值:select錯誤;
? 正值:某些文件可讀寫或出錯;
? 0:等待超時,沒有可讀寫或錯誤的文件。
錯誤碼:
執(zhí)行成功則返回文件描述詞狀態(tài)已改變的個數(shù),如果返回0代表在描述詞狀態(tài)改變前已超過timeout時間,當(dāng)有錯誤發(fā)生時則返回-1,錯誤原因存于errno,此時參數(shù)readfds,writefds,exceptfds和timeout的值變成不可預(yù)測。
?
函數(shù)說明:
select()用來等待文件描述詞狀態(tài)的改變。參數(shù)maxfdp代表最大的文件描述詞加1,參數(shù)readfds、writefds和exceptfds 稱為描述詞組,是用來回傳該描述詞的讀,寫或例外的狀況。底下的宏提供了處理這三種描述詞組的方式:
FD_CLR(inr fd,fd_set* set); 用來清除描述詞組set中相關(guān)fd的位FD_ISSET(int fd,fd_set *set); 用來測試描述詞組set中相關(guān)fd的位是否為真FD_SET(int fd,fd_set*set); 用來設(shè)置描述詞組set中相關(guān)fd的位FD_ZERO(fd_set *set); 用來清除描述詞組set的全部位結(jié)構(gòu)體說明:
1)struct fd_set可以理解為一個集合,這個集合中存放的是文件描述符(filedescriptor),即文件句柄,這可以是我們所說的普通意義的文件,當(dāng)然Unix下任何設(shè)備、管道、FIFO等都是文件形式,全部包括在內(nèi),所以毫無疑問一個socket就是一個文件,socket句柄就是一個文件描述符。
fd_set集合可以通過一些宏由人為來操作,比如
?
2)struct timeval是一個大家常用的結(jié)構(gòu),用來代表時間值,有兩個成員,一個是秒數(shù),另一個是微妙數(shù)。如下所示:
truct timeval {time_t tv_sec; //seconds 秒time_t tv_usec; //microseconds 微秒,1000000 微秒 = 1秒 }; //頭文件 #include <sys/time.h>注意:
1.select()函數(shù)里面的各個文件描述符fd_set集合的參數(shù)在select()前后發(fā)送了變化:
前:表示關(guān)心的文件描述符集合
后:有數(shù)據(jù)的集合(如不是在超時返回的情況下)
換句話說就是:我們先是設(shè)置了要監(jiān)控的各個I/O的文件描述符到fd_set集合,然后調(diào)用select(),最后fd_set集合只剩下有"異常"(包括讀、寫、異常)的文件描述符,所以上面說到的這些設(shè)置fd_set集合、調(diào)用select()、判斷是哪些IO有數(shù)據(jù)的步驟通常都是while循環(huán)中實現(xiàn)。
2.調(diào)用select函數(shù)前后fd_set集合發(fā)送的變化,是由kernel來完成的。
示例1:Linux下監(jiān)控鍵盤上是否有數(shù)據(jù)到來?
#include <sys/time.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <assert.h>#include <unistd.h> #include <iostream> using namespace std;int main () {int keyboard;int ret,i;char c;fd_set readfd;struct timeval timeout;keyboard = open("/dev/tty",O_RDONLY | O_NONBLOCK);assert(keyboard>0);while(1){timeout.tv_sec=1;timeout.tv_usec=0;FD_ZERO(&readfd); //結(jié)構(gòu)體清0FD_SET(keyboard,&readfd); //把鍵盤文件的句柄加入"讀集合"之中///監(jiān)控函數(shù)ret=select(keyboard+1,&readfd,NULL,NULL,&timeout); //第一個參數(shù)為最大文件句柄+1if(ret == -1) //錯誤情況cout<<"error"<<endl ;else if(ret) //返回值大于0 有數(shù)據(jù)到來if(FD_ISSET(keyboard,&readfd)) //檢查集合中鍵盤文件是否可以讀(因為加入"讀集合"的可能不止鍵盤){ /* 讀出鍵盤的數(shù)據(jù) */i=read(keyboard,&c,1);if('\n'==c)continue;printf("hehethe input is %c\n",c);if ('q'==c)break;}else //超時情況{cout<<"time out"<<endl;continue;}} }示例2:Linux下監(jiān)控多個文件是否可讀(偽代碼)?
1. fd_set tRFds; //定義fd_set類型結(jié)構(gòu)體的變量tRFdsFD_ZERO(&tRFds); //結(jié)構(gòu)體先清0int g_iMaxFd = -1; //定義g_iMaxFd記錄 最大句柄+1 2.比較要監(jiān)控的各個文件的句柄,取出最大值再加1賦給g_iMaxFdif(Fd1 > Fd2){g_iMaxFd = Fd1+1;}else{g_iMaxFd = Fd2+1;} 3.FD_SET(Fd1, &tRFds); //把Fd1、Fd2加入"讀集合"之中FD_SET(Fd2, &tRFds); //Fd1和Fd2為兩個不同的文件句柄... 4.iRet = select(g_iMaxFd, &tRFds, NULL, NULL, NULL);if (iRet > 0) //返回正數(shù)說明有數(shù)據(jù)可讀了{if (FD_ISSET(Fd1, &tRFds)) //是文件1可讀{Fd1_read(...);}else if(FD_ISSET(Fd2, &tRFds)) //是文件2可讀{ Fd2_read(...); } ...}本文主要參考:select函數(shù)的使用_mayue_web的博客-CSDN博客_select函數(shù)用法
總結(jié)
以上是生活随笔為你收集整理的Linux下IO多路复用之select函数的使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 成都欢乐谷三岁小朋友要买票吗
- 下一篇: 雪域迷城剧情介绍