Linux IO模型(多路复用)

【1】Linux IO模型:IO多路复用

场景假设二

假设妈妈有三个孩子,分别不同的房间里睡觉,需要及时获知每个孩子是否醒了,如何做?

1.一直在一个房间呆着:看不到其他两个孩子

2.每个房间不停的看:可以但是超级无敌累

3.听孩子哭不哭:不可行,因为只有一个信号,分辨不出来哪个孩子哭

4.妈妈在客厅呆着睡觉,孩子醒了之后会自己出来告诉妈妈醒了:既可以休息,也可以及时的获取还是是否醒了

应用程序中同时处理多路输入输出流,若采用阻塞模式,得不到预期的目的;

若采用非阻塞模式,对多个输入进行轮询,但又太浪费CPU时间;

若设置多个进程/线程,分别处理一条数据通路,将新产生进程/线程间的同步与通信问题,使程序变得更加复杂;

比较好的方法是使用I/O多路复用技术。其(select)基本思想是:

先构造一张有关描述符的表(最大1024),然后调用一个函数。

当这些文件描述符中的一个或多个已准备好进行I/O时函数才返回。

函数返回时告诉进程哪个描述符已就绪,可以进行I/O操作。

select

1.特点
  1. 一个进程最多可以监听1024个文件描述符
  2. select每次被唤醒之后,要重新轮询表,效率低
  3. select每次都会清空未发生响应的文件描述符,每次都要经过用户空间拷贝内核空间,效率低,开销大
2.编程步骤
  1. 构造一张关于文件描述符的表
  2. 清空表 FD_ZERO
  3. 将关心的文件描述符添加到表中 FD_SET
  4. 调用select函数,监听 select
  5. 判断到底是哪一个或者是哪些文件描述符发生了事件 FD_ISSET
  6. 做对应的逻辑处理

3.函数接口
int select(int nfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout);
功能:
	实现IO的多路复用
参数:
	nfds:关注的最大的文件描述符+1
    readfds:关注的读表
	writefds:关注的写表 
	exceptfds:关注的异常表
	timeout:超时的设置NULL:一直阻塞,直到有文件描述符就绪或出错
		时间值为0:仅仅检测文件描述符集的状态,然后立即返回
		时间值不为0:在指定时间内,如果没有事件发生,则超时返回0,并清空设置的时间值struct timeval {long tv_sec;		/* 秒 */long tv_usec;	/* 微秒 = 10^-6秒 */
};返回值:
	成功:准备好的文件描述符的个数
	失败:-1 0:超时检测时间到并且没有文件描述符准备好	注意:
	select返回后,关注列表中只存在准备好的文件描述符
操作表:
void FD_CLR(int fd, fd_set *set); //清除集合中的fd位
void FD_SET(int fd, fd_set *set);//将fd放入关注列表中
int  FD_ISSET(int fd, fd_set *set);//判断fd是否在集合中  是--》1   不是---》0
void FD_ZERO(fd_set *set);//清空关注列表

练习:

练习一:输入鼠标的时候, 响应鼠标事件, 输入键盘的时候, 响应键盘事件 (两路IO)

#include <stdio.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>int main(int argc, char const *argv[])
{char buf[128] = {0};int fd = open("/dev/input/mouse0", O_RDONLY);if (fd < 0){perror("open err");return -1;}// 1.构造一张关于文件描述符的表
    fd_set rfds;while (1){// 2.清空表 FD_ZEROFD_ZERO(&rfds);// 3.将关心的文件描述符添加到表中 FD_SETFD_SET(fd, &rfds); // 鼠标FD_SET(0, &rfds);  // 键盘// 4.调用select函数,监听 selectint ret = select(fd + 1, &rfds, NULL, NULL, NULL);if (ret < 0){perror("select err");return -1;}// 5.判断到底是哪一个或者是哪些文件描述符发生了事件 FD_ISSETif (FD_ISSET(0, &rfds)){// 6.做对应的逻辑处理fgets(buf, sizeof(buf), stdin);printf("keybroad:%s\n", buf);}if (FD_ISSET(fd, &rfds)){read(fd, buf, sizeof(buf));printf("mouse:%s\n", buf);}memset(buf, 0, sizeof(buf));}close(fd);return 0;
}

练习二用select创建并发服务器,可以同时连接多个客户端 (0,sockfd)(12min)

循环服务器:一个客户端可以连接多个客户端,但是不能同时

并发服务器:一个服务器可以同时处理多个客户端的请求

#include <stdio.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{char buf[128] = {0};int acceptfd, ret;int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket err");return -1;}printf("sockfd:%d\n", sockfd); // 3// 2.指定网络信息---------------------------》有号码struct sockaddr_in saddr, caddr;
    saddr.sin_family = AF_INET;            // IPV4
    saddr.sin_port = htons(atoi(argv[1])); // 端口号// saddr.sin_addr.s_addr = inet_addr("192.168.50.13"); // 虚拟机IP// saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
    saddr.sin_addr.s_addr = INADDR_ANY;int len = sizeof(caddr);// 3.绑定套接字(bind)------------------》绑定手机(插卡)if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0){perror("bind err");return -1;}printf("bind ok\n");// 4.监听套接字(listen)-----------------》待机if (listen(sockfd, 6) < 0){perror("listen err");return -1;}printf("listen ok\n");// 1.构造一张关于文件描述符的表
    fd_set rfds, tempfds;int maxfd; // 保存最大的文件描述符// 2.清空表 FD_ZEROFD_ZERO(&rfds);FD_ZERO(&tempfds);// 3.将关心的文件描述符添加到表中 FD_SETFD_SET(sockfd, &rfds); // sockfdFD_SET(0, &rfds);      // 键盘while (1){
        maxfd = sockfd;//将原来的表,复制给新表(备份表)
        tempfds = rfds;// 4.调用select函数,监听 select
        ret = select(maxfd + 1, &tempfds, NULL, NULL, NULL);if (ret < 0){perror("select err");return -1;}// 5.判断到底是哪一个或者是哪些文件描述符发生了事件 FD_ISSETif (FD_ISSET(0, &tempfds)){// 6.做对应的逻辑处理fgets(buf, sizeof(buf), stdin);printf("keybroad:%s\n", buf);}if (FD_ISSET(sockfd, &tempfds)){
            acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);if (acceptfd < 0){perror("accept err");return -1;}printf("port:%d ip:%s\n", ntohs(caddr.sin_port), inet_ntoa(caddr.sin_addr));printf("acceptfd:%d\n", acceptfd);}memset(buf, 0, sizeof(buf));}close(sockfd);return 0;
}

练习三用select创建并发服务器,可以与多个客户端进行通信(监听键盘、socket、多个acceptfd)

#include <stdio.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{char buf[128] = {0};int acceptfd, ret;int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket err");return -1;}printf("sockfd:%d\n", sockfd); // 3// 2.指定网络信息---------------------------》有号码struct sockaddr_in saddr, caddr;
    saddr.sin_family = AF_INET;            // IPV4
    saddr.sin_port = htons(atoi(argv[1])); // 端口号// saddr.sin_addr.s_addr = inet_addr("192.168.50.13"); // 虚拟机IP// saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
    saddr.sin_addr.s_addr = INADDR_ANY;int len = sizeof(caddr);// 3.绑定套接字(bind)------------------》绑定手机(插卡)if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0){perror("bind err");return -1;}printf("bind ok\n");// 4.监听套接字(listen)-----------------》待机if (listen(sockfd, 6) < 0){perror("listen err");return -1;}printf("listen ok\n");// 1.构造一张关于文件描述符的表
    fd_set rfds, tempfds;int maxfd; // 保存最大的文件描述符// 2.清空表 FD_ZEROFD_ZERO(&rfds);FD_ZERO(&tempfds);// 3.将关心的文件描述符添加到表中 FD_SETFD_SET(sockfd, &rfds); // sockfdFD_SET(0, &rfds);      // 键盘
    maxfd = sockfd;while (1){// 将原来的表,复制给新表(备份表)
        tempfds = rfds;// 4.调用select函数,监听 select
        ret = select(maxfd + 1, &tempfds, NULL, NULL, NULL);if (ret < 0){perror("select err");return -1;}// 5.判断到底是哪一个或者是哪些文件描述符发生了事件 FD_ISSETif (FD_ISSET(0, &tempfds)){// 6.做对应的逻辑处理fgets(buf, sizeof(buf), stdin);printf("keybroad:%s\n", buf);}if (FD_ISSET(sockfd, &tempfds)){
            acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);if (acceptfd < 0){perror("accept err");return -1;}printf("port:%d ip:%s\n", ntohs(caddr.sin_port), inet_ntoa(caddr.sin_addr));printf("acceptfd:%d\n", acceptfd);// 将用于通信的文件描述符放到表中FD_SET(acceptfd, &rfds);if (acceptfd > maxfd)
                maxfd = acceptfd;// 4 5 6 7 8 9}for (int i = sockfd + 1; i <= maxfd; i++){if (FD_ISSET(i, &tempfds)){
                ret = recv(i, buf, sizeof(buf), 0);if (ret < 0){perror("recv err");break;}else if (ret == 0){printf("client exit\n");close(i);         // 关闭对应的用于通信的文件描述符FD_CLR(i, &rfds); // 将文件描述符从原表中删除//4 5 6    while (!FD_ISSET(maxfd, &rfds))
                        maxfd--;}else{printf("buf:%s\n", buf);}}}memset(buf, 0, sizeof(buf));}close(sockfd);return 0;
}

超时检测

1.概念

什么是网络超时检测呢,比如某些设备的规定,发送请求数据后,如果多长时间后没有收到来自设备的回复,那么需要做出一些特殊的处理

比如: 链接wifi的时候,等了好长时间也没有连接上,此时系统会发送一个消息: 网络连接失败;

2.必要性

1. 避免进程在没有数据时无限制的阻塞;

2.规定时间未完成语句应有的功能,则会执行相关功能;

poll

1.特点
  1. 优化了文件描述符的限制
  2. poll每次唤醒之后,需要重新轮询,效率低,耗费CPU
  3. poll不需要构造文件描述符的表,采用结构体数组,每次调用也要经过用户空间到内核空间的拷贝

2.编程步骤
  1. 创建结构体数组
  2. 将关心的文件描述符添加到数组中,并赋予事件
  3. 保存数组内最后一个有效元素的下标
  4. 调用poll函数,监听
  5. 判断结构体内文件描述符实际触发的事件
  6. 根据不同文件描述符触发的不同事件做对应的逻辑处理

3.函数接口
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
功能: 监视并等待多个文件描述符的属性变化
参数:1.struct pollfd *fds:   关心的文件描述符数组,大小自己定义
   若想检测的文件描述符较多,则建 立结构体数组struct pollfd fds[N]; struct pollfd{int fd;	 //文件描述符short events;//等待的事件触发条件----POLLIN读时间触发short revents;	//实际发生的事件(未产生事件: 0 ))}2.   nfds:    最大文件描述符个数3.  timeout: 超时检测 (毫秒级)1000 == 1s      
                    如果-1,阻塞          如果0,不阻塞
返回值:  <0 出错		>0 表示有事件产生;
              如果设置了超时检测时间:&tv	   ==0 表示超时时间已到;

练习:

输入键盘事件,响应键盘事件,输入鼠标事件,响应鼠标事件(两路IO)

#include <stdio.h>
#include <poll.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>int main(int argc, char const *argv[])
{int ret;char buf[128] = {0};int fd = open("/dev/input/mouse0", O_RDONLY);if (fd < 0){perror("open err");return -1;}// 1.创建结构体数组struct pollfd fds[2];// 2.将关心的文件描述符添加到数组中,并赋予事件
    fds[0].fd = 0;          // 键盘
    fds[0].events = POLLIN; // 想要发生的事件// fds[0].revents=;//实际发生的事件    fds[1].fd = fd;
    fds[1].events = POLLIN;// 3.保存数组内最后一个有效元素的下标int last = 1;// 4.调用poll函数,监听while (1){        ret = poll(fds, last + 1, 2000);if (ret < 0){perror("poll err");return -1;}else if (ret == 0){printf("time out\n");}// 5.判断结构体内文件描述符实际触发的事件if (fds[0].revents == POLLIN){// 6.根据不同文件描述符触发的不同事件做对应的逻辑处理fgets(buf, sizeof(buf), stdin);printf("keybroad:%s\n", buf);}if (fds[1].revents == POLLIN){read(fd, buf, sizeof(buf));printf("mouse:%s\n", buf);}memset(buf, 0, sizeof(buf));}close(fd);return 0;
}

练习:使用poll实现client的收发功能(下期更新答案)

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

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

相关文章

WebGL系列教程五(使用索引绘制彩色立方体)

目录 1 前言2 立方体3 开始绘制3.1 声明顶点和颜色3.2 使用索引绘制3.3 效果3.4 完整代码 4 总结 1 前言 上一讲我们讲了如何绘制彩色的三角形&#xff0c;这一讲我们来说如何绘制立方体。为什么几乎所有的WebGL教程总是从开始绘制三角形开始&#xff0c;因为三角形是最小的面&…

服务器模型 Reactor 和 Proactor

Proactor 具体流程如下&#xff1a; 处理器发起异步操作&#xff0c;并关注 IO 完成事件&#xff1b;事件分离器等待操作完成事件&#xff1b;分离器等待过程中&#xff0c;内核并行执行实际的 IO 操作&#xff0c;并将结果存储入用户自定义的缓冲区&#xff0c;最后通知事件分…

【C++】C++ STL 探索:List使用与背后底层逻辑

C语法相关知识点可以通过点击以下链接进行学习一起加油&#xff01;命名空间缺省参数与函数重载C相关特性类和对象-上篇类和对象-中篇类和对象-下篇日期类C/C内存管理模板初阶String使用String模拟实现Vector使用及其模拟实现 本文将通过模拟实现List&#xff0c;从多个角度深入…

60 道 MySQL 精选面试题

基础 #1、关系型和非关系型数据库的区别&#xff1f; 关系型数据库的优点 容易理解&#xff0c;因为它采用了关系模型来组织数据。可以保持数据的一致性。数据更新的开销比较小。支持复杂查询&#xff08;带 where 子句的查询&#xff09; 非关系型数据库&#xff08;NOSQL…

微服务-nacos

nacos-注册中心 启动 服务注册到nacos

【Unity踩坑】创建新项目后提示编译错误要进入安全模式

在创建了新项目后&#xff08;比如URP&#xff0c;AR&#xff0c;VR&#xff09;&#xff0c;首次打开时提示有编译错误&#xff0c;要进入安全模式。 脚本是项目模板自带的&#xff0c;不会有问题。这时需要先选择进入安全模式&#xff0c;然后关闭项目&#xff0c;重新打开就…

9月12号作业

主要更改的代码 void Widget::read_solt() { QByteArray msg socket->readAll();//接受信息 if(QString::fromLocal8Bit(msg)msg2||msg3QString::fromLocal8Bit(msg)||msg6QString::fromLocal8Bit(msg)) { QListWidgetItem *listItem new QListWidgetItem(QString::fromL…

网络视频流解码显示后花屏问题的分析

问题描述 rtp打包的ps视频流发送到客户端后显示花屏。 数据分析过程 1、用tcpdump抓包 tcpdump -i eth0 -vnn -w rtp.pcap 2、用wireshark提取rtp的payload 保存为record.h264文件 3、用vlc播放器播放 显示花屏 4、提取关键帧 用xxd命令将h264文件转为txt文件 xxd -p…

2 创建会计科目表

定义解释 在SAP系统中&#xff0c;会计科目表是财务系统的基础数据之一&#xff0c;对于企业的财务核算和财务管理至关重要 定义&#xff1a;会计科目表是SAP系统中用于定义和分类总账科目的集合&#xff0c;它包含了所有需要在财务系统中记录和管理的会计科目。这些科目按照…

工厂安灯系统在设备管理中的重要性

在现代制造业中&#xff0c;设备管理是确保生产效率和产品质量的关键环节。随着工业4.0的推进&#xff0c;越来越多的企业开始采用智能化的设备管理系统&#xff0c;其中安灯系统作为一种有效的管理工具&#xff0c;逐渐受到重视。安灯系统最初源于日本的丰田生产方式&#xff…

内存管理篇-23 二级页表的创建过程-上

二级页表的填充过程&#xff1a;一级页表是4096项&#xff08;用虚拟地址高12位&#xff09;&#xff0c;二级页表是256项&#xff08;用虚拟地址中间10位&#xff0c;每个项能代表一个物理页&#xff0c;因此4KB*2561MB&#xff09;。因此&#xff0c;每个二级页表映射1MB的空…

Java stream使用与执行原理

stream简介 Stream: A sequence of elements supporting sequential and parallel aggregate operations stream为sequential即单线程串行操作&#xff0c;parallelStream支持并行操作&#xff0c;本文只讨论sequential的stream。 stream常用操作 Datastatic class Course {pr…

HarmonyOS应用开发( Beta5.0)HOS-用户认证服务:面部识别

介绍 User Authentication Kit&#xff08;用户认证服务&#xff09;提供了基于用户在设备本地注册的人脸和指纹来认证用户身份的能力。 用户向应用/系统服务请求访问某些个人数据或执行某些敏感操作时&#xff0c;应用/系统服务将调用系统用户身份认证控件对用户身份进行认证…

MySQL之库和表操作

目录 一&#xff1a;对库的操作 1.创建数据库 2.查看数据库列表 3.显示创建数据库的语句 4.删除数据库 5.字符集与校验集 6.确认当前所处的数据库 7.修改数据库 8.备份和恢复 9.查看连接情况 二:对表的操作 1.创建表 2.查看表 3.删除表 4.修改表 接下来的日…

Java 设计模式-状态模式

目录 一. 概述 二. 主要角色 三. 代码示例 四. 优缺点 优点&#xff1a; 缺点&#xff1a; 五. 常见应用场景 一. 概述 状态模式是一种行为设计模式&#xff0c;它允许一个对象在其内部状态改变时改变它的行为。对象看起来好像修改了它的类。状态模式把所有的与一个特定…

信息安全工程师(1)计算机网络分类

一、按分布范围分类 广域网&#xff08;WAN&#xff09;&#xff1a; 定义&#xff1a;广域网的任务是提供长距离通信&#xff0c;运送主机所发送的数据。其覆盖范围通常是直径为几十千米到几千千米的区域&#xff0c;因此也被称为远程网。特点&#xff1a;连接广域网的各个结点…

利用高德+ArcGIS优雅获取任何感兴趣的矢量边界

荷花十里&#xff0c;清风鉴水&#xff0c;明月天衣。 四时之景不同&#xff0c;乐亦无穷尽也。今天呢&#xff0c;梧桐君给大家讲解一下&#xff0c;如何利用高德地图&#xff0c;随机所欲的获取shp边界数据。 文章主要分成以下几个步骤&#xff1a; 首先搜索你想获取的矢量…

【文献阅读】Unsupervised Machine Learning for Bot Detection on Twitter

Abstract 引入新特征&#xff0c;并降低所提模型的复杂性&#xff0c;从而提高基于聚类算法的机器人识别准确性。 最小化数据集维度和选择重要特征来实现的。 实验证明该方法的特征可以与四种不同的聚类技术&#xff08;agglomerating、k-medoids、DBSCAN 和 K-means&#x…

企业数字化转型、建设和升级面临的主要难题和解决之道(2)

用爱编程30年&#xff0c;倾心打造工业和智能智造软件研发平台SCIOT,用创新的方案、大幅的让利和极致的营销&#xff0c;致力于为10000家的中小企业实现数字化转型&#xff0c;打造数字化企业和智能工厂&#xff0c;点击上边蓝色字体&#xff0c;关注“AI智造AI编程”或文末扫码…