【Linux系统编程】IO多路复用之select
00. 目录
文章目录
- 00. 目录
- 01. 概述
- 02. select函数
- 03. select程序示例
- 04. select优缺点
- 05. 附录
01. 概述
I/O 多路复用技术是为了解决进程或线程阻塞到某个 I/O 系统调用而出现的技术,使进程不阻塞于某个特定的 I/O 系统调用。
select(),poll(),epoll()都是I/O多路复用的机制。I/O多路复用通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪,就是这个文件描述符进行读写操作之前),能够通知程序进行相应的读写操作。但select(),poll(),epoll()本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。
与多线程和多进程相比,I/O 多路复用的最大优势是系统开销小,系统不需要建立新的进程或者线程,也不必维护这些线程和进程。
02. select函数
#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
功能:监视并等待多个文件描述符的属性变化(可读、可写或错误异常)。select()函数监视的文件描述符分 3 类,分别是writefds、readfds、和 exceptfds。调用后 select() 函数会阻塞,直到有描述符就绪(有数据可读、可写、或者有错误异常),或者超时( timeout 指定等待时间),函数才返回。当 select()函数返回后,可以通过遍历 fdset,来找到就绪的描述符。参数:nfds: 要监视的文件描述符的范围,一般取监视的描述符数的最大值+1,如这里写 10, 这样的话,描述符 0,1, 2 …… 9 都会被监视,在 Linux 上最大值一般为1024。readfd: 监视的可读描述符集合,只要有文件描述符即将进行读操作,这个文件描述符就存储到这。writefds: 监视的可写描述符集合。exceptfds: 监视的错误异常描述符集合。timeout: 超时时间,它告知内核等待所指定描述字中的任何一个就绪可花多少时间。其 timeval 结构用于指定这段时间的秒数和微秒数。
返回值:成功:就绪描述符的数目,超时返回 0,出错:-1timeout参数有三种可能:1)永远等待下去:仅在有一个描述字准备好 I/O 时才返回。为此,把该参数设置为空指针 NULL。2)等待固定时间:在指定的固定时间( timeval 结构中指定的秒数和微秒数)内,在有一个描述字准备好 I/O 时返回,如果时间到了,就算没有文件描述符发生变化,这个函数会返回 0。3)根本不等待(不阻塞):检查描述字后立即返回,这称为轮询。为此,struct timeval变量的时间值指定为 0 秒 0 微秒,文件描述符属性无变化返回 0,有变化返回准备好的描述符数量。struct timeval {long tv_sec; /* seconds */long tv_usec; /* microseconds */};中间的三个参数 readfds、writefds 和 exceptfds 指定我们要让内核监测读、写和异常条件的描述字。如果不需要使用某一个的条件,就可以把它设为空指针( NULL )。集合fd_set 中存放的是文件描述符,可通过以下四个宏进行设置://将一个给定的文件描述符从集合中删除void FD_CLR(int fd, fd_set *set);// 检查集合中指定的文件描述符是否可以读写int FD_ISSET(int fd, fd_set *set);//将一个给定的文件描述符加入集合之中void FD_SET(int fd, fd_set *set);//清空集合void FD_ZERO(fd_set *set);
03. select程序示例
我们写这么一个例子,同时循环读取标准输入的内容,读取有名管道的内容,默认的情况下,标准输入没有内容,read()时会阻塞,同样的,有名管道如果没有内容,read()也会阻塞,我们如何实现循环读取这两者的内容呢?最简单的方法是,开两个线程,一个线程循环读标准输入的内容,一个线程循环读有名管道的内容。而在这里,我们通过 select() 函数实现这个功能:
#include <sys/select.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main(int argc, char *argv[])
{fd_set rfds;struct timeval tv;int ret;int fd;ret = mkfifo("test_fifo", 0666); // 创建有名管道if(ret != 0){perror("mkfifo:");}fd = open("test_fifo", O_RDWR); // 读写方式打开管道if(fd < 0){perror("open fifo");return -1;}ret = 0;while(1){// 这部分内容,要放在while(1)里面FD_ZERO(&rfds); // 清空FD_SET(0, &rfds); // 标准输入描述符 0 加入集合FD_SET(fd, &rfds); // 有名管道描述符 fd 加入集合// 超时设置tv.tv_sec = 1;tv.tv_usec = 0;// 监视并等待多个文件(标准输入,有名管道)描述符的属性变化(是否可读)// 没有属性变化,这个函数会阻塞,直到有变化才往下执行,这里没有设置超时// FD_SETSIZE 为 <sys/select.h> 的宏定义,值为 1024ret = select(FD_SETSIZE, &rfds, NULL, NULL, NULL);//ret = select(FD_SETSIZE, &rfds, NULL, NULL, &tv);if(ret == -1){ // 出错perror("select()");}else if(ret > 0){ // 准备就绪的文件描述符char buf[100] = {0};if( FD_ISSET(0, &rfds) ){ // 标准输入read(0, buf, sizeof(buf));printf("stdin buf = %s\n", buf);}else if( FD_ISSET(fd, &rfds) ){ // 有名管道read(fd, buf, sizeof(buf));printf("fifo buf = %s\n", buf);}}else if(0 == ret){ // 超时printf("time out\n");}}return 0;
}
下面为上面例子的往有名管道写内容的示例代码:
#include <sys/select.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main(int argc, char *argv[])
{//select_demo(8);fd_set rfds;struct timeval tv;int ret;int fd;ret = mkfifo("test_fifo", 0666); // 创建有名管道if(ret != 0){perror("mkfifo:");}fd = open("test_fifo", O_RDWR); // 读写方式打开管道if(fd < 0){perror("open fifo");return -1;}while(1){char *str = "this is for test";write(fd, str, strlen(str)); // 往管道里写内容printf("after write to fifo\n");sleep(5);}return 0;
}
04. select优缺点
select优点:
select()目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。
select的缺点:
1)每次调用 select(),都需要把 fd 集合从用户态拷贝到内核态,这个开销在 fd 很多时会很大,同时每次调用 select() 都需要在内核遍历传递进来的所有 fd,这个开销在 fd 很多时也很大。
2)单个进程能够监视的文件描述符的数量存在最大限制,在 Linux 上一般为 1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但是这样也会造成效率的降低。
05. 附录
【Linux系统编程】IO多路复用之select相关推荐
- Linux网络编程(IO多路复用)
网络编程 1 操作系统 1.1 用户空间与内核空间 1.2 进程切换 1.3 进程的阻塞 1.4 文件描述符fd 2 IO多路复用 2.1 阻塞IO 2.1.1 多线程/多进程 2.1.2 线程池 2 ...
- linux io多路复用详解,Linux系统中IO多路复用
文章目录 1 什么是IO多路复用 1.1 阻塞IO模型 1.2 非阻塞IO模型 1.3 IO复用模型 1.4 信号驱动IO模型 1.5 异步IO模型 2 IO多路复用,epoll 1 什么是IO多路复 ...
- Linux系统编程——I/O多路复用select、poll、epoll
参考:https://segmentfault.com/a/1190000003063859 Linux下的I/O复用与epoll详解:https://www.cnblogs.com/lojunren ...
- Linux系统编程 / triggerhappy 源码分析(3.select 的应用)
哈喽,我是老吴,继续记录我的学习心得. 一.进步的滞后性 我们期望进步是线性: 每一个人付出一些努力后,都希望它有立竿见影的效果. 现实是: 做出努力后,结果的显现往往滞后. 只有在几个月或几年后,我 ...
- Linux系统编程25:基础IO之亲自实现一个动静态库
本文接:Linux系统编程24:基础IO之在Linux下深刻理解C语言中的动静态库以及头文件和库的关系 文章目录 A:说明 B:实现静态库 C:实现动态库 A:说明 前面说过,库其实就是头文件和和.a ...
- linux原子过程,linux系统编程:IO读写过程的原子性操作实验
所谓原子性操作指的是:内核保证某系统调用中的所有步骤(操作)作为独立操作而一次性加以执行,其间不会被其他进程或线程所中断. 举个通俗点的例子:你和女朋友OOXX的时候,突然来了个电话,势必会打断你们高 ...
- Linux系统编程【文件IO、进程、进程间通信、信号、线程、互斥】
linux系统编程 个人通过学习,手打了一份48000字的Linux系统编程的笔记,包含了[文件IO.进程.进程间通信.信号.多线程.互斥]等知识点,并给出了大量的代码案例对每个重要的知识点进行了代码 ...
- Linux系统编程之文件IO
Linux系统编程之文件IO 文件IO第一天 一.标准IO基本概念 1.1c语言函数 (c库函数) 调用实现某一个功能,(API) 1.2系统调用:内核中的程序接口,应用程序和硬件设备之间的中间层 1 ...
- linux系统io编程,Linux系统编程(1) —— 文件IO
本文主要对Linux系统中文件I/O的基本操作进行说明. 在Linux系统编程中,对文件进行处理的流程,通常是: 打开文件 读写文件 关闭文件 Linux内核对每一个进程维护一个打开的文件列表, 该文 ...
- 【Linux】一步一步学Linux系统编程教程汇总(暂时暂停更新......)
00. 目录 文章目录 00. 目录 01. 概述和标准 02. 文件操作 03. 进程概念 04. 进程间通信 05. 多线程 06. 信号 07. 同步与互斥 08. 高级IO 09. 其它 10 ...
最新文章
- 在S/4HANA扩展字段的Available Fields列表里,看不到自己创建的扩展字段该怎么办
- linux 关掉蓝牙自动,如何在Ubuntu 14.04中默认关闭蓝牙,蓝牙
- 微型计算机主板上安装的主要部件有,微型计算机的主板上安装的主要部件有()....
- 后置增强this advice advises no methods_增强消防意识 提高消防能力 重庆天一新城小学积极参加消防技能比赛活动...
- Python-copy()与deepcopy()之间的主要区别
- MFC双缓冲解决图象闪烁
- PHPMailer 报错:SMTP ERROR: Password command failed: 535 Login Fail
- 系统动力学建模代做,vensim建模代做,流图建模,不确定分析。
- Pandas query 的用法, df.query
- matlab仿真动画,用matlab制作简单仿真动画
- JMeter Linux下执行测试
- Mysql服务器安装步骤
- 轻松理解LTE网规网优FAQ基本概念
- 巨噬细胞膜包裹PLGA纳米粒HCPT-MCNP/MCF-7细胞膜包覆PLGA纳米球共载姜黄素和二氢卟吩e6的研究
- 怎样与团队成员沟通,从而提高团队的执行力?
- 医药之家:医疗器械龙头企业排名变动,11家中国企业入选全球百强!
- 以json格式输出 bro(zeek)日志
- Java项目:洗浴中心管理系统(java+SSM+JSP+jQuery+javascript+Mysql)
- 网管必备-CMD命令
- o2o项目部署前,阿里云的申请与环境搭建