专业网站设计公司排行榜,怎么利用360域名做网站,不同的网站有不同的风格,广州网页设计师学校目录 1. poll
2. epoll 2.1 epoll_ctl
2.2 epoll_wait 2.3 epoll原理
2.4 epoll的工作模式
2.5 epoll的惊群效应 使用建议
总结 1. poll poll也是实现 I/O 多路复用的系统调用#xff0c;可以解决select等待fd上限的问题#xff0c;将输入输出参数分离#xff0c;不需要…目录 1. poll
2. epoll 2.1 epoll_ctl
2.2 epoll_wait 2.3 epoll原理
2.4 epoll的工作模式
2.5 epoll的惊群效应 使用建议
总结 1. poll poll也是实现 I/O 多路复用的系统调用可以解决select等待fd上限的问题将输入输出参数分离不需要每次对参数重置 原型
#include poll.h
int poll(struct pollfd *fds, nfds_t nfds, int timeout);struct pollfd {int fd; /* file descriptor */short events; /* requested events */short revents; /* returned events */
};
参数
fds是一个poll函数监听的结构列表 每一个元素中包含了三部分内容文件描述符监听的事件集合, 返回的事件集合nfds表示fds数组的长度.由此可以看出fds其实就是一个数组timeout指定 poll 函数的超时时间单位为毫秒。其取值有以下几种情况 timeout 0poll 函数会阻塞直到指定的文件描述符上有事件发生或者超时时间到达。 timeout 0poll 函数会立即返回不会阻塞用于非阻塞检查文件描述符的状态。 timeout -1poll 函数会一直阻塞直到指定的文件描述符上有事件发生。 调用时调用poll时用户-内核OS帮用户关心fd上面的events事件 返回时poll返回时内核-用户用户关心的fd上面有哪些的revents事件准备就绪
返回结果
大于 0表示发生事件的文件描述符的数量。等于 0表示超时即在指定的时间内没有文件描述符发生事件。等于 -1表示发生错误同时会设置 errno 来指示具体的错误类型。 events和revents的取值 示例
#include stdio.h
#include poll.h
#include unistd.hint main() {struct pollfd fds[1];fds[0].fd 0; // 监控标准输入fds[0].events POLLIN; // 监控可读事件int ret poll(fds, 1, -1); // 一直阻塞直到有事件发生if (ret -1) {perror(poll);return 1;}if (fds[0].revents POLLIN) {char buf[1024];ssize_t n read(STDIN_FILENO, buf, sizeof(buf));if (n 0) {buf[n] \0;printf(Read from stdin: %s, buf);}}return 0;
}
pollserver示例poll_server 优点
可以等待多个fd效率高输入输出参数分离(events和revents)不用频繁对poll参数进行重置poll关心的fd没上限内部动态申请空间
缺点
用户到内核要进行数据拷贝struct pollfd 结构体数组传递给内核空间这涉及到用户空间和内核空间之间的数据拷贝操作应用层使用时仍然需要遍历在内核层面要遍历检测关心的fd是否有对应的事件就绪在应用层当 poll 函数返回后需要遍历 struct pollfd 结构体数组检查每个 fd 的 revents 成员以确定哪些文件描述符上发生了事件。在内核层监控依然是线性遍历
2. epoll epoll在man 手册中是这样描述的是为处理大批量句柄而作了改进的poll并且解决了poll遗留下来的问题
解决 poll 遍历开销大的问题epoll 的改进epoll 采用红黑树来管理需要监控的文件描述符。
epoll相对于select、poll、提供了更多的操作接口
int epoll_create(int size);int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); 2.1 epoll_ctl
用于控制 epoll 实例可以添加、修改或删除要监控的文件描述符及其事件
接口原型
#include sys/epoll.h
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);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 */
};
events表示要监听的事件类型如 EPOLLIN可读、EPOLLOUT可写等。data是一个联合体通常使用 fd 成员来存储关联的文件描述符也可以用 ptr 指向自定义的数据。
参数
epfdepoll_create 返回的 epoll 实例的文件描述符。op操作类型有 EPOLL_CTL_ADD添加、EPOLL_CTL_MOD修改、EPOLL_CTL_DEL删除。fd要监控的文件描述符。event指向 epoll_event 结构体的指针指定要监控的事件类型和关联的数据。
返回值成功返回 0失败返回 -1
2.2 epoll_wait
等待 epoll 实例中注册的文件描述符上的事件发生
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); 参数
epfdepoll 实例的文件描述符。events用于存储发生事件的 epoll_event 结构体数组。maxeventsevents 数组的最大元素个数。timeout超时时间单位为毫秒。-1 表示阻塞等待0 表示立即返回。
返回值成功返回发生事件的文件描述符数量超时返回 0失败返回 -1
events
EPOLLIN : 表示对应的文件描述符可以读 (包括对端SOCKET正常关闭);EPOLLOUT : 表示对应的文件描述符可以写;EPOLLPRI : 表示对应的文件描述符有紧急的数据可读 (这里应该表示有带外数据到来);EPOLLERR : 表示对应的文件描述符发生错误;EPOLLHUP : 表示对应的文件描述符被挂断;EPOLLET : 将EPOLL设为边缘触发(Edge Triggered)模式, 这是相对于水平触发(Level Triggered)来说的.EPOLLONESHOT只监听一次事件, 当监听完这次事件之后, 如果还需要继续监听这个socket的话, 需要 再次把这个socket加入到EPOLL队列里 简单使用示例
#include stdio.h
#include sys/epoll.h
#include unistd.h#define MAX_EVENTS 10int main() {// 创建 epoll 实例int epoll_fd epoll_create1(0);if (epoll_fd -1) {perror(epoll_create1);return 1;}// 定义要监控的事件struct epoll_event ev, events[MAX_EVENTS];ev.events EPOLLIN;ev.data.fd STDIN_FILENO;// 添加标准输入到 epoll 实例中进行监控if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, STDIN_FILENO, ev) -1) {perror(epoll_ctl: STDIN_FILENO);close(epoll_fd);return 1;}while (1) {// 等待事件发生int nfds epoll_wait(epoll_fd, events, MAX_EVENTS, -1);if (nfds -1) {perror(epoll_wait);close(epoll_fd);return 1;}// 处理事件for (int i 0; i nfds; i) {if (events[i].data.fd STDIN_FILENO) {char buf[1024];ssize_t n read(STDIN_FILENO, buf, sizeof(buf));if (n 0) {buf[n] \0;printf(Read from stdin: %s, buf);}}}}// 关闭 epoll 实例close(epoll_fd);return 0;
} epollserver的示例epoll_server 2.3 epoll原理 前边提到epoll是采用红黑树来解决遍历效率低的问题 而epoll_create的功能其实就是构建红黑树等一系列操作具体流程申请struct file创建epoll模型构建红黑树构建就绪队列设置回调机制把struct file设置到文件描述符表返回fd epoll_ctl的选项操作其实就是对epoll模型中的红黑树进行增删改设置进去的fd和event就会添加到红黑树中要关心的fd上的哪些事件 epoll_wait获取准备就绪的fd 可以直接到就绪队列获取(内核告诉用户哪些fd的哪些事件就绪)结合epoll_wait参数数组的首元素地址数组大小epoll会从就绪队列获取节点会按照顺序严格的放在 events *中(select和poll中的数组中间会出现空缺的情况)并用返回值表明就绪事件个数 epoll_wait检测是否有事件就绪时间复杂度O(1)检查就绪链表是否为空即可判断获取所有就绪事件事件复杂度O(N)——这个是必然无法优化遍历就绪链表将N个节点添加到 event* 数组中 如果 events * 数组满了怎么办满了就返回 struct eventpoll{ .... /*红黑树的根节点这颗树中存储着所有添加到epoll中的需要监控的事件*/ struct rb_root rbr; /*双链表中则存放着将要通过epoll_wait返回给用户的满足条件的事件*/ struct list_head rdlist; ....
}; 网卡从网络中读取到数据后通过硬件中断将数据读到网卡驱动网卡驱动允许注册相应的回调方法红黑树中每添加一个节点就会为该节点注册一个相应的回调方法这个回调方法主要完成两个工作
在内核中确认发生的事件(是否关心)向ready_queue中形成新节点表明哪个fd上的哪些事件已经发生 这整个过程都是依靠单线程/进程完成的那如果是多线程/进程呢 epoll模型创建返回值是一个文件描述符只需对文件描述符表的管理就可以做到对epoll模型进行管理
epoll的优点
接口使用方便虽然拆分成了三个函数, 但是反而使用起来更方便高效.不需要每次循环都设置关注的文件描述符也做到了输入输出参数分离开数据拷贝轻量只在合适的时候调用 EPOLL_CTL_ADD 将文件描述符结构拷贝到内核中这个操作并不频繁(而select/poll都是每次循环都要进行拷贝)事件回调机制避免使用遍历而是使用回调函数的方式将就绪的文件描述符结构加入到就绪队列中epoll_wait 返回直接访问就绪队列就知道哪些文件描述符就绪. 这个操作时间复杂度O(1). 即使文件描述 符数目很多, 效率也不会受到影响.没有数量限制: 文件描述符数目无上限.
2.4 epoll的工作模式 epoll有两种工作模式水平触发、边缘触发它是epoll提供给用户的一种通知事件就绪的策略
epoll默认是LT模式如果数据就绪但上层并不做处理epoll就会一直通知——LT模式
ET策略底层有数据只通知一次就不再通知直到下次数据发生变化的时候(底层收到新的数据)才通知
LT模式和ET模式相比ET模式更高效因为在通知策略中没有无效的通知全部都是有效的
ET模式只会通知一次倒逼上层程序员要取数据就要把本轮的数据取完
在TCP服务中这样的话底层也会给发送方告知窗口大小会通告一个更大的窗口发送方的滑动窗口大小就会变得更大从概率上也可以提高双方的通信效率 ET模式下要求数据一次读完那如何判断数据是否一次读完了循环读取直到缓冲区没有数据就会阻塞
但是epoll一般是单线程/进程一旦阻塞就没法继续后续的操作了所以ET模式下必须以非阻塞状态进行IO操作 epoll的高性能是有一定的特定场景的。如果场景选择的不适宜epoll的性能可能适得其反
对于多连接且多连接中只有一部分连接比较活跃时比较适合使用epoll
2.5 epoll的惊群效应 在多线程或者多进程环境下有些人为了提高程序的稳定性往往会让多个线程或者多个进程同时在epoll_wait监听的socket描述符。当一个新的链接请求进来时操作系统不知道选派那个线程或者进程处理此事件则干脆将其中几个线程或者进程都给唤醒而实际上只有其中一个进程或者线程能够成功处理accept事件其他线程都将失败这种现象称为惊群效应结果是肯定的惊群效应肯定会带来资源的消耗和性能的影响 解决办法
不建议让多个线程同时在epoll_wait监听的socket而是让其中一个线程epoll_wait监听的socet当有新的链接请求进来之后由epoll_wait的线程调用accept建立新的连接然后交给其他工作线程处理后续的数据读写请求每个子进程仍然管自己在监听的socket上调用epoll_wait当有新的链接请求发生时操作系统仍然唤醒其中部分的子进程来处理该事件仍然只有一个子进程能够成功处理此事件那么其他被惊醒的子进程捕获EAGAIN错误并无视创建一个全局的pthread_mutext在子进程进行epoll_wait前先获取锁在同一时刻永远都只有一个子讲程在监听的sacket 上epoll_wait 使用建议
发数据问题
什么时候不能发—— 发送缓冲区被写满的时候
什么时候能发—— 缓冲区不满刚开始的时候可以直接发(大部分情况下发送缓冲区不会被写满)不能发的时候把fd交给epoll让epoll关心什么时候可以发(写事件就绪)
怎么知道发送缓冲区写满
一直循环的去写直到发送条件不具备再交给epoll处理
对于多路转接而言
一般对于任何fdEPOLLIN事件,常设关心对于写事件按需关心即可 总结 以上便是本文的全部内容希望对你有所帮助感谢阅读