有意思的select~
前言
最近在寫一個小程序,也就是簡單的系統調用,但是神奇的是,我用的這個系統調用剛好就阻塞了。如果你也寫過應用程序,肯定也會遇到過這樣的問題。
后來,發現了select這個好東西,可以用來監聽文件描述。
select的作用
如果我們在read一個文件,如果文件馬上有東西返回,那是非常愉快的事情,但是經常遇到一些情況,read不能馬上返回數據,這時候,會造成我們的線程阻塞,就卡在那里不動。如果是ui界面,那情況就顯得很尷尬,你的ui卡主了,作為一個計算機用戶,那是一件非常崩潰的事情的。
人們為了解決這個問題,select就出現了。
select() and pselect() allow a program to monitor multiple file descriptors, waiting until one or more of the file descriptors become "ready" for some class of I/O operation (e.g., input possible). A file descriptor is considered ready if it is possible to perform the corresponding I/O operation (e.g., read(2)) without blocking.
select 和 pselect 允許程序監聽文件描述符,文件描述符是打開文件的時候返回從一個整數,這個整數代表了一個文件,大家都叫他做文件描述符。直到文件描述符準備好了IO操作。
原來的代碼
#include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <linux/ioctl.h> #include <unistd.h> #include <sys/time.h> #include <sys/types.h>#define RETRY_TIMES (20) #define COM_STR "ezsp ver"int main(int argc, char * const argv[]) {int fd_in,fd_out,size;int retry_times=0;int ret=0;//char s[ ]="info\n",buffer[1024];char s[ ]="version\n",buffer[1024];printf("=== weiqifa ===Zigbee test start ...\n");printf("argc:%d\n",argc);printf("argv[0]:%s\n",argv[0]);/*打開寫管道文件*/fd_in=open("/dev/gateway_in",O_RDWR);if(fd_in<0){printf("===weiqifa=== open error:%d\n",fd_in);return(0);}/*打開讀管道文件*/fd_out=open("/dev/gateway_out",O_RDWR);if(fd_out<0){printf("===weiqifa=== open error:%d\n",fd_out);return(0);}/*循環讀寫*/do{ret = write(fd_in,s,sizeof(s));size= read(fd_out,buffer,sizeof(buffer));printf("%s",buffer);if(strncmp(COM_STR,buffer,strlen(COM_STR) -1) == 0){break;}usleep(3000);}while(retry_times++ <=RETRY_TIMES);if(retry_times>= RETRY_TIMES){printf("\nfail\n");return (-1);}/*關閉管道*/close(fd_out);close(fd_in);printf("\nsuccess\n");return (0); }修改后的代碼
#include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <linux/ioctl.h> #include <unistd.h> #include <sys/time.h> #include <sys/types.h>#define RETRY_TIMES (20) #define COM_STR "ezsp ver"int main(int argc, char * const argv[]) {int fd_in,fd_out,size;int retry_times=0;int ret=0;struct timeval tv;fd_set rdfds;/*清空rdfds*/FD_ZERO(&rdfds);//char s[ ]="info\n",buffer[1024];char s[ ]="version\n",buffer[1024];printf("=== weiqifa ===Zigbee test start ...\n");printf("argc:%d\n",argc);printf("argv[0]:%s\n",argv[0]);/*打開寫管道文件*/fd_in=open("/dev/gateway_in",O_RDWR);if(fd_in<0){printf("===weiqifa=== open error:%d\n",fd_in);return(0);}/*打開讀管道文件*/fd_out=open("/dev/gateway_out",O_RDWR);if(fd_out<0){printf("===weiqifa=== open error:%d\n",fd_out);return(0);}/*循環讀寫*/do{ret = write(fd_in,s,sizeof(s));tv.tv_sec = 1; /*秒*/tv.tv_usec = 500; /*微秒*//*添加監聽的設備描述符*/FD_ZERO(&rdfds);FD_SET(fd_out,&rdfds);/*監聽fd_out*/ret = select(fd_out+1,&rdfds,NULL,NULL,&tv);if(ret<0){printf("selcet error\r\n");retry_times = RETRY_TIMES;break;}else if(ret == 0){ /*超時*/printf("timeout \r\n");retry_times = RETRY_TIMES;break;}else{printf("ret = %d \r\n",ret);}size= read(fd_out,buffer,sizeof(buffer));printf("%s",buffer);if(strncmp(COM_STR,buffer,strlen(COM_STR) -1) == 0){break;}usleep(3000);}while(retry_times++ <=RETRY_TIMES);if(retry_times>= RETRY_TIMES){printf("\nfail\n");return (-1);}/*關閉管道*/close(fd_out);close(fd_in);printf("\nsuccess\n");return (0); }select代碼的小例子
#include <stdio.h>#include <stdlib.h>#include <sys/select.h>intmain(void){fd_set rfds;struct timeval tv;int retval;/* Watch stdin (fd 0) to see when it has input. */FD_ZERO(&rfds);FD_SET(0, &rfds);/* Wait up to five seconds. */tv.tv_sec = 5;tv.tv_usec = 0;retval = select(1, &rfds, NULL, NULL, &tv);/* Don't rely on the value of tv now! */if (retval == -1)perror("select()");else if (retval)printf("Data is available now.\n");/* FD_ISSET(0, &rfds) will be true. */elseprintf("No data within five seconds.\n");exit(EXIT_SUCCESS);}執行 第一次執行的時候,我沒有輸入任何內容,這時候,select就一直監聽標準輸入,因為沒有輸入就一直等,等到了超時時間,程序就退出了。
第二次執行的時候,我給標準輸入輸入東西了,select馬上就返回,并打印了數據是有效的。
深入理解select模型
理解select模型的關鍵在于理解fd_set,為說明方便,取fd_set長度為1字節,fd_set中的每一bit可以對應一個文件描述符fd。則1字節長的fd_set最大可以對應8個fd。
執行fd_set set; FD_ZERO(&set); 則set用位表示是0000,0000。
若fd=5,執行FD_SET(fd,&set);后set變為0001,0000(第5位置為1)
若再加入fd=2,fd=1,則set變為0001,0011
執行select(6,&set,0,0,0)阻塞等待
若fd=1,fd=2上都發生可讀事件,則select返回,此時set變為0000,0011。注意:沒有事件發生的fd=5被清空。
最后舉個例子
執行截圖
===========
??
PS:想加入技術群的同學,加了我好友后,就給我發「籃球的大肚子」這句話,有可能機器人打瞌睡,可以多發幾次,不要發與技術無關的消息或者推廣。
如果想獲取學習資料,就在公眾號后臺回復「1024」,足夠多的學習資料可以讓你學習。
總結
以上是生活随笔為你收集整理的有意思的select~的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php常用函数最全总结
- 下一篇: 电脑连不上wifi的三种处理办法。