【Linux】13. 文件操作

news/2024/5/20 2:06:51/文章来源:https://blog.csdn.net/weixin_60915103/article/details/130998177

1. 重新认识文件

经过之前的linux命令操作、进程相关概念的学习,我们对于文件也并不陌生
首先需要明确以下概念:

  1. 即使是空文件,也要在磁盘当中占据空间
  2. 文件 = 文件内容 + 文件属性
  3. 文件操作 = 对文件内容的操作 或者 对文件属性的操作 或者 二者都有
  4. 标定一个文件:必须使用文件路径+文件名【具有唯一性】
  5. 如果没用指明对应的文件路径默认是在当前路径下对文件进行访问(其中当前路径指的是进程的当前路径,其实就是环境变量中的一个值,是可以被修改的)
  6. 当我们将fopen,fclose,fwrite等接口实现完成,代码编译完成形成二进制可执行程序后,不运行,文件所对应的操作有没有执行呢?
    — 没有

以上概念视为对之前的总结, 对文件的操作其本质是进程对文件进行的操作!
如果一个文件没有被打开,可以直接被访问嘛?— 不能,一个文件要被访问就必须先要打开(怎么打开呢? --用户进程调用文件打开接口+OS实现文件打开功能)
所以,综上所述,文件操作的本质就是描述进程和被打开文件的关系
那么,未被打开的文件呢? – 属于文件系统的部分(后序博客中会介绍到)

2. 重新理解文件操作

在C语言的学习过程中,我们学习到大量的文件操作接口并使用,但我们只是了解其如何使用,并未了解操作系统底层是如何实现的
那么对于其他语言(C++/Java/Python/php…)存不存在文件接口呢?
–我们不得而知,但按照常理而言都应该提供文件操作的接口 但是其接口实现都不一样

文件存储在磁盘当中(磁盘属于硬件,要访问硬件就绕不开OS(软硬件资源管理器),必须调用操作系统提供的接口)
所以不管上层语言如何发生变化,库函数底层都必须调用系统调用接口

那么如何降低学习成本呢? – 学习不变的东西(操作系统底层如何实现文件操作)

小知识点:批量化注释 Ctrl+v+j/k+I +“//” +Esc

在这里插入图片描述

3. 文件操作(C语言)

[hx@hx file_operation]$ cat myfile.c
#include <stdio.h>
#include <unistd.h>#define FILE_NAME "log.txt"int main()
{FILE *fp  = fopen(FILE_NAME,"w");if(NULL == fp){perror("fopen");return 1;}fclose(fp);return 0;
}

在我们#define FILE_NAME "log.txt"时,并未指明文件的路径,那么文件会创建在哪里呢? – 当前路径下,进程在执行代码,生成可执行程序时是在当前路径下的,文件也就对应的生成在该路径下(若是更改当前路径那么文件对应的位置也就发生改变)

来回顾一下文件使用方式:
" r " — 以只读的方式打开文件,文件不存在报错
" w " — 以只写的方式打开文件,文件不存在创建文件
" a " — 在文件末尾追加数据,文件不存在创建文件
" r+ " — 读写都可以,文件不存在报错
" w+ " — 读写都可以,文件不存在创建文件
" a+ " — 读写都可以,在文件末尾进行读写,文件不存在创建文件
在这里插入图片描述
在这里插入图片描述
在当前centos7下echo会默认在末尾添加\n,在其他系统下不一定(看系统配置)
在这里插入图片描述

  1. 如果我们以"w"的方式打开文件,而不写入数据,C语言会默认将其中的数据清空
  2. 文件创建出来,其权限默认是0666 真实的权限 (0666&~umask = 0664)
    在这里插入图片描述
    我们在文件中将umask设置成为0,此时生成的文件就是666,那为啥shell在执行umask命令时还是0002呢?
[hx@hx file_operation]$ umask
0002

– 因为是子进程在执行创建文件的命令,子进程umask被置为0,但并不影响父进程(shell)

4. 文件操作(操作系统)

4.1 open

在这里插入图片描述
一个int有多个比特位,操作系统采取比特位传递选项,下面来演示一下:
一个比特位对应一个选项 比特位的位置不能重复
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.2 write

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.3 read

在这里插入图片描述

4.4 总结

在这里插入图片描述

5. 深层次理解文件(文件描述符的引出)

之前提到文件操作的本质是进程和被打开文件之间的关系

  1. 进程可以打开多个文件嘛?
    当然可以,在编写程序时可以调用多个open函数,那么操作系统中肯定存在大量被打开的文件
  2. 这些被打开的文件是不是需要被操作系统中管理起来呢?
    肯定是需要管理的(操作系统管理各种软硬件资源)
  3. 操作系统如何管理这些文件呢?
    在之前的学习过程中,我们知道管理的本质就是先描述再组织
    操作系统为了管理对应的打开文件,必定会为文件创建对应的内核数据结构表示文件
    struct file{} 这其中包含着文件的大部分属性
    这里的file和C语言当中的FILE不存在联系

每一个被打开的文件都有一个struct file ,struct file可以通过链式结构链接起来,只要找到起始地址,操作系统对打开文件的管理就变成对链表的增删查改

在这里插入图片描述
open函数的返回值是整数,-1表示文件打开失败,那为啥是从3开始,0,1,2这三个整数去哪了?
在这里插入图片描述
现在我们就知道C语言的库函数封装了系统调用接口,C语言的FILE类型的指针也封装了操作系统的文件描述符
(种种迹象表明库函数就是系统调用接口的封装)

我们理解了为啥是从数字开始的,那为啥是0,1,2,3…连续的数组下标呢?
在这里插入图片描述
当我们再打开一个文件时,数组从上往下查找没有被占用的描述符,就被分配给刚创建好的struct file对象
将struct file对象的地址填入对应的3号描述符当中,此时3号描述符就执行新打开的文件了
然后我们再将3号描述符通过系统调用给用户返回,用户就得到了数字3

所以,进程在访问文件时,需要传入3号描述符,进程根据传入的值找到进程描述符表对应的地址 文件找到即可对文件进行操作

操作系统是通过数组(文件描述符表)将进程和被打开的文件关联起来
总结:文件描述符的本质就是数组的下标
在这里插入图片描述
上图是说明进程与文件的关系

6. 文件描述符的分配规则

在这里插入图片描述
通过上图的演示,我们可以初步得到以下结论:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7. 重定向

在这里插入图片描述
重定向的本质是:上层的fd不变,在内核中更改fd对应的struct file* 的地址,也就是说不是描述符之间的改变,而是改变描述符所对应的内容
上述这种输出重定向的方式是采取关闭stdout标准输出的方式,让1号描述符重新指向fd,如果我们同时将0,1,2都关闭是无法实现输出重定向的,也就是说这种方式是不满足需求的,操作系统提供了专门的重定向接口dup2()

7.1 dup2

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7.2 myshell当中实现各种重定向

这里可以结合之前自己模拟实现的myshell来操作
在这里插入图片描述

7.2.1 将重定向类型置为NULL

   12 #define NONE_REDIR   013 #define INPUT_REDIR  114 #define OUTPUT_REDIR 215 #define APPEND_REDIR 316 17 int RedirType = NONE_REDIR;18 char *redirFile = NULL;

将输出/输入/追加重定向先定义出来,一开始将重定向类型置为0,重定向文件为无 后面对重定向类型进行判断

7.2.2 重定向类型判断框架的构建

大致思路就是获取命令行,判断命令行当中是否存在重定向符号

   25 void commandCheck(char* commands)26 {                      27   assert(commands);                                                                                                                                                                                        28   char *start = commands;29   char *end = commands+strlen(commands);30 31   while(start<end)32   {33     // 追加/输出34     if(*start == '>')35     {36 37     }38     // 输入39     else if(*start == '<') 40     {41 42     }43     else 44     {45       start++;46     }47   }                                                                                                                                                    48 }                                                                                                                                                      

7.2.3 输入重定向的编写

   38     // 输入                                                                                                                                                        39     else if(*start == '<')                                                                                                                                         40     {                                                                                                                                                              41       // " cat <      file.txt"                                                                                                                                    42       *start = '\0';                                                                                                                                               43       start++;                                                                                                                                                     44       trimSpace(start);                                                                                                                                            45       // 填写重定向信息                                                                                                                                            46       RedirType = INPUT_REDIR;                                                                                                                                     47       redirFile = start;48       break;49     }

如果当前start为<,说明是输入重定向,将当前start置为\0(将命令行分割成两字符串),将重定向信息改为输出重定向,文件名就是start所指向的位置
但是此时又面临一个问题,如何获取文件名的首字符,需要将文件名之前的空格都过滤掉

7.2.4 过滤空格(重点)

又因为不仅仅是在输入重定向这一种情况需要过滤空格,而且在追加和输出重定向中也需要过滤空格,所以我们可以将其以宏函数的形式编写

   17 #define trimSpace(start)  do{\18                           while(isspace(*start))\19                               ++start;\20                           }while(0)

这里的代码看上去很复杂,一步步分析
首先,'\'表示的是续航符

为啥#define定义需要续航符?
因为宏替换是发生在程序的预处理阶段,进行直接替换,取到的是#define后面一行的内容,如果不存在\那么在替换时,不会将上下两行内容合并,所以需要加上续航符将函数当做一个整体进行宏替换

其次用到isspace判断空格++start找到文件名的开始位置,来获取文件名

7.2.5 输出重定向/追加重定向

先判断start是不是>
如果是再判断后一位(start++) 若
start等于>,则是追加重定向,若*start不等于>,则是输出重定向

   42     // 追加/输出43     if(*start == '>')44     {45       *start = '\0';46       start++;47       if(*start == '>')48       {49         // "ls -a >> file.txt"50         RedirType = APPEND_REDIR;51         start++;52       }53       else 54       {55         // "ls -a > file.txt"                                                                                                                                                                              56         redirType = OUTPUT_REDIR;     57       }                               58       trimSpace(start);               59       redirFile = start;     60       break;                                                                                                                                                       61     }       

判断完重定向类型后需要将对应的重定向类型修改,同样的需要把空格过滤找到文件名对应位置

8. 进程与重定向文件之间的关系

上面完成了对重定向类型的判断,那么到底如何执行重定向是需要进程来完成的
因为父进程是用来接收子进程退出信息,命令是由子进程进行完成的(shell的原则:王婆派遣实习生的案例)
所以真正的重定向工作是由子进程来完成的

8.1 子进程接收重定向类型基本框架

  144     // 子进程                                                                                                                                  145     if(id == 0)                                                                                                                                146     {                                                                                                                                          147       switch(RedirType)                                                                                                                        148       {                                                                                                                                        149         case NONE_REDIR:                                                                                                                       150           break;                                                                                                                               151         case INPUT_REDIR:                                                                                                                      152           break;                                                                                                                               153         case OUTPUT_REDIR:                                                                                                                     154           break;                                                                                                                               155         case APPEND_REDIR:                                                                                                                     156           break;                                                                                                                               157         default:                                                                                                                               158           printf("bug?\n");                                                                                                                    159           break;160       }

8.2 输入重定向

  149         case NONE_REDIR:150           // 什么都不做151           break;152         case INPUT_REDIR:153           {154             int fd = open(redirFile,O_RDONLY);155             if(fd<0)156             {157               perror("open");158               exit(errno);159             }160             //重定向的文件成功打开161             // dup2进行重定向162             dup2(fd,0);163           }164           break;      

调用dup2接口进行重定向,将fd文件指向0(标准输入/stdin)

8.3 输出/追加重定向

  165         case OUTPUT_REDIR:                                                                                                                     166         case APPEND_REDIR:                                                                                                                     167           {                                                                                                                                    168             //使得创建的文件按照自己的权限创建                                                                                                 169             umask(0);                                                                                                                          170             int flags = O_WRONLY | O_CREAT;                                                                                                    171             if(RedirType == APPEND_REDIR) flags |= O_APPEND;172             else flags |= O_TRUNC;173                 174             int fd = open(redirFile,flags,0666);175             if(fd < 0)    176             {177               perror("open");178               exit(errno);179             }180             dup2(fd,1);181           }182           break;183         default:184           printf("bug?\n");185           break;186       }       

追加重定向和输出重定向的差别就是打开文件的方式不一样,一个是以"a"的方式,一个是以"w"的方式
所以RedirType == APPEND_REDIR flags就或等上O_APPEND 否则就或等上O_TRUNC

8.4 理解重定向和父进程之间的关系

既然重定向文件的工作是由子进程来完成的,那么是不是就跟父进程无关呢?
当然不是,子进程是通过父进程创建(fork)出来的,子进程需要获取父进程对应的代码和数据,所以子进程也就从父进程当中获取到如何重定向文件的信息(到底是需要进行哪种重定向)
总而言之,父进程就是给子进程提供重定向信息的
子进程进行重定向会影响父进程嘛?
在这里插入图片描述
执行程序替换时,是否会影响到曾经进程打开的重定向文件呢?
不会,因为以上的结构(fork创建子进程)都是属于内核数据结构当中的模块,而程序替换是将磁盘上新的代码和数据替换到内存当中,完全是两个维度,最直观的表现就是程序替换不会影响内核数据结构当中的PCB(task_struct)

9. shell结果演示

在这里插入图片描述
在这里插入图片描述

10. 如何理解Linux下一切皆文件

在这里插入图片描述这层struct file是操作系统虚拟出来的文件对象(在Linux当中称之为vfs – 虚拟文件系统),摒弃掉底层设备的差别,统一使用文件接口的方式来进行文件操作
struct file当中包含方法指针,会进行初始化指向对应的方法,还会指向内核中的缓冲区。

拓展一下:文件的引用计数

假设我们在堆上申请了一块空间,有指针指向这块堆空间,当我们不需要再使用时就释放空间
在使用过程中,可能有多个指针指向这块堆空间,于是我们在区间加上count 用来计数(记录当前有多少指针指向我)
这个被称之为引用计数
当父进程fork创建子进程时,会将PCB和文件描述符表都给子进程创建一份,但是文件并未新建一份,所以此时子进程文件描述符表中的数据还是指向父进程所打开的文件,那么在进程在关闭文件时,是否就直接将文件关闭了?
不是,只是将文件所对应的引用计数–罢了,当文件的引用计数为0时,操作系统才会释放该打开文件
所以我们close文件时,只是告知操作系统我们不再使用该文件了·

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

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

相关文章

ChatGPT训练一次要耗多少电?

如果开个玩笑&#xff1a;问ChatGPT最大的贡献是什么&#xff1f; “我觉得它对全球变暖是有一定贡献的。”知名自然语言处理专家、计算机科学家吴军在4月接受某媒体采访时如是说。 随着ChatGPT引爆AIGC&#xff0c;国内外巨头纷纷推出自己的AI大模型&#xff0c;大家为人工智…

跨境电商独立站搭建-跨境电商源码网站开发部署,独立站技术

跨境电商独立站是指在国际互联网上建立并拥有自己独立的电商网站&#xff0c;在该网站上进行跨境电商业务&#xff0c;包括产品展示、交易处理、支付结算、物流配送等全流程。相较于在第三方平台上开店&#xff0c;跨境电商独立站具有更高的自主权和品牌形象&#xff0c;能够更…

Redis 高级数据结构 HyperLogLog

介绍 HyperLogLog(Hyper[ˈhaɪpə(r)])并不是一种新的数据结构(实际类型为字符串类型)&#xff0c;而是一种基数算法,通过HyperLogLog可以 利用极小的内存空间完成独立总数的统计&#xff0c;数据集可以是IP、Email、ID等。如果你负责开发维护一个大型的网站&#xff0c;有一天…

Java实现Mqtt收发消息

Java实现Mqtt收发消息 文章目录 Java实现Mqtt收发消息windows mqtt 平台服务搭建mqtt 客户端工具&#xff1a;mqttbox整体代码结构mqtt基础参数配置类mqtt客户端连接mqtt接收的消息处理类对应的MqttService注解和MqttTopic注解 MqttGateway 发送消息指定topic接收处理方法 java…

Servlet Cookie基本概念和使用方法

目录 Cookie 介绍 Cookie 主要有两种类型&#xff1a;会话 Cookie 和持久 Cookie。 Cookie使用步骤 使用Servlet和Cookie实现客户端存储的登录功能示例&#xff1a; LoginServlet类 index.jsp 删除Cookie 浏览器中查看Cookie的方法 Cookie 介绍 Cookie 是一种在网站和…

测试老鸟总结,自动化测试难点挑战应对方法,我的进阶之路...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 Python自动化测试&…

不定积分练习

不定积分练习 在看视频的时候遇到了一道比较有趣的题&#xff0c;在这里给大家分享一下。 题目 计算 ∫ ( 1 x − 1 x ) e x 1 x d x \int(1x-\dfrac 1x)e^{x\frac 1x}dx ∫(1x−x1​)exx1​dx 解&#xff1a; \qquad 原式 ∫ e x 1 x d x ∫ x ( 1 − 1 x 2 ) e x 1…

Promise-用法

目录 1.处理异步的几种方案 2.理解 3.promise状态&#xff1a;初始化 4.执行异步任务 5.执行异步任务成功 6.执行异步任务失败 7.执行异步任务成功-返回 8.执行异步任务失败-返回 1.处理异步的几种方案 纯粹callback&#xff0c;会剥夺函数return的能力promise&#xf…

【Java基础学习打卡01】计算机概述

目录 引言一、计算机是什么&#xff1f;1.计算机vs计算器2.计算机定义 二、计算机发展简史三、计算机分类四、计算机基本工作原理1.冯诺依曼2.冯诺依曼原理 总结 引言 其实我们在学习Java编程之前应该要对计算机有所了解&#xff0c;这里的了解不是说我们日常接触电脑就算是了…

postgres篇---docker安装postgres,python连接postgres数据库

postgres篇---docker安装postgres&#xff0c;python连接postgres数据库 一、docker安装postgres1.1 安装Docker&#xff1a;1.2 从Docker Hub获取PostgreSQL镜像1.3 创建PostgreSQL容器1.4 访问PostgreSQL 二. python连接postgres数据库2.1 connect连接2.2 cursor2.3 excute执…

docker-compose 搭建 zipkin 服务端

目录 基于docker-compose搭建服务端 数据库 服务器 docker-compose.yaml 问题 测试 基于docker-compose搭建服务端 数据库 我这边存储选择了Mysql存储&#xff0c;新建了 zipkin库&#xff0c;数据库脚本如下 -- -- Copyright 2015-2019 The OpenZipkin Authors -- -- Li…

Springboot3 + SpringSecurity + JWT + OpenApi3 实现认证授权

Springboot3 SpringSecurity JWT OpenApi3 实现双token 目前全网最新的 Spring Security JWT 实现双 Token 的案例&#xff01;收藏就对了&#xff0c;欢迎各位看友学习参考。此项目由作者个人创作&#xff0c;可以供大家学习和项目实战使用&#xff0c;创作不易&#xff…

linux 部署mysql

本文介绍下Centos7中mysql的安装(Centos7以下版本中有些命令和centos7中有些不同&#xff0c;安时需注意下自己的linux版本) 事先准备 1、查看系统中是否自带安装mysql yum list installed | grep mysql ![在这里插入图片描述](https://img-blog.csdnimg.cn/e322b2f4036c4d9…

电力能耗监测系统是如何运作的

电力能耗监测系统数据的采集主要通过多功能仪表、通讯管理机、通讯协议实现能耗数据的采集&#xff0c;能耗数据上传后经大数据计算实现能耗数据的展示&#xff0c;满足用户对能耗监测的需求。下面对电力能耗监测系统的是怎么采集数据的展开介绍&#xff1a; 1.多功能仪表 对高…

中国的互联网技术有多厉害?

1 很多人没有意识到&#xff0c;中国的互联网技术是相当厉害的。 给大家举几个例子。 我和朋友聊天的时候&#xff0c;手机上的app都在“侧耳倾听”&#xff0c;聊天的一些关键字很快就会出现在手机浏览器的搜索栏中。 携程会给我自动推荐景点&#xff0c;美团会给我推荐美食&…

【FLoyd算法(Floyd到底做什么用 进来看看)】牛的旅行【进来学习做一道题的步骤-1.读题根据样例 概括出题意-2.分析算法-3.优化算法】

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09;   文章字体风格&#xff1a; 红色文字表示&#…

混合动力汽车耐久测试

一 背景 整车厂可通过发动机和电机驱动的结合为多款车型提供混合动力驱动技术。汽车集成电机驱动可大大减少二氧化碳的排放&#xff0c;不仅如此&#xff0c;全电动驱动或混合动力驱动的汽车还将使用户体验到更好的驾驶感受&#xff0c;且这种汽车可通过电动机来实现更快的加速…

操作系统原理 —— 内存覆盖与交换(十九)

什么情况下需要覆盖与交换 要弄清楚什么是覆盖与交换的概念&#xff0c;首先我们要知道在什么情况下才会使用到覆盖与交换。 在早期的计算机内存很小的时候&#xff0c;比如 IBM 推出的第一台 PC 机最大只支持 1 MB 大小的内存&#xff0c;因此会经常出现内存大小不够的情况&…

Dell服务器安装Ubuntu系统

1、下载镜像&#xff0c;做启动盘 镜像链接 http://old-releases.ubuntu.com/releases/20.04.2/ubuntu-20.04.2-live-server-amd64.iso 版本可以根据自己要求选择。 做启动盘 我用的是ultraiso 记得先格式化&#xff0c;再写入。 2、 设置BIOS启动 按F11&#xff0c;进入BIOS…

708教室使用方法

一、教室平面图 708教室的布局如下&#xff0c;重要的设备已经在图中标出。总开关、一体机和机柜。   二、使用方法 2.1 房间机器上电 进门后首先走到“总开关位置”&#xff0c;将电匝闭合。 原来的开关如图所示&#xff0c;有3组开关&#xff0c;1号组开关用于控制插座、…