1、Unix可用的5種IO模型和區別:數組
1.阻塞式IO網絡
2.非阻塞式IO異步
3.IO複用(select和poll)socket
4.信號驅動式IO(SIGIO)函數
5.異步IO(POSIX的aio_系列函數)spa
2、1.阻塞式IO模型:code
最流行的IO模型是阻塞式IO模型orm
應用進程 內核blog
(recvfrom)------>系統調用---------> 無數據報準備好進程
|
等待數據
|
數據報準備好
|
將數據從內核複製到用戶空間
|
處理數據報<-----返回成功指示<----- 複製完成
進程調用recvform,其系統調用直到數據報到達且被複制到應用進程的緩衝區中或者發生錯誤才返回。最多見的錯誤是系統調用被信號中斷,咱們說進程在從調用recvform開始到它返回的整段時間內是被阻塞的。recvfrom成功返回後,應用進程開始處理數據報。
2.非阻塞式IO模型
應用進程 內核
recvfrom----->系統調用---------> 無數據準備好
<---EWOULDBLOCK----
recvfrom----->系統調用---------> 無數據準備好
<---EWOULDBLOCK----
... ...
recvfrom------->系統調用-------> 數據準備好
複製數據報
|
處理數據報<------返回成功指示------完成複製
進程把一個套接字設置成非阻塞:當全部請求的IO操做非得把本進程投入睡眠才能完成時,不要把本進程投入睡眠,而是返回一個錯誤。
當一個一個應用程序對一個非阻塞描述符循環調用recvfrom時,咱們稱之爲輪訓polling。應用程序持續輪訓內核,以查看某個操做是否就緒。
這麼作每每消耗大量CPU時間。
三、IO複用模型
有了IO複用,咱們就能夠調用select或poll,阻塞在這2個系統調用中的某一個之上,而不是阻塞在真正的IO系統調用上。
應用進程 內核
select ------系統調用-----------> 無數據準備好
|
等待數據
|
<-----返回可讀條件----- 數據準備好
recvfrom ----->系統調用------------> 複製數據報
|
將用戶從內核複製到用戶控件
|
處理數據報<----------返回成功指示-----複製完成
咱們阻塞於select調用,等待數據報套接字變爲可讀。當selsect返回套接字可讀這一條件時,咱們調用recvfrom把所讀數據報復制到應用進程緩衝區。
和上面模型比較IO複用並不顯得有什麼優點,事實上因爲使用select須要兩個而不是單個系統調用,IO複用還稍有劣勢。
不過使用select的優點在於咱們能夠等待多個描述符就緒。
select 代碼演示:
#include "unp.h" //裏面包含了全部關於網絡變成的頭文件 #include <vector> void main() { int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { printf("建立監聽套接字失敗\n"); return; } //建立sockaddr sockaddr_in listenAddr; listenAddr.sin_family = AF_INET; listenAddr.sin_port = htons(9000); listenAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //綁定端口 if (bind(sockfd, (sockaddr*)&listenAddr, sizeof(listenAddr)) < 0) { printf("bind error\n"); return; } //監聽 if (listen(sockfd, 5) < 0) { printf("listen error\n"); return; } printf("start listen... port:%d\n", listenAddr.sin_port); //建立描述符集合,這裏只有讀集合和所有基本 //使用select時最多見的兩個錯誤:1.忘了對最大描述符+1;2.忘了描述符集合是 //值-結果參數.每次調用selcet時,描述將指示哪些描述符以就緒。 fd_set rset, allset; int maxfd = sockfd + 1; //最大描述符+1 char recvline[256] = { 0 }; std::vector<int> clients;//用於保存連接客戶端的描述符 FD_ZERO(&rset); FD_ZERO(&allset); FD_SET(sockfd, &allset); for(;;) { rset = allset; int ret = select(maxfd, &rset, NULL, NULL, NULL); //客戶端連接 if (FD_ISSET(sockfd, &rset)) { //當客戶連接準備好時調用accpt函數接受客戶端連接,返回連接描述符 int connfd = accept(sockfd, NULL, NULL); if (connfd < 0) { printf("accept error\n"); continue; } FD_SET(connfd, &allset); //將新創建連接的客戶端加入到客戶端數組 clients.push_back(connfd); if (connfd + 1 > maxfd) { maxfd = connfd + 1; } printf("client connected fd = %d\n", connfd); if (--ret <= 0) { continue; } } //有數據到來 for(auto it = clients.begin(); it != clients.end(); it++) //check all clients for data { int fd = *it; if (FD_ISSET(fd, &rset)) { int len = recv(fd, recvline, 256, 0); if (len <= 0) { close(fd); it = clients.erase(it); FD_CLR(fd, &allset); printf("client %d disconnect\n", fd); } else { printf("FD:%d %s", fd, recvline); send(fd, recvline, 256, 0); memset(recvline, 0, 256); } if (--ret <= 0) { break; } } } } }