多路复用
应用程序中同时处理多路输入输出流,若采用阻塞模式,得不到预期的目的;
若采用非阻塞模式,对多个输入进行轮询,但又太浪费CPU时间;
若设置多个进程/线程,分别处理一条数据通路,将新产生进程/线程间的同步与通信问题,使程序变得更加复杂;
比较好的方法是使用I/O多路复用技术。其(select)基本思想是:
1.先构造一张有关描述符的表(最大1024),然后调用一个函数。
2.当这些文件描述符中的一个或多个已准备好进行I/O时函数才返回。
3. 函数返回时告诉进程哪个描述符已就绪,可以进行I/O操作。
select
通过man手册得到函数的用法及参数:
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
功能:
实现IO的多路复用
参数:
nfds:关注的最大的文件描述符+1
readfds:关注的读表
writefds:关注的写表
exceptfds:关注的异常表
timeout:超时的设置
编程步骤:
- 构造一张关于文件描述符的表
- 清空表 FD_ZERO
- 将关心的文件描述符添加到表中 FD_SET
- 调用select函数,监听 select
- 判断到底是哪一个或者是哪些文件描述符发生了事件 FD_ISSET
- 做对应的逻辑处理
通过代码实现:输入鼠标的时候, 响应鼠标事件, 输入键盘的时候, 响应键盘事件 (两路IO)
#include <stdio.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char const *argv[])
{
char buf[128] = {0}; // 用于存储读取的数据
int fd = open("/dev/input/mouse0", O_RDONLY); // 打开鼠标设备文件
if (fd < 0)
{
perror("open err"); // 打开文件出错
return -1;
}
fd_set rfds; // 文件描述符集合
while (1)
{
FD_ZERO(&rfds); // 清空文件描述符集合
FD_SET(fd, &rfds); // 将鼠标设备的文件描述符加入集合
FD_SET(STDIN_FILENO, &rfds); // 将标准输入的文件描述符(键盘)加入集合
// 监听文件描述符是否有可读事件
int ret = select(fd + 1, &rfds, NULL, NULL, NULL);
if (ret < 0)
{
perror("select err"); // select 出错
close(fd);
return -1;
}
// 检查标准输入是否有数据可读
if (FD_ISSET(STDIN_FILENO, &rfds))
{
fgets(buf, sizeof(buf), stdin); // 从标准输入读取数据
printf("键盘输入: %s\n", buf);
}
// 检查鼠标设备是否有数据可读
if (FD_ISSET(fd, &rfds))
{
ssize_t n = read(fd, buf, sizeof(buf)); // 从鼠标设备读取数据
if (n < 0)
{
perror("read err"); // 读取设备出错
}
else
{
printf("鼠标数据: ");
for (ssize_t i = 0; i < n; ++i)
{
printf("%02x ", (unsigned char)buf[i]); // 以十六进制打印鼠标数据
}
printf("\n");
}
}
memset(buf, 0, sizeof(buf)); // 清空缓冲区
}
close(fd); // 关闭文件描述符
return 0;
}