轉載: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):用來將文件描述符集合清零。
程序實例:
- #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
程序實例:
- #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? 觸發事件的描述符總數。
程序實例:
- #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);??
- }??
