【Linux】学习-基础IO—上

Linux基础IO—上

复习c语言接口

你真的懂文件吗?

文件的打开与关闭

深入了解文件读与写(C语言级别)

系统文件I/O

我们知道,文件是放在磁盘(硬件)上的,我们用代码访问文件的思路是:

写代码 -> 编译 -> 生成可执行exe -> 运行 -> 访问文件…

我们利用c语言提供的库文件的接口来完成文件访问,但你有没有想过,C语言又是如何做到访问磁盘上的文件的呢?

也就是说,访问文件的本质上到底是谁在访问??

其实答案很显然,访问硬件,本质上只有操作系统OS能够访问!!

而普通用户想访问,只能让操作系统提供特定的接口

因此,调用库函数访问文件时,我们调用的接口实际上底层是封装了系统接口

这样再来看刚刚提出的问题:访问文件本质上是谁在访问 ??

首先,我们写完代码之后,通过编译器进行编译链接,会形成一个可执行程序。此时,可执行程序本质上是一个文件,它是放在磁盘上的,而当我们去运行此可执行程序是,本质上就是将文件内的代码和数据加载到内存当中,经过操作系统的先描述再组织变成进程,说白了就是程序需要运行起来调用系统接口才能访问文件,而程序运行起来就会变成进程,因此访问文件本质上是进程在访问文件!!

那既然这样,为什么计算机语言还要大费周章专门为访问文件提供接口呢?

其实是因为系统接口比较难!!因此语言上进行了封装,为了让接口更好的被使用!

这样也导致了不同的语言有着不同的文件访问接口,但系统层面的文件接口只有一套!!因此我们学习系统接口,能让我们对访问文件的理解更加深刻!

默认打开的三个流

都说Linux下一切皆文件,我们也学习认识了其实显示器和键盘(详见复习篇)也是一种文件,因此显示器显示内容以及在键盘上打字的过程不就是访问文件的过程吗?那访问文件是要通过编写代码,调用接口,编译后执行程序才能访问的啊!我们平常写代码也没打开过显示器和键盘这两个文件呀?

其实,是系统帮助我们做了这些事情!(人还怪好嘞!)

任何程序,不管有没有手动打开显示器和键盘,只要运行后经过先描述再组织成进程后,系统都会默认帮助任何进程打开三个流:标准输入流(stdin),标准输出流(stdout)以及标准错误流(stderr)

其中stdin对应的就是键盘(输入,是一种读操作,内存读文件也就是读取人通过键盘传达的信息)

stdout对应的就是显示器(输出,是一种写操作,内存通过显示器这个文件写入信息能够被人所读取)

stderr对应的也是显示器,只是写给人类的信息都是有关错误的信息

*extern FILE stdin;

*extern FILE stdout;

*extern FILE stderr;

这三个流的类型实际上是FILE *类型,也就是文件指针,更加印证了显示器和键盘也是一种文件

系统打开文件接口open

接口介绍

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);pathname: 要打开或创建的目标文件
flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。
mode: 若是创建文件,设置该文件被创建时的权限flags参数:
O_RONLY:只读打开
O_WRONLY: 只写打开
O_RDWR : 读,写打开
这三个常量,必须指定一个且只能指定一个
O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
O_APPEND: 追加写
O_TRUNC: 若文件已存在,先清空原本的文件内容//返回值:成功:返回新打开文件的文件描述符(后面会介绍)失败:-1

当前路径 “.”

我们在Linux系统下,若没有将可执行程序添加到环境变量时,我们运行程序一般都会使用 “ ./+ xxx.exe” 的方式,而其中 “ ./ ”的含义就是当前路径下的.exe文件,那么当前路径的参照物是谁呢?当前路径的当前指的又是哪一条路径呢?

其实,当前路径指的是可执行程序运行时变成进程时所处的路径,也就是一个进程是可以知道当前程序执行时的位置的,这样一来,也能解释了为什么我们之前使用fopen时,若文件不存在会自动在当前路径下创建的原因了:

image-20230919205535264

当我运行程序后,在程序还没退出前,此程序会经过系统描述组织成进程,而此时可执行程序运行时所处的路径也会被进程所记录,这样一来,当前路径的概念也就确定了。

pathname参数

pathname表示需要打开或者创建的文件

当需要打开文件时,需要指定对应的路径和文件名,若没有路径则默认会在当前路径查找,若当前路径没有,则会默认在当前路径下创建一个新的文件。

flags参数

flags简称标志位,对应的是打开或创建文件时,所赋予文件的权限:

  • O_RONLY:只读打开

  • O_WRONLY: 只写打开

  • O_RDWR : 读,写打开

这三个常量,必须指定一个且只能指定一个

  • O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限

  • O_APPEND: 以追加写的方式打开

//举例:若需要以只读的方式创建并打开文件:
open("test.txt",O_RONLY | O_CREAT,0666);

若需要传多个选项则需要在中间用分隔符 “|” 隔开

这看似很方便且简单的传选项的方式,底层是如何实现的呢?

其实,我们以用户的视角在应用层上看到的一个看似简单且方便的动作,在系统接口层面,可能是经过了非常多复杂的动作完成的!

而flags的底层实现正是印证了这一说法,flags的底层需要用到位图思想!

flags参数的类型为整型,有32个比特位,每个比特位都可以用来设置成选项的标志位,而每一个标志位都可以实现不同的功能,使得函数效率大大提高,降低了冗余

我们直接模仿实现一下标志位功能:

image-20230919212923143

image-20230919212737157

这就是flags参数的底层原理

mode参数

mode参数是提供给用户如有创建文件的需求时能够给文件设置权限的参数,比如我想给用户设置的权限为

rw-rw-rw-,对应的参数就为0666,若这一块忘记的可以复习权限Linux权限一章

注意:若发现设置的值跟实际的不一致,可能是被umask值过滤了,要注意umask值默认掩码的存在,我们可以设置当前进程的umask值,这样不会影响其他进程image-20230919213510385

系统关闭文件接口close

这个跟我们学习过的fclose是一样的,只不过传的参数不同,fclose传的参数是打开文件时返回的文件指针,而close传的是打开文件时返回的文件描述符fd;

int close(fd);
//关闭成功返回0,失败则返回-1;

系统"写"文件接口write

系统接口对应向文件内写入内容的接口为write:

//原型
ssize_t write(int fd,const void* buf,size_t count);
//第一个参数为打开文件时返回的文件描述符
//第二个参数为从内存的哪个位置的内容向文件中写入
//第三个参数为写入的大小,单位为字节

image-20230920124457496

image-20230920124542802

返回值:成功时返回实际写入的字节数的大小,失败则返回-1

示例:

// test: write
int main()
{int fd = open("test.txt", O_WRONLY | O_CREAT ,0666);if (fd < 0){perror("open");exit(1);}const char *buffer = "hello write!\n";write(fd, buffer, strlen(buffer));//这里用的是指针,因此使用strlenclose(fd);return 0;
}

image-20230920130830925

系统"读"文件接口read

(注意看文件接口的小技巧就是站在内存的角度)

系统接口中对应向文件内读出内容到内存中的接口为read:

//原型
ssize_t read(int fd, void *buf, size_t count);
//第一个参数为打开文件时返回的文件描述符
//第二个参数为从文件中读出内容后放入内存中的位置
//第三个参数为读出内容的大小,单位为字节

image-20230920131146013

image-20230920131227424

返回值:返回实际读出内容的大小,单位为字节,若返回0,则代表已经读到了文件末尾,若读取失败,则返回-1;

示例:

//test:readint main(){int fd=open("test.txt",O_RDONLY);if(fd<0){perror("read");}char buffer[128];memset(buffer,0,sizeof buffer);ssize_t s=read(fd,buffer,sizeof buffer);if(s<0){perror("read");}printf("%s",buffer);return 0;}

image-20230920131930140

文件描述符file description

我们在学习open接口时,若成功打开文件,则会返回一个整型:

image-20230920132206404

这个整型就称为文件描述符file description,那么什么是文件描述符呢,在我们之前的学习中,fopen成功打开后返回的是一个维护FILE结构体的结构体指针FILE*(详见深入了解文件一章中关于文件指针的部分占位符),他跟文件描述符又有什么关系呢?

首先我们要明白,FILE结构体是C语言标准库所提供的用来便于管理文件的结构体,而库函数的内部一定是封装了系统层面的东西用来标识文件的,在系统的角度上看,他只认文件描述符,而不认识文件指针,因此FILE的底层必定是封装了文件描述符才得以管理文件的.

文件描述符既然这么重要,那他究竟是什么?

让我们一步一步的来探索:

一般情况下,进程要访问文件,肯定要打开文件的,那么一个进程可以打开多个文件吗,还是一个文件能被多个进程访问呢?其实,一般而言,进程:文件的比例为1:n,大量的文件在系统中被打开,就像大量杂乱无章的资料被扔在了领导的桌子上一样,系统肯定是会非常不满的,因此,系统为了使这些杂乱无章的被打开的文件被有条不紊的整理并管理好,他对这些文件进行了先描述,再组织的过程,也就形成了FILE结构体,再把这些FILE结构体以双链表的形式链接了起来,此双链表上串的都是被打开的文件,也就是从磁盘上加载到内存里的文件(因为只有被加载到内存中才能被进程直接访问)。但这仅仅是把文件给管理好了,进程要访问文件除了要打开文件还要和这个被打开的文件建立好联系,以便访问时能在双链表的众多文件中找,也就是被管理起来的文件,需要有一种方法知道该文件是被哪个进程所打开的并同时做好管理。

image-20230920200748959

如何建立进程与被打开文件间的联系呢?

刚刚我们提到被打开文件被描述组织成结构体后被放入了双链表中,而为了将其中的文件与进程联系起来,在内存中还存在着 files_struct 结构体,在此结构体内,有一个指针数组,而其中存放的指针,正是指向这些被打开文件的结构体指针!而其中数组的下标,就是我们对应的文件描述符!这样真相就水落石出了,原来文件描述符是指针数组的下标!!

但还要与进程建立链接还需要一部,那就是让进程每次都能够找到存放指针数组的结构体files_struct,因此,我们的程序在经过编译运行后被描述组织成进程后,进程中的进程控制块内还会存放一个files*,用来指向 files_struct 结构体,这样一来,进程就不怕把打开的文件弄丢了!

由于前面我们学习过系统会默认帮助我们打开三个流,标准输入流,标准输出流与标准错误流,而这三个流所对应的硬件是键盘与显示器,而我们又知道Linux下一切皆文件,因此,这三个也属于被打开的文件,既然是被打开的文件,那同样也经过了被描述并组织成结构体FILE,并将指向该结构体的指针放入files_struct中的指针数组fd_array中,对应的数组下标分别是:

标准输入流:0

标准输出流:1

标准错误流:2

现在我们已经了解了文件描述符是什么以及认识了进程是如何与被打开文件是如何联系起来的,下面让我们来将打开访问文件时的整套流程(以文件已经被创建为前提):

首先,有一个在磁盘上的文件,此时小陈同学写了一份代码,需要打开这份文件,小陈的代码中使用了系统调用接口open函数,此时系统就会用他自己的方法在路径中找到这个文件,并将此文件通过描述组织成FILE结构体并放入双链表中, 然后将指向此结构体的结构体指针写入files_struct中的指针数组fd_array中,写入成功后则返回fd给小陈接收,此时进程中也存在指向files_struct结构体的指针,小陈也知道了他打开文件的文件描述符,因此要访问这个文件时,只需要告诉接口read或者write他需要访问他手上拿的fd所对应的这个文件,那么接口就会通过他的方法在双链表中找到这个文件并写入或者读出小陈所需要的东西,这样也就完成了对文件的打开与访问操作!!!

文件描述符的分配规则

我们直接通过代码来测试一下文件描述符的分配规则:

// test: file description
int main()
{int fd0 = open("test_fd1", O_RDONLY | O_CREAT,0666);int fd1 = open("test_fd2", O_RDONLY | O_CREAT,0666);int fd2= open("test_fd3", O_RDONLY | O_CREAT,0666);int fd3 = open("test_fd4", O_RDONLY | O_CREAT,0666);printf("fd0: %d\n", fd0);printf("fd1: %d\n", fd1);printf("fd2: %d\n", fd2);printf("fd3: %d\n", fd3);close(fd0);close(fd1);close(fd2);close(fd3);return 0;
}

image-20230920212450365

运行后发现fd是从3开始按照打开的顺序往后依次递增,鉴于我们前面提到的三个标准流占用了0,1,2文件描述符,初步推断,文件描述符是通过文件打开的顺序依次往后填入的,但为了进一步验证,我们再做如下测试:

// test:fd
int main()
{close(0);// close(2);int fd = open("myfile", O_RDONLY | O_CREAT,0666);if (fd < 0){perror("open");return 1;}printf("fd: %d\n", fd);close(fd);return 0;
}

这时运行后我们发现结果是image-20230920212828119

通过验证,我们基本可以确定文件描述符的分配规则:遍历指针数组并找到没有被使用的最小下标作为新的文件描述符

;
// close(2);
int fd = open(“myfile”, O_RDONLY | O_CREAT,0666);
if (fd < 0)
{
perror(“open”);
return 1;
}
printf(“fd: %d\n”, fd);
close(fd);
return 0;
}


这时运行后我们发现结果是[外链图片转存中...(img-dlV5UjFS-1707561019361)]<font color='red'>通过验证,我们基本可以确定文件描述符的分配规则:遍历指针数组并找到没有被使用的最小下标作为新的文件描述符</font>

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

shell脚本编译与解析

文章目录 shell环境变量全局变量局部变量设置PATH 环境变量 启动文件环境变量持久化 脚本编写重定向判断 和循环命令行参数传入参数循环读取命令行参数获取用户输入 处理选项处理简单选项处理带值选项 重定向显示并且同时输出到文件 替换目录下的所有文件中某个关键字删除关键字…

服务器内存使用率多少会影响正常访问?

在探讨服务器内存使用率多少会影响正常访问这个问题时&#xff0c;我们首先要明白&#xff0c;服务器的性能和稳定性与内存使用状况息息相关。当服务器的内存使用率过高时&#xff0c;可能会导致系统运行缓慢、程序崩溃或者出现其他一些异常情况&#xff0c;从而影响到正常的网…

c语言求多边形面积

多边形有现成的面积公式&#xff0c;直接套用即可。area函数接受两个参数&#xff1a;顶点坐标&#xff0c;顶点个数。 #include <stdio.h> #include <math.h>struct point {int x;int y; };float area(point p[], int n) {int i;float sum 0.0;for (i 0; i <…

使用CICFlowMeter 实现对pcap文件的特征提取【教程】

使用CICFlowMeter 实现对pcap文件的特征提取【教程】 针对现有的关于CICFlowMeter 的使用教程不够全面&#xff0c;一些细节没有展示&#xff0c;我将结合网络上的相关资料和实际的经历&#xff0c;提供一些经验和建议。 configuration information --------------- Windows…

机器学习:Softmax介绍及代码实现

Softmax原理 Softmax函数用于将分类结果归一化&#xff0c;形成一个概率分布。作用类似于二分类中的Sigmoid函数。 对于一个k维向量z&#xff0c;我们想把这个结果转换为一个k个类别的概率分布p(z)。softmax可以用于实现上述结果&#xff0c;具体计算公式为&#xff1a; 对于…

嵌入式学习之Linux入门篇笔记——11,Linux目录结构讲解

配套视频学习链接&#xff1a;http://【【北京迅为】嵌入式学习之Linux入门篇】 https://www.bilibili.com/video/BV1M7411m7wT/?p4&share_sourcecopy_web&vd_sourcea0ef2c4953d33a9260910aaea45eaec8 1.Linux 目录结构 Linux 整个文件系统是从 / 目录&#xff08;根…

架构(十三)动态本地锁

一、引言 加锁大家都知道&#xff0c;但是目前提供动态锁的基本都是分布式锁&#xff0c;根据订单或者某个收费款项进行加锁。比如这个1订单要收刷卡费用&#xff0c;那就OREDER_1做为key丢到redis进行分布式加锁。这也是当下分布式锁最流行的方式。 但是对于平台项目或者一些并…

C++ | string类按位赋值小技巧

一切的起因是string类的谜之初始化。 在写代码的时候&#xff0c;我发现即使没有用字符串初始化string对象&#xff0c;也可以对string对象进行下标操作&#xff0c;就像这样&#xff1a; #include<iostream> #include<string> using namespace std; int main() {…

读书笔记之《运动改造大脑》:运动是最佳的健脑丸

《运动改造大脑》的作者是约翰•瑞迪&#xff08;John Ratey&#xff09; / 埃里克•哈格曼&#xff08;Eric Hagerman&#xff09;&#xff0c;原著名称为&#xff1a;Spark&#xff1a;the revolutionary new science of exercise and the brain&#xff0c;于 2013年出版约翰…

Activiti7(流程引擎)简单笔记,附带作者执行的Demo代码文件

文章目录 一、Activiti7流程基础1、最简单的流程2、流程值表达式3、方法表达式4、节点监听器5、流程变量6、候选人7、候选人组8、流程网关排他网关并行网关包容网关事件网关 二、Activiti7流程事件1、定时器事件定时器开始事件定时器中间事件定时器边界事件 2、消息事件消息开始…

【EAI 015】CLIPort: What and Where Pathways for Robotic Manipulation

论文标题&#xff1a;CLIPort: What and Where Pathways for Robotic Manipulation 论文作者&#xff1a;Mohit Shridhar1, Lucas Manuelli, Dieter Fox1 作者单位&#xff1a;University of Washington, NVIDIA 论文原文&#xff1a;https://arxiv.org/abs/2109.12098 论文出处…

问题:重热现象可使多级汽轮机的理想焓降增加,重热系数越大,多级汽轮机的内效率就越低。 #学习方法#微信#媒体

问题&#xff1a;重热现象可使多级汽轮机的理想焓降增加&#xff0c;重热系数越大&#xff0c;多级汽轮机的内效率就越低。 参考答案如图所示

FL Studio如何改变轨道颜色 FL Studio波形颜色如何自定义 flstudio21中文版下载 FL Studio 设置颜色

FL Studio如何改变轨道颜色&#xff1f;FL Studio的轨道颜色可以在播放列表或混音台界面进行修改。FL Studio波形颜色如何自定义&#xff1f;FL Studio的波形文件颜色、名称、图标等信息都是可以自定义的&#xff0c;下文将给大家详细讲述。 一、FL Studio如何改变轨道颜色 在…

05-编码篇-H264文件分析

通过前面的分析&#xff0c;我们可以看出常规情况下&#xff0c;是将视频以帧的单位进行处理&#xff0c;比如I帧&#xff0c;P帧&#xff0c;B帧等。 但是这些帧是如何以文件方式保存的呢&#xff0c;这节我们主要对H264的保存方式作一个了解。 一帧图片通过编码后&#xff0c…

【教3妹学编程-算法题】价值和小于等于 K 的最大数字

3妹&#xff1a;2哥&#xff0c;新年好鸭~ 2哥 : 新年好&#xff0c;3妹这么早啊 3妹&#xff1a;是啊&#xff0c;新年第一天要起早&#xff0c;这样就可以起早一整年 2哥 :得&#xff0c;我还不了解你&#xff0c;每天晒到日上三竿 3妹&#xff1a;嘿嘿嘿嘿&#xff0c;一年是…

CTFshow-WEB入门-信息搜集

web1&#xff08;查看注释1&#xff09; wp 右键查看源代码即可找到flag web2&#xff08;查看注释2&#xff09; wp 【CtrlU】快捷键查看源代码即可找到flag web3&#xff08;抓包与重发包&#xff09; wp 抓包后重新发包&#xff0c;在响应包中找到flag web4&#xff08;robo…

OpenAI---提示词工程的6大原则

OpenAI在官方的文档里上线了Prompt engineering&#xff0c;也就是提示词工程指南&#xff0c;其中OpenAI有提到写提示词的6条大的原则&#xff0c;它们分别是&#xff1a; &#xff08;1&#xff09;Write clear instructions&#xff08;写出清晰的指令&#xff09; &#xf…

基于PHP网上图书销售商城系统qo85w

软件体系结构方案&#xff1a;由于本系统需要在不同设备上都能运行&#xff0c;而且电脑配置要求也要越低越好&#xff0c;为了实现这一要求&#xff0c;经过考虑B/S结构成为最佳之选。使用B/S结构的系统可以几乎在任何电脑上运行&#xff0c;只要浏览器可以正常工作就可以正常…

锐捷(十九)锐捷设备的接入安全

1、PC1的IP地址和mac地址做全局静态ARP绑定; 全局下&#xff1a;address-bind 192.168.1.1 mac&#xff08;pc1&#xff09; G0/2:ip verify source port-securityarp-check 2、PC2的IP地址和MAC地址做全局IPMAC绑定&#xff1a; Address-bind 192.168.1.2 0050.7966.6807Ad…

MVVM模型

MVVM模型M模型&#xff08;Model&#xff09;对应data中的数据&#xff08;普通的JS对象&#xff09;V视图&#xff08;View&#xff09;对应模板&#xff08;Vue的模板经过解析形成的页面&#xff0c;页面生成的DOM结构&#xff09;VMVue实例对象&#xff08;ViewModel&#x…