Select多路复用
一切皆文件
- Linux中文件是什么?
– 狭义: 文件系统中物理意义上的文件(逻辑上关联的数据集合);
– 广义: 设备、管道、内存等Linux管理的一切对象; - 文件描述符(File Descriptor):
– 文件描述符是一种非负整数值,本质是一个句柄(Handle);
– 一切对用户(程序员)透明的资源标识都可以看作句柄;
– 用户使用文件描述符(句柄)与内核交互;
– 内核通过文件描述符操作对应资源的数据结构; - 一切皆文件的意义:
– 统一各种设备的操作方式(open / read / write / close); - 示例代码:
int fd = open("dev",O_RDWR);//"dev"是设备,O_RDWR为读写操作
if(fd != -1)
{
char buf[32] = {0};
int len = read(fd, buf, sizeof(buf));//读操作,返回len为实际读取的字节数
len = write(fd, buf, len);//写操作,返回的len为实际写入的字节数
close(fd);//释放申请的资源
}
事件相关函数的分类
- 阻塞式函数:
– 函数调用后需要等待某个事件发生才会返回; - 非阻塞式函数:
– 函数调用后能够及时返回(仅标记等待的事件);
– 事件发生后以回调函数方式传递。 - 对比:
– 轮询指依序询问每一个相关设备是否需要服务的方式;
– 轮询可用于解决阻塞函数导致程序无法继续执行的问题;
轮询的实现机制
- 神奇的 select 函数
– select函数用于监视指定的文件描述符是否产生事件;
– 可通过轮询的方式检测目标事件(事件产生则标记发生变化);
– 根据事件类型做出具体处理(如读取数据); - 函数原型:
– intselect
(int maxfd, fd_set* readset, fd_set* writeset, fd_set* exceptset,const struct timeval* timeout); - select函数的使用步骤:
- select相关数据类型及操作:
fd0 | fd1 | fd2 | fd3 | — |
---|---|---|---|---|
0 | 1 | 1 | 0 | … |
– FD_ZERO
(fd * fdset); //将fd_set变量的所有位设置为0
– FD_SET
(int fd,fd_set * fdset); //在fd_set中指定需要监听的fd
– FD_CLR
(int fd,fd_set * fdset); //在fd_set中剔除fd,不再监听
– FD_ISSET
(int fd,fd_set * fdset); //在fd_set中查看是否包含fd
实战轮询多路复用
#include <sys/select.h>
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
int iofd = 0;
char s[] = "hello\n";
int len = 0;
fd_set reads = {0};
fd_set temps = {0};
struct timeval timeout = {0};
FD_ZERO(&reads);
FD_SET(iofd,&reads);
while(1)
{
int r = -1;
temps = reads;
timeout.tv_sec = 0;
timeout.tv_usec = 5000;
r = select(1, &temps, 0, 0, &timeout);
if(r > 0)
{
len = read(iofd, s, sizeof(s)-1);
s[len] = 0;
printf("%s\n",s);
}
else if(r == 0)
{
usleep(10000);
}
else
{
break;
}
}
return 0;
}