Linux C select 的学习

一. select 系统调用

1. 函数说明

#include <sys/select.h>
#include <sys/time.h>int select(int nfds, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeval *timeout);
  • nfds: 是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加 1。在linux系统中,select 的默认最大值为1024 。设置这个值的目的是为了不用每次都去轮询这1024fd,假设我们只需要几个套接字,我们就可以用最大的那个套接字的值加上1作为这个参数的值,当我们在等待是否有套接字准备就绪时,只需要监测nfds+1个套接字就可以了,这样可以减少轮询时间以及系统的开销。

  • readfds: 用来检测输入是否准备就绪的文件描述符集合(从内核读,用的最多)

  • writefds: 用来检测输出是否准备就绪的文件描述符集合(往内核写,比较少用)

  • exceptset; 很少用到,我没用过。用来检测异常情况是否发生的文件描述符集合。

  • timeout: 堵塞的超时时间,如果为 NULL 则堵塞。

系统调用 select() 会一直堵塞,直到一个或者多个文件描述符集合称为就绪态,select 就会往下执行。

套接字可写的条件

这个概念很常见,跟套接字可读一样。

  1. 使用 select 监听,如果是可读套接字的 fd_set,那么套接字对应的内核中,一旦收到数据,那么就会使 select 返回,fd_set准备就绪。
  2. 同样道理,使用 select 监听,如果是可写的套接字的 fd_set,那么套接字对应的内核中,一旦数据的缓冲区有空余的位置写数据,那么就会使 select 返回,fd_set准备就绪。这句话有些难理解。

具体的说:

一、 满足下列四个条件中的任何一个时,一个套接字准备好读。

  1. 该套接字接收缓冲区中的数据字节数大于等于套接字接收缓存区低水位。 对于TCP和UDP套接字而言,缓冲区低水位的值默认为1。那就意味着,默认情况下,只要缓冲区中有数据,那就是可读的。我们可以通过使用SO_RCVLOWAT套接字选项(参见setsockopt函数)来设置该套接字的低水位大小。此种描述符就绪(可读)的情况下,当我们使用read/recv等对该套接字执行读操作的时候,套接字不会阻塞,而是成功返回一个大于0的值(即可读数据的大小)。
  2. 该连接的读半部关闭(也就是接收了FIN的TCP连接)。对这样的套接字的读操作,将不会阻塞,而是返回0(也就是EOF)。
  3. 该套接字是一个listen的监听套接字,并且目前已经完成的连接数不为0。对这样的套接字进行accept操作通常不会阻塞。
  4. 有一个错误套接字待处理。对这样的套接字的读操作将不阻塞并返回-1(也就是返回了一个错误),同时把errno设置成确切的错误条件。这些待处理错误(pending error)也可以通过指定SO_ERROR套接字选项调用getsockopt获取并清除。

二、满足下列四个条件中的任何一个时,一个套接字准备好写。

  1. 该套接字发送缓冲区中的可用空间字节数大于等于套接字发送缓存区低水位标记时,并且该套接字已经成功连接(UDP套接字不需要连接)。对于TCP和UDP而言,这个低水位的值默认为2048,而套接字默认的发送缓冲区大小是8k,这就意味着一般一个套接字连接成功后,就是处于可写状态的。我们可以通过SO_SNDLOWAT套接字选项(参见setsockopt函数)来设置这个低水位。此种情况下,我们设置该套接字为非阻塞,对该套接字进行写操作(如write,send等),将不阻塞,并返回一个正值(例如由传输层接受的字节数,即发送的数据大小)。
  2. 该连接的写半部关闭(主动发送FIN包的TCP连接)。对这样的套接字的写操作将会产生SIGPIPE信号。所以我们的网络程序基本都要自定义处理SIGPIPE信号。因为SIGPIPE信号的默认处理方式是程序退出。
  3. 使用非阻塞的connect套接字已建立连接,或者connect已经以失败告终。即connect有结果了。
  4. 有一个错误的套接字待处理。对这样的套接字的写操作将不阻塞并返回-1(也就是返回了一个错误),同时把errno设置成确切的错误条件。这些待处理的错误也可以通过指定SO_ERROR套接字选项调用getsockopt获取并清除。

在这里插入图片描述

  • 后面有一个例子,是关于一个readfdsfd_set 是一个标准输入,一个 writefdsfd_set 是标准输出。使用 select 堵塞等待。
  • 当键盘有输入时 readfds 中是标准输出, select 会立马返回就绪
  • 当监听writefds时,因为里面放的是标准输入,因为标准输入在正常情况下就是可写的,所以 select 会立马返回并就绪,不会堵塞。

2. 数据类型 fd_set

这个网上资料很多,细节很多。我有空回来再补

操作 fd_set 的四个宏:

#include <sys/select.h>   int FD_ZERO(int fd, fd_set *fdset);    // 初始化:相当于将 fd_set 清零
int FD_CLR(int fd, fd_set *fdset);     // 将文件描述符 fd 从 fd_set 中移除
int FD_SET(int fd, fd_set *fd_set);    // 将文件描述符 fd 加入 fd_set 中
int FD_ISSET(int fd, fd_set *fdset);   // 如果文件描述符 fd 是 fd_set 所指向的成员,返回 true

3. timeout 参数

  • 类型: struct timeval
#include <sys/time.h>struct timeval{long tv_sec;   /*秒 */long tv_usec;  /*微秒 */   }

上面说,timeoutNULL 时,select 会堵塞。如果 timeout 的两个值都为 0,不会堵塞。select直接立即返回

4. select 返回值

这个看多了用多了代码就会了,大部分使用select 的代码都会出现判断它的所有返回值的:

while(1)
{...ret = select(nfds, readfds, ...);if (ret > 0) {}else if (ret == 0) {}else if (ret < 0) {}...
}

返回值:

  • 大于 0:返回一个或多个已经就绪的文件描述符
  • 小于 0: 有错误发生:一般是 EBADFEINTR 。具体上网搜
  • 等于 0: 超时,每个文件描述符集合会清空。

5. example

"t_select.h" 在文末获取。

#include <sys/time.h>
#include <sys/select.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include "t_select.h"/*** example:*      ./t_select 10 0r 1w* * comment:*      10: timeout(second)*      0r: 以读形式读取标准输入*      1w: 以写形式去写标准输出
*/int main(int argc, char *argv[])
{fd_set readfds, writefds;int ready, nfds, fd, numRead, j;struct timeval timeout;struct timeval *pto;char buf[10]; /* Large enough to hold "rw\0" */if (argc < 2 || strcmp(argv[1], "--help") == 0)usageError(argv[0]);/* Timeout for select() is specified in argv[1] */// "-": 代表堵塞; 否则就是超时时间if (strcmp(argv[1], "-") == 0){pto = NULL; /* Infinite timeout */}else{pto = &timeout;timeout.tv_sec = getLong(argv[1], 0, "timeout");timeout.tv_usec = 0; /* No microseconds */}/* Process remaining arguments to build file descriptor sets */nfds = 0;FD_ZERO(&readfds);FD_ZERO(&writefds);for (j = 2; j < argc; j++){numRead = sscanf(argv[j], "%d%2[rw]", &fd, buf);if (numRead != 2)usageError(argv[0]);if (fd >= FD_SETSIZE)printf("file descriptor exceeds limit (%d)\n", FD_SETSIZE);if (fd >= nfds)nfds = fd + 1; /* Record maximum fd + 1 */if (strchr(buf, 'r') != NULL)FD_SET(fd, &readfds);if (strchr(buf, 'w') != NULL)FD_SET(fd, &writefds);}/* We've built all of the arguments; now call select() */// 第一个参数:套接字返回ready = select(nfds, &readfds, &writefds, NULL, pto);/* Ignore exceptional events */if (ready == -1)usageError("select");/* Display results of select() */printf("ready = %d\n", ready);for (fd = 0; fd < nfds; fd++)printf("%d: %s%s\n", fd, FD_ISSET(fd, &readfds) ? "r" : "",FD_ISSET(fd, &writefds) ? "w" : "");if (pto != NULL)printf("timeout after select(): %ld.%03ld\n",(long)timeout.tv_sec, (long)timeout.tv_usec / 1000);exit(EXIT_SUCCESS);
}

文末

/**************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>#define GET_NUM_H#define GN_NONNEG 01 /* Value must be >= 0 */
#define GN_GT_0 02   /* Value must be > 0 *//* By default, integers are decimal */
#define GN_ANY_BASE 0100 /* Can use any base - like strtol(3) */
#define GN_BASE_8 0200   /* Value is expressed in octal */
#define GN_BASE_16 0400  /* Value is expressed in hexadecimal *//* Print a diagnostic message that contains a function name ('fname'),the value of a command-line argument ('arg'), the name of thatcommand-line argument ('name'), and a diagnostic error message ('msg'). */
static void
gnFail(const char *fname, const char *msg, const char *arg, const char *name)
{fprintf(stderr, "%s error", fname);if (name != NULL)fprintf(stderr, " (in %s)", name);fprintf(stderr, ": %s\n", msg);if (arg != NULL && *arg != '\0')fprintf(stderr, "        offending text: %s\n", arg);exit(EXIT_FAILURE);
}
/* Convert a numeric command-line argument ('arg') into a long integer,returned as the function result. 'flags' is a bit mask of flags controllinghow the conversion is done and what diagnostic checks are performed on thenumeric result; see get_num.h for details.'fname' is the name of our caller, and 'name' is the name associated withthe command-line argument 'arg'. 'fname' and 'name' are used to print adiagnostic message in case an error is detected when processing 'arg'. */static long
getNum(const char *fname, const char *arg, int flags, const char *name)
{long res;char *endptr;int base;if (arg == NULL || *arg == '\0')gnFail(fname, "null or empty string", arg, name);base = (flags & GN_ANY_BASE) ? 0 : (flags & GN_BASE_8) ? 8: (flags & GN_BASE_16)  ? 16: 10;errno = 0;res = strtol(arg, &endptr, base);if (errno != 0)gnFail(fname, "strtol() failed", arg, name);if (*endptr != '\0')gnFail(fname, "nonnumeric characters", arg, name);if ((flags & GN_NONNEG) && res < 0)gnFail(fname, "negative value not allowed", arg, name);if ((flags & GN_GT_0) && res <= 0)gnFail(fname, "value must be > 0", arg, name);return res;
}
/* Convert a numeric command-line argument string to a long integer. See thecomments for getNum() for a description of the arguments to this function. */long getLong(const char *arg, int flags, const char *name)
{return getNum("getLong", arg, flags, name);
}
/* Convert a numeric command-line argument string to an integer. See thecomments for getNum() for a description of the arguments to this function. */int getInt(const char *arg, int flags, const char *name)
{long res;res = getNum("getInt", arg, flags, name);if (res > INT_MAX || res < INT_MIN)gnFail("getInt", "integer out of range", arg, name);return res;
}
/***********************************************************************************************************/static void usageError(const char *progName)
{fprintf(stderr, "Usage: %s {timeout|-} fd-num[rw]...\n", progName);fprintf(stderr, "      - means infinite timeout; \n");fprintf(stderr, "      r = monitor for read\n");fprintf(stderr, "      w = monitor for write\n\n");fprintf(stderr, "      e.g.: %s - 0rw 1w\n", progName);exit(EXIT_FAILURE);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/102535.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

日常学习记录随笔-zabix实战

使用zabix结合 实现一套监控报警装置 不管是web开发还是大数据开发 我们的离线项目还是实时项目也好&#xff0c;都需要把我们的应用提交到我们服务器或者容器中去执行 整个应用过程中怎么保证线上整体环境的稳定运行 监控很重要 现在比较主流的就是 普罗米修斯以及zabix 我要做…

Docker-harbor私有仓库部署与管理

搭建本地私有仓库 #首先下载 registry 镜像 docker pull registry #在 daemon.json 文件中添加私有镜像仓库地址 vim /etc/docker/daemon.json { "insecure-registries": ["20.0.0.50:5000"], #添加&#xff0c;注意用逗号结…

Apache Solr9.3 快速上手

Apache Solr 简介 Solr是Apache的顶级开源项目&#xff0c;使用java开发 &#xff0c;基于Lucene的全文检索服务器。 Solr比Lucene提供了更多的查询语句&#xff0c;而且它可扩展、可配置&#xff0c;同时它对Lucene的性能进行了优化。 安装 下载 : 下载地址解压 : tar -zxv…

day04-前台首页、导出项目依赖

1 前台首页 1.1 Header.vue 1.2 Footer.vue 1.3 Banner.vue 1.4 HomeView.vue 1.5 轮播图接口打通 2 导出项目依赖 3 抽取返回格式 1 前台首页 -在项目的components文件夹下&#xff1a; 新建&#xff1a;Header.vueFooter.vueBanner.vue1.1 Header.vue <template>&…

Unity可视化Shader工具ASE介绍——6、通过例子说明ASE节点的连接方式

大家好&#xff0c;我是阿赵。继续介绍Unity可视化Shader编辑插件ASE的用法。上一篇已经介绍了很多ASE常用的节点。这一篇通过几个小例子&#xff0c;来看看这些节点是怎样连接使用的。   这篇的内容可能会比较长&#xff0c;最终是做了一个遮挡X光的效果&#xff0c;不过把这…

Docker部署gitlab_ce(避坑版---社区版)

1 下载docker 2 下载gitlab镜像 3 运行 4 进入容器内部修改 5 在浏览器里访问 6 修改root密码&#xff08;如果忘记请修改&#xff09; 1 下载docker # 安装依赖 yum install -y yum-utils device-mapper-persistent-data lvm2# 设置yum源 yum-config-manager --add-repo https…

【动手学深度学习-Pytorch版】BERT预测系列——用于预测的BERT数据集

本小节的主要任务即是将wiki数据集转成BERT输入序列&#xff0c;具体的任务包括&#xff1a; 读取wiki数据集生成下一句预测任务的数据—>主要用于_get_nsp_data_from_paragraph函数从输入paragraph生成用于下一句预测的训练样本&#xff1a;_get_nsp_data_from_paragraph生…

css常见问题处理

文章目录 1&#xff1a;禁止文字被复制粘贴1.1 Css 处理1.2 Js 处理 2&#xff1a;元素垂直水平居中2.1:方案一2.2 方案二2.3 方案三2.4 方案四2.5 方案五 1&#xff1a;禁止文字被复制粘贴 1.1 Css 处理 <div class"text">我不可以复制信息</div> <…

语义分割笔记(三):通过opencv对mask图片来画分割对象的外接椭圆

文章目录 mask图像介绍步骤代码 mask图像介绍 根据 mask 图像来画分割对象的外接椭圆是一种常见的图像分割任务。Mask 图像通常是一个二值图像&#xff0c;其中包含了感兴趣对象的像素。通常情况下&#xff0c;白色像素表示对象&#xff0c;黑色像素表示背景。 步骤 以下是一…

CCAK—云审计知识证书学习

目录 一、CCAK云审计知识证书概述 二、云治理概述 三、云信任 四、构建云合规计划 <

js中 slice 用法用法全解析

slice 工作原理 在深入研究一些更高级的用法之前&#xff0c;让我们看一下 slice 方法的基础知识。如MDN文档&#xff0c; slice 是数组上的一个方法&#xff0c;它最多有两个参数: arr.slice([begin[, end]]) begin 从该索引处开始提取原数组中的元素,如果该参数为负数&am…

Linux和UNIX的关系及区别

UNIX 与 Linux 之间的关系是一个很有意思的话题。在目前主流的服务器端操作系统中&#xff0c;UNIX 诞生于 20 世纪 60 年代末&#xff0c;Windows 诞生于 20 世纪 80 年代中期&#xff0c;Linux 诞生于 20 世纪 90 年代初&#xff0c;可以说 UNIX 是操作系统中的"老大哥&…

Web 前端汇总

一、前端技术框架 1、Vue.js 官网&#xff1a;https://cn.vuejs.org/ Vue CLI&#xff1a;https://cli.vuejs.org/ 菜鸟教程&#xff1a;http://www.runoob.com/w3cnote… Nuxt.js&#xff1a;https://zh.nuxtjs.org/ 桌面应用Electron&#xff1a;https://electronjs.org/ 2、…

Python利用jieba分词提取字符串中的省市区(字符串无规则)

目录 背景库&#xff08;jieba&#xff09;代码拓展结尾 背景 今天的需求就是在一串字符串中提取包含&#xff0c;省、市、区&#xff0c;该字符串不是一个正常的地址;,如下字符串 "安徽省、浙江省、江苏省、上海市,冷运标快首重1kg价格xx元,1.01kg(含)-5kg(不含)续重价…

【web实现右侧弹窗】JS+CSS如何实现右侧缓慢弹窗动态效果『附完整源码下载』

文章目录 写在前面涉及知识点页面效果1、页面DOM创建1.1创建底层操作dom节点1.2 创建存放弹窗dom节点 2、页面联动功能实现&#xff08;关闭与弹出&#xff09;2.1 点击非右侧区域实现关闭2.2 点击叉叉及关闭按钮实现关闭功能 3、完整源码包下载3.1百度网盘3.2 123云盘3.3邮箱留…

按键中断小灯蜂鸣器风扇

按键1实现小灯亮灭&#xff0c;按键2实现蜂鸣器&#xff0c;安静3实现风扇 src/key_it.c #include"key_it.h"void key3_it_config() {//RCC使能GPIOF时钟RCC->MP_AHB4ENSETR | (0x1<<5);GPIOF->MODER & (~(0x3<<16));EXTI->EXTICR3 &…

基于若依ruoyi-nbcio支持flowable流程增加自定义业务表单(二)

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 之前讲了自定义业务表单&#xff0c;现在讲如何与流程进行关联 1、后端部分 WfCustomFormMapper.xml &…

ARM按键中断控制事件

设置按键中断&#xff0c;按键1按下&#xff0c;LED亮&#xff0c;再按一次&#xff0c;灭按键2按下&#xff0c;蜂鸣器响。再按一次&#xff0c;不响按键3按下&#xff0c;风扇转&#xff0c;再按一次&#xff0c;风扇停 src/key_it.c #include"key_it.h" //GPIO初…

Acwing.4736步行者(模拟)

题目 约翰参加了一场步行比赛。 比赛为期 N 天&#xff0c;参赛者共 M 人&#xff08;包括约翰&#xff09;。 参赛者编号为 1∼M&#xff0c;其中约翰的编号为 P。 每个参赛者的每日步数都将被赛事方记录并公布。 每日步数最多的参赛者是当日的日冠军&#xff08;可以有并…

Flink-SQL join 优化 -- MiniBatch + local-global

背景 问题1. 近期在开发flink-sql期间&#xff0c;发现数据在启动后&#xff0c;任务总是进行重试&#xff0c;运行一段时间后&#xff0c;container heartbeat timeout&#xff0c;内存溢出(GC overhead limit exceede) &#xff0c;作业无法进行正常工作 023-10-07 14:53:3…