轉載:http://blog.csdn.net/dodo_328/article/details/39081183
1.Selet:本質上是通過設置或者檢查存放fd標志位的數據結構來進行下一步處理。
?????????????? 缺點:1 單個進程可監視的fd數量被限制,因為受描述符集合fd_set限制,fd數量最大不超過1024;
????????????????????????? 2 需要維護一個用來存放大量fd的數據結構,這樣會使得用戶空間和內核空間在傳遞該結構時復制開銷大;
????????????????????????? 3 對socket進行掃描時是線性掃描;
????????????????????????? 4 Linux的實現中select返回時會將timeout修改為剩余時間,所以重復利用timeout需要注意。
函數原型:int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);
參數:nfds 需要監聽的最大fd 值加1;
???????????readfds 等待從此集合中的文件描述符中到來的數據;
?????????? writefds 等待向此集合中的文件描述符寫入的數據;
?????????? exceptfds 等待這些文件描述符操作的異常;
?????????? timeout? 超過此時間后函數返回。
返回值:-1? 發生錯誤;
????????????? 0??? 超時;
?????????????num?? 滿足需求的文件描述符數目。
?
void FD_CLR(int fd,fd_set *set):用來從文件描述符集合中刪除一個文件描述符。
void FD_ISSET(int fd, fd_set *set):用來測試在這個文件描述符集合中,此文件描述符是否被觸發。
void FD_SET(int fd, fd_set *set):用來將一個文件描述符設置到文件描述符集合中去。
void FD_ZERO(fd_set *set):用來將文件描述符集合清零。
程序實例:
[objc]?view plain
?copy #include<stdio.h>?? #include<stdlib.h>?? #include<sys/time.h>?? #include<sys/types.h>?? #include<unistd.h>?? #include<fcntl.h>?? ?? #define?oops(m,x){perror(m);exit(x);}?? void?showdata(charchar?*,int?);?? ?? int?main(int?ac,charchar?*av[])?? {?? ????int?fd1,fd2;?? ????int?max_fd;?? ????fd_set?readfds;?? ????struct?timeval?timeout;?? ????int?retval;?? ?????? ????if(ac?!=?4)?? ????{?? ????????fprintf(stderr,"Usage:%s?file1?file2?timeout\n",*av);?? ????????exit(1);?? ????}?? ?? ????if((fd1?=?open(av[1],O_RDONLY))?==?-1)?? ????????oops("file1?open",2);?? ?? ????if((fd2?=?open(av[2],O_RDONLY))?==?-1)?? ????????oops("file2?open",3);?? ?? ????max_fd?=?1?+?((fd1?>?fd2)?fd1:fd2);?? ?? ????while(1)?? ????{?? ????????FD_ZERO(&readfds);?? ????????FD_SET(fd1,&readfds);?? ????????FD_SET(fd2,&readfds);?? ?? ????????timeout.tv_sec?=?atoi(av[3]);?? ????????timeout.tv_usec?=?0;?? ?? ????????retval?=?select(max_fd,&readfds,NULL,NULL,&timeout);?? ?? ????????if(retval?==?-1)?? ????????????oops("select",4);?? ????????if(retval?>?0)?? ????????{?? ????????????if(FD_ISSET(fd1,&readfds))?? ????????????????showdata(av[1],fd1);?? ????????????if(FD_ISSET(fd2,&readfds))?? ????????????????showdata(av[2],fd2);?? ????????}?? ????????else?? ????????????printf("no?input?after?%d?seconds\n",atoi(av[3]));?? ????}?? }?? ?? void?showdata(charchar?*fname,int?fd)?? {?? ????char?buf[BUFSIZ];?? ????int?n;?? ?? ????printf("%s:",fname);?? ????fflush(stdout);?? ????n?=?read(fd,buf,BUFSIZ);?? ????if(n?==?-1)?? ????????oops(fname,5);?? ????write(1,buf,n);?? ????write(1,"\n",1);?? }??
?
2.Poll:它將用戶傳入的數組拷貝到內核空間,然后查閱每個fd對應的設備狀態,如果設備就緒則在設備等待隊列中加入一項并繼續遍歷,如果遍歷完所有的fd沒有發現設備就緒,則掛起當前進程,直到設備就緒或者主動超時,被喚醒后,它又要再次遍歷fd,這個過程經歷了多次無謂的遍歷。
????????? 優點:沒有最大鏈接數的限制,原因是因為它是基于鏈表來存儲的;不會修改timeout的值。
????????? 缺點:大量的fd數組被整體復制于用戶態和內核地址空間之間。
????????? 特點:“水平觸發”:如果報告fd后,沒有被處理,那么下次poll時會再次報告該fd。
struct pollfd{
?????? int fd;? //文件描述符
???????short events;? //等待的事件
?????? short? revents; //實際發生的事件
}
函數原型:int poll(struct pollfd? fds[ ], nfds_t nfds,int timeout);
參數:fds[]? 文件描述符以及等待的事件結構數組;
?????????? nfds? 表示監聽的fds的長度;
???????????timeout 超時;
返回值:-1 發生錯誤;
????????????? 0?? 超時;
???????????? num 滿足需求的文件描述符總數。
events/revents:POLLIN|POLLOUT
程序實例:
[objc]?view plain
?copy #include<stdio.h>?? #include<stdlib.h>?? #include<unistd.h>?? #include<poll.h>?? #include<fcntl.h>?? ?? #define?oops(m,x){perror(m);exit(x);}?? ?? void?showdata(charchar?*,int);??? ?? int?main(int?ac,charchar?*av[])?? {?? ????int?timeout,fd1,fd2;?? ????struct?pollfd?poll_array[2];?? ?????? ????int?ret;?? ?? ????if(ac?!=?4)?? ????{?? ????????fprintf(stderr,"Usage:%s?file1?file2?timeout\n",*av);?? ????????exit(1);?? ????}?? ?? ????timeout?=?atoi(av[3]);?? ?? ????if((fd1?=?open(av[1],O_RDONLY))?==?-1)?? ????????oops(av[1],2);?? ????if((fd2?=?open(av[2],O_RDONLY))?==?-1)?? ????????oops(av[2],3);?? ?? ????poll_array[0].fd?=?fd1;?? ????poll_array[0].events?=?POLLIN;?? ????poll_array[1].fd?=?fd2;?? ????poll_array[1].events?=?POLLIN;?? ?? ????while(1)?? ????{?? ????????ret?=?poll(poll_array,2,timeout);?? ?? ????????if(ret?==?-1)?? ????????????oops("poll?error\n",1);?? ????????if(ret?==?0)?? ????????{?? ????????????printf("timeout..\n");?? ????????????continue;?? ????????}?? ?? ????????if(poll_array[0].revents?&?POLLIN)?? ????????{?? ????????????showdata(av[1],fd1);?? ????????}?? ?? ????????if(poll_array[1].revents?&POLLIN)?? ????????????showdata(av[2],fd2);?? ????}?? ????return?0;?? }?? ?? void?showdata(charchar?*fname,int?fd)?? {?? ????char?buf[BUFSIZ];?? ????int?n;?? ?? ????printf("%s:",fname);?? ????fflush(stdout);?? ????n?=?read(fd,buf,BUFSIZ);?? ????printf("n:%d\n",n);?? ????if(n?==?-1)?? ????????oops(fname,4);?? ????write(1,buf,n);?? ????write(1,"\n",1);?? }??
?
3.Epoll:解決了select和poll的幾個性能上的缺陷
????????????1. 不限制監聽的描述符個數,只受進程打開的描述符總數的限制;
????????????2. 監聽性能不隨著監聽描述符數的增加而增加,是0(1)的,不再是輪詢描述符來探測事件,而是描述符主動上報事件;
????????????3.使用共享內存的方式,不在用戶和內核之間反復傳遞監聽的描述信息;
??????????? 4.返回參數就是觸發事件的列表,不再遍歷。
??????????? 注意:1.epoll創建了描述符,最后要close(epfd);
?????????????????????? 2.支持水平和邊緣觸發。
int epoll_creat(int size)
功能:用來創建epoll文件描述符。
參數:size 能在epoll上關注的最大fd數。
返回值:epfd。
int epoll_ctl(int epfd,int op,int fd, struct epoll_event *event)
功能:控制對指定的文件描述符執行op操作。
參數:epfd? epoll_creat()函數的返回值。
?????????? op? EPOLL_CTL_ADD(增加)/EPOLL_CTL_DEL(刪除)/EPOLL_CTL_MOD(修改)。
???????????fd?? 文件描述符。
?????????? event? 與fd 相關聯的監聽事件。
struct? epoll_event{
???????????_uint32_t? events;
?????????? epoll_data_t? data;
};
events:EPOLLIN 可讀。
??????????? EPOLLOUT 可寫。
??????????? EPOLLRDHUP 套接口對端close或shutdown寫,在ET(邊緣)模式下比較有用。
??????????? EPOLLET 邊緣觸發模式,在描述符狀態跳變時才上報監聽事件(監聽默認是LT(水平)模式)。
??????????? EPOLLPRI 緊急數據可讀。
??????????? EPOLLERR 異常事件。
??????????? EPOLLHUP 掛起。 EPOLLERR 和EPOLLHUP始終由epoll_wait監聽,不需要用戶設置。
??????????? EPOLLONESHOT? 只一次有效,描述符在觸發一次事件之后自動失效。fd還在繼續監聽,直到使用EPOLL_CTL_MOD重新激活,設置新的監聽事件。
data: 是個共用體,可以存放和fd綁定的描述符信息。比如ip/port等。
typedef? union? epoll_data{
????????? void *ptr;//存放與fd綁定的信息。
??????????int fd;
????????? _uint32_t? u32;
????????? _uint32_t? u64;
}epoll_data_t;
int epoll_wait(int epfd,struct epoll_event *events,int maxevents, int timeout)
參數:epfd? epoll_creat()的返回值.
?????????? events? 用于回傳待處理事件的數組,值結果參數。
?????????? maxevents? 每次能處理的事件數。
?????????? timeout? 超時設置。
返回值:-1? 發生錯誤;
????????????? 0?? 超時;
???????????? num? 觸發事件的描述符總數。
程序實例:
[objc]?view plain
?copy #include<stdio.h>?? #include<stdlib.h>?? #include<poll.h>?? #include<unistd.h>?? #include<fcntl.h>?? #include<sys/types.h>?? #include<sys/epoll.h>?? ?? #define?oops(m,x){perror(m);exit(x);}?? #define?MAX_SIZE_EVENT??500?? ?? void?showdata(int?,charchar?*);?? ?? int?main(int?ac?,charchar?*av[])?? {?? ????int?fd1,fd2;?? ????int?time;?? ????int?epfd;?? ????struct?epoll_event?eventList[MAX_SIZE_EVENT];?? ????struct?epoll_event?fd1_event;?? ????struct?epoll_event?fd2_event;?? ????int?ret;?? ????int?n?;?? ?????? ????if(ac?!=?4)?? ????{?? ????????fprintf(stderr,"Usage:%s?file1?file2?timeout\n",*av);?? ????????exit(1);?? ????}?? ?? ????if((fd1?=?open(av[1],O_RDONLY))?==?-1)?? ????????oops("file1?open",2);?? ????if((fd2?=?open(av[2],O_RDONLY))?==?-1)?? ????????oops("file2?open",3);?? ?????? ????time?=?atoi(av[3]);?? ?? ????epfd?=?epoll_create(MAX_SIZE_EVENT);?? ????fd1_event.events?=?EPOLLIN|EPOLLET;?? ????fd1_event.data.fd?=?fd1;?? ????fd1_event.data.ptr?=?av[1];?? ????fd2_event.events?=?EPOLLIN|EPOLLET;?? ????fd2_event.data.fd?=?fd2;?? ????fd2_event.data.ptr?=?av[2];?? ?? ????if(epoll_ctl(epfd,EPOLL_CTL_ADD,fd1,&fd1_event)?<?0)?? ????????oops("fd1_event?epoll?addfail",4);?? ????if(epoll_ctl(epfd,EPOLL_CTL_ADD,fd2,&fd2_event)?<?0)?? ????????oops("fd2_event?epoll?addfail",5);?? ?? ????while(1)?? ????{?? ????????ret?=?epoll_wait(epfd,eventList,MAX_SIZE_EVENT,time);?? ????????if(ret?<?0)?? ????????????oops("epoll?error",6);?? ????????if(ret?==?0)?? ????????{?? ????????????printf("timeout\n");?? ????????}?? ?? ????????for(n?=?0;n?<?ret;?n++)?? ????????{?? ????????????if(eventList[n].events?&?EPOLLERR?||?eventList[n].events?&?EPOLLHUP?||?!(eventList[n].events?&?EPOLLIN))?? ????????????{?? ????????????????printf("epoll?error");?? ????????????????close(epfd);?? ????????????????close(eventList[n].data.fd);?? ????????????????return?-1;?? ????????????}?? ????????????else?if(eventList[n].events?&?EPOLLIN)?? ????????????????showdata(eventList[n].data.fd,eventList[n].data.ptr);?? ????????}?? ?? ????}?? ????close(epfd);?? ????close(fd1);?? ????close(fd2);?? ????return?0;?? }?? ?? void?showdata(int?fd,charchar?*fname)?? {?? ????char?buf[BUFSIZ];?? ????int?n;?? ?? ????printf("%s:",fname);?? ????fflush(stdout);?? ?? ????n?=?read(fd,buf,BUFSIZ);?? ????if(n?==?-1)?? ????????oops("fd?read",6);?? ????write(1,buf,n);?? ????write(1,"\n",1);?? }??
總結
以上是生活随笔為你收集整理的select、poll、epoll 比较的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。