linux文件——深度学习文件fd、文件系统调用

        前言:从本片开始正式进入linux文件的学习,本片内容主要是文件的fd。 本篇内容博主将要先带友友回忆C语言中的文件操作接口,然后再过渡到操作系统中的系统调用的学习,最后理解操作系统中的文件操作。

         ps:本节内容设计一点linux下的task_struct的知识,友友们最好了解一点进程PCB相关知识后再进行观看

目录

C语言下的各种文件操作接口

fopen

chdir

fwrite

>

strlen是否加一的问题

认识文件系统

open

O_WRONLY

O_CREAT

 mode

write 

O_TRUNC清空写

O_APPEND追加写

访问文件的本质

文件fd——系统级别

FILE——语言级别


C语言下的各种文件操作接口

fopen

        我们先使用man fopen, 查看一下c里面的打开文件操作:

        然后我们从图中就可以看到fopen的返回值是一个FILE的类型, 而这个东西以前我们不知道是什么。 但是现在我们可以知道了, 这个东西其实就是一种叫做文件句柄的东西。 

        我们以前说过, 如果fopen打开一个文件, 这个文件在当前目录下不存在, 又是以写的方式方式打开。 那么这个文件就会自动在当前程序的目录下创建一个文件。 

        这个规则在man手册中也有提到:

        就是如上图的红框框, 如上图就是说将文件清空, 或者创建一个新的文件。

        现在我们写下面这个程序:        

运行后,就会发现, 创建了一个新的文件。 

chdir

        现在, 我们回到代码当中, 看我们给fopen传的第一个参数。(如下图)

这个参数是我们打开文件的路径和文件名。 但是如果只有文件名, 那么就默认在当前路径下创建一个文件。 但是问题来了,  当前路径是什么呢?——我们在讲进程的时候, 程序打开, 就会形成进程, 进程有一个当前工作路径, 这个当前工作路径cwd, 就是我们所说的当前路径。 ——如果我更改了当前进程cwd, 就可以把文件新建到其他目录。 而更改工作路径的系统调用就是chdir

        现在我们来试验一下:

我们先在当前路径下创建一个cydir目录, 一会我们就要将newfile.exe的工作路径改到cydir里面去。

这个时候里面还没有什么东西:

修改一下我们的程序, 让这个程序睡眠上100秒, 方便我们观察proc命令, 查看当前进程的工作路径。 

输入ls/proc/9238 -l指令

就可以查到当前进程的工作路径cwd:

         然后我们修改我们的程序——使用chdir修改工作路径:

        然后我们就会发现我们的工作路径确实发生了改变。 

fwrite

先使用man手册查看一下fwrite

        fwrite有四个参数, 第一个参数是ptr, 代表需要拷贝的数据的起始地址; 第二个参数是要拷贝的大小; 第三个参数是要拷贝的个数;第四个参数是要拷贝到哪里去。 

        下面是我们的程序以及运行结果:

        现在, 我们再写入其他的字符串, 就会发现原本的数据都被清空了, 只剩下新写入的数据:

下面是代码以及运行结果:

这是为什么呢? 我们重新看一下man手册中对w方法的论述, 就如同下图的红框框——对于w方法, 如果没有该文件, 那么就创建一个文件。如果有这个文件, 就让这个文件清空再写入。 

>

>和w类似, 重定向到文件中的时候, 我们的文件会先被清空,再写入。

如果什么都没写, 只是 > 文件名, 那么文件会被清空, 里面没有任何数据。

 

        由此, 我们可以发现, 其实>的底层就是"w"方法!!!两者都是打开文件即是空文件!!

strlen是否加一的问题

        我们这里思考一个问题, 我们在c语言中, 每一个字符串都有一个\0, 这个\0在strlen的时候不会计算, 并且不会显示。 那么, 我们看下面这个代码:

        这个strlen(src)处要不要加1? 我们目前虽然不知道要不要加一, 但是我们可以试验一下, 如果加1会打印什么, 这里我们实验, 下面是运行结果:

        我们可以发现, 在换行符后面又多打印了一串乱码。 ——而博主可以告诉友友们, 这个乱码其实就是\0。 strlen + 1就是将最后一个\0也包括进来了, 所以会打印\0

        那么问题来了, 我们平时打印的时候, 要不要加这个1呢? ——答案是不加, 因为我们的字符串后面默认补\0是c语言中的规则, c语言规定字符串的长度默认加1, 但是这是c语言规定, 而不是操作系统规定。 所以, 对于操作系统来说, 我们不加1。

认识文件系统

认识文件系统之前,我们需要理解几个知识点:

  •         首先, 文件是不是在磁盘上的?——是的。 那么磁盘是不是硬件设备呢?——是的。 那么访问文件的本质上其实就是访问硬件。 ——无论是什么文件, 比如显示器, 向显示器上面打印数据的本质上就是在访问显示器文件。 又比如键盘, 我们从键盘输入本质上就是在访问键盘文件。 而且, 要知道, 磁盘文件就是硬件, 而文件保存在磁盘里。 我们平时在IO的时候访问文件, 那么是不是就是在访问磁盘?——结论就是, 访问文件, 本质上就是访问硬件。
  •         我们知道, 整个计算机结构的最上层是用户, 下面是程序, 再下面是操作系统, 然后是驱动器, 最后是硬件。 如下图:

        并且, 对于用户来说, 不存在用户越过中间的程序、操作系统、驱动器而直接去访问硬件。用户做不到, 因为操作系统不相信任何人。 用户要访问硬件必须从上向下贯穿整个操作系统。 又因为操作系统不相信任何人, 所以操作系统和程序又有一层系统调用。——几乎所有的库只要是访问硬件,必定要封装系统调用!!!比如fprintf/printf/fscanf/fread/gets/fgetss………都是库函数!!!都能访问硬件, 所以一定封装系统调用!!!

open

        现在我们来看一个系统调用——open, 下面是man手册:

        使用man 2 open

        这里有两个函数, 两个的区别就是第三个参数:mode_t mode。

        这两个参数, 第一个参数是我们要打开的文件名——即路径。 第二个是我们要打开的文件的模式——即权限。

        然后第二个open里面的特殊的第三个参数, 这个参数就是指定我们创建一个新文件的时候创建这个文件的默认权限。

        那么就是说, 第一个open一般用于我们已经存在这个文件时候的打开。 第二个open一般用于这个文件不存在的时候再打开。

        那么第一个参数就是要开的文件的所在路径, 这个路径相对和绝对可以, 但是如果不带路径, 只有文件名那么就以进程的当前工作目录来决定了。

        第二个参数叫做flags, 这个flags有几个常见的选项O_RDONLY、O_WRONLY、O_RDWR、O_ERCAT、O_APPEND——这些选项是宏定义。

        我们如果学过位图, 我们就知道, 对于一个整形来说, 有32个比特位, 那么就是有32个标志位。 而这种标志位就是flags, flags利用了这种比特位级别的标志方式。

        接下来我们做一些实验。

        我们想要看ONE处的标志位, 那么我们就可以show(ONE), 想看TWO处的标志位, 那么就show(TWO)。 如果想要看一下ONE以及TWO处的标记位, 我们就show(ONE|TWO)。

        然后结果就是如下:

        接下来我们测试一下具有两个参数的open函数:

O_WRONLY

我们使用下面的代码测试, 注意, 这个时候我们只以的方式打开:

我们会发现, 没有文件被创建出来, 就如同下图:

O_CREAT

而我们想要创建出这个文件, 就需要使用O_WRONLY和O_CREAT两种方式组合。

 这个时候再运行就能看到新创建出来的文件里。 但是这个时候新创建出来的文件权限是---s-wx---, 这个很显然不是文件被创建出来的时候的默认权限。

 mode

        想要获得默认权限, 两个参数的open不能解决这个问题。 必须使用三个参数的open。 第三个参数就是规定新创建的文件的默认权限。——第三个参数是默认权限, 我们在传参的时候必须传八进制。

        创建出来后, 我们就会发现我们创建出来的是664权限而不是666权限。 这是为什么呢? ——因为我们传的是默认权限, 而真正的权限等于默认权限 & (~umask)博主此时的umask是002, 所以最终权限是664.

        而如果想要不考虑umask, 就在对应的进程中运行函数umask(0)。

        这个时候, 我们创建的文件权限就是666了!!!

write 

write函数是写函数, 下面是man手册:

       

  •         第一个参数是打开文件的时候的返回值。——open的返回值。  
  •         第二个参数是要拷贝的数据来源的地址。
  •         第三个参数是要拷贝的字节数。

如下图, write的实际应用:

运行后, 运行的结果如下图:

但是我们修改写入的内容, 会发现文件的内容没有被清空。 下面是实验过程:
先写入一行bbbbbbbb后又写入了一行aaaa

下面是运行结果:

O_TRUNC清空写

        想要能够清空文件, 就要另外加一个选项:O_TRUNC, 这个选项就是先清空文件。

         加了选项之后我们再运行程序:

        首先打印一行bbbbbbbbbbbbbb

然后再写入一行aaaa, 下面是代码和运行结果:

我们可以发现, 已经清空了文件!!!

O_APPEND追加写

追加写需要用到O_APPEND选项:

现在我们回头看一线fopen、fpeintf这些函数的底层一定是封装了open, write这些函数。 另外, 像python, java这类语言, 他们也一定有类似于fprintf, fopen类似的函数。 这些函数的底层一定会封装open, write, read这些系统调用。 

访问文件的本质

文件fd——系统级别

首先, 我们知道, 文件一定是保存在磁盘中, 我们的一个进程启动时, 会加载一个PCB。 而当进程访问一个文件的时候, 首先会打开这个文件。 然后就会内存中加载一个叫做struct_file的结构体。 这个结构体里面描述了一个被打开的文件的信息。 

        对于每一个进程, 都可能打开多个文件, 这些文件都会有对应的struct file结构体, 也就是上图的连成串的链表的一个节点。 那么进程如何找到这些struct file结构体呢?

        其实, 每一个进程里面都有一个struct file_struct* files, 指向一个名叫struct file_struct的结构体, 这个结构体里面保存了一个指针数组。 这个数组的名字叫做struct file* fd_array[]。 这些文件指针数组指向一个个描述文件的结构体。

        当我们打开一个文件的时候, 就会生成一个描述文件的结构体。 同时进程会在文件指针数组里面找一个空位置保存刚刚创建的描述文件的结构体的地址。 然后再讲这个数组的下标返回给用户。 这个返回值就是open的返回值!!!最终, 进程就可以根据这一张文件描述符表, 就可以把我们打开的文件找到了。

        那么open的返回值具体是如何返回的呢? ——当我们使用open打开一个文件, 操作系统就会创建一个文件描述对象。 然后文件描述符表就会在指针数组中找空位置指向这个文件描述对象。 然后将这个位置的下标返回给用户, 返回值就是一个整型类型的, 我们称为fd
 

        在上面那张图中, 我们如果划分区域, 可以划分如下:

那么左侧的就是进程管理, 右边的就是文件管理。 而两者的联系点就是struct file_struct。

多个文件创建的时候, 因为进程练习文件的本质就是使用指针数组。——是一个数组, 所以返回值fd是按照从小到大的顺序返回的。 如下图实验:

        运行结果3, 4, 5按照顺序创建的fd。

那么我们知道了, 文件的返回值fd时按照文件的打开顺序返回, 那么请问, 文件的返回值, 上图中是从3开始的。 那么0, 1, 2去哪里了呢? 或者说有0号下标吗?

我们需要知道, c程序在启动的时候, 会默认打开三个文件——stdin、stdout、stderr。 这三个文件会默认占用0, 1, 2三个fd所以我们在新建文件的时候, 是从3开始的。 

//现在我们使用fd == 1, 也就是显示器文件。 在这个文件里打印数据, 就能将数据在终端中打印出来:

//接下来测试fd == 0, 事实stdin——我们要使用read函数读取数据:

        由上面两个测试我们可以发现, 当进程打开的时候, 就会默认将0, 1, 2打开!!!

        那么我们再思考一下, 请问我们默认打开的三个标准输入输出, 是c语言的特性吗???不是, 是操作系统的特性!操作系统会默认打开进程的三个标准输入输出!!!

        那么, 操作系统为什么要默认打开呢?

        因为计算机开机的时候, 我们是不是就能看到显示器画面里, 而且键盘也已经被识别了? 所以, 我们计算机开机的时候, 操作系统已经将我们的显示器和键盘打开了。 而且我们在进行编程的时候, 是必须要用显示器和键盘的。 所以, 当我们打开一个进程, 那么只需要将已经打开的显示器, 键盘文件的地址填到自己进程PCB里。——而之所以要这样, 是因为程序员天然就需要用键盘输入, 用显示器看结果。

FILE——语言级别

        FILE是什么东西? ——对于FILE* fopen来说, 这个是c库自己封装的一个结构体。 而因为linux访问文件的时候只认文件描述符, 所以我们就可以确定, 这个FILE结构体里面必须封装文件描述符!!!

        就比如我们看一下C语言中的stdin, stdout, stderr:

        三个默认文件流底层一定封装了:stdin, 对应fd == 0; stdout, 对应fd == 1; stderr, 对应fd == 2;——也就是说, FILE类型里面一定给封装有fd这样的文件描述符字段。

        而且, 我们也知道, 我们的C库里面有各种接口, 这里我们用fwrite举例:

        fwrite一定要拿到FILE类型的结构体才能执行写入操作。 而fwrite底层的系统调用是write, write写入需要fd。 那么换句话说, FILE里面一定有fd字段。 也就是文件描述符!!!那么我们现在测试一下到底是不是这样的呢? 下面是代码和测试结果:

现在我们关闭1号文件, 看看是否能够关闭:

很显然, 不能打印了, 说明1号文件, 也就是显示器文件被关闭了。

但是这里有一个问题, 就是我们在stderr文件中打印, 我们仍旧可以看到打印结果:

这是为什么呢? ——这是因为虽然1号和二号都是指向显示器文件。 但是显示器文件有引用计数并不仅仅只有显示器文件有引用计数, struct file结构体里面都有引用计数), 关闭1号文件不会彻底关闭显示器文件!!! 也就是说, 1号和2号是根据文件描述符分开打印的!!! 关闭文件的本质就是让struct file里面的引用计数减减和自己这个进程里面指向这个struct file的下标置为空就可以了

        那么我们重新捋一下这个过程——也就是说一开始1, 2号fd都是指向显示器文件, 此时显示器文件的引用计数是2. 当1关闭, 显示器文件的引用计数减减, 变成1, 1号fd指向空, 但是2号fd不变。 所以2号fd依旧可以向显示器打印。

        那么我们就可以确定, 我们C++里面使用的iostream, 虽然可能包含了继承体系或者各种基类, 但是最底层, 一定包含了fd!——因为什么都可以改变, 但是操作系统的系统调用不会变, 变的只是上面语言级别的各种外壳!

----------------------------------------------以上, 就是本节全部内容, 下面是本节笔记:

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

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

相关文章

AI 深度学习让金融 β 系数估算更精确

作者:老余捞鱼 原创不易,转载请标明出处及原作者。 写在前面的话: 传统的金融贝塔系数估计方法往往依赖于严格的假设,难以准确捕捉 Beta 的动态变化,这限制了它们在实际应用中的有效性。为了解决这些问题,本文开发了一种新方法:NeuralBeta,利用神经网络进行Bet…

Centos7安装高版本内核

背景 买到的设备安装Centos7系统,但是新的处理器已经不支持低版本的Linux内核了。而且在 CentOS 使用过程中,高版本的应用环境可能需要更高版本的内核才能支持,所以难免需要升级内核。 内核种类 关于内核种类: kernel-ml:kerne…

趋动科技陈飞:从小模型到大模型,AI时代下的数据中心建设

自AI大模型横空出世,不断推动着AI从学术界到产业界向大众破圈,新的时代正在来临。11月15-16日,由CDCC主办的“2023第11届数据中心标准大会”在北京国家会议中心盛大开幕。 本届大会的主题围绕“AI时代 重塑未来”,聚焦数据中心领…

Spring:springboot集成jetcache循环依赖问题

springboot版本:2.6.14 jetcache版本:2.6.2 启动项目报错如下: 解决方案: jetcache版本升级到2.6.4 https://github.com/alibaba/jetcache/issues/624

phpstudy搭建sqlilabs本地靶场

请先在网上解决好前置条件的下载和安装:phpstudy、vscode、navicat premium(非必要)、sqlilab的压缩包/文件夹 phpstudy--安装sqlilabs 1.打开phpstudy后,我们会用到MySQL5.7.26和Nginx1.15.11 #mysql5.7.26是因为sqlilabs靶场不支持高版本MySQL 2.在软…

Spring配置

1.Spring的两大核心思想IOC和AOP思想 1.1类注解 1.Controller, Service, Configuration, Component, Repository 1.2方法注解 bean(这个方法搭配上面的五大注解进行使用) 2.Bean的名称 2.1.类注解名称 (1)默认首字母小写驼…

【C语言篇】字符和字符串以及内存函数的详细介绍与模拟实现(上篇)

文章目录 字符函数字符输入输出函数字符输入函数字符输出函数 字符分类函数字符转换函数 字符串函数字符串输入输出函数字符串输入函数字符串输出函数 strlen函数的使用和模拟实现strcpy函数的使用和模拟实现strcat函数的使用和模拟实现strcmp函数的使用和模拟实现strncpy函数的…

三十三、【人工智能】【机器学习】【监督学习】- LightGBM算法模型

系列文章目录 第一章 【机器学习】初识机器学习 第二章 【机器学习】【监督学习】- 逻辑回归算法 (Logistic Regression) 第三章 【机器学习】【监督学习】- 支持向量机 (SVM) 第四章【机器学习】【监督学习】- K-近邻算法 (K-NN) 第五章【机器学习】【监督学习】- 决策树…

【计算机操作系统】死锁的概念

文章目录 死锁的定义死锁、饥饿、死循环的区别死锁产生的必要条件死锁的处理策略小结(思维导图) 死锁的定义 死锁是指多个进程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推…

二维差分日常练习

前言&#xff1a;一开始写题的时候以为自己还记得&#xff0c;但是写的时候忘记了一个易错点 定义长度为 len , 那么 左上角的坐标为 &#xff08; i , j ) , 那么右下角的坐标为 为 ( i len -1 ,j len -1 ) #define _CRT_SECURE_NO_WARNINGS #include<bits/stdc.h> us…

【阿旭机器学习实战】【39】脑肿瘤数据分析与预测案例:数据分析、预处理、模型训练预测、评估

《------往期经典推荐------》 一、【100个深度学习实战项目】【链接】&#xff0c;持续更新~~ 二、机器学习实战专栏【链接】&#xff0c;已更新31期&#xff0c;欢迎关注&#xff0c;持续更新中~~ 三、深度学习【Pytorch】专栏【链接】 四、【Stable Diffusion绘画系列】专…

苹果电脑维护工具:CleanMyMac X让你的Mac焕发新生!

在我们的数字生活中&#xff0c;苹果电脑&#xff08;Mac&#xff09;已成为不可或缺的一部分&#xff0c;无论是为工作披星戴月&#xff0c;还是为娱乐畅游云端。但是&#xff0c;就像任何长时间运行的机器一样&#xff0c;Mac也可能会因为积累的文件和不必要的数据而开始变慢…

Linux云计算 |【第二阶段】NETWORK-DAY4

主要内容&#xff1a; NAT 原理与配置&#xff08;私有IP地址、静态NAT转换、Easy IP&#xff09;、VRRP解析&#xff08;主路由器、备份路由器、虚拟路由器、优先级&#xff09; 一、NAT概述 NAT 网络地址转换&#xff08;Network Address Translation&#xff09;是一种网络…

NLP从零开始------9文本进阶处理之文本相似度计算

1.文本相似度计算简介 在自然语言处理中&#xff0c;经常会涉及度量两个文本相似度的问题。在诸如对话系统和信息减速等中&#xff0c;度量句子或短语之间的相似度尤为重要。在新闻学传媒中应用文本相似度可以帮助读者快速检索到想要了解的报道。 文本相似度的定义式如下所示&a…

YOLO系列:从yolov1至yolov8的进阶之路 持续更新中

一、基本概念 1.YOLO简介 YOLO&#xff08;You Only Look Once&#xff09;&#xff1a;是一种基于深度神经网络的对象识别和定位算法&#xff0c;其最大的特点是运行速度很快&#xff0c;可以用于实时系统。 2.目标检测算法 RCNN&#xff1a;该系列算法实现主要为两个步骤&…

江科大/江协科技 STM32学习笔记P22

文章目录 AD单通道&AD多通道ADC基本结构和ADC有关的库函数AD单通道AD.cmain.c连续转换&#xff0c;非扫描模式的AD.c AD多通道AD.cmain.c AD单通道&AD多通道 ADC基本结构 第一步&#xff0c;开启RCC时钟&#xff0c;包括ADC和GPIO的时钟&#xff0c;ADCCLK的分频器也需…

华为hcip-big data 学习笔记《一》大数据应用开发总指导

一、大数据应用开发总指导 1. 前言 随着大数据技术的飞速发展和大数据应用的不断普及&#xff0c;大数据已经成为当今时代最热门的话题之一。不过对于大数据的了解&#xff0c;很多人还只是停留在表面&#xff0c;提到大数据&#xff0c;很多人只是直到它是最新的科技&#x…

集成新的 AI 服务时需要考虑的问题

让我们来谈论最近发生的几个恐怖故事。 去年年底&#xff0c;一家雪佛兰经销商在其主页上部署了一个由大型语言模型 (LLM) 驱动的聊天机器人。该 LLM 经过雪佛兰汽车详细规格的训练&#xff0c;旨在仅回答有关雪佛兰汽车的问题。 然而&#xff0c;用户很快就找到了绕过这些限…

在 Linux 9 上安装 Oracle 19c:克服兼容性问题 (INS-08101)

Oracle 数据库 19c 的基础版本 (19.3) 发布的时候还没有 Linux 9 &#xff0c;因此在Linux 9上面安装Oracle 19c会遇到很多兼容性问题。本文将探讨如何解决这些问题。 安装步骤 设置环境变量以绕过操作系统检查&#xff1a; Oracle 19.3 安装程序无法识别 Linux 9。 [WARNIN…

【机器学习】 Sigmoid函数:机器学习中的关键激活函数

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 Sigmoid函数&#xff1a;机器学习中的关键激活函数1. 引言2. Sigmoid函数定义3.…