Linux 下的IO模型

一:四种IO模

1.1:阻塞式IO(最简单,最常用,效率最低)

阻塞I/O 模式是最普遍使用的I/O 模式,大部分程序使用的都是阻塞模式的I/O 。
缺省情况下(及系统默认状态),套接字建立后所处于的模式就是阻塞I/O 模式。
学习的读写函数在调用过程中会发生阻塞。相关函数如下:
•读操作中的read、recv、recvfrom读阻塞--》需要读缓冲区中有数据可读,读阻塞解除
•写操作中的write、send
写阻塞--》阻塞情况比较少,主要发生在写入的缓冲区的大小小于要写入的数据量的情况下,写操作不进行任何拷贝工作,将发生阻塞,一旦缓冲区有足够的空间,内核将唤醒进程,将数据从用户缓冲区拷贝到相应的发送数据缓冲区。 
注意:sendto没有写阻塞

1.2:非阻塞式IO(可以处理多路IO,需要轮询)

1.2.1:非阻塞式IO的设置
1)通过函数参数设置

Recv函数最后一个参数写为0,为阻塞,写为MSG_DONTWAIT:表示非阻塞

1.2.2:通过fctnl函数设置 
int fcntl(int fd, int cmd, ... /* arg */ );
功能:获取/设置文件描述符属性    状态属性(O_RDONLY  O_NONBLOCK非阻塞)
参数:fd:文件描述符cmd:功能选择   状态属性: F_GETFL  :获取文件描述符原来的属性F_SETFL  :设置文件描述符属性arg:根据cmd决定是否填充值   int
返回值:失败:-1成功:F_GETFL - 返回值的文件描述符号属性的值 intF_SETFL   0
  int flag;flag=fcntl(0,F_GETFL);//获取文件描述符原属性flag |= O_NONBLOCK;//添加非阻塞属性// flag &= ~O_NONBLOCK;//取消非阻塞属性fcntl(0,F_SETFL,flag);//将新属性设置回去  

二:信号驱动IO(异步IO)

特点:异步通知模式,需要底层驱动的支持

//将APP进程号告诉驱动程序
fcntl(fd, F_SETOWN, getpid());//使能异步通知
int flag;
flag = fcntl(fd,F_GETFL);
flag|= O_ASYNC ;
fcntl(fd,F_SETFL,flag);signal(SIGIO,handler)

例子:鼠标键盘事件:

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <signal.h>
int fd;
void handler(int num) // 信号处理
{char buf[128]={};int ret = read(fd,buf,sizeof(buf)-1);buf[ret]='\0';printf("buf:%s\n",buf);}
int main(int argc, char const *argv[])
{// 打开文件fd = open("/dev/input/mouse0", O_RDONLY);if (fd < 0){perror("open err");return -1;}// 将进程号告诉驱动fcntl(fd, F_SETOWN, getpid());// 开启异步通知int flag;flag = fcntl(fd,F_GETFL);flag |= O_ASYNC;fcntl(fd,F_SETFL,flag);// 收到信号,调用函数signal(SIGIO,handler);while (1){printf("welcome to hqyj\n");sleep(1);}return 0;
}

三:IO多路复用

3.1:实现方式

3.1.1:select函数

 int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);功能:select用于监测是哪个或哪些文件描述符产生事件;参数:nfds:    监测的最大文件描述个数(这里是个数,使用的时候注意,与文件中最后一次打开的文件描述符所对应的值的关系是什么?)readfds:  读事件集合; //读(用的多)writefds: 写事件集合;  //NULL表示不关心exceptfds:异常事件集合;  timeout:超时检测 如果不做超时检测:传 NULL 如果设置了超时检测时间:&tv返回值:<0 出错>0 表示有事件产生;==0 表示超时时间已到;struct timeval {long    tv_sec;         /* seconds */long    tv_usec;        /* microseconds */};void FD_CLR(int fd, fd_set *set);//将fd从表中清除int  FD_ISSET(int fd, fd_set *set);//判断fd是否在表中void FD_SET(int fd, fd_set *set);//将fd添加到表中void FD_ZERO(fd_set *set);//清空表
3.1.2:实现流程

3.1.3:select并发式服务器的实现
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>char buf[1024] = {};int main(int argc, char const *argv[])
{// 创建套接字int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket create err\n");return -1;}// 填充结构体struct sockaddr_in addr, caddr;addr.sin_family = AF_INET;   // IPV4addr.sin_port = htons(8881); // 主机字节序转换为网络字节序addr.sin_addr.s_addr = inet_addr("192.168.31.88");int len = sizeof(caddr);// 绑定int ret;if (ret = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0){perror("bind err\n");return -1;}// 监听if (listen(sockfd, 5) < 0){perror("listen err\n");return -1;}// 创建表fd_set readfds, tempfd;FD_ZERO(&readfds);// 讲关心的文件描述符添加到表中FD_SET(0, &readfds);FD_SET(sockfd, &readfds);int maxfd = sockfd;while (1){tempfd = readfds;int ret = select(maxfd + 1, &tempfd, NULL, NULL, NULL);if (FD_ISSET(0, &tempfd)){fgets(buf, sizeof(buf), stdin);if (buf[strlen(buf) - 1] == '\n')buf[strlen(buf) - 1] = '\0';printf("key:%s\n", buf);// 下面的文件描述符发生变化时,需要将数组里存储的内容重新发送for (int i = sockfd + 1; i <= maxfd; i++){// 判断其有没有在表中if (FD_ISSET(i, &readfds)){send(i, buf, sizeof(buf), 0);}}}if (FD_ISSET(sockfd, &tempfd)){// 任何客户端都可以 ,返回通信文件描述符int acceptfd = accept(sockfd, (struct sockaddr *)&addr, &len);if (acceptfd < 0){perror("accept err\n");return -1;}// 来电显示printf("通信文件描述符:%d\t", acceptfd);printf("客户端端口:%d\t客户端ip:%s\n", ntohs(addr.sin_port), inet_ntoa(addr.sin_addr));// close(acceptfd);// 重新创表FD_SET(acceptfd, &readfds);// 更新文件描述符if (maxfd < acceptfd){maxfd = acceptfd;}}for (int i = 4; i <= maxfd; i++){if (FD_ISSET(i, &tempfd)){int rec = recv(i, buf, sizeof(buf), 0);if (rec < 0){perror("recv err\n");return -1;}else if (rec == 0){printf("i==客户端退出:%d\n", i);// break;// 链接关闭之后,关闭其所在的文件描述符,用时close(i);FD_CLR(i, &readfds);if (i == maxfd){maxfd--;}exit(1);}else{printf("%s\n", buf);memset(buf, 0, sizeof(buf));}}}}// 关闭文件描述符close(sockfd);return 0;
}

select实现io多路复用的特点:

  1. 一个进程最多只能监听1024个文件描述符 (千级别)FD_SETFILE
  2. select被唤醒之后需要重新轮询一遍驱动的poll函数,效率比较低(消耗CPU资源);
  3. select每次会清空表,每次都需要拷贝用户空间的表到内核空间,效率低

 

3.2:poll函数

int poll(struct pollfd *fds, nfds_t nfds, int timeout);参数:struct pollfd *fds关心的文件描述符数组struct pollfd fds[N];nfds:个数timeout: 超时检测毫秒级的:如果填1000,1秒如果-1,阻塞struct pollfd {int   fd;         /* 检测的文件描述符 */short events;     /* 检测事件 */short revents;    /* 调用poll函数返回填充的事件,poll函数一旦返回,将对应事件自动填充结构体这个成员。只需要判断这个成员的值就可以确定是否产生事件 */};事件:     POLLIN :读事件POLLOUT : 写事件POLLERR:异常事件

3.2.1:poll函数与select函数的区别

流程

select

poll

1.建立一个文件描述符的表

fd_set线性表

struct pollfd fds[n]结构体数组

2.将关心的文件描述符加到表中

FD_SET(fd,&readfds)

结构体内容填充fds[m].fd= fd

fds[m].events=POLLIN

3. 然后调用一个函数。 select / poll

4. 当这些文件描述符中的一个或多个已准备好进行I/O操作的时候

该函数才返回(阻塞)

select

poll

5.判断

FD_ISSET

revents==POLLIN

6.相关操作

 3.2.2:函数实现
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <poll.h>char buf[1024] = {};int main(int argc, char const *argv[])
{int fd = open("/dev/input/mouse0", O_RDONLY);if (fd < 0){perror("open faild\n");return -1;}// 创建结构体数组struct pollfd fds[1024] = {};// 添加文件描述符fds[0].fd = 0;fds[0].events = POLLIN;fds[1].fd = fd;fds[1].events = POLLIN;int last = 1;while (1){poll(fds,last+1, -1);for (int i = 0; i <= last; i++){if (fds[i].revents == POLLIN){if (fds[i].fd == 0){// scanf("%s", buf);fgets(buf, sizeof(buf), stdin);if (buf[strlen(buf) - 1] == '\n'){buf[strlen(buf) - 1] = '\0';}printf("。。。。。。:%s\n", buf);}if (fds[i].fd == fd){read(fd, buf, sizeof(buf));printf(".......:%s\n", buf);}}}}return 0;
}

3.2.3:特点

 优化文件描述符个数的限制;(根据poll函数第一个参数来定,如果监听的事件为1个,则结构体数组元素个数为1,如果想监听100个,那么这个结构体数组的元素个数就为100,由程序员自己来决定)

poll被唤醒之后需要重新轮询一遍驱动的poll函数,效率比较低

poll不需要重新构造文件描述符表,只需要从用户空间向内核空间拷贝一次数据即可

3.3:epoll函数 

特点:

  1. 监听的最大的文件描述符没有个数限制(理论上,取决与你自己的系统)
  2. 异步I/O,Epoll当有事件产生被唤醒之后,文件描述符主动调用callback(回调函数)函数直接拿到唤醒的文件描述符,不需要轮询,效率高
  3. epoll不需要重新构造文件描述符表,只需要从用户空间向内核空间拷贝一次数据即可.

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

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

相关文章

vue3项目部署在阿里云轻量应用服务器上

文章目录 概要整体部署流程技术细节小结 概要 vue3前端项目部署在阿里云轻量服务器 整体部署流程 首先有一个Vue3前端项目和阿里云应用服务器 确保环境准备 如果是新的服务器&#xff0c;在服务器内运行以下命令更新软件包 sudo apt update && sudo apt upgrade -y …

tcpdump交叉编译

TCPDUMP在Libpcap上开发。 首先需要编译libcap。 网上那么多教程&#xff0c;下载地址都只给了一个英文的官网首页&#xff0c; 你尽可以试试&#xff0c;从里面找到下载地址都要费半天时间。 \color{red}网上那么多教程&#xff0c;下载地址都只给了一个英文的官网首页&#…

KubeSphere 最佳实战:K8s 构建高可用、高性能 Redis 集群实战指南

首发&#xff1a;运维有术。 本指南将逐步引导您完成以下关键任务&#xff1a; 安装 Redis&#xff1a;使用 StatefulSet 部署 Redis。自动或手动配置 Redis 集群&#xff1a;使用命令行工具初始化 Redis 集群。Redis 性能测试&#xff1a;使用 Redis 自带的 Benchmark 工具进…

02 python基础 python解释器安装

首先在网站&#xff1a;Welcome to Python.org进行下载安装python 最新的解释器不一定是最好的&#xff0c;最稳定的才一定是最好的&#xff1b;要关注解释器最后维护 的时间。 一、python的安装 python安装的时候一定要在下载勾选好添加path环境 安装的时候尽量选择好自己的安…

java编程开发基础,正则表达式的使用案例Demo

java编程开发基础,正则表达式的使用案例Demo!实际开发中&#xff0c;经常遇到一些字符串&#xff0c;信息的裁剪和提取操作&#xff0c;正则表达式是经常使用的&#xff0c;下面的案例&#xff0c;可以帮助大家快速的了解和熟悉&#xff0c;正则表达式的使用技巧。 package com…

Windows Pycharm 远程 Spark 开发 PySpark

一、环境版本 环境版本PyCharm2024.1.2 (Professional Edition)Ubuntu Kylin16.04Hadoop3.3.5Hive3.1.3Spark2.4.0 二、Pycharm远程开发 文件-远程-开发 选择 SSH连接&#xff0c;连接虚拟机&#xff0c;选择项目目录即可远程开发

WebGL进阶(十一)层次模型

理论基础&#xff1a; 效果&#xff1a; 源码&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"vie…

【H2O2|全栈】JS进阶知识(九)ES6(5)

目录 前言 开篇语 准备工作 class类 概念 形式 直接继承 概念 优点 案例 重写 概念 案例 关于重载 结束语 前言 开篇语 本系列博客主要分享JavaScript的进阶语法知识&#xff0c;本期为第九期&#xff0c;依然围绕ES6的语法进行展开。 本期内容为&#xff1a…

Prompting LLMs to Solve Complex Tasks: A Review

文章目录 题目简介任务分解未来方向结论 题目 促使 LLM 解决复杂任务&#xff1a; 综述 论文地址&#xff1a;https://www.intjit.org/cms/journal/volume/29/1/291_3.pdf 简介 大型语言模型 (LLM) 的最新趋势显而易见&#xff0c;这体现在大型科技公司的投资以及媒体和在线社…

学会Lambda,让程序Pythonic一点

Lambda是Python里的高阶用法&#xff0c;要把代码写得Pythonic&#xff0c;就需要了解这些高阶用法&#xff0c;想说自己是一名真正的Python程序员&#xff0c;先要把代码写得Pythonic。 今天聊下Lambda的用法&#xff0c;写篇简短的用法说明。 Lambda是匿名函数的意思&#…

加速科技精彩亮相中国国际半导体博览会IC China 2024

11月18日—20日&#xff0c;第二十一届中国国际半导体博览会&#xff08;IC China 2024&#xff09;在北京国家会议中心顺利举办&#xff0c;加速科技携重磅产品及全系测试解决方案精彩亮相&#xff0c;加速科技创始人兼董事长邬刚受邀在先进封装创新发展论坛与半导体产业前沿与…

window11编译pycdc.exe

一、代码库和参考链接 在对python打包的exe文件进行反编译时&#xff0c;会使用到uncompyle6工具&#xff0c;但是这个工具只支持python3.8及以下&#xff0c;针对更高的版本的python则不能反编译。 关于反编译参考几个文章&#xff1a; Python3.9及以上Pyinstaller 反编译教…

【深度学习之回归预测篇】 深度极限学习机DELM多特征回归拟合预测(Matlab源代码)

深度极限学习机 (DELM) 作为一种新型的深度学习算法&#xff0c;凭借其独特的结构和训练方式&#xff0c;在诸多领域展现出优异的性能。本文将重点探讨DELM在多输入单输出 (MISO) 场景下的应用&#xff0c;深入分析其算法原理、性能特点以及未来发展前景。 1、 DELM算法原理及其…

前端-react(class组件和Hooks)

文章主要以Hooks为主,部分涉及class组件方法进行对比 一.了解react 1.管理组件的方式 在React中&#xff0c;有两种主要的方式来管理组件的状态和生命周期&#xff1a;Class 组件和 Hooks。 Class 组件&#xff1a; Class 组件是 React 最早引入的方式&#xff0c;它是基于…

Ollama vs VLLM:大模型推理性能全面测评!

最近在用本地大模型跑实验&#xff0c;一开始选择了ollama,分别部署了Qwen2.5-14B和Qwen2.5-32B&#xff0c;发现最后跑出来的实验效果很差&#xff0c;一开始一直以为prompt的问题&#xff0c;尝试了不同的prompt&#xff0c;最后效果还是一直不好。随后尝试了vllm部署Qwen2.5…

基于深度学习CNN算法的花卉分类识别系统01--带数据集-pyqt5UI界面-全套源码

文章目录 基于深度学习算法的花卉分类识别系统一、项目摘要二、项目运行效果三、项目文件介绍四、项目环境配置1、项目环境库2、环境配置视频教程 五、项目系统架构六、项目构建流程1、数据集2、算法网络Mobilenet3、网络模型训练4、训练好的模型预测5、UI界面设计-pyqt56、项目…

【PCIE常见面试问题-1】

PCIE常见面试问题-1 1 PCIE概述1.1 PCI为何发展开PCIE&#xff1f;1.2 什么是Root Complex(RC)1.3 什么是EP&#xff1f;1.4 什么是Swith1.5 PCIE协议如何组织通信的&#xff1f;1.6 简要介绍一下PCIE的分层结构&#xff0c;为什么需要分层&#xff1f;1.7 PCIE的事务类型有哪些…

解决 Docker Desktop 启动报错:Docker Desktop is unable to detect a Hypervisor

在使用 Docker Desktop 时&#xff0c;有时会遇到启动报错&#xff1a;“Docker Desktop is unable to detect a Hypervisor.” 这是由于系统的虚拟化功能未正确启用或配置导致的。本文将分步骤指导如何解决该问题。 一、检查虚拟化是否已启用 打开任务管理器 按下 Ctrl Shift…

订单日记为“惠采科技”提供全方位的进销存管理支持

感谢温州惠采科技有限责任公司选择使用订单日记&#xff01; 温州惠采科技有限责任公司&#xff0c;成立于2024年&#xff0c;位于浙江省温州市&#xff0c;是一家以从事销售电气辅材为主的企业。 在业务不断壮大的过程中&#xff0c;想使用一种既能提升运营效率又能节省成本…

rust中解决DPI-1047: Cannot locate a 64-bit Oracle Client library问题

我们在使用rust-oracle crate连接oracle进行测试的过程中&#xff0c;会发现无法连接oracle&#xff0c;测试运行过程中抛出“DPI-1047: Cannot locate a 64-bit Oracle Client library”错误。该问题是由于rust-oracle需要用到oracle的动态连接库&#xff0c;我们通过安装orac…