epoll
来源:互联网 发布:简单游软件下载 编辑:程序博客网 时间:2024/05/17 04:28
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include <signal.h>
#include <poll.h>
#include <sys/types.h>
#include <error.h>
void call_poll(void)
{
struct pollfd fds;
int32_t timeout_msecs = 5000;
int err;
fds.fd = 1;
fds.events = POLLIN | POLLPRI ;
// poll系统调用
// poll()系统调用是System V的多元I/O解决方案。它解决了select()的几个不足,尽管select()仍然经常使用(多数还是出于习惯,或者打着可移植的名义):
// 用户空间调用的poll函数定义如下:
// 和select()不一样,poll()没有使用低效的三个基于位的文件描述符set,而是采用了一个单独的结构体pollfd数组,由fds指针指向这个组。pollfd结构体定义如下:
err = poll( &fds, 1, timeout_msecs );
if ( err > 0 ) {
printf("Data is available now.\n");
}
else if ( err == 0 ) {
printf("No data within five seconds.\n");
}
else {
perror( "poll()" );
}
}
#include <sys/epoll.h>
//所用到的数据结构
typedef union epoll_data {
void *ptr;
//*ptr: 通过指针ptr携带应用层数据, 当事件的通知到来时,
//它不仅告诉你发生了什么样的事件,
//还同时告诉这次事件所操作的数据是哪些
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
struct epoll_event { //epoll_event 被用于注册所感兴趣的事件和回传所发生待处理的事件
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
// epoll_data 联合体用来保存触发事件的某个文件描述符相关的数据,
//例如一个client连接到服务器,服务器通过调用accept函数可以得
//到与这个client对应的socket文件描述符,可以把这文件描述符赋给
//epoll_data的fd字段以便后面的读写操作在这个文件描述符上进行
};
epoll_event 结构体的events字段是表示感兴趣的事件和被触发的事件可能的取值为:
EPOLLIN :表示对应的文件描述符可以读;
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET:表示对应的文件描述符有事件发生;
void call_epoll(void)
{
int epfd;
struct epoll_event ev_stdin;
int err;
// 函数声明:int epoll_create(int size)
// 该函数生成一个epoll专用的文件描述符,
//其中的参数是指定生成描述符的最大范围
epfd = epoll_create(1);
if ( epfd < 0 ) {
perror( "epoll_create()" );
return ;
}
bzero( &ev_stdin, sizeof( struct epoll_event) );
ev_stdin.events =
// available for read operations
EPOLLIN | EPOLLPRI
// available for write operations
// | EPOLLOUT
// Error condition && Hang up happened
| EPOLLERR | EPOLLHUP
// Sets the Edge Triggered behaviour
| EPOLLET
// Sets the one-shot behaviour.
// must call epoll_ctl with EPOLL_CTL_MOD to re-enable
| EPOLLONESHOT
;
// epoll_ctl函数
// 函数声明:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
// 该函数用于控制某个文件描述符上的事件,
//可以注册事件,修改事件,删除事件。
err = epoll_ctl( epfd, EPOLL_CTL_ADD, 1, &ev_stdin );
if ( err < 0 ) {
perror( "epoll_ctl()" );
goto _out;
}
// 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事件发生的超时值;
返回发生事件数。
err = epoll_wait( epfd, &ev_stdin, 1, 5000 );
if ( err < 0 ) {
perror( "epoll_wait()" );
}
else if ( err == 0 ) {
printf("No data within five seconds.\n");
}
else {
printf("Data is available now.\n");
}
//err = epoll_ctl( epfd, EPOLL_CTL_DEL, 1, &ev_stdin );
//
err = epoll_ctl( epfd, EPOLL_CTL_DEL, 1, &ev_stdin );
if ( err < 0 ) {
perror( "epoll_ctl()" );
}
for(i=0; i
{
if(epoll_event.data.fd == sock)// the listener port hava data
{}
}
_out:
close( epfd );
}
epoll_wait运行的原理是
等侍注册在epfd上的socket fd的事件的发生,如果发生则将发生的sokct fd和事件类型放入到events数组中。
并 且将注册在epfd上的socket fd的事件类型给清空,所以如果下一个循环你还要关注这个socket fd的话,则需要用epoll_ctl(epfd,EPOLL_CTL_MOD,listenfd,&ev)来重新设置socket fd的事件类型。这时不用EPOLL_CTL_ADD,因为socket fd并未清空,只是事件类型清空。这一步非常重要。
int main ()
{
call_epoll();
return 0;
}
单个epoll并不能解决所有问题,特别是你的每个操作都比较费时的时候,因为epoll是串行处理的。
所以你还是有必要建立线程池来发挥更大的效能。
//////////////////////////////////////////////////////////////////////////////
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);
}
}
此时使用的是ET模式,即,边沿触发,类似于电平触发,epoll中的边沿触发的意思是只对新到的数据进行通知,而内核缓冲区中如果是旧数据则不进行通知,所以在do_use_fd函数中应该使用如下循环,才能将内核缓冲区中的数据读完。
while (1) {
len = recv(*******);
if (len == -1) {
if(errno == EAGAIN)
break;
perror("recv");
break;
}
do something with the recved data........
}
在上面例子中没有说明对于listen socket fd该如何处理,有的时候会使用两个线程,一个用来监听accept另一个用来监听epoll_wait,如果是这样使用的话,则listen socket fd使用默认的阻塞方式就行了,而如果epoll_wait和accept处于一个线程中,即,全部由epoll_wait进行监听,则,需将listen socket fd也设置成非阻塞的,这样,对accept也应该使用while包起来(类似于上面的recv),因为,epoll_wait返回时只是说有连接到来了,并没有说有几个连接,而且在ET模式下epoll_wait不会再因为上一次的连接还没读完而返回,这种情况确实存在,我因为这个问题而耗费了一天多的时间,这里需要说明的是,每调用一次accept将从内核中的已连接队列中的队头读取一个连接,因为在并发访问的环境下,有可能有多个连接“同时”到达,而epoll_wait只返回了一次。
唯一有点麻烦是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确认。
- epoll
- epoll
- epoll
- epoll
- epoll
- epoll
- epoll
- epoll
- epoll
- epoll
- epoll
- epoll
- epoll
- epoll
- epoll
- epoll
- epoll
- epoll
- 互联网产品设计:产品即服务
- Estjs combobox多级联动 去除第一次加载所有都读取的问题
- HDU_4125 Moles 线段树
- Oracle的行列转换
- Struts2 + Spring 2.5 +Hibernate 3.2整合图文教程
- epoll
- 黑马程序员_java编程基础13 多线程
- 基于PIC16F876A的摇摇棒
- CentOS上安装Ruby
- 切金条付工资
- SRM 554 DIV2
- (Codeforces Round #136 (Div. 2))A. Little Elephant and Function(递归分析,简单)
- 黑马程序员_java编程基础14 String
- PIC中数据存储格式及原理