生活随笔
收集整理的這篇文章主要介紹了
Linux网络编程——I/O复用之poll函数
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
一、回顧前面的select
select優(yōu)點(diǎn):
目前幾乎在所有的平臺(tái)上支持,其良好跨平臺(tái)支持也是它的一個(gè)優(yōu)點(diǎn)
select缺點(diǎn):
1.每次調(diào)用 select(),都需要把 fd 集合從用戶態(tài)拷貝到內(nèi)核態(tài),這個(gè)開銷在 fd 很多時(shí)會(huì)很大,同時(shí)每次調(diào)用 select() 都需要在內(nèi)核遍歷傳遞進(jìn)來的所有 fd,這個(gè)開銷在 fd 很多時(shí)也很大。
?
2.單個(gè)進(jìn)程能夠監(jiān)視的文件描述符的數(shù)量存在最大限制,在 Linux 上一般為 1024,可以通過修改宏定義甚至重新編譯內(nèi)核的方式提升這一限制,但是這樣也會(huì)造成效率的降低
二、poll函數(shù)概述
select() 和 poll() 系統(tǒng)調(diào)用的本質(zhì)一樣,poll() 的機(jī)制與 select() 類似,與 select() 在本質(zhì)上沒有多大差別,管理多個(gè)描述符也是進(jìn)行輪詢,根據(jù)描述符的狀態(tài)進(jìn)行處理,但是 poll()沒有最大文件描述符數(shù)量的限制(但是數(shù)量過大后性能也是會(huì)下降)。poll() 和 select() 同樣存在一個(gè)缺點(diǎn)就是,包含大量文件描述符的數(shù)組被整體復(fù)制于用戶態(tài)和內(nèi)核的地址空間之間,而不論這些文件描述符是否就緒,它的開銷隨著文件描述符數(shù)量的增加而線性增大。
poll()函數(shù)介紹
頭文件:
[csharp]?view plain
?copy #include?<poll.h>?? 函數(shù)體:
[csharp]?view plain
?copy int?poll(struct?pollfd?*fds,?nfds_t?nfds,?int?timeout);?? 功能: 監(jiān)視并等待多個(gè)文件描述符的屬性變化
參數(shù):
fds:指向一個(gè)結(jié)構(gòu)體數(shù)組的第0個(gè)元素的指針,每個(gè)數(shù)組元素都是一個(gè)struct pollfd結(jié)構(gòu),用于指定測(cè)試某個(gè)給定的fd的條件
?
[csharp]?view plain
?copy struct?pollfd{??????int?fd;?????????????short?events;???????short?revents;??};?? fd:每一個(gè) pollfd 結(jié)構(gòu)體指定了一個(gè)被監(jiān)視的文件描述符,可以傳遞多個(gè)結(jié)構(gòu)體,指示 poll() 監(jiān)視多個(gè)文件描述符。
events:指定監(jiān)測(cè)fd的事件(輸入、輸出、錯(cuò)誤),每一個(gè)事件有多個(gè)取值,如下:
revents:revents 域是文件描述符的操作結(jié)果事件,內(nèi)核在調(diào)用返回時(shí)設(shè)置這個(gè)域。events 域中請(qǐng)求的任何事件都可能在 revents 域中返回.
?
注意:每個(gè)結(jié)構(gòu)體的 events 域是由用戶來設(shè)置,告訴內(nèi)核我們關(guān)注的是什么,而 revents 域是返回時(shí)內(nèi)核設(shè)置的,以說明對(duì)該描述符發(fā)生了什么事件
?
nfds:用來指定第一個(gè)參數(shù)數(shù)組元素個(gè)數(shù)
timeout: 指定等待的毫秒數(shù),無論 I/O 是否準(zhǔn)備好,poll() 都會(huì)返回.
?
?
返回值:
成功時(shí),poll() 返回結(jié)構(gòu)體中 revents 域不為 0 的文件描述符個(gè)數(shù);如果在超時(shí)前沒有任何事件發(fā)生,poll()返回 0;
失敗時(shí),poll() 返回 -1,并設(shè)置 errno 為下列值之一:
EBADF:一個(gè)或多個(gè)結(jié)構(gòu)體中指定的文件描述符無效。
EFAULT:fds 指針指向的地址超出進(jìn)程的地址空間。
EINTR:請(qǐng)求的事件之前產(chǎn)生一個(gè)信號(hào),調(diào)用可以重新發(fā)起。
EINVAL:nfds 參數(shù)超出 PLIMIT_NOFILE 值。
ENOMEM:可用內(nèi)存不足,無法完成請(qǐng)求。
?
三、poll示例舉例
用poll實(shí)現(xiàn)udp同時(shí)收發(fā)
代碼:
[csharp]?view plain
?copy #include?<string.h>??#include?<stdio.h>??#include?<stdlib.h>??#include?<unistd.h>??#include?<sys/select.h>??#include?<sys/time.h>??#include?<sys/socket.h>??#include?<netinet/in.h>??#include?<arpa/inet.h>??#include?<poll.h>????int?main(int?argc,char?*argv[])??{??????int?udpfd?=?0;??????int?ret?=?0;??????struct?pollfd?fds[2];????struct?sockaddr_in?saddr;??????struct?sockaddr_in?caddr;????????bzero(&saddr,sizeof(saddr));??????saddr.sin_family?=?AF_INET;??????saddr.sin_port???=?htons(8000);??????saddr.sin_addr.s_addr?=?htonl(INADDR_ANY);????????????bzero(&caddr,sizeof(caddr));??????caddr.sin_family??=?AF_INET;??????caddr.sin_port????=?htons(8000);????????????????if(?(udpfd?=?socket(AF_INET,SOCK_DGRAM,?0))?<?0)??????{??????????perror("socket?error");??????????exit(-1);??????}????????????????if(bind(udpfd,?(struct?sockaddr*)&saddr,?sizeof(saddr))?!=?0)??????{??????????perror("bind?error");??????????close(udpfd);?????????????????exit(-1);??????}????????printf("input:?\"sayto?192.168.220.X\"?to?sendmsg?to?somebody\033[32m\n");????????fds[0].fd?=?0;??????????fds[1].fd?=?udpfd;????????????fds[0].events?=?POLLIN;?????fds[1].events?=?POLLIN;???????????while(1)??????{?????????????????????????????ret?=?poll(fds,?2,?-1);?????????????????????write(1,"UdpQQ:",6);????????????????????if(ret?==?-1){?????????????perror("poll()");????????????}??????????else?if(ret?>?0){?????????????char?buf[100]?=?{0};????????????????if(?(?fds[0].revents?&?POLLIN?)?==??POLLIN?){???????????????????????????????????fgets(buf,?sizeof(buf),?stdin);??????????????????buf[strlen(buf)?-?1]?=?'\0';??????????????????if(strncmp(buf,?"sayto",?5)?==?0)??????????????????{??????????????????????char?ipbuf[16]?=?"";??????????????????????inet_pton(AF_INET,?buf+6,?&caddr.sin_addr);????????????????????printf("\rsay?to?%s\n",inet_ntop(AF_INET,&caddr.sin_addr,ipbuf,sizeof(ipbuf)));??????????????????????continue;??????????????????}??????????????????else?if(strcmp(buf,?"exit")==0)??????????????????{??????????????????????close(udpfd);??????????????????????exit(0);??????????????????}??????????????????sendto(udpfd,?buf,?strlen(buf),0,(struct?sockaddr*)&caddr,?sizeof(caddr));????????????????????????????????????}??????????????else?if(?(?fds[1].revents?&?POLLIN?)?==??POLLIN?){?????????????????struct?sockaddr_in?addr;??????????????????char?ipbuf[INET_ADDRSTRLEN]?=?"";??????????????????socklen_t?addrlen?=?sizeof(addr);????????????????????????????????????bzero(&addr,sizeof(addr));????????????????????????????????????recvfrom(udpfd,?buf,?100,?0,?(struct?sockaddr*)&addr,?&addrlen);??????????????????printf("\r\033[31m[%s]:\033[32m%s\n",inet_ntop(AF_INET,&addr.sin_addr,ipbuf,sizeof(ipbuf)),buf);????????????????}????????????????????????????}??????????else?if(0?==?ret){?????????????printf("time?out\n");????????????}????????}????????????return?0;??}?? 運(yùn)行結(jié)果:
轉(zhuǎn)載于:https://www.cnblogs.com/jiangzhaowei/p/8831083.html
總結(jié)
以上是生活随笔為你收集整理的Linux网络编程——I/O复用之poll函数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。