select 的使用

本文详细介绍了Linux系统编程中select函数的使用,包括头文件、函数原型及参数说明。select函数用于在指定时间内轮询文件描述符集合,检测是否有可读、可写或异常情况。通过设置timeval结构体,可以实现不同阻塞时间的效果。在实际应用中,select常被用于网络编程和多路复用I/O。文章还提到了select与驱动层的交互,以及在连续调用select时的注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

select是在指定时间内(最后一个参数指定的时间)轮询指定fd集合的接口

1. 需要包含的头文件

#include <sys/time.h>  //select是在指定时间内轮询,所以有时间相关的参数
#include <sys/types.h>
#include <unistd.h>

2. 函数原型以及参数说明

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

//参数1: int nfds: 最大的文件描述符 + 1 
         (1)由此可见,select轮询的是int的文件描述符,而非FILE*的
//参数2: 待检测  是否有数据可读取的  fd集合
          fd_set实际上是long类型数组,每个数组元素都与一打开的文件句柄建立联系,相关的API如下
          (1) FD_ZERO(fd_set*) //将fd_set清空不含任何描述符
         (2)FD_SET(fd fd_set*)  //将fd(int)加入 fd_set集合 
         (3)FD_CLR(fd, fd_set*) //将fd(int)从fd_set中删除
         (4)FD_ISSET(fd,fd_set*)//select结束后调用,用于判断fd是否在set集合中,
                    fd在集合中则返回真,不在集合中返回假 
//参数3:待检测  是否有数据可写的  fd集合 【目前我所见过的大部分填NULL】
//参数4: 待检测  是否有数据可执行的 fd集合【目前我所见过的大部分填NULL】 
//参数5:timeval: 检测的时间 

3. 函数返回值

>0:  就绪描述符的数量
=-1: 出错 
0:  超时(无就绪的描述符)

使用注意:

1. 第二到四个参数分别是待检测可读fd集合,  待检测可写fd集合,待检测可执行fd集合,我们可以只填写部分集合,如果三个集合都不填写的话,就得到了一个比sleep更精准的定时器(timeval中精确到微秒,sleep精确到秒)

2. 第二到四个参数是类似的,只是监视文件的动作不同,以第二个readset为例,会在time参数指定的时间内监视该fd_set集合中是否有可读的文件,如果有,则返回可读的描述符的个数>0,如果在time指定的时间内没有可读的描述符,则返回0. 只有在select发生错误的时候才返回-1 

3. timeval的三种使用方法:

    1)timeval = NULL: 表示时间无限长,一直到描述符集合中某个描述符变化为止。如果描述符集合中没有发生变化的描述符,则一直阻塞。

    2) timeval = 0:  只轮询描述符集合一次,如果没有发生变化的,返回0; 如果有,则返回变化的描述符的数量。 

    3) timeval > 0: 在该时间内阻塞轮询描述符集合,如果没有发生变化的,则返回0,如果有,则返回发生变化的描述符的数量,

void main()
{
	int sock; int fd;
	fd_set fds;
	struct timeval timeout={0,3}; //select等待3微秒,3微秒轮询,要非阻塞就置0
	
	while(1)
	{
		FD_ZERO(&fds);     //每次循环都要清空集合,否则不能检测描述符变化
		FD_SET(sock,&fds); //添加描述符 sock 
		FD_SET(fd,&fds);   //添加描述符fd
		timeout.tv_sec=0;
		timeout.tv_usec=3; //select函数会不断修改timeout的值,所以每次循环都应该重新赋值
		maxfdp=sock>fd?sock+1:fd+1;                    //描述符最大值加1
		switch(select(maxfdp,&fds,&fds,&fds,&timeout)) 
		{
			case -1: 
				exit (-1);
				break;         //select错误,直接exit退出
            case 0:
				break;         //0代表超时,没有发生变化的fd
			default:
			if(FD_ISSET(sock,&fds))    //测试sock是否可读
			{
				if(FD_ISSET(fd,&fds))           //测试文件是否可写
			} 
		}     
	}
}

使用注意:

1. select 多数情况下是连续调用的,要么是select被包在一个循环里面,要么是软件逻辑保证不停的select 。

2. select内部会对后面的四个参数进行修改,所以每一次select之前后面的四个参数需要重新设置。

3. 第二三四个参数(描述符集合)可以是重复的。

4. select轮询后,会把没有发生变化的描述符从集合中删除,集合中剩余的描述符即发生变化的描述符。所以只需要select后用FD_ISSET检测某个描述符是否还在集合中,即可判断该描述符是否发生了变化。

扩展:select与 驱动层的关系:

应用层的selcet调用后,会调用到驱动层的  file_operations->poll  接口,在这个函数里应该调用poll_wait(),将current加到某个等待队列(这里调用poll_wait()),并检查是否有效,如果无效就调用schedule_timeout();去睡眠。事件发生后,schedule_timeout()回来,调用fop->poll(),检查到可以运行,就调用poll_freewait(&table);从而完成select系统调用。重要的是fop->poll()里面要检查是否就绪,如果是,要返回相应标志。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值