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;下载地址都只给了一个英文的官网首页&#…

linux基本命令(1)

1. 文件和目录操作 ls — 列出目录内容 ls # 显示当前目录的文件和目录 ls -l # 显示详细的文件信息&#xff08;权限、大小、修改时间等&#xff09; ls -a # 显示所有文件&#xff08;包括隐藏文件&#xff09; ls -lh # 显示详细信息并以易读的方式显示文件大小 cd — 改…

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

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

使用 Python 实现目标检测

目录 简介环境准备数据集模型选择预处理模型加载与推理结果可视化优化与调参部署与应用参考资料 简介 目标检测是计算机视觉中的一个重要任务&#xff0c;旨在识别图像或视频中的特定对象并标注它们的位置。近年来&#xff0c;深度学习技术的发展使得目标检测的准确性和效率…

【人工智能】Python与Scikit-learn的模型选择与调参:用GridSearchCV和RandomizedSearchCV提升模型性能

解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 在机器学习建模过程中,模型的表现往往取决于参数的选择与优化。Scikit-learn提供了便捷的工具GridSearchCV和RandomizedSearchCV,帮助我们在参数空间中搜索最佳组合以提升模型表现。本文将从理论和实践两个角度…

Oracle-索引的创建和优化

-- Oracle数据库会为表的主键和包含唯一约束的列自动创建索引 -- 索引种类 -- 普通索引 create index idx_emp_index_sal on emp_index(sal); -- 唯一索引 create unique index uq_idx_emp_index_ename on emp_index(ename); -- 组合索引 create index idx_…

02 python基础 python解释器安装

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

视频截断,使用 FFmpeg

使用 FFmpeg 截取视频并去掉 5 分 49 秒后的内容&#xff0c;可以使用以下命令&#xff1a; ffmpeg -i input.mp4 -t 00:05:49 -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 192k output.mp4-i input.mp4&#xff1a; 指定输入视频文件 input.mp4。 -t 00:05:49&#x…

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…

【自动化】如何从列表中找到图片并命名保存下来

以下是对这段 Python 代码的分析&#xff1a; 代码功能概述 这段代码主要使用了 DrissionPage 库&#xff08;看起来是用于自动化网页操作相关的库&#xff09;来与浏览器&#xff08;基于 Chromium 内核&#xff09;进行交互&#xff0c;实现以下功能&#xff1a; 打开豆瓣…

01 认识python

# 1.什么是编程&#xff0c;什么是编程语音&#xff1f; # 编程&#xff1a;用代码编程程序 # 编程语言&#xff1a;用那种语法规则来编写程序 # a 10 > 10100101(exe) # # # 2.编程语言分类&#xff1a;C语言 GO语言 # 1&#xff0c;编译型&#xff1a;一次性把源代码进行翻…

如何在Linux系统中排查GPU上运行的程序

如何在Linux系统中排查GPU上运行的程序 在Linux系统中&#xff0c;随着深度学习和高性能计算的普及&#xff0c;GPU资源的管理和监控变得越来越重要。当您遇到GPU资源不足或性能下降的问题时&#xff0c;需要能够快速定位并解决这些问题。本文将介绍几种常用的方法来帮助您排查…

【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 反编译教…