linux epoll
来源:互联网 发布:拍照的姿势软件 编辑:程序博客网 时间:2024/05/20 23:29
epoll 是 poll 的一种变种,在监听大量的文件描述符(file descriptors)有很好的伸缩性。
epoll_create
- // 这里两个系统调用失败时都是返回 -1,成功返回 epoll 文件描述符
- #include <sys/epoll.h>
- // 从 Linux 内核 2.6.8 以后,size 就已经不再使用了
- // size 并非指定后备存储器(backing store)的最大大小
- int epoll_create(int size);
- // flags 为 0 其含义同于 epoll_create
- // flags 可以被设置为 EPOLL_CLOEXEC 表示
- // 对于此 epoll 实例对应的文件描述符设置 close-on-exec 标识
- // close-on-exec 在 http://name5566.com/3201.html 中有详细的描述
- int epoll_create1(int flags);
epoll_create 系统调用用于创建 epoll 并返回一个表示创建的 epoll 实例的文件描述符。epoll_create1 则扩展了 epoll_create 的功能。如果某个 epoll 实例不再使用,那么应该调用close 进行关闭。
epoll_ctl
- #include <sys/epoll.h>
- // epfd 表示 epoll 实例对应的文件描述符
- // op 表示控制操作
- // fd 表示目标文件描述符(op 操作的对象)
- // 函数调用成功返回 0,失败返回 -1
- int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll_ctl 系统调用主要是完成一些控制操作,这些操作包括(op 参数可选值):
- EPOLL_CTL_ADD
注册 fd 目标文件描述符到 epoll 实例上,并且关联事件 event 和目标文件描述符 fd(也就是指定文件描述符 fd 关心的事件) - EPOLL_CTL_MOD
改变和目标文件描述符 fd 相关联的事件 event - EPOLL_CTL_DEL
从 epoll 实例中移除目标文件描述符 fd。event 参数会被忽略,一般来说可以传递 NULL,但是在内核版本 2.6.9 之前 event 不能为 NULL,虽然 event 参数是被忽略的
epoll_event 结构体被定义如下:
- typedef union epoll_data {
- void *ptr;
- int fd;
- uint32_t u32;
- uint64_t u64;
- } epoll_data_t;
- struct epoll_event {
- // events 表示一个事件集
- uint32_t events;
- epoll_data_t data;
- };
这里的 epoll_event 中的 events 成员是一个 bits 集合(事件集合),可以使用了以下事件类型:
- EPOLLIN
相关文件对 read(以及 accept)操作有效 - EPOLLOUT
相关文件对 write 操作有效 - EPOLLRDHUP(最低要求 2.6.17 内核)
Stream socket(TCP)关闭连接或者关闭连接的写的一方 - EPOLLPRI
对于 read 操作存在可用的 urgent 数据了 - EPOLLERR
相关文件描述符出现错误情况。我们不需要设置此事件类型,epoll_wait 总是会等待此事件 - EPOLLHUP
相关文件描述符出现挂起情况。我们不需要设置此事件类型,epoll_wait 总是会等待此事件 - EPOLLET
对相关文件描述符设置边缘触发行为(Edge triggered behavior)。epoll 默认的行为是水平触发(Level triggered) - EPOLLONESHOT(最低要求 2.6.2 内核)
epoll_wait
- #include <sys/epoll.h>
- // epfd 参数表示 epoll 实例对应的文件描述符
- // events 输出参数用于在函数返回时获取到多个 epoll_event 对象,events 通常都是数组的首地址
- // maxevents 参数用于描述 events 数组的长度
- // maxevents 的详细讨论参考:http://stackoverflow.com/questions/2969425/epoll-wait-maxevents
- // timeout 参数表示此函数调用等待的最长时间,单位为毫秒,值为 -1 表示无限等待
- //
- // 出错时函数返回 -1,否则返回为 I/O 请求已做好准备的文件描述符的数量
- int epoll_wait(int epfd, struct epoll_event *events,
- int maxevents, int timeout);
- // 此函数特有一个参数 sigmask 用于设置信号掩码,使用 NULL 表示不进行设置
- int epoll_pwait(int epfd, struct epoll_event *events,
- int maxevents, int timeout,
- const sigset_t *sigmask);
epoll_wait 用来等待在某个 epoll 上发生的 I/O 事件。epoll_pwait 和 epoll_wait 的区别在于,epoll_pwait 可以设定一个信号掩码(http://name5566.com/3206.html 中谈到了信号掩码)。我们这样使用 epoll_pwait:
- ready = epoll_pwait(epfd, &events, maxevents, timeout, &sigmask);
其等价于:
- sigset_t origmask;
- sigprocmask(SIG_SETMASK, &sigmask, &origmask);
- ready = epoll_wait(epfd, &events, maxevents, timeout);
- sigprocmask(SIG_SETMASK, &origmask, NULL);
水平触发和边缘触发
水平触发和边缘触发两个术语也被用于其他领域。抽象来说:
- 水平触发:在特定状态下触发
- 边缘触发:在状态变化时触发
我们假定:
- 存在一个用于从某个管道中读取数据的文件句柄 rfd 且 rfd 被注册到 epoll 中
- 上述管道的另一端被写入了 2kB 的数据
- 调用 epoll_wait 完成并且返回 rfd(也就是 rfd 为一个准备就绪的文件描述符)
- 上述管道读的一方从 rfd 中读取了 1kB 的数据
- 再次调用 epoll_wait
如果 rfd 文件描述符在加入 epoll 时使用了 EPOLLET 标志(也就是使用边缘触发),在第 5 步中调用 epoll_wait 将可能阻塞(虽然在此文件输入缓冲区中仍有可用数据)。对于边缘触发模式,只有被监听的文件描述符发生变化时(changes occur)才投递事件。使用了 EPOLLET 标志(也就是使用边缘触发)意味着我们需要使用非阻塞的文件描述符并且一直调用 accept、read、write 直到出现错误 EAGAIN。
如果我们使用水平触发模式,它就相当于一个快速的 poll,只要文件描述符上还有数据可以读,则每次调用 epoll_wait 都会返回(而非阻塞)。在水平触发模式下,我们编写会容易一些,也可以不需要使用(当然也可以选择使用)非阻塞的文件描述符。从效率上来说,边缘触发通常被认为效率更高。
————————————————————————————————————————————————————————————————————————
简述EPOLL
TCP协议栈发现一个socket有事件发生时,它就会唤醒阻塞中的进程(调用select、poll、epoll_wait而睡眠的应用程序)。区别在于,select和poll的内核实现中,协议栈仅仅是唤醒应用程序,没有其他后续操作,所以select内部醒来后还得自己轮询所有的socket;当socket的数量很大时,这里的效率就有了瓶颈。
epoll不同,在内核支持epoll后,唤醒应用程序的操作中,可以挂载回调函数,所以epoll实现的回调函数就把发生事件的socket信息记录到一个就绪队列中了。当epoll_wait醒来的时候,它当然不需要轮询,直接就从就绪队列中就可以取出所有发生了事件的套接字。
这里面,显然有计算机中中断和轮询的思想在。
2,ET和LT
简单地说,LT的套接字从就绪队列中取出后,如果该套接字上确实是有事件,那么内核还会将它再入放入就绪队列,下次epoll_wait还会返回该套接字。ET就不一样,从就绪队列中取出来后就不会再放进去了,除非TCP协议栈再次因为有了新事件再次将其放入队列。
这样导致的差别是LT的套接字不会发生“饿死”的现象,比如说LT的套接字收到了10000个字节的数据,那么你每次read 100个字节后继续epoll_wait,该套接字的事件依然会被返回(因为每次epoll_wait时,内核发现数据没有读完,依然会让该套接字保持在就绪队列中),不管有没有新的数据到达。ET就不一样,如果你读了100字节后就继续epoll_wait,只要没有新数据(或事件)到达,那么就永远不会监听到该套接字的事件了。所以ET的套接字常常设置为非阻塞的,在epoll_wait返回后一次性读完,直到返回EAGAIN为止。LT的套接字当然最好也设置为非阻塞的。
3,结论
写技术文章是件很麻烦的事,不知道说明白没有,EPOLL的关键是在内核里设置了一个“就绪队列”以及回调函数机制。
范例
http://man7.org/linux/man-pages/man7/epoll.7.html 中给出了一个简单的范例,而https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/epoll-example.c 则有一个更加详细的范例,此详细范例也可以在这里查看。
- linux epoll
- linux epoll
- Linux Epoll
- linux epoll
- linux-epoll
- linux epoll
- Linux Epoll
- linux epoll
- Linux epoll
- linux epoll
- linux epoll
- Linux-epoll
- linux epoll
- linux ---- epoll
- Linux epoll
- linux- epoll
- linux epoll
- linux-----epoll
- BZOJ 1834 网络扩容
- HACMP启停Oracle 两个常用脚本
- TestNG-Excute selenium with TestNG.
- linux hook demo
- css 标签格式
- linux epoll
- 探讨LoadRunner的并发用户和集合点
- poj1149 PIGS --- 最大流EK
- Hadoop namenode无法启动问题解决
- 做一名合格的程序员
- Android自动化测试之MonkeyRunner工具
- Using MS Shell Dlg and MS Shell Dlg 2 (zz)
- 2、有一段文本,将文本中的所有单词,存放到一个字符指针数组中(要求每个单词内存恰好)。
- 保函