58上怎么做装修网站/万能的搜索引擎
1、概述
多路 I/O 复用技术,是要先构造一张我们感兴趣的文件描述符列表,然后调用一个函数,直到这些描述符其中的一个已准备好进行 I/O 时,该函数才返回。select、pselect、poll、ppoll这 4 个函数可以实现多路 I/O 复用功能,从这些函数返回时,进程或者线程会被告知哪些文件描述符已准备好进行 I/O 操作。
下面详细介绍 select 函数、pselect 函数、poll 函数和 ppoll 函数的相关细节。
2、select 函数组
2.1接口原型
#include <sys/select.h>
int select(int iWidth,fd_set *pfdsetRead,fd_set *pfdsetWrite,fd_set *pfdsetExcept,struct timeval *ptmvalTO);
int pselect(int iWidth, fd_set *pfdsetRead,fd_set *pfdsetWrite,fd_set *pfdsetExcept,const struct timespec *ptmspecTO,const sigset_t *sigsetMask);
函数 select 原型分析:
- 此函数成功返回等到的描述符数量,超时返回 0,失败返回-1 并设置错误号;
- 参数 iWidth 是文件描述符列表中最大描述符加 1;
- 参数 pfdsetRead 是读描述符集;
- 参数 pfdsetWrite 是写描述符集;
- 参数 pfdsetExcept 是异常描述符集;
- 参数 ptmvalTo 是等待超时时间。
函数 pselect 原型分析:
- 此函数成功返回等到的描述符数量,超时返回 0,失败返回-1 并设置错误号;
- 参数 iWidth 是文件描述符列表中最大描述符加 1;
- 参数 pfdsetRead 是读描述符集;
- 参数 pfdsetWrite 是写描述符集;
- 参数 pfdsetExcept 是异常描述符集;
- 参数 ptmspecTo 是等待超时时间;
- 参数 sigsetMask 是等待时阻塞的信号。
2.2接口分析
我们来看 select 函数的参数,从传递给内核的参数可以看出下面这几点:
- 告诉内核我们所关心的文件描述符;
- 对于每个文件描述符我们所关心的条件(读、写、异常);
- 愿意等待多长时间(可以永远等待、可以不等待、可以等待指定的时间);
当 select 返回时,我们可以得知多少文件描述符就绪了,以及哪些文件描述符就绪了,使用这些就绪的文件描述符可以进行 read、write 等操作。
参数 ptmvalTo 可以分 3 种情况: - 当 ptmvalTo == NULL 时,表示永远等待,
- 当 ptmvalTo->tv_sec == 0 && ptmvalTo->tv_usec == 0 时,表示不等待,
- 当 ptmvalTo->tv_sec != 0 || ptmvalTo->tv_usec != 0 时,表示等待指定的秒和微秒数。
参数 pfdsetRead、pfdsetWrite 和 pfdsetExcept 是指向文件描述符集的指针,每个文件描述符集存储在一个 fd_set 数据类型的变量中,这个类型,不同的系统可能有不同的实现,这里我们可以认为它是一个很大的字节数组,每一个文件描述符占据其中一位。在 SylixOS 中,对于 fd_set 类型的变量提供了下面一组宏进行操作:
在声明了一个 fd_set 类型的文件描述符集后,首先需要使用 FD_ZERO 清除该文件描述符集,然后使用 FD_SET 将我们关心的文件描述符放到该集中,当 select 成功返回后,使用FD_ISSET 来判断是否是我们关心的文件描述符。
select 函数有 3 种可能的返回值:
- 返回值-1 表示错误。例如,在所指定的文件描述符都没有准备好时捕捉到一个信号,将返回-1;
- 返回值 0 表示没有文件描述符准备好,因为在指定的时间内,没有文件描述符准备好,也即调用超时;
- 返回值是一个大于 0 的整数,该值是 3 个文件描述符集中所有准备好的文件描述符数之和。
select 函数返回准备好的文件描述符和,这里“准备好”具有下面的意思:
- 对于读集中的一个文件描述符 read 操作不会阻塞;
- 对于写集中的一个文件描述符 write 操作不会阻塞;
- 对于异常集中的一个文件描述符有一个未决异常条件。
需要注意的是,如果在一个文件描述符上碰到了文件尾端,则 select 函数会认为该文件描述符可读,然后调用 read 函数将返回 0。
在上面的定义中我们看到 pselect 函数除了最后两个参数和 select 函数不同外,其他参数是相同的,下面我们来介绍一下这两个不同的参数。
select 函数超时值类型是 struct timeval 而 pselect 函数超时值类型是 struct timespec,timespec 结构以秒和纳秒表示超时值,也就是说 pselect 函数提供了比 select 函数更精准的超时时间。
pselect 函数可使用信号屏蔽字。如 sigmask 为 NULL, pselect 函数的运行状况和 select函数相同。否则,sigmask 指向一信号屏蔽字,在调用 pselect 函数时,以原子操作的方式安装该信号屏蔽字。在返回时,恢复以前的信号屏蔽字。
2.3接口示例
下面程序展示了 select 函数的使用,程序等待标准输入(STDIN_FILENO)描述符可读,如果在超时时间内 select 返回,则读取标准输入,并打印读到的字符。
#include <stdio.h>
#include <sys/select.h>
int main (int argc, char *argv[])
{fd_set fdset;int ret;struct timeval timeout;char ch;timeout.tv_sec = 10;timeout.tv_usec = 0;for (;;) {FD_ZERO(&fdset);FD_SET(STDIN_FILENO, &fdset);ret = select(STDIN_FILENO + 1, &fdset, NULL, NULL, &timeout);if (ret <= 0) {break;} else if (FD_ISSET(STDIN_FILENO, &fdset)) {read(STDIN_FILENO, &ch, 1);if (ch == '\n') {continue;}fprintf(stdout, "input char: %c\n", ch);if (ch == 'q') {break;}}}return (0);
}
在 SylixOS Shell 下运行程序,并输入字符,可以看到相应的打印。
# ./select_test
h
input char: h
3、poll 函数组
poll 函数功能类似于 select 函数,但是函数接口有所不同。
3.1接口原型
#include <poll.h>
int poll (struct pollfd fds[], nfds_t nfds, int timeout);
int ppoll (struct pollfd fds[], nfds_t nfds, const struct timespec *timeout_ts,const sigset_t *sigmask);
函数 poll 原型分析:
- 此函数成功返回等到的描述符数量,超时返回 0,失败返回-1 并设置错误号;
- 参数 fds 是 poll 文件描述符数组;
- 参数 nfds 是 fds 数组的元素个数;
- 参数 timeout 是超时值。
函数 ppoll 原型分析: - 此函数成功返回等到的描述符数量,超时返回 0,失败返回-1 并设置错误号;
- 参数 fds 是 ppoll 文件描述符数组;
- 参数 nfds 是 fds 数组的元素个数;
- 参数 timeout_ts 是超时值;
- 参数 sigmask 是等待时阻塞的信号。
与 select 函数不同,poll 函数不是为每个条件构造一个文件描述符集,而是构造一个pollfd 结构的数组,每个数组元素指定一个文件描述符编号以及我们对该文件描述符感兴趣的条件。
struct pollfd {int fd; /* file descriptor being polled */short int events; /* the input event flags */short int revents; /* the output event flags */
};
3.2 接口分析
调用 poll 函数时,应将每个 fds 元素中的 events 设置成表 5.13 中某个或某些值,通过这些值告诉内核我们关心的是每个文件描述符的哪些事件。返回时,revents 成员由内核设置,用于说明每个文件描述符发生了哪些事件。
poll 函数的超时等待参数和 select 函数类似,也分 3 种情况。我们需要注意,poll 函数和 select 函数不会因为一个文件描述符是否阻塞而影响其本身的阻塞情况。
ppoll 函数的行为类似于 poll,不同的是,ppoll 函数可以指定信号屏蔽字。