再介绍一下epoll的基本结构和系统调用
epoll里有两个关键结构。一个是红黑树,每一个节点都代表了一个文件描述符;另外一个是双向链表,也叫做就绪列表。
为了维护epoll的结构,有三个关键的系统调用。
epoll_create:也就是创建一个epoll结构
epoll_ctl:管理epoll里面那些文件描述符,简单来说就是增删改查那些文件描述符
epoll_wait:根据你的要求,返回符合条件的文件描述符,也就是查。
那么显然你可以猜到,如果你写一个从网络中读取数据的程序,看起来应该是这样的
epoll = epoll_create();
// fd 是一个套接字对应的文件描述符,并且告诉它,你关心读事件
// 你可以加很多个
epoll_ctl(epoll, ADD, fd, READ)
while true {
// 你需要可读数据的套接字,等待时间是 1000 毫秒
fds = epoll_wait(epoll, READ, 1000)
// 一步步处理
}
你可以进一步补充epoll是怎么把文件描述符挪到就绪列表的。
需要注意的是,epoll并不是在我发起epoll调用的时候才把文件描述符挪到就序列表的。而是在epoll创建之后,不管你有没有发起epoll_wait调用,只要文件描述符满足条件了,就会被挪到就绪列表。
发起epoll调用
因此,当你发起epoll_wait的时候,有两种情况。第一种情况是就绪列表里面有符合条件的套接字,直接给你。
第二种情况就是就绪列表里面没有符合条件的套接字,这时候传入不同的超时时间,会有不同的响应。记住关键字,-1永远阻塞,0立刻返回,正数等待直到超时。
如果在发起超时调用的时候,传入的超时时间是-1,那么调用者会被阻塞,直到有满足条件的文件描述符。如果传入的超时时间是0,那么会立刻返回,不管有没有满足条件的文件描述符。如果传入的是正数n,那么就会最多等待n毫秒,直到有数据或超时。
亮点:epoll与中断
可以刷一个亮点,就是epoll怎么知道数据来了?又或者epoll怎么知道超时了?答案是中断,也就是在操作系统基本原理里学到的中断。
每一个和IO有关的文件描述符都有一个对应的驱动,这个驱动会告诉epoll发生了什么。比如说,当有数据发送到网卡的时候,会触发一个中断。借助这个中断,网卡的驱动会告诉epoll,这里有数据了。而超时也是利用了中断,不过是时钟中断。时钟中断之后,内核会去检查发起epoll_wait的线程有没有超时,如果超时了就唤醒这个线程。调用者会得到超时响应。
这部分内容我建议你主动提起,因为很少有面试官会在面试中追问到这里。而且,你可以将比较上层的应用原理和底层的中断机制关联在一起,能体现你计算机基础很扎实。