only edge-triggered epoll is truly O(1), while level-trggered epoll has same asymptotics of O(N)

A scalable I/O event notification mechanism used in Linux programming to efficiently monitor multiple file descriptors to see if I/O is possible on any of them.
(The more recent epoll_create1(2) extends the functionality of epoll_create(2).)

  • epoll 现在是线程安全的,而 select 和 poll 不是。
  • epoll 基于事件驱动,epoll_ctl 注册事件并注册 callback 回调函数,epoll_wait 只返回发生的事件避免了像 select 和 poll 对事件的整个轮寻操作。Red Black Tree: key 是 fd,value 是监听事件类型以及使用方自定义拓展数据.
  • epoll_create
  • epoll_ctl 对 epoll 事件表内的 fd 执行变更操作
  • epoll_wait.

Level-triggered and edge-triggered

水平触发 (level-trggered)

  • 只要文件描述符关联的读内核缓冲区非空,有数据可以读取,就一直发出可读信号进行通知,
  • 当文件描述符关联的内核写缓冲区不满,有空间可以写入,就一直发出可写信号进行通知

边缘触发 (edge-triggered)

  • 当文件描述符关联的读内核缓冲区由空转化为非空的时候,则发出可读信号进行通知,
  • 当文件描述符关联的内核写缓冲区由满转化为不满的时候,则发出可写信号进行通知

To use edge-triggered polling you must put the file descriptors into nonblocking mode. Then you must call read or write until they return EWOULDBLOCK every time. If you fail to meet these conditions you will miss notifications from the kernel. But there’s a big upside of doing this: each call to epoll_wait will be more efficient, which can be very important on programs with extremely high levels of concurrency.

Example

#include <stdio.h>     // for fprintf()
#include <unistd.h>    // for close(), read()
#include <sys/epoll.h> // for epoll_create1(), epoll_ctl(), struct epoll_event
#include <string.h>    // for strncmp
 
int main()
{
	int running = 1, event_count, i;
	size_t bytes_read;
	char read_buffer[READ_SIZE + 1];
	struct epoll_event event, events[MAX_EVENTS];
	int epoll_fd = epoll_create1(0);
 
	if (epoll_fd == -1) {
		fprintf(stderr, "Failed to create epoll file descriptor\n");
		return 1;
	}
 
	event.events = EPOLLIN;
	event.data.fd = 0;
 
	if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, 0, &event))
	{
		fprintf(stderr, "Failed to add file descriptor to epoll\n");
		close(epoll_fd);
		return 1;
	}
 
	while (running) {
		printf("\nPolling for input...\n");
		event_count = epoll_wait(epoll_fd, events, MAX_EVENTS, 30000);
		printf("%d ready events\n", event_count);
		for (i = 0; i < event_count; i++) {
			printf("Reading file descriptor '%d' -- ", events[i].data.fd);
			bytes_read = read(events[i].data.fd, read_buffer, READ_SIZE);
			printf("%zd bytes read.\n", bytes_read);
			read_buffer[bytes_read] = '\0';
			printf("Read '%s'\n", read_buffer);
		
			if(!strncmp(read_buffer, "stop\n", 5))
			running = 0;
		}
	}
 
	if (close(epoll_fd)) {
		fprintf(stderr, "Failed to close epoll file descriptor\n");
		return 1;
	}
 
	return 0;
}