多路IO之epoll边沿触发+非阻塞

  1. 源码地址:https://github.com/whuwzp/linuxc/tree/master/epoll_ET_LT_NOBLOCK_example
  2. 源码说明:
    • server.cpp: 监听127.1:6666,功能是将收到的数据打印到屏幕

1. 概要

这里没有用socket文件描述符,而是用了管道,关于管道的知识可以参考: https://www.bilibili.com/video/av41308301?p=13。

//设置边沿触发
evt.data.fd = pfd[0];
evt.events = EPOLLIN | EPOLLET; //边沿
// evt.events = EPOLLIN; //水平(默认)
epoll_ctl(epfd, EPOLL_CTL_ADD, pfd[0], &evt); //设置非阻塞
int flag = 0;
flag = fcntl(pfd[0], F_GETFL);
flag |= O_NONBLOCK; //设置为非阻塞
fcntl(pfd[0], F_SETFL, flag); //后面也有直接用以下设置的方法
fcntl(pfd[0], F_SETFL, O_NONBLOCK);

1.1 边沿触发和水平触发

假设一个情景: 接收了100字节的数据到fd的接收缓冲区, epoll_wait返回,说fd有数据,于是开始处理,但是只从缓冲区中read了10字节(所以缓冲区中还有90字节), 那么下一次调用epoll_wait时(假设fd没有新的数据到),是否还要返回fd有信号?

  • 边沿触发: 不返回了, 只有新数据来才返回
  • 水平触发: 仍然返回, 直接缓冲区还有数据就返回

1.2 边沿触发的优势缺点

优势:

  1. 不会像水平触发傻瓜式一直触发epoll_wait返回
  2. 边沿触发自定义更方便,有新数据了告诉我,至于我是否处理了怎么处理都不用管,当然这种难度相对大

缺点: 万一没有处理完数据,就会出问题了

1.3 边沿触发缺点的改进方法

int flag = 0;
flag = fcntl(pfd[0], F_GETFL);
flag |= O_NONBLOCK; //设置为非阻塞
fcntl(pfd[0], F_SETFL, flag); while ((len = (int)read(pfd[0], buf, 3)) > 0) { //体现非阻塞
write(STDOUT_FILENO, buf, (size_t)len);
}

while read>0确保read完所有数据, 因为在非阻塞模式下,如果缓冲区中没有数据了read也会返回, 关于read在非阻塞的返回值可以搜索.

ssize_t Read(int fildes, void *buf, size_t nbyte) {
readagain:
ssize_t ret = read(fildes, buf, nbyte);
if (ret == -1) {
if (errno == EINTR) {
goto readagain;
} else if (errno == EWOULDBLOCK || errno == EAGAIN) { //在非阻塞模式下,这个是正常的,表示没数据可读了
return ret;
} else {
perror_exit("read failed");
}
}
return ret;
}

2. 核心代码

这是用另一种文件描述符证明.

#include "include/wrap.h"
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <unistd.h> int main() {
int pfd[2];
int pid = 0;
char buf[3] = {0};
char buf1[3] = {0};
int len = 0; pipe(pfd);
pid = fork(); if (pid == -1) { // error
perror_exit("fork failed");
} if (pid == 0) { // child
close(pfd[0]);
int j = 0;
while (true) {
sprintf(buf1, "%d\n", j++);
write(pfd[1], buf1, 3);
sprintf(buf1, "%d\n", j++);
write(pfd[1], buf1, 3);
sleep(3);
}
close(pfd[1]);
} else { // parent
close(pfd[1]);
int epfd = 0;
struct epoll_event evts[1];
struct epoll_event evt; int flag = 0;
flag = fcntl(pfd[0], F_GETFL);
flag |= O_NONBLOCK; //设置为非阻塞
fcntl(pfd[0], F_SETFL, flag); epfd = epoll_create(1);
evt.data.fd = pfd[0];
evt.events = EPOLLIN | EPOLLET; //边沿
// evt.events = EPOLLIN; //水平(默认)
epoll_ctl(epfd, EPOLL_CTL_ADD, pfd[0], &evt);
while (true) {
epoll_wait(epfd, evts, 1, -1);
if (evts[0].data.fd == pfd[0]) {
while ((len = (int)read(pfd[0], buf, 3)) > 0) { //体现非阻塞
write(STDOUT_FILENO, buf, (size_t)len);
}
}
}
close(pfd[0]);
} return 0;
}
  • 水平触发: (不设置不阻塞, 用EPOLLIN, 不用while read),需要调用两次epoll_wait
  • 边沿触发: 只调用一次epoll_wait

3. 参考网址

  1. https://www.bilibili.com/video/av53016117?p=66
  2. https://www.bilibili.com/video/av41308301?p=13

最新文章

  1. 在IIS下部署Thinkphp项目,验证码不能显示的解决办法
  2. C/C++:提升_头文件的使用
  3. sql date时间加减几天几小时
  4. [IT学习]一些有用的工具
  5. 水晶报表(web)表格信息展示
  6. GDB使用
  7. js和css分别实现透明度的动画实现
  8. CentOS如何查看端口是被哪个应用/进程占用
  9. python比较两个列表
  10. C# 使用Sqlite 如何返回统计行数
  11. Repeater控件的嵌套使用
  12. wing带你玩转自定义view系列(3)模仿微信下拉眼睛
  13. 在Eclipse上安装Activiti插件
  14. 子类化QTreeWidgetItem实现增加Item的属性
  15. 【C#】Skip和Tack方法实现分页
  16. ajax 多个参数问题,如何既能表单序列化获取,又能加参数,加全部代码
  17. USB学习笔记连载(十五):USB固件更新以及安装驱动
  18. 一张图了解SSH端口转发
  19. [转] 用协议分析工具学习TCP/IP
  20. &lt;mvc:annotation-driven/&gt;的作用

热门文章

  1. 从一个小例子引发的Java内存可见性的简单思考和猜想以及DCL单例模式中的volatile的核心作用
  2. 图解I/O模型
  3. shell脚本基础-四种启动方式
  4. [bash]http serevr部署wordpress
  5. 题解 P5663 【加工零件【民间数据】】
  6. 关于LSTM实现长短期记忆功能问题
  7. reset slave all更彻底
  8. 给社团同学做的R语言爬虫分享
  9. ClickHouse学习系列之一【安装说明】
  10. C#中的字符串处理