select函數原型如下:int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
函 數的最後一個參數timeout顯然是一個超時時間值,其類型是struct timeval *,即一個struct timeval結構的變量的指針,所以我們在程序裏要申明一個struct timeval tv;然後把變量tv的地址&tv傳遞給select函數。struct timeval結構如下:
struct timeval {long tv_sec; /* seconds */long tv_usec; /* microseconds */};
第2、3、4三個參數的類型是一樣的: fd_set *,即我們在程序裏要申明幾個fd_set類型的變量,比如定義了rfds, wfds, efds。
另外關於fd_set類型的變量,還有一組標準的宏定義來處理此類變量:
FD_ZERO(fd_set *fdset):清空fdset與所有文件描述符的聯繫。
FD_SET(int fd, fd_set *fdset):建立文件描述符fd與fdset的聯繫。
FD_CLR(int fd, fd_set *fdset):清除文件描述符fd與fdset的聯繫。
FD_ISSET(int fd, fd_set *fdset):檢查fd_set聯繫的文件描述符fd是否可讀寫,>0表示可讀寫。
(關 於fd_set及相關宏的定義見/usr/include/sys/types.h)定義的這三個參數都是描述符的集合,第一個rfds是用來保存這樣的 描述符的:當描述符的狀態變成可讀的時系統就會告訴select函數返回,第二個wfds是指有描述符狀態變成可寫的時系統就會告訴select函數返 回,第三個參數efds是特殊情況,即描述符上有特殊情況發生時系統會告訴select函數返回。下面以一個輸入爲例來說明:
int fd1, fd2; /* 在定義兩個描述符*/
fd1 = socket(...); /* 創建socket連接*/
fd2 = open(“/dev/tyS0”,O_RDWR); /* 打開一個串口*/
FD_ZERO(&rfds); /* 用select函數之前先把集合清零 */
FD_SET(fd1, &rfds); /* 分別把2個描述符加入讀監視集合裏去 */
FD_SET(fd2, &rfds);
int maxfd = 0;
maxfd = (fd1>fd2)?(fd1+1):(fd2+1); /* 注意是最大值還要加1 */
ret = select(maxfd, &rfds, NULL, NULL, &tv); /*然後調用select函數*/
這樣就可以使用一個開關語句(switch語句)來判斷到底是哪一個輸入源在輸入數據。具體判斷如下:
switch(ret){
case -1:perror("select");/* 這說明select函數出錯 */
case 0:printf("超時\n"); /* 說明在設定的時間內,socket的狀態沒有發生變化 */
default:
if(FD_ISSET(fd1, &rfds)) 處理函數1();/*socket有數據來*/
if(FD_ISSET(fd2, &rfds)) 處理函數2();/*ttyS0有數據來*/
}
以下來自網絡搜索:
Linux下select調用的過程:
1.用戶層應用程序調用select(),底層調用poll())
2.核心層調用sys_select() ------> do_select()
最終調用文件描述符fd對應的struct file類型變量的struct file_operations *f_op的poll函數。
poll指向的函數返回當前可否讀寫的信息。
1)如果當前可讀寫,返回讀寫信息。
2)如果當前不可讀寫,則阻塞進程,並等待驅動程序喚醒,重新調用poll函數,或超時返回。
3.驅動需要實現poll函數。
當驅動發現有數據可以讀寫時,通知核心層,核心層重新調用poll指向的函數查詢信息。
poll_wait(filp,&wait_q,wait) // 此處將當前進程加入到等待隊列中,但並不阻塞
在中斷中使用wake_up_interruptible(&wait_q)喚醒等待隊列