epoll笔记

xiaoxiao2021-02-28  71

基础概念

epoll - I/O event notification facility,一种I/O事件通知机制

I/O事件

我们知道,内核有缓冲区。假设有两个进程A,B,进程B想读进程A写入的东西(即进程A做写操作,B做读操作)。进程A需要先写入到内核缓冲区中,然后B从内核缓冲区中读取,如图: 进程B会监听内核缓冲区的变化。

I/O事件的阻塞与同步
当内核缓冲区为空的时候,进程B会阻塞住当A往内核缓冲区写入时,内核缓冲区就不是空状态了,这时候就会唤醒进程B如果缓冲区满了,但是进程B没有被唤醒,就会通知进程A,告诉A不要再写入数据了,也就是进程A被阻塞当进程B被唤醒后,B就从缓冲区读取数据,由于B在读数据,缓冲区就不会是满的状态了,这时候就会通知A继续写数据,也就是进程A被唤醒如果进程A还没有唤醒,而缓冲区被B读完了(缓冲区为空),这时候就会阻塞进程B
阻塞I/O的缺点

在阻塞I/O情况下,一个线程只能处理一个流的I/O事件。也就是说,如果想处理多个流的I/O事件,就必须使用多进程或者多线程——效率低

非阻塞I/O

非阻塞I/O涉及事件通知机制及轮询机制。

通知机制——就是当事件发生的时候,去通知他轮询机制——通知机制的反面,工作过程类似枚举,效率很低

最开始能想到的就是用轮询的方法:依次询问每个流,如果缓冲区不为空,就进行操作;否则,询问下一个流 但是这种方法效率很低,会白白浪费掉CPU资源。于是便引入了代理——poll

poll

poll代理可以同时观察很多I/O流事件,在空闲的时候(即没有I/O事件的时候),会阻塞当前线程;当有I/O事件的时候,会被唤醒,然后把所有流轮询一遍。这样就能通过减少盲目的轮询来减少对CPU资源的浪费。但是,缺点也很明显:由于每次唤醒都需要把所有流都轮询一遍,当流很多的时候,轮询的时间会很长

poll进化版——epoll

epoll是基于事件的轮询,它会记录是哪个流产生了I/O事件,然后针对这个流来进行操作,大大降低了复杂度。

水平触发与边缘触发

参考epoll边缘触发与水平触发

Linux中的EAGAIN含义

Linux环境下开发经常会碰到很多错误(设置errno),其中EAGAIN是其中比较常见的一个错误(比如用在非阻塞操作中)。 从字面上来看,是提示再试一次。这个错误经常出现在当应用程序进行一些非阻塞(non-blocking)操作(对文件或socket)的时候。

例如,以 O_NONBLOCK的标志打开文件/socket/FIFO,如果你连续做read操作而没有数据可读。此时程序不会阻塞起来等待数据准备就绪返回,read函数会返回一个错误EAGAIN,提示你的应用程序现在没有数据可读请稍后再试。 又例如,当一个系统调用(比如fork)因为没有足够的资源(比如虚拟内存)而执行失败,返回EAGAIN提示其再调用一次(也许下次就能成功)。

epoll接口介绍

epoll_create

创建epoll实例,会创建所需要的红黑树,以及就绪链表,以及代表epoll实例的文件句柄。

//epoll_create, epoll_create1 - open an epoll file descriptor #include <sys/epoll.h> int epoll_create(int size); int epoll_create1(int flags);

更多参考EPOLL_CREATE

epoll_ctl

添加,修改,或者删除注册到epoll实例中的文件描述符上的监控事件。

//epoll_ctl - control interface for an epoll file descriptor #include <sys/epoll.h> int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

epfd: epoll实例file descriptor,epoll_create函数返回值

op:操作类型: op | description

| :-: | -: EPOLL_CTL_ADD | 添加事件 EPOLL_CTL_MOD | 修改事件 EPOLL_CTL_DEL|移除事件

fd: 操作的目标文件描述符

event: 要操作的事件

// The event argument describes the object linked to the file descriptor fd. The struct epoll_event is defined as: 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 */ };

更多参考EPOLL_CTL

epoll_wait

等待epoll实例中注册的事件触发。

//epoll_wait, epoll_pwait - wait for an I/O event on an epoll file descriptor #include <sys/epoll.h> int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); int epoll_pwait(int epfd, struct epoll_event *events, int maxevents, int timeout, const sigset_t *sigmask); epfd: epoll实例文件描述符events: 数组出参,用来记录被触发的events,其大小应该和maxevents一致maxevents: 返回的events的最大个数,如果最大个数大于实际触发的个数,则下次epoll_wait的时候仍然可以返回timeout: 等待事件,毫秒为单位 -1:无限等待 0:立即返回

更多参考EPOLL_WAIT


参考文档:

EPOLL Linux Man官方文档 epoll内核源码分析 The C10K problem IO多路复用之epoll总结

转载请注明原文地址: https://www.6miu.com/read-2632098.html

最新回复(0)