Linux下的I/O多路复用select,poll,epoll浅析
轉(zhuǎn)載:http://blog.csdn.net/u011573853/article/details/52105365
一,什么是I/O多路復(fù)用?
所謂的I/O多路復(fù)用在英文中其實(shí)叫 I/O multiplexing. 就是單個(gè)線程,通過記錄跟蹤每個(gè)I/O流(sock)的狀態(tài),來同時(shí)管理多個(gè)I/O流 。)?
I/O multiplexing 這里面的 multiplexing 指的其實(shí)是在單個(gè)線程通過記錄跟蹤每一個(gè)Sock(I/O流)的狀態(tài)(對(duì)應(yīng)空管塔里面的Fight progress strip槽)來同時(shí)管理多個(gè)I/O流. 發(fā)明它的原因,是盡量多的提高服務(wù)器的吞吐量?
如圖?
?
二,多路復(fù)用的方式很多今天說一下select原理:?
select函數(shù)會(huì)等待,直到描述符句柄中有可用資源(可讀、可寫、異常)時(shí)返回,返回值是可用資源(可讀/可寫/異常等)描述符的個(gè)數(shù)(>0),0代表超時(shí),-1代表錯(cuò)誤。具體到內(nèi)核大致是:當(dāng)應(yīng)用程序調(diào)用select() 函數(shù), 內(nèi)核就會(huì)相應(yīng)調(diào)用 poll_wait(), 把當(dāng)前進(jìn)程添加到相應(yīng)設(shè)備的等待隊(duì)列上,然后將該應(yīng)用程序進(jìn)程設(shè)置為睡眠狀態(tài)。直到該設(shè)備上的數(shù)據(jù)可以獲取,然后調(diào)用wake_up()喚醒該應(yīng)用程序進(jìn)程。select每次輪訓(xùn)都會(huì)遍歷所有描述符句柄。?
使用select模型的路徑圖如下?
?
三,函數(shù)?
int select(int nfds, fd_set *readfds, fd_set *writefds,?
fd_set *exceptfds, struct timeval *timeout);?
nfds: 監(jiān)控的文件描述符集里最大文件描述符加1,因?yàn)榇藚?shù)會(huì)告訴內(nèi)核檢測(cè)前多少個(gè)文件描述符的狀態(tài)?
readfds:監(jiān)控有讀數(shù)據(jù)到達(dá)文件描述符集合,傳入傳出參數(shù)?
writefds:監(jiān)控寫數(shù)據(jù)到達(dá)文件描述符集合,傳入傳出參數(shù)?
exceptfds:監(jiān)控異常發(fā)生達(dá)文件描述符集合,如帶外數(shù)據(jù)到達(dá)異常,傳入傳出參數(shù)?
timeout:定時(shí)阻塞監(jiān)控時(shí)間,3種情況?
1.NULL,永遠(yuǎn)等下去?
2.設(shè)置timeval,等待固定時(shí)間?
3.設(shè)置timeval里時(shí)間均為0,檢查描述字后立即返回,輪詢?
struct timeval {?
long tv_sec; /* seconds */?
long tv_usec; /* microseconds */?
};?
void FD_CLR(int fd, fd_set *set); 把文件描述符集合里fd清0?
int FD_ISSET(int fd, fd_set *set); 測(cè)試文件描述符集合里fd是否置1?
void FD_SET(int fd, fd_set *set); 把文件描述符集合里fd位置1?
void FD_ZERO(fd_set *set); 把文件描述符集合里所有位清0
select函數(shù)執(zhí)行結(jié)果:執(zhí)行成功則返回文件描述詞狀態(tài)已改變的個(gè)數(shù),如果返回0代表在描述詞狀態(tài)改變前已超過timeout時(shí)間,沒有返回;當(dāng)有錯(cuò)誤發(fā)生時(shí)則返回-1,錯(cuò)誤原因存于errno,此時(shí)參數(shù)readfds,writefds,exceptfds和timeout的值變成不可預(yù)測(cè)。錯(cuò)誤?
值可能為:?
EBADF 文件描述詞為無效的或該文件已關(guān)閉?
EINTR 此調(diào)用被信號(hào)所中斷?
EINVAL 參數(shù)n 為負(fù)值。?
ENOMEM 核心內(nèi)存不足?
要想理解好select模型就要理解好fd_set,為說明方便,取fd_set長(zhǎng)度為1字節(jié),fd_set中的每一bit可以對(duì)應(yīng)一個(gè)文件描述符fd。則1字節(jié)長(zhǎng)的fd_set最大可以對(duì)應(yīng)8個(gè)fd。?
(1)執(zhí)行fd_set set;FD_ZERO(&set);則set用位表示是0000,0000。?
(2)若fd=5,執(zhí)行FD_SET(fd,&set);后set變?yōu)?001,0000(第5位置為1)?
(3)若再加入fd=2,fd=1,則set變?yōu)?001,0011?
(4)執(zhí)行select(6,&set,0,0,0)阻塞等待?
(5)若fd=1,fd=2上都發(fā)生可讀事件,則select返回,此時(shí)set變?yōu)?000,0011。注意:沒有事件發(fā)生的fd=5被清空。?
基于上面的討論,可以輕松得出select模型的特點(diǎn):?
(1)可監(jiān)控的文件描述符個(gè)數(shù)取決與sizeof(fd_set)的值。我這邊服務(wù)器上sizeof(fd_set)=128,每bit表示一個(gè)文件描述符,則我服務(wù)器上支持的最大文件描述符是128*8=1024。?
(2)將fd加入select監(jiān)控集的同時(shí),還要再使用一個(gè)數(shù)據(jù)結(jié)構(gòu)array保存放到select監(jiān)控集中的fd,一是用于再select返回后,array作為源數(shù)據(jù)和fd_set進(jìn)行FD_ISSET判斷。二是select返回后會(huì)把以前加入的但并無事件發(fā)生的fd清空,則每次開始 select前都要重新從array取得fd逐一加入(FD_ZERO最先),掃描array的同時(shí)取得fd最大值maxfd,用于select的第一個(gè)參數(shù)。?
(3)可見select模型必須在select前循環(huán)array(加fd,取maxfd),select返回后循環(huán)array(FD_ISSET判斷是否有時(shí)間發(fā)生)。
四,優(yōu)缺點(diǎn)?
1,簡(jiǎn)單,可以在多種系統(tǒng)上使用?
2,select能監(jiān)聽的文件描述符個(gè)數(shù)受限于FD_SETSIZE,一般為1024,單純改變進(jìn)程打開?
的文件描述符個(gè)數(shù)并不能改變select監(jiān)聽文件個(gè)數(shù)?
3,解決1024以下客戶端時(shí)使用select是很合適的,但如果鏈接客戶端過多,select采用的是輪詢模型,會(huì)大大降低服務(wù)器響應(yīng)效率,不應(yīng)在select上投入更多精力?
五 案例?
server
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
client
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<arpa/inet.h> #include<netinet/in.h> #include<string.h> #define MAXLINE 80 #define SERV_PORT 8000int main(int argc,char *argv[]) {struct sockaddr_in servaddr;char buf[MAXLINE];int sockfd,n;sockfd = socket(AF_INET,SOCK_STREAM,0);bzero(&servaddr,sizeof(servaddr));servaddr.sin_family = AF_INET;inet_pton(AF_INET,"192.168.1.103",&servaddr.sin_addr);servaddr.sin_port = htons(SERV_PORT);connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));while(fgets(buf,MAXLINE,stdin)!=NULL){write(sockfd,buf,strlen(buf));n = read(sockfd,buf,MAXLINE);if (n==0){printf("the other ha closed\n");break;}elsewrite(STDOUT_FILENO,buf,n);}close(sockfd);return 0; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
總結(jié)
以上是生活随笔為你收集整理的Linux下的I/O多路复用select,poll,epoll浅析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DNF在哪里遴选属性
- 下一篇: Linux IO复用区别与epoll详解