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…

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…

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

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

Linux和UNIX的关系及区别

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

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 &…

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…

Commonjs与ES Module

commonjs 1 commonjs 实现原理 commonjs每个模块文件上存在 module&#xff0c;exports&#xff0c;require三个变量,然而这三个变量是没有被定义的&#xff0c;但是我们可以在 Commonjs 规范下每一个 js 模块上直接使用它们。在 nodejs 中还存在 __filename 和 __dirname 变…

Kaadas凯迪仕助力亚运盛会,尽展品牌硬核科技与智能锁行业风采

9月23日至10月8日&#xff0c;亚洲最大规模体育赛事亚运会在杭州举办。作为国际性体育赛事&#xff0c;除赛中的各类竞赛项目外&#xff0c;杭州亚运会前后相关活动也吸引了大众目光的聚焦。 Kaadas凯迪仕智能锁作为此次杭州亚运会官方指定智能门锁&#xff0c;以#凯迪仕守护每…

【Mybatis】基于Mybatis插件+注解,实现敏感数据自动加解密

一、介绍 业务场景中经常会遇到诸如用户手机号&#xff0c;身份证号&#xff0c;银行卡号&#xff0c;邮箱&#xff0c;地址&#xff0c;密码等等信息&#xff0c;属于敏感信息&#xff0c;需要保存在数据库中。而很多公司会会要求对数据库中的此类数据进行加密存储。 敏感数据…

什么是大数据,大数据简介

大数据的概念通俗的说法 大数据&#xff0c;按照我的理解比较通俗易懂的是在数据量很多很大的情况下数据处理速度需要足够快&#xff0c;用我们以前传统意义上的的技术比如关系型数据库mysql没办法处理或者处理起来非常复杂&#xff0c;必须有一些新的处理技术也就是大数据处理…

深入理解强化学习——序列决策(Sequential Decision Making)

分类目录&#xff1a;《深入理解联邦学习》总目录 在本文中我们将介绍序列决策&#xff08;Sequential Decision Making&#xff09;过程中的各个过程。 智能体与环境 强化学习研究的问题是智能体与环境交互的问题&#xff0c;下图左边的智能体一直在与下图右边的环境进行交互…

微信小程序 movable-view 控制长按才触发拖动 轻轻滑动页面正常滚动效果

今天写 movable-areamovable-view遇到了个头疼的问题 那就是 movable-view 监听了用户拖拽自己 但 我们小程序 上下滚动页面靠的也是拖拽 也就是说 如果放在这里 用户拖动 movable-view部分 就会永远触发不了滚动 那么 我们先可以 加一个 bindlongpress"longpressHandler…

扫描器(xray和bp联动)

文章目录 分类主动扫描和被动扫描bp与xray联动 分类 扫描器分为对web的扫描器和对主机的扫描器 主动扫描和被动扫描 主动扫描&#xff1a; 输入某个URL&#xff0c;然后由扫描器中的爬虫模块爬取所有链接&#xff0c;对GET、POST等请求进行参数变形和污染&#xff0c;进行重放测…

什么是信创测试?信创测试工具有哪些?

信创全称是“信息技术应用创新”&#xff0c;旨在实现信息技术自主可控&#xff0c;规避外部技术制裁和风险&#xff0c;其涉及产业链包括硬件、基础软件、应用软件、云服务、数据安全等领域。 信创测试是指对信创工程项目中的产品、系统等进行测试和验证&#xff0c;以确保其…