select函数使用

select 函数是在网络编程和文件I/O中广泛使用的一个系统调用,特别是在UNIX、Linux和类UNIX系统中。它允许程序监视多个文件描述符(file descriptors),以等待一个或多个文件描述符上的某些类型的事件发生(比如数据可读、写就绪、异常条件等)。当select函数返回时,它会告诉程序哪些文件描述符已经准备好进行I/O操作。

函数原型

在POSIX兼容的系统中,select函数的原型通常定义在<sys/select.h><sys/time.h>头文件中(具体取决于系统),其原型大致如下:

#include <sys/select.h>  
#include <sys/time.h>  
#include <unistd.h>  
  
int select(int nfds, fd_set *readfds, fd_set *writefds,  
           fd_set *exceptfds, struct timeval *timeout);

参数说明

  • nfds:这是一个整数值,指定了文件描述符集合中最大文件描述符的值加1。这个参数用于确定select需要检查的文件描述符的范围。
  • readfds:指向fd_set的指针,这个集合包含了所有需要被检查读条件的文件描述符。如果不需要检查读条件,可以设置为NULL。
  • writefds:指向fd_set的指针,这个集合包含了所有需要被检查写条件的文件描述符。如果不需要检查写条件,可以设置为NULL。
  • exceptfds:指向fd_set的指针,这个集合包含了所有需要被检查异常条件的文件描述符。如果不需要检查异常条件,可以设置为NULL。
  • timeout:指向timeval结构的指针,指定了select等待的最长时间。如果设置为NULL,则select将无限期等待,直到至少有一个文件描述符准备好。

返回值

  • 成功时,select返回准备好的文件描述符的总数(可能包括0,如果超时但没有任何文件描述符准备好)。
  • 出错时,返回-1,并设置相应的errno以指示错误。

使用场景

select函数常用于实现非阻塞的I/O操作,例如,在服务器程序中监听多个客户端连接。通过select,服务器可以等待多个套接字(socket)上的数据,而无需为每个套接字创建单独的线程或进程。

然而,select也有一些限制,比如它能监视的文件描述符数量有限(通常是1024),并且在文件描述符集合较大时,select的效率可能会下降,因为它会遍历整个集合来查找哪些文件描述符已经准备好。为了解决这些问题,现代系统通常提供了更高效的替代方案,如pollepoll(Linux特有)。

FD_ZERO

FD_ZERO是Linux及类Unix系统编程中用于操作文件描述符集合(fd_set)的一个宏。在对文件描述符集合进行设置前,必须对其进行初始化,以确保集合中不包含任何文件描述符。这是因为系统分配内存空间后,通常并不自动进行清空处理,如果不进行初始化,集合中的内容是未知的,可能会导致不可预测的行为。

FD_ZERO(fd_set *fdset);

这个宏接受一个指向fd_set类型的指针作为参数,并将该集合中的所有位都清零,即清空集合中的所有文件描述符。

重要性

  • 初始化:在使用fd_set集合之前,必须通过FD_ZERO进行初始化,以确保集合是空的,从而避免未定义行为。
  • 准备阶段:在调用selectpoll等函数之前,通常需要先使用FD_ZERO清空fd_set集合,然后通过FD_SET等宏将需要监视的文件描述符添加到集合中。

与其他宏的关系

  • FD_SET:用于在文件描述符集合中增加一个新的文件描述符。
  • FD_CLR:用于在文件描述符集合中删除一个文件描述符。
  • FD_ISSET:用于测试指定的文件描述符是否在该集合中。
#include <sys/select.h>  
#include <stdio.h>  
#include <unistd.h>  
  
int main() {  
    fd_set readfds;  
  
    // 初始化fd_set集合  
    FD_ZERO(&readfds);  
  
    // 假设我们有一个文件描述符fd  
    int fd = 0; // 例如,标准输入的文件描述符  
  
    // 将文件描述符fd添加到readfds集合中  
    FD_SET(fd, &readfds);  
  
    // ...(后续可以使用select等函数来监视readfds集合中的文件描述符)  
  
    return 0;  
}

FD_SET

FD_SET是Linux及类Unix系统编程中用于操作文件描述符集合(fd_set)的一个宏。它允许程序将一个特定的文件描述符加入到文件描述符集合中,以便后续的I/O多路复用函数(如selectpoll等)可以监视这个文件描述符的状态变化。

基本用法

 

c复制代码

FD_SET(int fd, fd_set *fdset);
  • fd:需要加入到集合中的文件描述符。
  • fdset:指向fd_set类型的指针,表示要操作的文件描述符集合。

功能

FD_SET宏将指定的文件描述符fd加入到fdset指向的文件描述符集合中。这样,在后续的select调用中,就可以监视这个集合中的文件描述符,以查看它们是否准备好进行I/O操作(如读、写或异常)。

注意事项

  1. 初始化:在使用FD_SET之前,必须首先使用FD_ZERO宏将fd_set集合初始化,以确保集合是空的,不包含任何文件描述符。
  2. 范围fd的值必须在文件描述符的有效范围内,并且通常应该小于FD_SETSIZE(一个定义在<sys/select.h>中的常量,表示fd_set集合能包含的文件描述符的最大数量)。不过,现代系统通常允许更大的值,但最好查阅系统文档以获取准确信息。
  3. 效率:由于fd_set集合通常是通过位图来实现的,因此添加或删除文件描述符的操作效率很高。然而,当集合中的文件描述符数量非常多时,遍历整个集合来检查哪些文件描述符已经准备好的效率可能会降低。
  4. 兼容性FD_SET宏是POSIX标准的一部分,因此在大多数类Unix系统中都是可用的。然而,不同系统之间的实现细节可能有所不同,因此在跨平台编程时需要特别注意。

示例

#include <sys/select.h>  
#include <stdio.h>  
#include <unistd.h>  
  
int main() {  
    fd_set readfds;  
  
    // 初始化fd_set集合  
    FD_ZERO(&readfds);  
  
    // 假设我们有两个文件描述符fd1和fd2  
    int fd1 = 0; // 例如,标准输入的文件描述符  
    int fd2 = 1; // 例如,标准输出的文件描述符(通常不需要监视,仅作示例)  
  
    // 将文件描述符fd1添加到readfds集合中  
    FD_SET(fd1, &readfds);  
  
    // ...(后续可以使用select等函数来监视readfds集合中的文件描述符)  
  
    return 0;  
}

在这个示例中,我们首先使用FD_ZERO初始化了readfds集合,然后使用FD_SET将文件描述符fd1(即标准输入)添加到了集合中。接下来,我们可以使用select函数来监视readfds集合中的文件描述符,以查看它们是否准备好进行读操作。

FD_ISSET

FD_ISSET是一个在Linux及类Unix系统编程中广泛使用的C语言宏,用于检查指定的文件描述符(file descriptor)是否在给定的文件描述符集合(fd_set)中被设置。这个宏通常与selectpoll等系统调用一起使用,以在I/O多路复用场景中检测文件描述符的状态。

基本用法

FD_ISSET(int fd, fd_set *fdset);
  • fd:需要检查的文件描述符。
  • fdset:指向fd_set类型的指针,表示要检查的文件描述符集合。

功能

FD_ISSET宏检查fdset集合中是否包含了指定的文件描述符fd。如果fd在集合中被设置(即,该文件描述符已经准备好进行I/O操作),则宏返回非零值(通常是1,表示真);如果fd不在集合中,则返回零(表示假)。

使用场景

select调用之后,select会更新传入的文件描述符集合,将那些准备好进行I/O操作的文件描述符的对应位设置为1,而将其他文件描述符的对应位清零。此时,可以使用FD_ISSET来检查哪些文件描述符已经准备好,以便进行相应的处理。

注意事项

  1. 初始化:在使用FD_ISSET之前,必须确保fd_set集合已经被正确初始化(通常使用FD_ZERO宏),并且已经通过FD_SET宏将需要监视的文件描述符添加到了集合中。
  2. 范围fd的值必须在文件描述符的有效范围内,并且小于FD_SETSIZE(一个定义在<sys/select.h>中的常量,表示fd_set集合能包含的文件描述符的最大数量)。然而,现代系统通常允许更大的值,但最好查阅系统文档以获取准确信息。
  3. 效率:由于fd_set集合通常是通过位图来实现的,因此FD_ISSET操作的效率很高。然而,当集合中的文件描述符数量非常多时,遍历整个集合来检查每个文件描述符的效率可能会降低。

示例

以下是一个使用FD_ISSET的示例代码片段,该代码片段假设已经通过select调用了readfds集合,并且需要检查文件描述符fd是否在该集合中:

#include <sys/select.h>  
#include <stdio.h>  
#include <unistd.h>  
  
// ...(假设readfds已经通过FD_ZERO和FD_SET初始化,并且已经调用了select)  
  
if (FD_ISSET(fd, &readfds)) {  
    // fd在readfds集合中,表示该文件描述符已经准备好进行读操作  
    // 执行相应的读操作...  
} else {  
    // fd不在readfds集合中,表示该文件描述符尚未准备好进行读操作  
    // 执行其他操作...  
}

在这个示例中,FD_ISSET用于检查文件描述符fd是否在readfds集合中被设置,以判断该文件描述符是否准备好进行读操作。如果FD_ISSET返回非零值,则执行相应的读操作;否则,执行其他操作。

FD_CLR

FD_CLR是一个在Linux及类Unix系统编程中用于操作文件描述符集合(fd_set)的宏。它的主要作用是从文件描述符集合中删除一个指定的文件描述符。这个宏通常与selectpoll等系统调用一起使用,在I/O多路复用场景中管理文件描述符集合。

基本用法

FD_CLR(int fd, fd_set *fdset);
  • fd:需要从集合中删除的文件描述符。
  • fdset:指向fd_set类型的指针,表示要操作的文件描述符集合。

功能

FD_CLR宏将指定的文件描述符fdfdset指向的文件描述符集合中删除。这样,在后续的select调用中,就不会再监视这个被删除的文件描述符的状态变化。

使用场景

在I/O多路复用场景中,select调用会阻塞等待直到一个或多个文件描述符准备好进行I/O操作,或者超时发生。然而,在某些情况下,程序可能需要在select调用之前或之后从文件描述符集合中动态地添加或删除文件描述符。这时,就可以使用FD_SET宏来添加文件描述符,使用FD_CLR宏来删除文件描述符。

注意事项

  1. 初始化:在使用FD_CLR之前,通常需要先使用FD_ZERO宏将fd_set集合初始化,以确保集合是空的,不包含任何文件描述符。然而,在调用FD_CLR时,集合已经被初始化并可能包含了一些文件描述符,因此这一步并不是FD_CLR的直接要求,但它是管理文件描述符集合时的常见做法。
  2. 范围fd的值必须在文件描述符的有效范围内,并且小于FD_SETSIZE(一个定义在<sys/select.h>中的常量,表示fd_set集合能包含的文件描述符的最大数量)。然而,现代系统通常允许更大的值,但最好查阅系统文档以获取准确信息。
  3. 效率:由于fd_set集合通常是通过位图来实现的,因此FD_CLR操作的效率很高。它只需要将集合中对应文件描述符的位清零即可。

示例

以下是一个使用FD_CLR的示例代码片段,该代码片段假设已经有一个包含多个文件描述符的fd_set集合,并且需要从该集合中删除文件描述符fd

#include <sys/select.h>  
#include <stdio.h>  
  
// ...(假设fdset已经通过FD_ZERO和FD_SET等宏初始化,并包含了一些文件描述符)  
  
// 从fdset集合中删除文件描述符fd  
FD_CLR(fd, &fdset);  
  
// ...(现在fdset集合中不再包含文件描述符fd)

在这个示例中,FD_CLR用于从fdset集合中删除文件描述符fd。这样,在后续的select调用中,就不会再监视这个文件描述符的状态变化了。

应用案列

static void* _light_event_Loop(void *user) {
	int kbd = open(DEV_INPUT1_NODE, O_RDONLY);
	if (kbd < 0) {
		printf("open %s fail!\n", DEV_INPUT1_NODE);
		return NULL;
	}

	fd_set fdset;
	FD_ZERO(&fdset);

	while (1) {
		FD_SET(kbd, &fdset);
		if (select(FD_SETSIZE, &fdset, NULL, NULL, NULL) > 0) {
			if (FD_ISSET(kbd, &fdset)) {
				FD_CLR(kbd, &fdset);
				struct input_event event;
				if (read(kbd, &event, sizeof(event)) == sizeof(event) &&
					event.type == EV_KEY &&
					s_backlight_callback) {

                    if(event.value == 1)
                    {
                        s_backlight_callback(event.code, event.value);
                    }				
				}
			}

		}
	}

	close(kbd);
	printf("_light_event_Loop...\n");
	return NULL;
}

  • 27
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值