I/O 多路复用之select

转载:http://blog.csdn.net/u012432778/article/details/47347133

概述

Linux提供了三种 I/O 多路复用方案:select,poll和epoll。在这一篇博客里先讨论select, poll 在将下一篇中介绍,epoll是Linux特有的高级解决方案,将在接下来中介绍。

select()

select()系统调用提供了一种实现同步 I/O 多路复用的机制:

         #include <sys/select.h>

         int select (int n,
                          fd_set *readfds,
                          fd_set *writefds,
                          fd_set *exceptfds,
                          struct timeval *timeout);

         FD_CLR(int fd, fd_set *set);
         FD_ISSET(int fd, fd_set *set);
         FD_SET(int fd, fd_set *set);
         FD_ZERO(fd_set *set);

在给定的文件描述符 I/O 就绪之前并且还没有超出指定的时间限制,select()调用就会阻塞。

监视的文件描述符可以分为3类,分别等待不同的事件。对于 readfds 集中的文件描述符,监视是否有数据可读(即某个读操作是否可以无阻塞完成);对于 writefds 集中的文件描述符,监视是否有某个写操作可以无阻塞完成;对于 exceptfds 中的文件描述符,监视是否有发生异常,或者出现带外 (out-of-band) 数据(这些场景只适用于socket)。指定的集合可能是NULL,在这种情况下,select() 不会监视该事件。

成功返回时,每个集合都修改成只包含相应类型的 I/O 就绪的文件描述符。举个例子,假定 readfds 集中有两个文件描述符 7 和 9。当调用返回时,如果描述符 7 还在集合中,它在 I/O 读取时不会阻塞。如果描述符 9 不在集合中,它在读取时很可能会发生阻塞。(这里说的是“很可能”是因为在调用完成后,数据可能已经就绪了。在这种场景下,下一次调用 select() 就会返回描述符可用。)

第一个参数 n,其值等于所有集合中文件描述符的最大值加 1 。因此,select() 调用负责检查哪个文件描述符值最大,将该最大值 加 1 后传给第一个参数。

参数 timeout 是指向 timeval 结构体的指针,定义如下:

        #include <sys/time.h>
        struct timeval {
               long tv_sec;             /* seconds */
               long tv_usec;           /* microseconds */
        };

如果该参数不是NULL,在 tv_sec 秒 tv_usec 微妙后。select() 调用会返回,即使没有一个文件描述符处于 I/O 就绪状态。返回时,在不同的UNIX系统中,该结构体是未定义的,因此每次调用必须(和文件描述符集一起)重新初始化。实际上,当前Linux版本会自动修改该参数,把值修改成剩余的时间。因此,如果超时设置是 5 秒,在文件描述符可用之前已逝去了 3 秒,那么在调用返回时,tv.tv_sec 的值就是 2。

如果超时值都是设置成 0,调用会立即返回,调用时报告所有事件都挂起,而不会等待任何后续事件。

不是直接操作文件描述符集,而是通过辅助宏来管理。通过这种方式,UNIX系统可以按照所希望的方式来实现。不过,大多数系统把集合实现成位数组。

FD_ZERO 从指定集合中删除所有的文件描述符。每次调用 select() 之前,都应该调用该宏。

       fd_set writefds;
       FD_ZERO(&writefds);

FD_SET 向指定集中添加一个文件描述符,而 FD_CLR 则从指定集中删除一个文件描述符。

       FD_SET(fd, &writefds);           /* add 'fd' to the set */
       FD_CLR(fd, &writefds);           /* oops, remove 'fd' from the set */

设计良好的代码应该不需要使用FD_CLR,极少使用该宏。

FD_ISSET 检查一个文件描述符是否在给定集合中。如果在,则返回非0值,否则返回 0。当select() 调用返回时,会通过 FD_ISSET 来检查文件描述符是否就绪:

       if (FD_ISSET(fd, &readfds)) 
                                     /* 'fd' is readable without blocking */

由于文件描述符集是静态建立的,所以文件描述符数存在上限值,而且存在最大文件描述符值,这两个值都是由 FD_SETSIZE 设置。在Linux,该值是1024。

返回值和错误码

select() 调用成功时,返回三个集合中 I/O 就绪的文件描述符总数。如果给出了超时设置,返回值可能是 0 。出错时。返回 -1 ,并把 errno 值设置成如下值之一:

EBADF        某个集合中存在非法文件描述符。
EINTR        等待时捕获了一个信号,可以重新发起调用。
EINVAL      参数 n 是负数,或者设置的超时时间值非法。
ENOMEN    没有足够的内存来完成该请求

select() 示例

[cpp] view plain copy
  1. #include <stdio.h>  
  2. #include <sys/time.h>  
  3. #include <sys/types.h>  
  4. #include <unistd.h>  
  5.   
  6. #define TIMEOUT   5            /* select timeout to seconds */  
  7. #define BUF_LEN   1024         /* read buffer in bytes */  
  8.   
  9. int main(void)   
  10. {  
  11.     struct timeval tv;  
  12.     fd_set readfds;  
  13.     int    ret;  
  14.   
  15.     /* Wait on stdin for input */  
  16.     FD_ZERO(&readfds);  
  17.     FD_SET(STDIN_FILENO, &readfds);  
  18.   
  19.     /* Wait up to five seconds */  
  20.     tv.tv_sec = TIMEOUT;  
  21.     tv.tv_usec = 0;  
  22.   
  23.     /* All right, now block */  
  24.     ret = select(STDIN_FILENO + 1,  
  25.                  &readfds,  
  26.                  NULL,  
  27.                  NULL,  
  28.                  &tv);  
  29.     if (ret == -1) {  
  30.         perror("select");  
  31.         return 1;  
  32.     } else if(!ret){  
  33.         printf("%d seconds eclapsed.\n", TIMEOUT);  
  34.     }  
  35.   
  36.     /*  
  37.      * Is our file descriptor ready to read? 
  38.      * (It must be, as it was the only fd that 
  39.      *  we provied and the call returned 
  40.      *  nonzero, but we will humor ourselves.) 
  41.      */  
  42.     if (FD_ISSET(STDIN_FILENO, &readfds)) {  
  43.         char buf[BUF_LEN];  
  44.         int len;  
  45.         /* guaranteed to not block */  
  46.         len = read(STDIN_FILENO, buf, BUF_LEN);  
  47.         if (len == -1) {  
  48.             perror("read");  
  49.             return 1;  
  50.         }  
  51.   
  52.         if (len) {  
  53.             buf[len] = '\0';  
  54.             printf("read: %s\n", buf);  
  55.         }  
  56.         return 0;  
  57.     }  
  58.   
  59.     fprintf(stderr, "This should not happen!\n");  
  60.     return 1;  
  61. }  

用select() 实现可移植的sleep功能

在各个Unix系统中,相比微秒级的sleep功能,对select() 的实现更普遍,因此select() 调用常常被作为可移植的sleep实现机制:把所有三个集都设置成NULL,超时值设置为非NULL。如下:

       struct timeval tv;
       tv.tv_sec = 0;
       tv.tv_usec = 500;

       /* sleep for 500 microseconds */
       select(0, NULL, NULL, NULL, &tv);
Linux提供了更高精度的sleep机制。

pselect()

select()系统调用很流行,它最初是在 4.2BSD 中引入的,但是 POSIX 标准在 POSIX 1003.1g-2000 和后来的 POSIX 1003.1-2001中定义了自己的 pselect() 方法:

       #define _XOPEN_SOURCE 600
       #include <sys/select.h>

       int pselect(int n,
                         fd_set *readfds,
                         fd_set *writefds,
                         fd_set *exceptfds,
                         const struct timespec *timeout,
                         const sigset_t *sigmask);

       /* these are the same as those used by select() *
       FD_CLR(int fd, fd_set *set);
       FD_ISSET(int fd, fd_set *set);
       FD_SET(int fd, fd_set *set);
       FD_ZERO(fd_set *set);

pselect() 和 select() 存在三点区别:
  • pselect() 的timeout 参数使用了timespec 结构体,而不是 timeval 结构体。timespec 结构体使用秒和纳秒,而不是毫秒,从理论上讲更精确些。但是实际上,这两个结构体在毫秒精度上已经不可靠了。
  • pselect() 调用不会修改 timeout 参数。因此,在后续调用中,不需要重新初始化该参数。
  • select() 系统调用没有sigmask参数。当这个参数设置为NULL时,pselect() 的行为和select() 相同。
timespec 结构体定义如下:

      #include <sys/time.h>
      struct timespec {
             long tv_sec;         /* seconds */
             long tv_nsec;       /* nanoseconds */
      };

把pselect() 添加到UNIX工具箱主要原因是为了增加 sigmask 参数,该参数是为了解决文件描述符和信号之间等待出现竞争条件。假设信号处理程序设置了全局标志位(大部分如此),进程每次调用 select() 之前会检查该标志位。现在,假定在检查标志位和调用之间收到信号,应用可能会一直阻塞,永远都不会响应该信号。pselect() 提供了一组可阻塞信号,应用在调用时可以设置这些信号来解决这个问题。阻塞的信号要等到解除阻塞才会处理。一旦 pselect() 返回,内核会恢复老的信号掩码。

在Linux内核2.6.16之前,pselect() 还不是系统调用,而是由 glibc 提供的对 select() 调用的简单封装。该封装对出现竞争的风险最小化,但是没有完全的消除竞争。当真正引入了新的系统调用pselect() 之后,才彻底解决了这个竞争问题。

虽然和 select() 相比,pselect() 有一定的改进,但大多数应用还是使用 select(),有的是出于习惯,有的是为了更好的可移植性。


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

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

相关文章

leetcode(283)移动零

283. 移动零 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 示例: 输入: [0,1,0,3,12] 输出: [1,3,12,0,0] 说明: 必须在原数组上操作&#xff0c;不能拷贝额外的数组。 尽量减少操作次数。 方法一&#xff1…

exec函数族实例解析

转载&#xff1a;http://www.cnblogs.com/blankqdb/archive/2012/08/23/2652386.html fork()函数通过系统调用创建一个与原来进程(父进程)几乎完全相同的进程(子进程是父进程的副本&#xff0c;它将获得父进程数据空间、堆、栈等资源的副本。注意&#xff0c;子进程持有的是上述…

leetcode(167)两数之和 II - 输入有序数组

两数之和 II - 输入有序数组 给定一个已按照 升序排列 的整数数组 numbers &#xff0c;请你从数组中找出两个数满足相加之和等于目标数 target 。 函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。numbers 的下标 从 1 开始计数 &#xff0c;所以答案数组应当满足 …

常量指针与指针常量的区别(转帖)

转载&#xff1a;http://www.cnblogs.com/witty/archive/2012/04/06/2435311.html 三个名词虽然非常绕嘴&#xff0c;不过说的非常准确。用中国话的语义分析就可以很方便地把三个概念区分开。 一) 常量指针。 常量是形容词&#xff0c;指针是名词&#xff0c;以指针为中心的一个…

c/c++错题总结

1.类名 对象名 默认调用“对象名()”这个构造函数&#xff0c;在栈内存中存在对象名&#xff0c;在堆内存中存在实际对象&#xff1b; 2.类名 对象名(一个或以上个参数) 默认调用相应的构造函数&#xff0c;在栈内存中存在对象名&#xff0c;在堆内存中也是存在实际对象的&a…

智能指针学习笔记

转载&#xff1a;http://www.cnblogs.com/wuchanming/p/4411878.html 1. 介绍 本文介绍智能指针的使用。智能指针是c 中管理资源的一种方式&#xff0c;用智能指针管理资源&#xff0c;不必担心资源泄露&#xff0c;将c 程序员 从指针和内存管理中解脱出来&#xff0c;再者&…

c++程序编译过程

c程序编译分成四个过程&#xff1a;编译预处理&#xff0c;编译&#xff0c;汇编&#xff0c;链接 编译预处理&#xff1a;处理以#为开头 编译&#xff1a;将.cpp文件翻译成.s汇编文件 汇编&#xff1a;将.s汇编文件翻译成机器指令.o文件 链接&#xff1a;汇编生产的目标文件.o…

仿函数(函数对象)

转载&#xff1a;http://www.cnblogs.com/wuchanming/p/4411867.html 本文乃作者学习《C标准程序库》的学习笔记&#xff0c;首先介绍了仿函数&#xff08;函数对象&#xff09;和函数适配器&#xff08;配接器&#xff09;的概念&#xff0c;然后列出STL中所有的仿函数&#x…

C++ template —— 动多态与静多态(六)

转载&#xff1a;http://www.cnblogs.com/yyxt/p/5157517.html 前面的几篇博文介绍了模板的基础知识&#xff0c;并且也深入的讲解了模板的特性。接下来的博文中&#xff0c;将会针对模板与设计进行相关的介绍。 ------------------------------------------------------------…

变量之间的区别

全局变量、局部变量、静态全局变量、静态局部变量的区别 c变量根据定义具有不同的生命周期&#xff0c;会有不同的作用域&#xff0c;主要有六个作用域&#xff1a;全局作用域&#xff0c;局部作用域&#xff0c;文件作用域&#xff0c;类作用域&#xff0c;语句作用域&#xf…

计算机的网络体系以及参考模型

计算机的网络体系以及参考模型一、OSI七层模型二、TCP/IP参考模型三、TCP/IP 五层参考模型四、OSI 模型和 TCP/IP 模型异同比较五、OSI 和 TCP/IP 协议之间的对应关系六、为什么 TCP/IP 去除了表示层和会话层&#xff1f;七、数据如何在各层之间传输&#xff08;数据的封装过程…

C++ 模板详解(二)

转载&#xff1a;http://www.cnblogs.com/gw811/archive/2012/10/25/2736224.html 四、类模板的默认模板类型形参 1、可以为类模板的类型形参提供默认值&#xff0c;但不能为函数模板的类型形参提供默认值。函数模板和类模板都可以为模板的非类型形参提供默认值。 2、类模板的类…

c++类对象的创建方式

对象创建限制在堆或栈 c类对象的创建方式对象创建限制在堆或栈C 中的类的对象的建立模式如何将类限制在堆上呢&#xff1f;C 中的类的对象的建立模式 C 中的类的对象的建立模式分为两张&#xff1a;静态建立&#xff0c;动态建立 静态建立&#xff1a;由编译器为对象在栈空间…

C++ 模板详解(一)

转载&#xff1a;http://www.cnblogs.com/gw811/archive/2012/10/25/2738929.html C模板 模板是C支持参数化多态的工具&#xff0c;使用模板可以使用户为类或者函数声明一种一般模式&#xff0c;使得类中的某些数据成员或者成员函数的参数、返回值取得任意类型。 模板是一种对类…

剑指Offer09. 用两个栈实现队列

class CQueue { public:stack<int> stack1,stack2;CQueue() {//初始化栈while(!stack1.empty()){stack1.pop();}while(!stack2.empty()){stack2.pop();}}void appendTail(int value) {stack1.push(value);}int deleteHead() {if(stack2.empty()){while(!stack1.empty()){…

rk3588 之启动

目录 uboot版本配置修改编译 linux版本配置修改编译 启动sd卡启动制作spi 烧录 参考 uboot 版本 v2024.01-rc2 https://github.com/u-boot/u-boot https://github.com/rockchip-linux/rkbin 配置修改 使用这两个配置即可&#xff1a; orangepi-5-plus-rk3588_defconfig r…

C++引用详解

转载&#xff1a;http://www.cnblogs.com/gw811/archive/2012/10/20/2732687.html 引用的概念 引用&#xff1a;就是某一变量&#xff08;目标&#xff09;的一个别名&#xff0c;对引用的操作与对变量直接操作完全一样。 引用的声明方法&#xff1a;类型标识符 &引用名目标…

剑指Offer03.数组中重复的数字

找出数组中重复的数字。 在一个长度为 n 的数组 nums 里的所有数字都在 0&#xff5e;n-1 的范围内。数组中某些数字是重复的&#xff0c;但不知道有几个数字重复了&#xff0c;也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。 示例 1&#xff1a; 输入&…

C++ 模板全特化中的函数特化

转载&#xff1a;http://blog.csdn.net/rain_qingtian/article/details/15815251 [cpp] view plaincopy print?#include <iostream> using namespace std; template<typename T> bool isLess(T x, T y) { cout << "general version\n&q…

c++面向对象总结

c面向对象总结什么是面向对象&#xff1f;面向对象的三大特性重写和重载的区别隐藏和重写&#xff0c;重载的区别什么是多态&#xff1f;多态如何实现什么是面向对象&#xff1f;面向对象的三大特性 面向对象&#xff1a;对象是指具体的某一个事物&#xff0c;这些事物的抽象就…