学习《Unix 网络编程》,知道 select poll 是两种 IO 多路复用的解决方法,那么它们有什么区别。本文通过对 select poll 操作的 数据结构 进行分析,进一步了解它们到底做了什么。

本文使用 Linux 版本是 Ubuntu 18.04 。

select, poll

文中的示例从《UNIX 网络编程》源码的 tcpservselect01.c 和 tcpservpoll01.c 改编而来。可以在 Github 上下载。

select

echo-select.c

select 中 fd_set 的定义如下(可以使用 vscode 查看源码):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* The fd_set member is required to be an array of longs.  */
typedef long int __fd_mask;

/* Number of descriptors that can fit in an `fd_set'. */
#define __FD_SETSIZE 1024

#define __NFDBITS (8 * (int) sizeof (__fd_mask))

/* fd_set for select and pselect. */
typedef struct
{
__fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->__fds_bits)
} fd_set;

使用一个由数组构成的结构体来表示一定长度的位(一般是 1024),这样一个 fd_set 代表一种类型,读/写/“异常”。

添加了一些输出代码,用来显示 select 操作的 fd_set 的数据变化。

  1. 使用 make echo-select 编译程序
  2. 使用 ./echo-select 运行程序
  3. 在另一个终端使用 telnet 127.0.0.1 9999 进行连接
  4. 查看 echo-select 输出

如上图, listenfd 是 3 ,将 …0008 按二进制展开,即 …0000000000001000 ,表示 3 号文件描述符已经准备好。

poll

echo-poll.c

poll 中的 pollfd 定义:

1
2
3
4
5
6
7
/* Data structure describing a polling request.  */
struct pollfd
{
int fd; /* File descriptor to poll. */
short int events; /* Types of events poller cares about. */
short int revents; /* Types of events that actually occurred. */
};

使用一个结构体表示,一个文件描述符的相关信息。

  1. 使用 make echo-poll 编译程序
  2. 使用 ./echo-poll 运行程序
  3. 使用 telnet 127.0.0.1 9999 进行连接
  4. 查看 echo-poll 输出

首先打印 pollfd 的宏定义。

listenfd 是 3 ,第一个 pollfd 写入 3 ,events 为 0001 ,即需要检查 POLLIN 。