I/O多路复用之select系统调用
I/O多路复用模型允许我们同时等待多个套接字描述符是否就绪。Linux系统为实现I/O多路复用提供的最常见的一个函数是select
函数,该函数允许进程指示内核等待多个事件中的任何一个发生,并只有在一个或多个事件发生或经历一段指定的时间后才唤醒它。
作为一个例子,我们可以调用select
,告知内核仅在下列情况发生时才返回:
- 当集合{0, 4}中任意描述符准备好读时返回
- 当集合{1, 2, 7}中任意描述符准备好写时返回
- 已经历了10.2秒
也就是说,我们调用select
可以告知内核我们对哪些描述符感兴趣以及等待多久时间。select
是一个复杂的函数,有许多不同的应用场景,我们将只讨论第一种场景:等待一组描述符准备好读。
1 |
|
我们来看下select
函数的参数。参数n
指定需要测试的描述符的数目,测试的描述符范围从0到n-1。第二个参数fdset
指定需要测试的可读描述符集合。当fdset
集合中有描述符可读,或者经历了timeout
时间时,select
将返回。当select
返回时,作为一个副作用,select
修改了参数fdset
指向的描述符集合,这时fdset
变成由读集合中准备好可以读了的描述符组成。select
函数的返回值则指明了就绪集合的基数。值得注意的是,由于这个副作用,我们必须每次在调用select
时都更新读集合。
1 |
|
上面的代码展示了如何使用select
来编写多并发服务器的过程。服务器可以让select
调用同时检查监听套接字和已连接套接字。一旦select
指示有活动发生,就可以用FD_ISSET
来遍历所有可能的文件描述符,以检查是哪个描述符上面有活动发生。
如果是监听套接字可读,这说明正有一个客户试图建立连接,此时就可以调用accept
创建一个客户的已连接套接字而不用担心阻塞。如果是某个客户描述符准备好,这说明该描述符上有一个客户请求需要我们读取处理。如果读操作返回零字节,这表示有一个客户进程已结束,这时我们可以关闭该套接字并把它从描述符集合中删除。
参考资料
- 深入理解计算机系统,第2版,机械工业出版社
- Linux程序设计(第4版),Neil Matthew等著,人民邮电出版社,2010年
- UNIX 网络编程卷1:套接字联网API(第三版), W.Richard Stevens 等著