Linux之基础IO(上)

目录

库函数文件操作

写文件

读文件

系统调用文件操作 

写文件

读文件

文件描述符fd 

深刻理解linux下一切皆文件

重定向原理 


在c语言中我们学习了fopen,fread,fwrite接口,用于进行文件相关的操作,在之前我们学习了计算机的相关结构,由下往上依次为硬件层,驱动层,操作系统层,系统调用层,用户层,c语言中的接口是处于用户层的,用户层是不能直接跨过操作系统从而对对应的硬件设备进行读写操作,必须从上往下贯穿整个操作系统才能进行读写操作,本期我们将详细学习这一部分知识的基本原理。

库函数文件操作

写文件

#include<stdio.h>
#include<string.h>
int main()
{FILE* fp=fopen("./log.txt","w");if(!fp){printf("open fail\n");}else
{const char* msg="hello world\n";
//fwrite为实际写了多少字节的数据fwrite(msg,strlen(msg),1,fp); }fclose(fp);return 0;
}

fopen打开文件之后,返回的是一个文件指针,文件指针指向了一个文件结构体,文件结构体里包含了打开的文件相关信息。

读文件

int main()
{FILE* fp=fopen("./log.txt","r");if(!fp){printf("open fail\n");}else{char buf[1024];const char* msg="hello yjd\n";//一次读取一个字节,msg的一个元素一个字节,总共读取strlen(msg)个字节,返回值为实际读到的字节的个数。ssize_t s= fread(buf,1,strlen(msg),fp); if(s){printf("%s\n",buf); }}fclose(fp);return 0;
}

log.txt中的文件。

 代码中的ptintf想必大家都很熟悉,就是将要打印的文件打印到标准输出上,那么什么是标准输出呢?下来我们一一进行解答。

在C中我们有三个输出流,标准输入标准输出和标准错误,分别对应了键盘文件,显示器文件和显示器文件。

不难发现这三个流的返回类型也是FILE*,我们发现打开文件的返回类型也是FILE*,这个FILE*究竟是何种神圣呢?

其实,流中的操作其实都可以理解为对流文件的读写操作,标准输入流就是从键盘文件中进行读取,标准输出流和标准错误流都是往显示器文件中进行写入操作。所有的读取和写入的前提都是先打开文件。既然进程要打开文件,那么操作系统为了进行管理就必须创建打开文件对应的数据结构,从而对打开的文件进行管理,也即我们一直所讲述的先描述后组织。所以fopen和三个流的返回值类型为FILE*也就可以理解了,在C++中也一样,stdin,stdout和stderr分别对应cin,cout和cerr。

上述的对文件的读写操作都是站在用户的角度对文件进行读写的,实质上是对系统调用文件读写接口进行了封装,下来我们将学习系统调用文件读写接口。 

系统调用文件操作 

上一个标题我们使用的是库函数中的文件接口进行文件操作,实际上库函数接口的实现往往是基于系统调用接口。

写文件

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main()
{int fd=open("./log.txt",O_WRONLY|O_CREAT,0644);if(fd<0){printf("open err\n");return 1;}else{const char* msg="hello YJD\n";int len=strlen(msg);//write函数的返回值是实际写了多少字节的数据write(fd,msg,len);}close(fd);return 0;
}

代码中的fd表示的是文件描述符,至于什么是文件描述符,我们下面会讲到。 

读文件

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main()
{int fd=open("./log.txt",O_RDONLY);if(fd<0){printf("open err\n");return 1;}else{char buff[1024];const char* msg="hello YJD\n";int len=strlen(msg);//read函数的返回值是实际读了多少字节的数据ssize_t s = read(fd,buff,len);if(s<0){printf("read err\n");return 1;}else{printf("%s",buff);}}close(fd);return 0;
}

以上便是系统调用文件操作的相关接口。

文件描述符fd 

在上个标题我们提出了文件描述符的概念,那么究竟什么是文件描述符呢?

在一个文件没有被打开时,这个文件是在磁盘上的,当一个文件被进程打开时,就被加载到了内存中,我们知道操作系统是系统软件和硬件的管理者,当一个文件被打开时,也就意味着一个软件被打开,所以操作系统要管理被打开的文件,就得请出我们的六字真言“先描述,在组织”,所以当一个文件被打开加载到内存时,操作系统就得为这些打开的文件创建对应的数据结构,从而方便进行管理。被打开的文件的数据结构我们用 struct file来表示,每打开一个文件,就为这个文件创建对应的struct file结构体对象,这个数据结构中存储的是对应的打开的文件的相关属性数据。以图示为大家进一步解释。

 进程相关的属性和数据存储在进程控制块中。进程再打开文件时,一次可以打开多个文件,那么进程如何知道自己打开的多个文件是什么文件呢,所以在进程控制块中有一个struct files_struct*的结构体指针,指向了struct files_struct结构体,在这个结构体里面有一个指针数组,数组的每一个元素都是一个struct file*类型的结构体指针,分别指向了该进程在内存中打开的文件。所以进程通过这样一个结构体指针的方式能够清晰的知道自己打开了哪些文件。

在图示中我们会发现一个struct file* fd_arry[fd]的指针数组,我们上述所讨论的文件描述符就是这个数组的下标,所以当进程在内存中打开一个文件,对应就会在指针数组中分配一个下标,让该下标所对应的元素指向被打开的文件。

阅读下面的代码。

#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main()
{int fd = open("./log.txt",O_RDONLY);printf("%d\n",fd);return 0;
}

通过运行截图我们不难看出,这个fd的值竟然是3,为什么是3呢?下标难道不应该是从0开始的吗?

我们之前提到了标准输入stdin(键盘文件),标准输出stdout(显示器文件),标准错误stderr(显示器文件)。其实,fd就是从0开始的,但是我们所有的进程都是1号进程bash的子进程,bash是终端进程,终端就意味着要输入命令,要输出结果,要输出错误。所以bash终端进程就默认会打开键盘文件和显示器文件,所以操作系统也就会为bash进程默认分配0,1,2这个文件描述符,对应打开的键盘文件,显示器文件和显示器文件。这里还有一个知识点,就是所有的子进程都会继承父进程的数据结构,所以父子进程的 struct files_struct结构体也是两份相同的数据结构,所以对应的指针数组也是相同的,因为所有进程都是bash的子进程,所以这些进程的指针数组和bash进程的指针数组也是相同的,也就意味着所有的进程都会默认打开三个对应的文件,都会默认占用0,1,2这三个文件描述符。

深刻理解linux下一切皆文件

图示如下。

上层通过open接口打开文件,通过write和read接口进行文件的读写操作,但是实际上这个write和read接口有很多种,因为文件的种类有很多种。struct file是打开的文件的数据结构,这些数据结构中对应了多种文件,键盘文件,显示器文件,磁盘文件,显卡文件,其它文件。上层open打开一个文件就会为这个打开的文件分配一个文件描述符,后期通过文件描述符找到对应打开的文件,然后调用write和read方法进行读写,write方法和read方法对于每种文件是不一样的,通过虚拟文件层的函数指针找到具体种类文件的读写方法,这些读写方法存在于操作系统中的驱动层。所以上层的read和write方法就类似于后期我们学习的C++语法中的多态的基本概念,每种文件的读写方法对系统调用的读写方法进行了重写,在调用读写方法时,可以根据不同的文件种类调用不同文件种类的读写方法。

综上,操作系统是不考虑打开的文件是哪种类型文件的,在虚拟文件层都把不同种类的文件统一用struct file类型的结构体进行了描述,基于此,我们称在linux操作系统下,一切皆文件。

重定向原理 

阅读下面代码。

int main()
{close(1);int fd = open("./log.txt",O_WRONLY|O_CREAT,0644);printf("hello yjd\n");return 0;
}

运行上述代码,我们发现再运行了可执行程序之后,终端并没有打印hello yjd,但是我们惊奇的发现在log.txt中,竟然出现了hello yjd这个字符串,这究竟是为什么呢?

其实,整个过程就是重定向的原理,将在显示器上打印的数据写入了文件中。整个过程的原理如图所示。

 上图展示的是输出重定向的全过程。

printf函数是向标准输出文件(显示器文件)写入数据,从而在显示器上进行显示。标准输出文件对应的文件描述符为1,对于上述代码,当我们关闭1号文件描述符,即让显示器文件与1号文件描述符对应的结构体指针取消链接关系,然后我们打开了log.txt文件,然后让给其分配了1号文件描述符。这便是问题所在,与其说printf函数是向标准输出文件写入数据,不如说,printf是向1号文件描述符所对应的结构体指针指向的文件写入数据,因为此时1号文件描述符对应的结构体指针指向的文件时log.txt,所以此时printf就会向log.txt中写入数据。

这便是我们打印字符串,在显示器上看不见字符串,但是在log.txt中可以看到对应字符串现象的原理解释。输入重定向的原理也是类似的。

除了close描述符,还有dup2函数。代码如下。

 int main()
{int fd = open("./log.txt",O_WRONLY|O_CREAT,0644);dup2(fd,1);printf("hello yjd\n");return 0;
}

dup2函数的含义为,让第二个参数(文件描述符)指向第一个参数(文件描述符)对应的结构体指针所指向的文件。

以上便是本期的所有内容。

本期内容到此结束^_^

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

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

相关文章

科普文:银行信贷系统概叙

信贷业务流程 资金需求者提交申请&#xff1a;资金需求者通过不同渠道&#xff08;如APP、网站、门店等&#xff09;提交贷款申请。 系统交互完成审批&#xff1a;系统通过自动化和人工相结合的方式&#xff0c;对贷款申请进行初步筛选和审批。 系统交互完成策略判断&#xf…

AFAC2024-基于保险条款的问答 比赛日记 llamafactory qwen npu 910B1

AFAC2024: 基于保险条款的问答挑战——我的实战日记 概述 在最近的AFAC2024竞赛中&#xff0c;我参与了基于保险条款的问答赛道。这是一次深度学习与自然语言处理的实战演练&#xff0c;旨在提升模型在复杂保险文本理解与问答生成方面的能力。本文将分享我的参赛过程&#xf…

秒杀优化: 记录一次bug排查

现象 做一人一单的时候&#xff0c;为了提升性能&#xff0c;需要将原来的业务改造成Lua脚本加Stream流的方式实现异步秒杀。 代码改造完成&#xff0c;使用Jmeter进行并发测试&#xff0c;发现redis中的数据和预期相同&#xff0c;库存减1&#xff0c;该用户也成功添加了进去…

【Linux】Linux的基本使用

一.Linux的背景知识. 1.1什么是Linux Linux是一种开源的类Unix操作系统内核. 和Windows是" 并列 "的关系. 1.2Linux的发行版本. Linux 严格意义来说只是一个 “操作系统内核”.一个完整的操作系统 操作系统内核 配套的应用程序. 由于 Linux 是一个完全开源免费…

C++:格式化输入和输出、非格式化输入和输出(控制布尔值格式、整型值格式、浮点数格式;单字节操作put和get、多字节操作getline等)

1、格式化输入和输出 (1)What 标准库定义了一组操纵符&#xff08;本质是函数或对象&#xff09;来修改流的格式状态 当操作符改变流的格式状态时&#xff0c;通常改变后的状态对所有后续 IO 都生效 (2)Which A.控制布尔值的格式 bool bFlag true; std::cout<<std::b…

HTML+CSS3网页字符下雨特效

HTMLCSS3网页字符下雨特效https://www.bootstrapmb.com/item/14952 要在HTML和CSS3中创建一个字符下雨的特效&#xff0c;我们需要使用HTML来构建基本结构&#xff0c;然后使用CSS3的动画和关键帧&#xff08;keyframes&#xff09;来创建动画效果。但请注意&#xff0c;CSS3本…

pdf的下载,后端返回工作流,前端进行转换

前端将后端返回的工作流进行转换 项目中接触到了pdf的下载和预览的功能&#xff0c;记录一下~ 这里pdf的下载和预览的接口&#xff0c;后端返回的数据结构与其他的接口返回的数据结构有点不同&#xff0c;是直接返回的工作流&#xff0c;在控制台接口的响应预览内容大致是这样…

Aquila优化算法(基本原理+matlab源代码)—— 基于Aquila Optimizer原始论文分析

Matlab源代码位于&#xff1a; Aquila Optimizer: A meta-heuristic optimization algorithm - File Exchange - MATLAB Central (mathworks.cn) 1 Aquila优化算法 AO是一种基于种群优化方法&#xff0c;受启发于Aquila捕获猎物的方式。Aquila捕获猎物的方式主要有四种&#x…

JVM常用工具中jmap实现手动进行堆转储(heap dump文件)并使用MAT(Memory Analyzer Tool)进行堆分析-内存消耗分析

场景 JVM-常用工具(jps、jstat、jinfo、jmap、jhat、jstack、jconsole、jvisualvm)使用&#xff1a; JVM-常用工具(jps、jstat、jinfo、jmap、jhat、jstack、jconsole、jvisualvm)使用_jvm分析工具-CSDN博客 上面讲了jmap的简单使用。 下面记录其常用功能&#xff0c;实现堆…

【瑞芯微RV1126(板端摄像头图像数据采集)】②使用v4l2视频设备驱动框架采集图像数据

RV1126开发板&#xff1a;使用v4l2视频设备驱动框架采集图像数据 前言一、按键二、LCD显示三、V4L2 摄像头应用编程四、完整代码 前言 本系列的目的是&#xff0c;不仅仅将能够进行图片推理的模型部署于板端&#xff0c;还提供了两种摄像头数据采集的方法&#xff0c;集成到自…

国际化技术参考

一、概述 国际化就是用户可以选择对应的语言,页面展示成对应的语言; 一个系统的国际化按照信息的所在位置,可以分为三种国际化信息: 前端页面信息后端提示信息数据库的字典类信息二、前端页面国际化 使用i18n库实现国际化 i18n国际化库思路:通过jquery或者dom操作拿到需…

15现代循环神经网络—GRU与LSTM

目录 1.门控循环单元 GRU关注一个序列门候选隐状态(candidate hidden state)隐状态总结从零开始代码实现代码简洁实现2.长短期记忆网络 LSTM门候选记忆单元(candidate memory cell)记忆单元隐状态代码1.门控循环单元 GRU GRU 是最近几年提出来的,在 LSTM 之后,是一个稍微简…

Spring Boot + Spring Cloud 入门

运行配置 java -jar spring-boot-config-0.0.1-SNAPSHOT.jar --spring.profiles.activetest --my1.age32 --debugtrue "D:\Program Files\Redis\redis-server.exe" D:\Program Files\Redis\redis.windows.conf "D:\Program Files\Redis\redis-cli.exe" &q…

开源安全态势感知平台Security Onion

简介 Security Onion是一款由安全防御人员为安全防御人员构建的免费开放平台。它包括网络可见性、主机可见性、入侵检测蜜罐、日志管理和案例管理等功能。详细信息可以查看官网Security Onion Solutions 在网络可见性方面&#xff0c;Security Onion提供了基于签名的检测&…

一文了解LLM大模型会话 QA 增强

概述 在日常对话中&#xff0c;由于我们的大脑记录了对话的历史信息&#xff0c;为了减少冗余的内容&#xff0c;在进行回复时通常会存在指代和省略的情况。因为人脑具有记忆的能力&#xff0c;能够很好地重建对话历史的重要信息&#xff0c;自动补全或者替换对方当前轮的回复…

创建最佳实践创建 XML 站点地图--SEO

您是否正在努力让您的网站被搜索引擎索引&#xff1f;您想提高您网站的搜索引擎知名度吗&#xff1f;如果是&#xff0c;您可能会错过 XML 站点地图的重要性。XML 站点地图在改善您网站的 SEO 方面发挥着至关重要的作用。‍ XML 站点地图是您网站结构的蓝图&#xff0c;可帮助…

详解Stable Diffusion 原理图

参考英文文献&#xff1a;The Illustrated Stable Diffusion – Jay Alammar – Visualizing machine learning one concept at a time. 在这个Stable Diffusion模型的架构图中&#xff0c;VAE&#xff08;变分自编码器&#xff09;模型对应的是图中的 E 和 D 部分。 具体来说…

【深入理解SpringCloud微服务】深入理解Eureka核心原理

深入理解Eureka核心原理 Eureka整体设计Eureka服务端启动Eureka三级缓存Eureka客户端启动 Eureka整体设计 Eureka是一个经典的注册中心&#xff0c;通过http接收客户端的服务发现和服务注册请求&#xff0c;使用内存注册表保存客户端注册上来的实例信息。 Eureka服务端接收的…

JS 鼠标拖动实现移动滚动条的滚动效果

效果 现在很多场景都以移动端为基本开发&#xff0c;比如说需要隐藏滚动条&#xff0c;在pc上实现鼠标拖动和手机触摸拖动差不多的效果。 实现 以mdn的overflow属性中范例为基础&#xff0c;内容溢出时候可使用overflow: auto;和overflow: scroll;实现滚动效果。 要实现鼠标…

聚焦智慧出行,TDengine 与路特斯科技再度携手

在全球汽车行业向电动化和智能化转型的过程中&#xff0c;智能驾驶技术正迅速成为行业的焦点。随着消费者对出行效率、安全性和便利性的需求不断提升&#xff0c;汽车制造商们需要在全球范围内实现低延迟、高质量的数据传输和处理&#xff0c;以提升用户体验。在此背景下&#…