IO多路复用的三种实现:epoll

同其他I/O多路复用和信号驱动一样,linux的epoll(event poll)API可以检查多个文件描述符上的I/O就绪状态。Epoll API的主要优点如下。

当检查大量的文件描述符时,epoll的性能延展性比select和poll高很多。

 epoll API既支持水平触发也支持边缘触发。与之相反,select和poll只支持水平触发,而信号驱动I/O只支持边缘触发。

 epoll API的核心数据结构称为epoll实例, 它和一个打开的文件描述符相关联。这个文件描述符不是用来做I/O操作的,相反,他说内核数据结构的句柄,这些内核数据结构实现了两个目的。

记录了在进程中声明过的感兴趣的文件描述符列表--interest list。

维护了处于I/O就绪态的文件描述符列表--ready list。

 ready list中的成员是interest list的子集。

 epoll API由以下3个系统调用组成。

系统调用epoll_create()创建一个epoll实例,返回代表该实例的文件描述符。

系统调用epoll_ctl()操作同epoll实例相关的兴趣列表。通过epoll_ctl(),我们可以增加新的描述符到列表中,将已有的文件描述符从该列表中移除,以及修改代表文件描述符上事件类型的位掩码。

系统调用epoll_wait()返回与epoll实例相关联的就绪列表中的成员。

创建epoll实例:epoll_create():

系统调用epoll_create()创建了一个新的epoll实例,其对应的兴趣列表初始化为空。

IO多路复用的三种实现:epoll_第1张图片

修改epoll的兴趣列表:epoll_ctl():

系统调用epoll_ctl()能够修改由文件描述符epfd所代表的epoll实例中的兴趣列表。

IO多路复用的三种实现:epoll_第2张图片

参数fd指明了要修改兴趣列表中的哪-一个文件描述符的设定。该参数可以是代表管道、套接字、 POSIX 消息队列等。但是,这里fd不能作为普通文件或目录的文件描述符(会出现EPERM 错误)参数op用来指定需要执行的操作,它可以是如下几种值。

事件等待:epoll_wait():

系统调用epoll_wait()返回epoll实例中处于就绪态的文件描述符信息。单个epoll_wait()调用能返回多个就绪态文件描述的信息。

 IO多路复用的三种实现:epoll_第3张图片

参数events所指向的结构体数组中返回的是有关就绪态文件描述符的信息。数组events的空间由调用者负责申请,所包含的元素个数在参数maxevents中指定。当我们调用epoll_ctl将文件描述符添加到兴趣列表中时,应该要么将event.data.fd设为文件描述符号,要么将event.data.ptr设为指向包含文件描述符号的结构体。调用成功后,epoll_wait()返回数组events中的元素个数。

epoll中events字段上的位掩码值:

IO多路复用的三种实现:epoll_第4张图片

EPOLLONESHOT标志:

如果指定了这个标志,那么在下一个epoll_wait()调用通知我们对应的文件描述符处于就绪态后,这个描述符就回在兴趣列表中被标记为非激活态,之后的epoll_wait调用都不会再通知我们有关这个描述符的状态了。

上代码:

#include
#include
#include                                                              
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define SERPORT 8000
#define SERIP "192.168.6.174"


int main(int argc, char* argv[])
{
    int lfd = socket(AF_INET,SOCK_STREAM,0);

    struct sockaddr_in seraddr,cliaddr;
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(SERPORT);
    int dst;
    inet_pton(AF_INET,SERIP,(void*)&dst);
    seraddr.sin_addr.s_addr = dst ;

    int ret = bind(lfd,(struct sockaddr*)&seraddr,sizeof(seraddr));
    listen(lfd,64);

    socklen_t addrlen = sizeof(cliaddr);

    char buf[1024];
    char clip[32];
    int i;

    //将lfd交给epoll监听
    int epfd = epoll_create(64);
    struct epoll_event events[1025], event;
    event.events = EPOLLIN;
    event.data.fd = lfd;
    epoll_ctl(epfd, EPOLL_CTL_ADD,lfd, &event);

    while(1){
        int eret = epoll_wait(epfd, events, 1025, -1);
        if(eret<0){
            perror("epoll_wait error");
            exit(1);
        }
        for(i = 0;i

你可能感兴趣的:(linux网络编程,linux,服务器,网络,信息与通信)