linux系统中select函数的用法实现

简介: linux系统中select函数的用法实现

前言:

select机制已经被很多人都讲解过,select使用起来也不是特别难,为什么还要花时间再次讲解select机制?

在回答这个问题之前,我们先问一下自己,是否有足够的信心保证在使用select编程时不出错,select机制虽然看起来简单,实际在使用时有很多坑,当我们没有从原理上真正理解select机制时,我们随时会掉入这些坑,所以这篇文章我尽量把select实现原理和细节交待清楚。

1.select简介

select机制是一种I/O多路复用技术,它可以同时监视多个文件描述符,当某个文件描述符就绪(一般是读写就绪)时,就会通知程序进行相应的操作。

select机制的优点是可以同时处理多个连接,避免了大量的线程或进程切换,提高了系统的并发性能。

在Linux系统中,select机制可以通过select函数来实现。select函数的参数包括需要监视的文件描述符集合、超时时间等。当select函数返回时,程序可以通过遍历文件描述符集合来确定哪些文件描述符已经就绪,然后进行相应的操作。

2.select实现原理

(老规矩先上原理图)

这张原理图把select主要活动轨迹都进行了描述。

  • select核心实现原理是位图,select总共有三种位图,分别为读,写,异常位图,用户程序预先将socket文件描述符注册至读,写,异常位图,然后通过select系统调用轮询位图中的socket的读,写,异常事件。
  • select底层通过轮询方式获取读,写,异常位图中注册的socket文件事件,如果检测到有socket文件处于就绪状态,则会将socket对应的事件设置到输出位图,等所有位图中的socket都被轮询完,会统一将输出位图通过copy_to_user函数复制到输入位图,并且覆盖掉输入位图注册信息(也就是用户初始化的位图被内核修改)
  • select轮询完所有位图,如果未检测到任何socket文件处于就绪状态,根据超时时间确定是否返回或者阻塞进程。
  • socket检测到读,写,异常事件后,会通过注册到socket等待队列的回调函数poll_wake将进程唤醒,唤醒的进程将再次轮询所有位图。
  • select返回时会将剩余的超时时间通过copy_to_user覆盖原来的超时时间。

3.select位图

3.1 select位图定义

#define __FD_SETSIZE        1024

#define __NFDBITS   (8 * (int) sizeof (long int))

typedef struct

{

   long int fds_bits[__FD_SETSIZE / __NFDBITS];

} fd_set;

  • select位图为1024比特位图,通过整型数组模拟而成。
  • select位图每个比特对应一个文件描述符数值。
  • select位图数组长度为16,每个数组元素为8字节,一个字节为8比特,位图大小=16 * 8 * 8 = 1024比特。

3.2 常用位图操作函数:

void FD_CLR(int fd, fd_set *set); //设置fd对应位图位置为0。

int FD_ISSET(int fd, fd_set *set); //判断fd对应位图位置是否为1。

void FD_SET(int fd, fd_set *set); //设置fd对应位图位置为1。

void FD_ZERO(fd_set *set);//整个位图清零。

4.select编程

4.1 select函数原型

int select(int nfds, fd_set *readfds, fd_set *writefds,          

         fd_set *exceptfds, struct timeval *timeout);

功能:select函数是Linux系统中的一种I/O多路复用机制,它可以同时监视多个文件描述符。

参数:

nfds:最大文件描述符+1。

readfds:读文件描述符集合,可设置为NULL。

writefds:写文件描述符集合,可设置为NULL。

exceptfds:异常文件描述符集合,可设置为NULL。

timeout:超时时间,设置为NULL为阻塞模式,

struct timeval {

   __kernel_time_t     tv_sec;     //秒,超时多少秒。

   __kernel_suseconds_t    tv_usec;    //微妙,超时多少微秒。

};

返回值:

成功:返回检测到的文件描述符数量。

失败:返回-1,设置errno。

超时:返回0。

4.2 select使用示例:

fd_set rfds;

FD_ZERO(&rfds_storage); //清空位图

FD_SET(sock_fd, &rfds_storage); //设置位图

struct  timeval tv = {.tv_sec = 5, .tv_usec = 0}; //设置超时时间

ret = select(max_fd + 1, &rfds, NULL, NULL, &tv);

4.3 select编程模型

5.select常见问题?

问题1:select函数最大文件描述(maxfd)有什么作用?

select使用1024比特位图监测最多1024个文件描述符,然而实际的情况很少会到达1024文件描述符上限,使用maxfd可以避免每次都轮询1024个文件描述符,从而提高轮询效率。

maxfd通常设置为已打开最大文件描述符+1,目的是为了保证位图中每个文件描述符都被轮询到。

问题2:select优缺点有哪些?

优点:

  • select支持多种文件描述符类型,包括socket、标准输入输出、管道、FIFO等。
  • select是跨平台的,可以在多种操作系统上使用。

缺点:

  • select的效率不高,每次调用select都需要将所有的文件描述符从用户态复制到内核态,这个过程比较耗时。
  • select返回后需要遍历所有的文件描述符,找到就绪的文件描述符,这个过程也比较耗时。
  • select支持的文件描述符数量有限,通常是1024个。

问题3:select为什么会有1024文件描述符限制?

  • 进程默认打开最大文件描述符为1024(次要原因)。
  • select采用轮询的方式获取读,写,异常事件,如果需要轮询的文件描述符比较多的话,select执行效率会非常低(个人观点)。
  • select是通过一个整型数组来模拟位图,整型数组长度和元素大小已经固定,只能模拟出1024比特位图,使用select如果超过1024文件描述符的限制,可能会导致内存越界和其他未知问题(真正原因)。

问题4:select有哪些设计缺陷?

  • select最大文件描述符为1024,所以无法满足高并发应用场景,高并发场景请使用epoll机制。
  • select采用轮询的方式获取读,写,异常事件,轮询的方式效率低,不管文件事件是否就绪,都需要去做检测。
  • select位图设置和获取采用覆盖方式,用户输入的读,写,异常位图会被内核修改,编程非常容易出错,可参考select实现原理图分析,所以每次调用select之前都要重新设置位图。
  • select执行完后会返回剩余超时时间,剩余超时时间会覆盖原来的超时时间,导致超时机制异常。可参考select实现原理图分析,所以每次调用select之前都要重新设置超时时间。
目录
相关文章
|
6天前
|
Linux
Linux系统之whereis命令的基本使用
Linux系统之whereis命令的基本使用
50 23
Linux系统之whereis命令的基本使用
|
3天前
|
Shell Linux
【linux】Shell脚本中basename和dirname的详细用法教程
本文详细介绍了Linux Shell脚本中 `basename`和 `dirname`命令的用法,包括去除路径信息、去除后缀、批量处理文件名和路径等。同时,通过文件备份和日志文件分离的实践应用,展示了这两个命令在实际脚本中的应用场景。希望本文能帮助您更好地理解和应用 `basename`和 `dirname`命令,提高Shell脚本编写的效率和灵活性。
54 32
|
3天前
|
存储 Linux
linux中的目录操作函数
本文详细介绍了Linux系统编程中常用的目录操作函数,包括创建目录、删除目录、读取目录内容、遍历目录树以及获取和修改目录属性。这些函数是进行文件系统操作的基础,通过示例代码展示了其具体用法。希望本文能帮助您更好地理解和应用这些目录操作函数,提高系统编程的效率和能力。
48 26
|
1月前
|
缓存 安全 Linux
Linux系统查看操作系统版本信息、CPU信息、模块信息
在Linux系统中,常用命令可帮助用户查看操作系统版本、CPU信息和模块信息
109 23
|
2月前
|
Linux
【Linux】System V信号量详解以及semget()、semctl()和semop()函数讲解
System V信号量的概念及其在Linux中的使用,包括 `semget()`、`semctl()`和 `semop()`函数的具体使用方法。通过实际代码示例,演示了如何创建、初始化和使用信号量进行进程间同步。掌握这些知识,可以有效解决多进程编程中的同步问题,提高程序的可靠性和稳定性。
87 19
|
2月前
|
Linux Android开发 开发者
linux m、mm、mmm函数和make的区别
通过理解和合理使用这些命令,可以更高效地进行项目构建和管理,特别是在复杂的 Android 开发环境中。
81 18
|
2月前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
121 13
|
3月前
|
Linux 网络安全 数据安全/隐私保护
Linux 超级强大的十六进制 dump 工具:XXD 命令,我教你应该如何使用!
在 Linux 系统中,xxd 命令是一个强大的十六进制 dump 工具,可以将文件或数据以十六进制和 ASCII 字符形式显示,帮助用户深入了解和分析数据。本文详细介绍了 xxd 命令的基本用法、高级功能及实际应用案例,包括查看文件内容、指定输出格式、写入文件、数据比较、数据提取、数据转换和数据加密解密等。通过掌握这些技巧,用户可以更高效地处理各种数据问题。
342 8
|
3月前
|
监控 Linux
如何检查 Linux 内存使用量是否耗尽?这 5 个命令堪称绝了!
本文介绍了在Linux系统中检查内存使用情况的5个常用命令:`free`、`top`、`vmstat`、`pidstat` 和 `/proc/meminfo` 文件,帮助用户准确监控内存状态,确保系统稳定运行。
1128 6