系统提供select函数来实现多路复用输出/输入模型:
系统调用select是为了使一个程序监视多个文件描述符的状态变化,程序会停在select这里等待,直到被监视的文件描述符有一个或者多个文件描述符发生了状态改变。
#include<sys/select.h>
int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);
int nfds, ——需要监视的最大的文件描述符加1
fd_set *readfds, ——需要检测的可读文件描述符的集合
fd_set *writefds, ——需要检测的可写文件描述符的集合
fd_set *exceptfds, ——需要检测的异常文件描述符的集合
struct timeval *timeout, ——用来设置select()的等待时间
我们看到都是一个结构体—— fd_set
我们只需要看看这个抽象的结构体:
typedefstruct fd_set {
u_int fd_count;
socket fd_array[FD_SETSIZE];
} fd_set;
这个结构就是一个整数数组,是一个“位图”,使用位图中的位来表示要监视的文件描述符。
提供了一组操作fd_set的接口:
void FD_CLR(int fd, fd_set *set); // 用来清除描述词组set中相关fd 的位
int FD_ISSET(int fd, fd_set *set); // 用来测试描述词组set中相关fd 的位是否为真
void FD_SET(int fd, fd_set *set); // 用来设置描述词组set中相关fd的位
void FD_ZERO(fd_set *set); // 用来清除描述词组set的全部位
ps:理解select执行过程,fd_set长度为一个字节,(0000 0000)我们取fd_set中的每一个bit可以对应一个文件描述符fd,则字节长的fd_set最大可以对应8个文件描述符。
(1)执行FD_ZERO(),则set用位表示为(0000 0000)
(2)若fd = 5 ,则set变为(0001 0000),第五位变成1
(3)若fd = 1,则set变为(0001 0001),第一位变成1
(4)执行select(6,&set,NULL,NULL,NULL)
(5)若fd = 1上都发生可读事件,则select返回,此时set变为(0000 0001)。——注意,此时没有事件发生的fd = 5被清空
select特点
1.可监控的文件描述符个数取决于sizeof(fd_set)的值,一般都是 sizeof(fd_set)* 8
2.将fd添加到select监视集的同时,还需要同时使用一个数组array保存原select监控集的数据
…(1)用于select返回后,array作为源数据和fd_set进行FD_ISSET判断
…(2)select返回后会把以前加入的但是无事件发生的fd清空,则每次开始select前都要重新的从array中把fd重新添加入监控集中(ZERO优先),扫描array的同时,记录下最大值maxfd,用于select的第一个参数
select缺点
1.每次调用select,都需要手动设置fd的集合,从接口使用角度来说很不方便
2.每次调用select,都需要把fd集合从用户态拷贝到内核态,fd很多时,拷贝开销会很大
3.每次调用select都需要在遍历内核传递进来的所有fd,fd很多时开销会很大
4.select支持的文件描述符太小
下面是 select 简单的监控标准输入实现:
#include<stdio.h>
#include<unistd.h>
#include<sys/select.h>
int main()
{
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(0,&read_fds);
while(1)
{
printf(">");
fflush(stdout);
int ret = select(1,&read_fds,NULL,NULL,NULL);
if(ret < 0)
{
perror("select");
continue;
}
if(FD_ISSET(0,&read_fds))
{
char buf[1024] = {0};
read(0,buf,sizeof(buf)-1);
printf("input:%s",buf);
}else{
printf("error! invaild fd\n");
continue;
}
FD_ZERO(&read_fds);
FD_SET(0,&read_fds);
}
return 0;
}
select编写网络服务器:
有兴趣的戳一下:
GitHub:https://github.com/Shaweia/project/tree/master/select