多路IO复用与异步IO
多路IO復用與異步IO
- 同步阻塞IO(BIO)與同步非阻塞IO(NIO)
- 同步阻塞IO
- 同步非阻塞IO
- 多路IO復用
- select()函數(shù)使用示例
- 代碼演示
- poll()函數(shù)使用示例
- 代碼演示
- 異步IO
- 代碼演示
什么時多路IO復用? 多路IO復用是一種同步IO模型,,它可以實現(xiàn)一個線程可以同時監(jiān)視多個文件描述符,一旦有某個文件描述符準備就緒,就會通知應用程序,對該文件描述符進行操作。在監(jiān)視的各個文件描述符沒有準備就緒的時候,應用程序線程就會阻塞,交出cpu,讓cpu去執(zhí)行其他的任務,以此提高cpu的利用率。
同步阻塞IO(BIO)與同步非阻塞IO(NIO)
同步阻塞IO
關于同步阻塞IO可以打個比方就是我們要讀取鍵盤和鼠標但是由于read()函數(shù)的阻塞作用導致我們只能讀取按照程序編寫的讀取順序來讀取兩個設備的數(shù)據(jù),因為如果read()函數(shù)正在阻塞讀取鍵盤數(shù)據(jù),但是此時我們沒有鍵盤輸入,去動鼠標,鼠標就會返回數(shù)據(jù),但是由于read()函數(shù)阻塞在了鍵盤讀取上,所以我們就錯過了讀取鼠標數(shù)據(jù)的機會。
同步非阻塞IO
而同步非阻塞則是在一個while()循環(huán)里面使用設置了非阻塞的read()函數(shù)讀取鍵盤和鼠標數(shù)據(jù),如果沒有讀取到數(shù)據(jù)就會立即返回錯誤,然后繼續(xù)讀取鼠標和鍵盤,直到兩個設備有數(shù)據(jù)來就會成功讀取到數(shù)據(jù)。
多路IO復用
單線程使用select()/poll()等系統(tǒng)調(diào)用實現(xiàn)對多個文件句柄的監(jiān)視,如果有某個文件句柄的事件發(fā)生就會返回,沒有的話就會阻塞等待,單其實select()和poll()都是表現(xiàn)為外部阻塞式,內(nèi)部非阻塞式自動輪詢多路阻塞式IO。
select()函數(shù)使用示例
單個線程打開的FD是有限的,通常默認是1024個,可以通過FD_SETSIZE設置
每次調(diào)用select()都需要把fd集合從用戶態(tài)拷貝到內(nèi)核態(tài),在需要輪詢的fd很多時,會造成較大的性能開銷。
select()前面提到了select()是通過輪詢掃描檢測事件有沒有發(fā)生的所以執(zhí)行效率比較低。
代碼演示
在設置了select()函數(shù)的超時時間后,當函數(shù)超時后就會返回,后面再次執(zhí)行到select()函數(shù)時,select()并不會阻塞了,不管有沒有監(jiān)視的文件句柄事件發(fā)生,select()函數(shù)都會返回超時結(jié)果。
也可應個select()的時間設置參數(shù)地址給NULL,這樣select()函數(shù)就會不斷地監(jiān)視文件句柄。
poll()函數(shù)使用示例
poll()函數(shù)與select()函數(shù)一樣每次調(diào)用都需要把fd集合從用戶態(tài)拷貝到內(nèi)核態(tài),在需要輪詢的fd很多時,會造成較大的性能開銷。
poll()對文件句柄的監(jiān)視也是和select()函數(shù)一樣采用線性掃描的方式進行輪詢,所以在高并發(fā)的情況下效率也是比較底下的。
但是poll()與select()不同的一點就是poll沒有監(jiān)視數(shù)量的限制
代碼演示
在poll的使用過程中當程序開始運行等待超時后,如果發(fā)生IO事件后還能夠檢測到,但是當檢測到IO事件和poll()就不會再超時了,就會一直等待監(jiān)聽事件的發(fā)生。這一點和select()有所不同,select()超時后就不在監(jiān)聽IO事件的發(fā)生了,而是一直返回超時。
#include<stdio.h> #include<stdlib.h> #include<string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/time.h> #include <signal.h> #include <poll.h>int main(void) {int fd=-2;char buf[20];struct pollfd pfd[2]={0}; //監(jiān)視兩個文件句柄所以定義了一個結(jié)構(gòu)體數(shù)組bzero(buf,sizeof(buf));fd=open("/dev/input/mouse0",O_RDONLY);if (fd<0){perror("open:");}printf("already read\n");pfd[0].fd=0; //監(jiān)視的文件描述符pfd[0].events=POLLIN; //設置為監(jiān)視讀取事件pfd[1].fd=fd;pfd[1].events=POLLIN; while (1){int ret=poll(pfd,fd+1,1000);if (ret<0){perror("why");}else if (ret==0){printf("等待超時\n");}else{if(pfd[0].events==pfd[0].revents) //判斷是否為監(jiān)視的事件發(fā)生{bzero(buf,sizeof(buf));read(0,buf,5);printf("read keyboard data=%s\n",buf);}if (pfd[1].events==pfd[1].revents){bzero(buf,sizeof(buf));read(fd,buf,5);printf("read mouse data=%s\n",buf);}}}return 0; }異步IO
異步IO相當于系統(tǒng)內(nèi)核為我們實現(xiàn)的一套軟件中斷程序,如下代碼中的function()函數(shù)就可以比喻為中斷服務函數(shù)。主程序在完成一些異步IO的初始化之后,在while()循環(huán)中檢測鍵盤的輸入,但是如果注冊了異步事件的IO事件發(fā)生,就會去執(zhí)行異步任務函數(shù),即獲取鼠標輸入的數(shù)據(jù)。
代碼演示
#include<stdio.h> #include<stdlib.h> #include<string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <signal.h> int fd=0; void function(int sig) {char buf[5]={0};if (sig!=SIGIO){return;}else{read(fd,buf,sizeof(buf));printf("read mouse data=%s\n",buf);} }int main(void) {char buf[20];int flag=-1;bzero(buf,sizeof(buf));fd=open("/dev/input/mouse0",O_RDONLY);if (fd<0){perror("open:");}printf("already read\n");//把鼠標的文件描述符設置為可以接收一部IOflag=fcntl(fd,F_GETFL);flag|=O_ASYNC;fcntl(fd,F_SETFL,flag);//把異步IO事件的接收進程設置為當前進程fcntl(fd,F_SETOWN,getpid());//注冊當前進程的SINGIO信號捕獲函數(shù)signal(SIGIO,function);while (1){memset(buf,0,sizeof(buf));read(0,buf,sizeof(buf));printf("read keynoard =%s\n",buf);}return 0; }總結(jié)
以上是生活随笔為你收集整理的多路IO复用与异步IO的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python画蜡烛致敬烈士_「」matp
- 下一篇: 微软最强命令行工具 Windows Te