//偽代碼while(1){??read(keyboard);??printf("keyboard...");??read(mouse);??printf("mouse...");}
這樣的程序確實可以讀取鍵盤和鼠標的內容并且打印出來,但是必須老老實實按照代碼里的,先讀鍵盤,再讀鼠標這樣往復,如果用戶想要先讀鼠標,再讀鍵盤,抱歉,它會卡在前面這個read這里,因為read函數是阻塞式的,沒有讀到東西它就一直卡在那里。這顯然不是我們希望的,我們希望像按鍵盤就按鍵盤,想動鼠標就動鼠標,并且它都能打印出來。于是,有了以下幾種方法來解決這個問題。一、以非阻塞的方式來打開文件在使用open函數的時候,加上O_NONBLOCK屬性,變為非阻塞,而標準輸入一開始就打開了,對應文件描述符為0,所以不能用上面的方法,應該用fcntl函數來添加。變為非阻塞式的好處就是當read沒有讀到什么東西的時候會立馬返回,不會卡在那里。代碼如下:#include #include #include #include #include #include #define pathname "/dev/input/mice"int main(){ int fd; int ret; char buf[100]={0}; fd=open(pathname,O_RDWR | O_NONBLOCK); if(fd<0) { perror("open failed"); return 0; } flag = fcntl(0, F_GETFL); // 先獲取原來的flag flag |= O_NONBLOCK; // 添加非阻塞屬性 fcntl(0, F_SETFL, flag); // 更新flag while(1) { memset(buf,0,sizeof(buf)); ret= read(fd,buf,50); //讀鼠標 // if(ret<0) // { // printf("read mouse ret=%d\n",ret); // perror("read mouse failed"); // // return 0; // } if(ret>0) { printf("讀出的鼠標內容是:[%s]\n",buf); } memset(buf,0,sizeof(buf)); ret= read(0,buf,5); //讀鍵盤 // if(ret<0) // { // perror("read keyboard failed"); // printf("read keyboard ret=%d\n",ret); // // return 0; // } if(ret>0) { printf("讀出的鍵盤內容是:[%s]\n",buf); } } return 0;}
上面的代碼其實就實現了不管是按下鍵盤,還是點擊鼠標,都能及時反應,打印出數據。但是還有更好的方法,使用系統里帶的select函數或者是poll函數來監聽IO的狀況。二、使用select函數或者poll函數select函數和poll函數功能上差不多,是Unix兩個不同的派系衍生出來的函數,后來linux把它們都吸收了。select函數在上一節使用到了,可以回顧一下:Linux筆記(11)| 網絡編程之自己動手寫一個服務器和客戶端select函數首先把把要監聽的文件描述符fd添加到一個集合里面,然后調用select函數去監聽,通過返回值可以判斷監聽的fd的狀態,比如已經可寫了、或者是可讀了。代碼如下:#include #include #include #include #include #include #include #include #define pathname "/dev/input/mice"int main(void){ int ret; int fd; char buf[200]; struct timeval tv; tv.tv_sec = 5; tv.tv_usec = 0; fd_set myset; fd=open(pathname,O_RDONLY); if(fd<0) { perror("open mice failed"); return 0; } while(1) { FD_ZERO(&myset); FD_SET(0, &myset); FD_SET(fd, &myset); ret=select(fd+1,&myset,NULL,NULL,NULL); if(ret<0) { perror("select"); return 0; } else if(ret==0) { printf("time out\n"); sleep(2); } else { if( FD_ISSET(fd,&myset)) { memset(buf,0,sizeof(buf)); read(fd,buf,5); printf("讀鼠標:[%s]\n",buf); } if(FD_ISSET(0,&myset)) { memset(buf,0,sizeof(buf)); read(0,buf,5); printf("讀鍵盤:[%s]\n",buf); } } } return 0;}
poll函數實現的功能差不多,只是用法上有些不一樣,這里直接把代碼貼上:#include #include #include #include #include #include #include #define pathname "/dev/input/mice"int main(void){ int fd; int ret; char buf[100]; struct pollfd mypoll[2]={0}; fd=open(pathname,O_RDONLY); if(fd<0) { perror("open failed"); return 0; } while(1) { mypoll[0].fd=0; mypoll[0]. events=POLLIN; mypoll[1].fd=fd; mypoll[1]. events=POLLIN; ret=poll(mypoll,fd+1,10000); if(ret<0) { perror("poll"); return 0; } else if(ret==0) { printf("time out\n"); } else { // printf("mypoll.revents=%d\n",mypoll.revents); // printf("mypoll.events=%d\n",mypoll.events); if(mypoll[0].revents==mypoll[0].events) { memset(buf,0,sizeof(buf)); ret=read(0,buf,10); if(ret<0) { perror("read keyboard failed "); return 0; } printf("read keyboard:[%s]",buf); } if(mypoll[1].revents==mypoll[1].events) { memset(buf,0,sizeof(buf)); ret=read(fd,buf,2); if(ret<0) { perror("read mouse failed "); return 0; } printf("read mouse:[%s]\n",buf); //這里沒加換行就不會及時打印 } } } return 0;}
三、使用異步IO第三種方法就是使用異步IO,這種方法類似于中斷,就是在主函數里來處理鼠標(或者鍵盤也一樣),然后注冊一個異步IO事件,當有鍵盤按下的時候,產生一個異步IO信號,這個信號就會觸發一個注冊號的函數來處理它。代碼如下:#include #include #include #include #include #include #include #include typedef void (*sighandler_t)(int);#define pathname "/dev/input/mice"void handler(int sig);char buf[200]; int fd;int main(void){ int flag; int ret; fd=open(pathname,O_RDONLY); if(fd<0) { perror("open failed"); return 0; } // 把鼠標的文件描述符設置為可以接受異步IO flag=fcntl(fd,F_GETFL); flag|=O_ASYNC; fcntl(fd,F_SETFL,flag); // 把異步IO事件的接收進程設置為當前進程 fcntl(fd,F_SETOWN,getpid()); // 注冊當前進程的SIGIO信號捕獲函數 signal(SIGIO,handler); while(1) { memset(buf,0,sizeof(buf)); ret=read(0,buf,10); // if(ret<0) // { // perror("read failed"); // return 0; // } if(ret>0) printf("read keyboard :[%s]\n",buf); //sleep(2); } return 0;}void handler(int sig){ int ret; if(sig!=SIGIO) return; memset(buf,0,sizeof(buf)); ret=read(fd,buf,5); if(ret<0) { perror("read failed"); return ; } printf("read mouse :[%s]\n",buf);}
以上是今天分享的幾種方法,實際上還可以用多進程或者多線程的方法,這在上一節里也有涉及,這里就不多說了。猜你喜歡:Linux筆記(11)| 網絡編程之自己動手寫一個服務器和客戶端基于紅外傳輸的多點溫度采集系統教你如何用蜂鳴器演奏樂譜