I/O多路转接之epoll

来源:互联网 发布:u盘安装ubuntu系统安装 编辑:程序博客网 时间:2024/05/16 11:49


epoll是对select和poll的改进,表现在:
1、epoll保证了每个fd在整个过程中只会拷贝一次。
2、获取事件时,它无须遍历整个被监听的描述符集,只要遍历那些被内核I/O事件异步唤醒而加入就绪队列的描述符集合即可。
3、epoll所支持的fd上限是最大可以打开文件的数目,这个数字一般远大于2048,在1GB内存的机器上大约是10万左右,具体数目可以cat /proc/sys/fs/file-max查看,该数目与系统内存有关。
这里写图片描述
—————————————————————————————————————————————-
epoll提供了三个系统调用接口来实现上面的功能
这里写图片描述
创建一个epoll的句柄,size参数是被忽略的。当创建好epoll句柄后,它就是会占用一个fd值,在使用完epoll后,必须调用close关闭,否则可能导致fd被耗尽。当调用该函数时,操作系统则创建一个就绪队列和一个红黑树(当然还有其他的底层结构);就绪队列用来存放就绪的fd,红黑树用来存放要监听的fd。


这里写图片描述
epoll的事件注册函数,它不同于select是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型(往红黑树上注册)
参数1:epfd是epoll_create的返回值;
参数2:表示动作,用三个宏来表示:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;
参数3:fd是需要监听的fd。
参数4:是告诉内核需要监听什么事,struct epoll_event结构如下:
这里写图片描述
events可以是以下几个宏的集合:
EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的;
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里;


这里写图片描述
收集在epoll监听的事件中已经就绪的事件。等待红黑树上的某个fd的某个事件就绪,若有就绪的fd,则将该fd拷贝一份放至就绪队列中(操作系统依靠回调函数来完成)。
参数2:events是分配好的epoll_event结构体数组,epoll将会把发生的事件(就绪队列)复制到events数组中(有序且全部有效)(events不可以是空指针,内核只负责把数据复制到这个events数组中,不会去帮助我们在用户态中分配内存)。
参数3:maxevents告之内核这个events有多大,这个 maxevents的值不能大于epoll_create函数的size;
参数4:timeout是超时时间(毫秒,0会立即返回,-1永久阻塞);
返回值:
成功:返回对应I/O上已就绪的文件描述符数目;
0:表示timeout已超时;
-1:出错。
epoll的两种工作方式:
水平触发(LT):(有数据时)一直在等,低效但是可靠。
边缘触发(ET):有数据时即通知(只通知一次),会出现数据丢失问题,但是高效。
注: epoll默认为水平触发工作方式。可以通过epoll_ctl函数来设置。
使用epoll实现一个tcp服务器:

//server端:#include<stdio.h>#include<sys/types.h>#include<sys/socket.h>#include<arpa/inet.h>#include<netinet/in.h>#include<sys/epoll.h>#include<stdlib.h>#include<string.h>int array_fds[1024];static void Usage(const char* proc){    printf("Usage:%s [local_ip][local_port]\n",proc);}typedef struct fd_buf{    int fd;    char buf[10240];}fd_buf_t,*fd_buf_p;static void*  alloc_fd_buf(int fd){    fd_buf_p tmp = (fd_buf_p)malloc(sizeof(fd_buf_t));    if(!tmp)    {        perror("malloc");        return NULL;    }    tmp->fd= fd;    return tmp;} void Default(int epollfd,int listen_sock,int num,struct epoll_event ev,struct epoll_event *evs){    int i = 0;    for(;i<num;i++)    {        fd_buf_p fp = (fd_buf_p)evs[i].data.ptr;        if(fp->fd==listen_sock && (evs[i].events & EPOLLIN))        {            struct sockaddr_in client;            socklen_t len=sizeof(client);            int new_sock=accept(fp->fd,(struct sockaddr*)&client,&len);            if(new_sock<0)            {                perror("accept");                continue;            }            printf("new client--> %s:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));            ev.events = EPOLLIN;            ev.data.ptr = alloc_fd_buf(new_sock);            epoll_ctl(epollfd,EPOLL_CTL_ADD,new_sock,&ev);          }        else if(fp->fd !=listen_sock)        {            if(evs[i].events & EPOLLIN)            {                ssize_t s = read(fp->fd,fp->buf,sizeof(fp->buf));                if(s>0)                {                    fp->buf[s]=0;                    printf("client say:%s\n",fp->buf);                    ev.events = EPOLLOUT;                    ev.data.ptr = fp;                    epoll_ctl(epollfd,EPOLL_CTL_MOD,fp->fd,&ev);                    }                else if(s == 0)                {                    printf("client is quit!!\n");                    close(fp->fd);                    epoll_ctl(epollfd,EPOLL_CTL_DEL,fp->fd,NULL);                    free(fp);                }                else                {                    perror("read");                    close(fp->fd);                    epoll_ctl(epollfd,EPOLL_CTL_DEL,fp->fd,NULL);                    free(fp);                }            }            else if(evs[i].events & EPOLLOUT)            {                const char *msg="HTTP/1.0 200 OK\r\n\r\n<html><h1>LIUJINHUA</h1></html>\r\n";                write(fp->fd,msg,strlen(msg));                close(fp->fd);                epoll_ctl(epollfd,EPOLL_CTL_DEL,fp->fd,NULL);                free(fp);            }        }    }}int Startup(const char* _ip,int _port){    int sock= socket(AF_INET,SOCK_STREAM,0);    if(sock<0)    {        perror("socket");        exit(1);    }    int flag = 1;    setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag));    struct sockaddr_in server;    server.sin_family = AF_INET;    server.sin_port= htons(_port);    server.sin_addr.s_addr = inet_addr(_ip);    if(bind(sock,(struct sockaddr*)&server,sizeof(server))<0)    {        perror("bind");        exit(2);    }    if(listen(sock,10)<0)    {        perror("listen");        exit(3);    }    return sock;}int main(int argc,const char* argv[]){    if(argc!=3)    {        Usage(argv[0]);        return 1;    }    int listen_sock = Startup(argv[1],atoi(argv[2]));    int epollfd = epoll_create(256);    if(epollfd<0)    {        perror("epoll_create");        close(listen_sock);        return 2;    }    struct epoll_event ev;    ev.events= EPOLLIN;    ev.data.ptr = alloc_fd_buf(listen_sock);    if(epoll_ctl(epollfd,EPOLL_CTL_ADD,listen_sock,&ev)!=0)    {        perror("epoll_ctl");        close(epollfd);        close(listen_sock);        return 3;    }    while(1)    {        int num = 0;        int timeout=-1;        struct epoll_event evs[64];        switch(num=epoll_wait(epollfd,evs,64,timeout))        {            case 0:                printf("timeout...\n");                break;            case -1:                perror("epoll_wait");                break;            default:                Default(epollfd,listen_sock,num,ev,evs);                break;        }    }    close(epollfd);    return 0;}
//client端:#include <stdio.h>#include <sys/types.h>#include <sys/socket.h>#include <arpa/inet.h>#include <stdlib.h>#include <string.h>#include <unistd.h>static void usage(const char* proc){    printf("Usage:%s[server_ip][server_port]\n",proc);}int main(int argc,char *argv[]){    if(argc != 3)    {        usage(argv[0]);        return 1;    }    int sock = socket(AF_INET,SOCK_STREAM,0);    if(sock<0)    {        perror("socket");        return 2;    }    struct sockaddr_in peer;    peer.sin_family = AF_INET;    peer.sin_port = htons(atoi(argv[2]));    peer.sin_addr.s_addr = inet_addr(argv[1]);    int ret = connect(sock,(struct sockaddr*)&peer,sizeof(peer));    if(ret<0)    {        perror("connect");        printf("%s\n",strerror(ret));        return 3;    }    char buf[1024];    while(1)    {        printf("please enter: ");        fflush(stdout);        ssize_t s=read(0,&buf,sizeof(buf));        if(s<0)        {            perror("read");            return 4;        }        buf[s-1]=0;        write(sock,&buf,strlen(buf));        printf("server echo: %s\n",buf);            }    close(sock);    return 0;}

原创粉丝点击