Linux杂谈(2):Select简介
2021/12/6 7:18:57
本文主要是介绍Linux杂谈(2):Select简介,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
文章目录
- 1 select简介
- 2 函数详解
- 2.1 函数原型
- 2.2 参数
- 2.2.1 nfds
- 2.2.2 readfds
- 2.2.3 writefds
- 2.2.4 exceptfds
- 2.2.5 timeout
- 2.3 文件描述集合操作
- 2.3.1 FD_ZERO()
- 2.3.2 FD_SET()
- 2.3.3 FD_CLR()
- 2.3.4 FD_ISSET()
- 2.4 注意
- 2.5 示例
- 3 文件描述符集合细探
- 4 select的缺点
1 select简介
在linux下,我们可以使用select来进行I/O复用,可以监视多个文件描述符,判断是否有符合条件的事件发生。
使用select函数时,我们可以查看是否有可读、可写或者错误的事件发生。
2 函数详解
2.1 函数原型
#include <sys/select.h> int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
2.2 参数
2.2.1 nfds
整形变量,它的值是所有文件描述符中最大的值加1。它的最大值为FD_SETSIZE,FD_SETSIZE一般定义为1024,不同的系统可能不一样。
2.2.2 readfds
这个文件描述集合用于监视此文件集中的文件是否有数据可读,比如我们监视串口是否有数据可读,可以将串口的文件描述符放到此集合中去。当监视到有可读的事件后,readfds文件描述集合将清除不可读的文件描述符,只保留可读的文件描述符。不需要时,将其设置为NULL。
2.2.3 writefds
这个文件描述集合用于监视是否有可写的事件发生,当监视到后,会清除不可写的文件描述符,只保留下可写的文件描述符。不需要时,将其设置为NULL。
2.2.4 exceptfds
这个文件描述集合用于监视是否发送错误。不需要时,将其设置为NULL。
2.2.5 timeout
用于所监视的文件集合中的事件没有发生时候的等待时间。为NULL表示阻塞等待一直到事件发生,为0表示立马返回,不为0表示等待的时间。
2.3 文件描述集合操作
所谓的文件描述集合fd_set,文件描述符的集合,在linux下,万物都是文件,因此设备、管道、socket等等都是文件,都有自己的文件描述符。
在上面提到的文件描述集合fd_set,主要有以下4个宏可以进行操作。同时文件描述集合中的文件数量最大为FD_SETSIZE,超过此值,会有一些未知情况发生。
2.3.1 FD_ZERO()
清空文件描述集合,将文件描述集合全部置0。
2.3.2 FD_SET()
将某个文件描述符加入文件描述集合。
2.3.3 FD_CLR()
将某个文件描述符从文件描述集合中删除。
2.3.4 FD_ISSET()
用于确定某个文件描述符是否是文件描述集合的成员。
2.4 注意
当函数正常返回,即有事件发生时候,会清除掉没有发生的事件,所以在处理完事件之后,如果需要继续监听全部事件,就需要重新将全部文件描述符重新加入文件描述集合中(即先情况描述集合,然后将全部文件描述符重新加入)。
因为select函数会更新超时参数,比如设置超时时间为5s,然后超时退出后,select会将超时时间更新为0,所以如果需要再次监听,那么就需要重新设置超时时间。
2.5 示例
如下,一个小demo,循环监听两个文件描述符,超时时间为5s。
int test() { int s32FD1 = 17; //文件描述符1 此处demo写为固定值 int s32FD2 = 18; //文件描述符2 此处demo写为固定值 fd_set fds; //文件描述集合 int s32MaxFd = s32FD2 + 1; //所有文件描述符中的最大值加1 int s32Ret = 0; struct timeval tv; while(1) { /* 重新加入文件描述集合 */ FD_ZERO(&fds); //清空文件描述集合 FD_SET(s32FD1, &fds); //加入描述集合 FD_SET(s32FD2, &fds); //加入描述集合 /* 重新设置超时时间 */ tv.tv_sec = 5; tv.tv_usec = 0; s32Ret = select(s32MaxFd, &fds, NULL, NULL, &tv); if (s32Ret > 0) { if (FD_ISSET(s32FD1, &fds) //FD1 文件有可读事件 { //do something... } else if (FD_ISSET(s32FD2, &fds) //FD2 文件有可读事件 { //do something... } } } return 0; }
3 文件描述符集合细探
在上面介绍了文件描述集合,到底文件描述集合是个什么样的东东,在下面来追踪一下。
查看fd_set结构体原型,如下:
/* The fd_set member is required to be an array of longs. */ typedef long int __fd_mask; /* fd_set for select and pselect. */ typedef struct { /* XPG4.2 requires this member name. Otherwise avoid the name from the global namespace. */ #ifdef __USE_XOPEN __fd_mask fds_bits[__FD_SETSIZE / __NFDBITS]; # define __FDS_BITS(set) ((set)->fds_bits) #else __fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS]; # define __FDS_BITS(set) ((set)->__fds_bits) #endif } fd_set;
从上面可以看出,fd_set结构体里面的成员是long int数组,在我的linux系统中 ,fd_set的长度为128位,FD_SETSIZE的值为1024。128*8=1024,因此每一位对应8个文件描述符。而该数组第一位元素对应于描述符0~31, 第二位元素对应于描述符32-63,一次类推。
在上面的示例中,我们将文件描述符17 18加入集合中,17 18处于第一位元素,因此我们可以打印第一位元素的值来进一步确定。
printf("fdset:%ld, sizeof:%d, FD_SETSIZE:%d\n", (long int)fds.fds_bits[0], sizeof(fd_set), FD_SETSIZE); #fdset:393216, sizeof:128, FD_SETSIZE:1024
fdset的值为393216,转换为2进制为110 0000 0000 0000 0000,可以看到从0开始计数,第17 18位被置为1.
当select监听17 18时候,17有可读事件,打印fd_set的值:
printf("fdset:%ld\n", (long int)fds.__fds_bits[0]); #fdset:131072
fdset的值为131072,转换为2进制为10 0000 0000 0000 0000,可以看到从0开始计数,第17 位被置为1, 而第18位被置为0,因此需要重新select时候,需要重新将全部的文件描述符加入fd_set。
4 select的缺点
select的文件描述集包含了所有需要被监听的文件描述符,如果数量很大,那么每一次调用,都需要从头开始遍历,增加了CPU的消耗。
当select返回时,我们需要遍历知道是那个文件描述符被触发。
当需要重新监听时,我们需要重新将所有的文件描述符加入文件描述符集中。
这篇关于Linux杂谈(2):Select简介的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-12如何创建可引导的 ESXi USB 安装介质 (macOS, Linux, Windows)
- 2024-11-08linux的 vi编辑器中搜索关键字有哪些常用的命令和技巧?-icode9专业技术文章分享
- 2024-11-08在 Linux 的 vi 或 vim 编辑器中什么命令可以直接跳到文件的结尾?-icode9专业技术文章分享
- 2024-10-22原生鸿蒙操作系统HarmonyOS NEXT(HarmonyOS 5)正式发布
- 2024-10-18操作系统入门教程:新手必看的基本操作指南
- 2024-10-18初学者必看:操作系统入门全攻略
- 2024-10-17操作系统入门教程:轻松掌握操作系统基础知识
- 2024-09-11Linux部署Scrapy学习:入门级指南
- 2024-09-11Linux部署Scrapy:入门级指南
- 2024-08-21【Linux】分区向左扩容的方法