转载: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);
- }