select函数使用浅析

2023-11-09

一、函数原型及参数说明

    int select(int maxfdp, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
    
    返回值  : 负值:select错误,正值:某些文件可读写或出错,0:等待超时,没有可读写或错误的文件。
    
    maxfdp :是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。
    
    readfds : 是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。

    writefds :是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。
               
    errorfds : 同上面两个参数的意图,用来监视文件是否发生错误异常。
    
    timeout : 是select的超时时间,这个参数至关重要。它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于永久阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来返回正值,超时返回0。                  


    fd_set结构体定义
    
    #define __FD_SETSIZE    1024
    
    typedef __kernel_fd_set     fd_set;
    
    typedef struct {
        unsigned long fds_bits[__FD_SETSIZE / (8 * sizeof(long))];
    } __kernel_fd_set;

    可以看出,fd_set结构体里面是一个无符号长整型的数组,总共有1024/(8 * 4) = 32个元素,然而这并不是说select最多只能监控32个文件的变化。过去,描述符集被作为一个整数位屏蔽码得到实现,但是这种实现对于多于32个的文件描述符将无法工作。描述符集现在通常用整数数组中的位域表示,数组元素的每一位对应一个文件描述符。例如,一个整数占32位,那么整数数组的第一个元素代表文件描述符0到31,数组的第二个元素代表文件描述符32到63,以此类推。宏FD_SET设置整数数组中对应于fd文件描述符的位为1,宏FD_CLR设置整数数组中对应于fd文件描述符的位为0,宏FD_ZERO设置整数数组中的所有位都为0。fd_set可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor),即文件句柄。
    
    struct timeval结构体定义
    
    struct timeval {
        __kernel_time_t        tv_sec;        /* seconds */
        __kernel_suseconds_t    tv_usec;    /* microseconds */
    };

    tv_sec  单位是秒
    tv_usec 单位是微秒,不是毫秒!毫秒的英文单词是millisecond。


二、操作描述字集的四个宏


    FD_ZERO(&set);      /* 将set清零 */
    FD_SET(fd, &set);   /* 将fd加入set */
    FD_CLR(fd, &set);   /* 将fd从set中清除 */
    FD_ISSET(fd, &set); /* 如果fd在set中则真 */
    
    关于FD_ISSET多说一句,select返回时会将没有准备就绪的文件描述符从set中清除,所以FD_ISSET(fd, &set)判断fd是否在set中,如果在说明他没有被清除,该描述符的状态发生了变化(可读、可写或者异常)。


三、一个简单的例子

int main(int argc, char **argv)
{

    int sock;
    int ret;
    FILE *fp;
    struct fd_set fds;
    struct timeval timeout={3,0}; //select等待3秒,3秒轮询,要非阻塞就置0。
    char buffer[256]={0}; //256字节的接收缓冲区

    while(1)
    {
        FD_ZERO(&fds);      //每次循环都要清空集合,否则不能检测描述符变化
        FD_SET(sock,&fds);  //添加描述符
        FD_SET(fp,&fds);    //添加描述符

        maxfdp=sock>fp?sock+1:fp+1; //描述符最大值加1

        ret = select(maxfdp, &fds, &fds, NULL, &timeout);
        
        if(ret < 0) {
            break;    //select错误,退出程序
        } else if(ret == 0) {
            continue; //select超时,再次轮询
        } else {
            if(FD_ISSET(sock,&fds)) //测试sock是否可读,即是否网络上有数据
            {
                recvfrom(sock,buffer,256,.....);           //接受网络数据

                if(FD_ISSET(fp, &fds)) {                   //测试文件是否可写
                    fwrite(buffer, 1, sizeof(buffer), fp); //写入文件
                }

                memset(buffer, 0, sizeof(buffer));
             }
        }
    }
    
    return 0;
}

四、什么情况下使用select()

    read()函数和write()函数本身就有阻塞的功能,那么为什么还要用select呢?个人觉得用select主要有以下两个原因
    1、select可以监控多个文件描述符的状态,等相应的描述符有变化了再去读写。这时如果不用select的话,每个描述符你都要开一个进程去等待,太浪费资源了。
    2、使用select可以进行非阻塞开发。如果不想在网络包还没来之前一直阻塞在recv(),这时候就可以设置select参数timeout的值来处理了。
    
五、select函数的驱动实现

    select为什么会阻塞?我想自己开发一个驱动程序,给上层提供select的功能,我的驱动该怎么做?欲知后事如何,请看我的另一篇博文《select原理及驱动实现》。


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

select函数使用浅析 的相关文章

随机推荐

  • 使用ggplot2包绘制分组带状图实战

    使用ggplot2包绘制分组带状图实战 在数据可视化中 分组带状图是一种常用的图表类型 可以直观地展示多个组别之间的差异和变化趋势 而R语言中的ggplot2包提供了丰富的绘图函数 其中geom jitter函数可以用来创建分组带状图 下面
  • SpringBoot + Websocket 实现实时聊天

    SpringBoot WebSocket 实现实时聊天 最近有点小时间 上个项目正好用到了websocket实现广播消息来着 现在来整理一下之前的一些代码 分享给大家 WebSocket协议是基于TCP的一种新的网络协议 它实现了浏览器与服
  • ctfshow-web10 with rollup 绕过

    0x00 前言 CTF 加解密合集 CTF Web合集 网络安全知识库 文中工具皆可关注 皓月当空w 公众号 发送关键字 工具 获取 0x01 题目 0x02 Write Up 基本方法 到处点一点 点到取消的时候 突然发现 可以下载一个文
  • C语言中signed和unsigned怎么理解?C语言unsigned long、long long 取值范围???

    C语言中signed和unsigned signed意思为有符号的 也就是第一个位代表正负 剩余的代表大小 例如 signed int 大小区间为 128 127 unsigned意思为无符号的 所有的位都为大小 没有负数 例如 unsig
  • java实现下载excel读取与生成超详细

    背景 没啥背景 就是要做这个功能 创建ExcelUtil工具类 具体导入导出方法如下 excel导入 param inputStream 导入的excel文件 return public static List
  • 酱香咖啡喝了没?用数据分析揭秘瑞幸咖啡的7500万用户增长策略

    瑞幸 X 茅台 这波联名赢麻了 不仅狂卖 542 万杯 甚至带动茅台市值飙升200亿 瑞幸这几年联名搞了不少 又是线条小狗的爱情故事 又是椰树 维密 周大福 足球的 下面老李就从数据分析角度 带大家来看一下近几年 瑞幸咖啡的用户增长策略 是
  • 数据结构笔记之链式栈的基本操作

    include stdio h include stdlib h include io h include math h include time h define OK 1 define ERROR 0 define TRUE 1 def
  • VMWare安装

    1 1 VMWare简介 VMWare是一个虚拟技术的合集 它提供了众多的相关软件 类似于Parallels VMWare是商业应用 而且价格非常的贵 所以 通常我们使用的是网上别人破解的版本 而不是使用官方的正版 VMWare官网 VMw
  • 【UnicodeDecodeError: ‘utf-8‘ codec can‘t decode byte 0xd3 in position 0: invalid continuation byte】

    UnicodeDecodeError utf 8 codec can t decode byte 0xd3 in position 0 invalid continuation byte F jupyter work dir MMLAB m
  • 有一个公网IP,在内网如何架设多台服务器?

    进行内网ip到外网ip的映射 也就是pat 这个工作现在多半由防火墙来完成 不过如果没有防火墙 用路由器也可以完成 只不过会在高峰时加重路由器的负担 思科2600路由可以独立完成各种nat pat但是因为这款产品本身属于低端产品 所以能够担
  • Java8学习记录(一)——Lambda表达式

    这两天看了 Java8实战 做一下记录 目录 一 行为参数化 1 什么是行为参数化 二 函数式接口 1 概念 三 Lambda表达式 四 方法引用 注意点 1 静态方法引用 2 实例方法引用 重点来了 任意类型的实例方法引用 现有对象的实例
  • 【深度学习】树莓派Zero w深度学习模型Python推理

    在机器学习开发过程中 当模型训练好后 接下来就要进行模型推理了 根据部署环境可分为三类场景 边缘计算 一般指手机 嵌入式设备 直接在数据生成的设备上进行推理 因为能避免将采集到的数据上传到云端 所以实时性非常好 端计算 介于云和边缘设备之间
  • Spring Boot 集成Mybatis实现多数据源

    总体来说多数据源配置有两种方式 一种是静态的 一种是动态的 静态的方式 我们以两套配置方式为例 在项目中有两套配置文件 两套mapper 两套SqlSessionFactory 各自处理各自的业务 这个两套mapper都可以进行增删改查的操
  • GPU版本安装Pytorch教程最新方法

    目录 步骤 第一步 安装 Anaconda 和 Pycharm 软件 第二步 下载安装CUDA11 3 1 首先查看自己电脑GPU版本 方式一 搜索框输入nvidia 打开nvidia控制面板 方式二 win R打开cmd 输入nvidia
  • ubuntu的FTP服务器搭建

    1 安装服务器 sudo apt get install vsftpd 如果有错误 先执行 apt update 2 创建用户 Linux下的用户 创建专用目录 mkdir home ftp 命令添加ftp用户 sudo useradd d
  • 基于tiny6410的led驱动程序

    今天弄了一个晚上了 终于弄懂了第一个简单的linux驱动感觉很多人编写的第一个驱动也是拿led开刀的吧 本文的led驱动是基于tiny6410的四个led驱动 用字符设备的驱动模块 而不是混杂设备驱动模块来编写 本人在使用混杂设备方法编写的
  • 5735. 雪糕的最大数量

    夏日炎炎 小男孩 Tony 想买一些雪糕消消暑 商店中新到 n 支雪糕 用长度为 n 的数组 costs 表示雪糕的定价 其中 costs i 表示第 i 支雪糕的现金价格 Tony 一共有 coins 现金可以用于消费 他想要买尽可能多的
  • pyspark指定schema

    通过StructType对象指定DataFrame的Schema 没有嵌套结构的json jsonString id 01001 city AGAWAM pop 15338 state MA id 01002 city CUSHMAN po
  • r语言 col_co,cob,col,con,cor,cog前缀其实都是com变化而已

    c开头前缀有co cob col com con cor cog等 哪它们有什么关联呢 下面我们来分析 come 英 k m 美 k m v 来 来到 到达 com前缀可以看成come变化而成 全部来到就是聚集到一起 表聚集 加强 强调的
  • select函数使用浅析

    一 函数原型及参数说明 int select int maxfdp fd set readfds fd set writefds fd set errorfds struct timeval timeout 返回值 负值 select错误