epoll

epoll是linux内核所支持的网络系统调用。


核心数据结构 
typedef union epoll_data { 
void *ptr; 
int fd; 
__uint32_t u32; 
__uint64_t u64; 
} epoll_data_t; 


struct epoll_event { 
__uint32_t events; /* Epoll events */ 
epoll_data_t data; /* User data variable */ 
}; 

如: 
struct epoll_event ev; 
//设置与要处理的事件相关的文件描述符 
ev.data.fd=listenfd; 
//设置要处理的事件类型 
ev.events=EPOLLIN|EPOLLET; 
//注册epoll事件 
epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev); 


常用的事件类型: 
EPOLLIN :表示对应的文件描述符可以读; 
EPOLLOUT:表示对应的文件描述符可以写; 
EPOLLPRI:表示对应的文件描述符有紧急的数据可读 
EPOLLERR:表示对应的文件描述符发生错误; 
EPOLLHUP:表示对应的文件描述符被挂断; 
EPOLLET:表示对应的文件描述符有事件发生;

1.epoll_create函数 

函数声明:int epoll_create(int size) 

该 函数生成一个epoll专用的文件描述符。它其实是在内核申请一空间,用来存放你想关注的socket fd上是否发生以及发生了什么事件。size就是你在这个epoll fd上能关注的最大socket fd数。这个数字可以很大,可以为系统可以打开的最大文件数。cat /proc/sys/fs/file-max ,我们的机器上是655290

2. epoll_ctl函数 
函数声明:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) 
该函数用于控制某个epoll文件描述符上的事件,可以注册事件,修改事件,删除事件。 
参数: 
epfd:由 epoll_create 生成的epoll专用的文件描述符; 
op:要进行的操作例如注册事件,可能的取值EPOLL_CTL_ADD 注册、EPOLL_CTL_MOD 修 改、EPOLL_CTL_DEL 删除 
fd:关联的文件描述符; 
event:指向epoll_event的指针; 
如果调用成功返回0,不成功返回-1 

比如:

if((newId = accept(sd, (sockaddr *)&remote_addr, (socklen_t*)&sin_size)) < 0)
return -1;

// set receive time out
SetRecvTimeout(sd, timeout);


// add to comm epoll
{
epoll_event event;
event.events = EPOLLIN | EPOLLERR/* | EPOLLET*/;
event.data.fd = newId;
if(epoll_ctl(m_epfd, EPOLL_CTL_ADD, newId, &event) < 0 && errno != EEXIST)
{
TCP_SOCKET_CLOSE(sd);
return -1;
}
}

3.epoll_wait函数 
函数声明:int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout) 
该函数用于轮询I/O事件的发生; 
参数: 
epfd:由epoll_create 生成的epoll专用的文件描述符; 
epoll_event:用于回传代处理事件的数组; 
maxevents:每次能处理的事件数; 
timeout:等待I/O事件发生的超时值;-1相当于阻塞,0相当于非阻塞。一般用-1即可 
返回发生事件数。


注意:epoll有2种工作方式:LT和ET。


LT(level triggered)是缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表. 


ET (edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once),不过在TCP协议中,ET模式的加速效用仍需要更多的benchmark确认。


man中给出了epoll的用法,example程序如下:

for(;;) { 
           nfds = epoll_wait(kdpfd, events, maxevents, -1); 


           for(n = 0; n < nfds; ++n) { 
               if(events[n].data.fd == listener) { 
                   client = accept(listener, (struct sockaddr *) &local, 
                                   &addrlen); 
                   if(client < 0){ 
                       perror("accept"); 
                       continue; 
                   } 
                   setnonblocking(client); 
                   ev.events = EPOLLIN | EPOLLET; 
                   ev.data.fd = client; 
                   if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) { 
                       fprintf(stderr, "epoll set insertion error: fd=%d\n", 
                               client); 
                       return -1; 
                   } 
               } 
               else 
                   do_use_fd(events[n].data.fd); 
           } 
       } 

在do_use_fd函数中应该使用如下循环,才能将内核缓冲区中的数据读完。 
        while (1) { 
           len = recv(*******); 
           if (len == -1) { 
             if(errno == EAGAIN) 
                break; 
             perror("recv"); 
             break; 
           } 
           do something with the recved data........ 
        } 


客户端发送流程示例:

1.m_epfdsend = epoll_create(MAX_SOCKET_COUNT);

2.send_sd;

3.epoll_event event;

  event.events = EPOLLOUT | EPOLLET;

  event.data.fd = send_fd;

  epoll_ctl(m_epfdsend,EPOLL_CTL_ADD,send_fd,&event);

4.epoll_event events[MAX_SOCKET_COUNT]; 

  int evcount = epoll_wait(m_epfdsend, &events, MAX_SOCKET_COUNT,time_out);

  for(int i = 0; i < evcount;++i)

  {

       sd = events[i].data.fd;

       evs = events[i].events;

       send(sd, data, size, flag);

  }

服务器端监听流程示例:

 1.m_epfd = epoll_create(MAX_SOCKET_COUNT);

 2.sd = socket(...);

    setsockopt(sd, ...);

    fcntl(sd, ...);

3.bind(sd,(sockaddr*)&addr, sizeof(addr));

4 listen(sd, 128);

5. epoll_event event;

   event.data.fd = sd;

   event.events = EPOLLIN | EPOLLERR;

   epoll_ctl(m_epfd,EPOLL_CTL_ADD, sd, &event);

6. newid = accept(sd,(sock_addr*)&remote_addr, (socklen_t*)&sin_size);

   setsockopt(sd, ...);

   epoll_event event;

   event.events = EPOLLIN|EPOLLERR;

   event.data.fd = newid;

   epoll_ctl(m_epfd, EPOLL_CTL_ADD, newid, &event);

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值