Linux文件系统(1)

Linux文件系统(1)

📟作者主页:慢热的陕西人

🌴专栏链接:Linux

📣欢迎各位大佬👍点赞🔥关注🚓收藏,🍉留言

本博客主要内容从系统层面重新认识我们的文件系统

文章目录

  • Linux文件系统(1)
    • 我们先问几个问题:
    • 一些现有的结论
    • 快速回忆C文件操作
    • 系统的文件接口
    • 总结

我们先问几个问题:

①你真的理解文件原理和操作了吗?

②是不是只有C/C++有文件操作呢?有没有一种统一的视角去看所有的语言的文件操作呢?

③操作文件的时候,第一件事,都是打开文件,打开文件是做什么呢?如何理解呢?

一些现有的结论

①文件 = 内容 + 属性 —>针对文件的操作,对内容的操作,对属性的操作。

②当文件没有被操作的时候,文件一般在什么位置?磁盘

③当我们对文件进行操作的时候,文件需要在哪里?内存,冯诺依曼体系结构决定了。

④当我们对文件进行操作的时候,文件需要提前被load到内存,load是内容还是属性?至少得有属性。

⑤当我们对文件进行操作的时候,文件需要提前被load到内存,是不是只有我们一个人在load呢?不是,内存中一定存在着大量不同的文件的属性。

⑥所以综上所述,打开文件就是将文件的核心的属性和一些内容load到内存,OS内部一定会同时存在大量的被打开的文件,那么操作系统要不要管理这些被打开的文件呢?先描述再组织!

先描述:先构建在内存中的文件结构体(对象) struct XXX{就可以从磁盘来, struct file * next}

a.每一个被打开的文件,都要在OS内对应文件对象的struct结构体,可以将所有的struct file结构体用某种数据结构链接起来----,在OS内部,对被打开的文件进行管理,就被转换成了对链表的增删查改。

结论:文件被打开,OS要为被打开的文件,创建对应的内核数据结构,

struct file
{
    //各种属性
    //各种链接关系
}

⑦文件其实可以被分为两大类:磁盘文件内存文件(被我们打开的文件)。

⑧文件被打开,是谁在打开?OS,是谁让操作系统打开的呢?进程

⑨我们之前的所有的文件操作,都是进程和被打开文件的关系。

⑩进程和被打开文件的关系:struct struct_taskstruct file的关系

快速回忆C文件操作

fopen函数:用于打开文件的函数

image-20231112175415441

一些参数:

image-20231112184405352

perror函数:用于将错误码装化成对用的strerror

image-20231112175639963

fclose函数

image-20231112180051947

fputs函数:向文件中写入

image-20231112180231175

写一个程序尝试向文件中写入字符串:


运行结果:

image-20231112181203458

查看文件大小的命令:du -参数 文件名, 参数是单位,比如m,k等,例如我们刚刚这个文件就是1m

[mi@lavm-5wklnbmaja lesson7]$ du -m log.txt
1	log.txt

snprintf函数:向字符串中写入(C语言的格式化支持的很好)

1.第一个函数是向屏幕输出

2.第二函数是向文件输出

3.第三个函数是向字符串(缓冲区)输出

4.第四个函数是向字符串(缓冲区)中输出n个字符

image-20231112182333353

尝试使用一下:

#include<stdio.h>    
    
#define LOG "log.txt"    
    
int main()    
{    
  //w:默认写方式打开文件,如果文件不存在,就创建它    
  //默认如果只是打开,文件内容会自动被清空    
  //同时,每次进行写入的时候,都会从最开始开始写入     
  FILE *fp = fopen(LOG, "w");    
  if(fp == NULL)    
  {    
    perror("fopen"); // fopen:xxxx    
    return 0;    
  }    
    
  //正常进行文件操作    
  const char* msg = "hello xupt, hello xiyou";                                                                                                                 
  int cnt = 5;    
  while(cnt)    
  {    
    fprintf(fp, "%s : %d : xupt \n", msg, cnt);    
   // fputs(msg, fp);    
    cnt--;    
  }    
    
  fclose(fp);    
  return 0;    
}  

运行结果:

image-20231112182923960

snprintf的使用:

#include<stdio.h>    
    
#define LOG "log.txt"    
    
int main()    
{    
  //w:默认写方式打开文件,如果文件不存在,就创建它    
  //默认如果只是打开,文件内容会自动被清空    
  //同时,每次进行写入的时候,都会从最开始开始写入     
  FILE *fp = fopen(LOG, "w");    
  if(fp == NULL)    
  {    
    perror("fopen"); // fopen:xxxx    
    return 0;    
  }    
    
  //正常进行文件操作    
  const char* msg = "hello xupt, hello xiyou";    
  int cnt = 5;    
  while(cnt)    
  {    
    char buffer[256];    
    snprintf(buffer, sizeof(buffer), "%s : %d\n", msg , cnt);                                                                                                  
    fputs(buffer, fp);    
   // fprintf(stdout, "%s : %d : xupt \n", msg, cnt);    
   // fputs(msg, fp);    
    cnt--;    
  }    
    
  fclose(fp);    
  return 0;    
}  

运行结果:

image-20231112184036214

stdout

那么我们观察到它们的类型都是我们对应的FILE因为在Linux系统中一切皆文件,接下来我们尝试用fprintf函数向stdout中输出一些字符串看看

image-20231112183052514

代码:

#include<stdio.h>    
    
#define LOG "log.txt"    
    
int main()    
{    
  //w:默认写方式打开文件,如果文件不存在,就创建它    
  //默认如果只是打开,文件内容会自动被清空    
  //同时,每次进行写入的时候,都会从最开始开始写入     
  FILE *fp = fopen(LOG, "w");    
  if(fp == NULL)    
  {    
    perror("fopen"); // fopen:xxxx    
    return 0;    
  }    
    
  //正常进行文件操作    
  const char* msg = "hello xupt, hello xiyou";    
  int cnt = 5;    
  while(cnt)    
  {    
    fprintf(stdout, "%s : %d : xupt \n", msg, cnt);                                                                                                            
   // fputs(msg, fp);    
    cnt--;    
  }    
    
  fclose(fp);    
  return 0;    
} 

运行结果:

直接向屏幕输出了字符串

image-20231112183450307

fgets函数

把文件中的信息写入到缓冲区中

代码:

#include<stdio.h>    
    
#define LOG "log.txt"    
    
int main()    
{    
  //w:默认写方式打开文件,如果文件不存在,就创建它    
  //1.默认如果只是打开,文件内容会自动被清空    
  //2.同时,每次进行写入的时候,都会从最开始开始写入     
  //a:不会清空文件,每次都从文件的结尾开始写入     
  //r:以读的形式,打开文件    
  FILE *fp = fopen(LOG, "r");    
  if(fp == NULL)    
  {    
    perror("fopen"); // fopen:xxxx    
    return 0;    
  }    
    
    
  while(1)    
  {    
    char line[128];    
    if(fgets(line, sizeof(line), fp) == NULL) break;    
    else printf("%s", line);    
  }    
   fclose(fp);
  return 0;
}

运行结果:

image-20231112185104125

那么到此结束,开始系统的文件操作

系统的文件接口

open

pathname:表示要打开的文件路径+文件名

flag:代表打开文件的方式

image-20231112185909086

返回值:

返回一个文件描述符,-1表示错误,然后回设置error

image-20231112190103226

参数:

这些参数都是一些宏定义,所以函数参数的类型是int,他们分别对应一些含义,这只是其中一部分。

这些参数被称为标志位,类似于之前介绍的位图,每个参数对应一个比特位

我们如果要传多个标志位:直接将他们|运算一下即可。

image-20231112190508804

image-20231112190641570

OS一般如何让用户给自己传递标志位的?

1.我们怎么做的?

2.系统怎么做的?我们用一个比特位表示一个标记位,那么一个int类型就可以表示32个标志位(位图)。

接下来一个小demo1实际操作演示一下:

#include<stdio.h>

#define ONE 0x1
#define TWO 0x2
#define THREE 0x4
#define FOUR 0x8
#define FIVE 0x10
    
void Print(int flag)    
{    
  if(flag & ONE) printf("hello 1\n");    
  if(flag & TWO) printf("hello 2\n");    
  if(flag & THREE) printf("hello 3\n");    
  if(flag & FOUR) printf("hello 4\n");    
  if(flag & FIVE) printf("hello 5\n");    
}    
    
int main()    
{    
  Print(ONE);   //打印1 
  printf("-------------------------------\n");    
  Print(TWO);    //打印2
  printf("-------------------------------\n");    
  Print(THREE);    //打印3
  printf("-------------------------------\n");    
  Print(FOUR);    //打印4
  printf("-------------------------------\n");    
  Print(FIVE);    //打印5                                                                                                                                      
  printf("-------------------------------\n");    
  Print(ONE | TWO | THREE | FOUR | FIVE);   //打印12345  
  printf("-------------------------------\n");    
  Print(ONE | THREE | FIVE); //打印135
  return 0;    
}

运行结果:

image-20231112192748966

那么其实flag就是如此工作的。

尝试使用一下:

#include<stdio.h>    
#include<errno.h>    
#include<string.h>    
#include<stdlib.h>    
#include<sys/types.h>    
#include<sys/stat.h>    
#include<unistd.h>    
    
#include<fcntl.h>    
//系统方案    
    
#define LOG "log.txt"    
    
int main()    
{    
  int fd = open(LOG, O_WRONLY);    
    
  printf("fd : %d errno : %d errnostring: %s\n", fd, errno, strerror(errno));                                                                                  
    
  close(fd);    
  return 0;    
}  

我们查看结果却返回了-1,原因是:open函数不会创建文件,没有的指定文件的时候就会报错!

image-20231112194839982

我们创建了一个之后或者我们在参数中加上O_CREAT

  int fd = open(LOG, O_WRONLY | O_CREAT); 

image-20231112194856013

但是我们去尝试查看这个文件的时候:

image-20231112195241507

那么我们可以使用open的一个重载:int open(const char *pathname, int flags, mode_t mode);

第三个参数mode是我们可以给予文件的默认权限:比如我们给个0666:

  int fd = open(LOG, O_WRONLY | O_CREAT, 0666); 

image-20231112195528363

但是我们发现文件log.txt的权限却不是0666,原因其实是因为文件的权限还受umask的影响:默认权限&(~mask)

我们查看一下umask是否是2:果然如此我们把它改成0000在运行一下程序看看

image-20231112195724141

是我们想要的结果了!

image-20231112195825596

其实我们已经可以猜到:各种语言中的文件操作其实内部都是封装了操作系统给的接口,每个语言都进行了自己的个性化,所以每个语言的文件操作都不尽相同,但是底层都是封装了操作系统给的接口!

write:操作系统提供的,向文件写入的接口

fd:文件描述符

buf:缓冲区

count: 写入缓冲区的大小

那么write的功能就是:将缓冲区buf中的count个字节的信息,写入到open返回的fd的对应的文件

返回值:表示实际写入了多少字节,写入失败返回-1

image-20231112200426541

代码:

#include<string.h>    
#include<stdlib.h>    
#include<sys/types.h>    
#include<sys/stat.h>    
#include<unistd.h>    
    
#include<fcntl.h>    
//系统方案    
    
#define LOG "log.txt"    
    
int main()    
{    
  int fd = open(LOG, O_WRONLY | O_CREAT, 0666);    
    
  printf("fd : %d errno : %d errnostring: %s\n", fd, errno, strerror(errno));    
    
    
  //写入    
  const char * msg = "hello xupt";    
  int cnt = 5;    
  while(cnt)    
  {    
    char line[128];    
    snprintf(line, sizeof(line), "%s , %d \n", msg, cnt);    
    
    write(fd, line, strlen(line));    
                                                                                                                                                               
    cnt--;    
  } 

运行结果:

image-20231112201547388

那么其实这里涉及到一个问题:就是我是否要往 write(fd, line, strlen(line));中的strlen(line) + 1,结论是不需要的。因为\0是C语言的标准,操作系统内部没有\0的标准。所以不需要去+1;

另外:我们这里的调用int fd = open(LOG, O_WRONLY | O_CREAT, 0666);,不会对文件做清空,如果我们要打开的时候清空,只需要加上参数O_TRUNC;

就是这样:int fd = open(LOG, O_WRONLY | O_CREAT | O_TRUNC, 0666); 等价于我们的fopen(filename, "w");

我们的:fopen(filename, "a"); 等价于int fd = open(LOG, O_WRONLY |O_APPEND | O_CREAT, 0666);

read:将文件中的信息整体读取到缓冲区buf中;

image-20231112203754608

代码:

#include<sys/stat.h>    
#include<unistd.h>    
    
#include<fcntl.h>    
//系统方案    
    
#define LOG "log.txt"    
    
int main()    
{    
  //int fd = open(LOG, O_WRONLY | O_CREAT | O_TRUNC, 0666);    
  int fd = open(LOG, O_RDONLY, 0666);    
    
  printf("fd : %d errno : %d errnostring: %s\n", fd, errno, strerror(errno));    
    
    
//读取    
    
  char buf[1024];    
    
  ssize_t n = read(fd, buf, sizeof(buf) - 1); //使用操作系统进行IO的时候,要注意\0问题    
  if(n > 0)    
  {    
    buf[n] = '\0';    
    
    printf("%s", buf);    
  }    
   return 0;
}

运行结果:

image-20231112204423201


总结

接下来我们可以回答开始的三个问题:

①你真的理解文件原理和操作了吗?

文件原理和操作是需要从操作系统的角度去学习,我们从语言角度学习的远远不够。

②是不是只有C/C++有文件操作呢?有没有一种统一的视角去看所有的语言的文件操作呢?

并不是,所有的编程语言都有自己的文件操作,其实他们都是对操作系统文件操作的封装。所以我们从操作系统的角度去统一认识文件操作。

③操作文件的时候,第一件事,都是打开文件,打开文件是做什么呢?如何理解呢?

打开文件就是将文件的属性和一些内容加载到内存,创建对应的struct_file

到这本篇博客的内容就到此结束了。
如果觉得本篇博客内容对你有所帮助的话,可以点赞,收藏,顺便关注一下!
如果文章内容有错误,欢迎在评论区指正

在这里插入图片描述

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

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

相关文章

java的类和继承构造

一些小技巧 类和对象 什么是类&#xff0c;对象&#xff0c;方法&#xff1f; 在下面的 Java 代码中&#xff0c;定义了一个名为 Person 的类&#xff0c;并提供了构造方法来初始化对象的属性。类中定义了 eat、sleep 和 work 三个方法&#xff0c;用于表示人的行为。在 main 方…

为什么Android 手机这么慢?如何提高 Android 手机的运行速度

速印机&#xff08;理想、荣大等&#xff09;、复印机&#xff08;夏普、东芝、理光、佳能、震旦等全系列&#xff09;、打印机、扫描仪、传真机、多媒体教学一体机、交互式电子白板、报警器材、监控、竞业达监考设备及其它监考设备、听力考试设备、特种安防设备维护及维修。吴…

JavaScript_动态表格_添加功能

1、动态表格_添加功能.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>动态表格_添加功能</title><style>table{border: 1px solid;margin: auto;width: 100%;}td,th{text-align: ce…

Django 基于ORM的CURD、外键关联,请求的生命周期

文章目录 基于ORM进行的CURDORM外键关联Django请求的生命周期流程图 基于ORM进行的CURD 本质上就是通过面向对象的方式&#xff0c;对数据库的数据进行增、删、改、查。 这里将会将我们之前所有内容结合到一起&#xff0c;首先确保基于上序操作已经建立好了UserInfo表&#xff…

odoo16 库存初始化 excel导入问题2

产品导入模板: excel内容: 导入测试 查看可能的值,发现没有ml,在计量单位中增加ml选项(不选创建,知道为什么不,仔细想想,创建不知ml是什么单位) 位置不能在此导入,故取消 测试正常 导入成功 总结:产品导入时,位置无法指定,只建产品名称,计量单位,采购单位,

自定义Graph Component:1.1-JiebaTokenizer具体实现

JiebaTokenizer类继承自Tokenizer类&#xff0c;而Tokenizer类又继承自GraphComponent类&#xff0c;GraphComponent类继承自ABC类&#xff08;抽象基类&#xff09;。本文使用《使用ResponseSelector实现校园招聘FAQ机器人》中的例子&#xff0c;主要详解介绍JiebaTokenizer类…

python工具CISCO ASA设备任意文件读取

​python漏洞利用 构造payload&#xff1a; /CSCOT/translation-table?typemst&textdomain/%2bCSCOE%2b/portal_inc.lua&default-language&lang../漏洞证明&#xff1a; 文笔生疏&#xff0c;措辞浅薄&#xff0c;望各位大佬不吝赐教&#xff0c;万分感谢。 免…

golang Copier 数据复制

Copier I am a copier, I copy everything from one to another Copier是golang实现的&#xff0c;实现不同数据结构之间数据复制的工具包 github地址 使用方法 以User和Employee之间相互复制为例 使用的版本为 v0.3.5 入门 package mainimport ("fmt""git…

DevChat 初探之 RBAC 模型的实现

今天我们来尝试一款编程辅助助手 DevChat, 看能不能提升咱们的日常编程效率。作为一款编程助手&#xff0c;我们来看看它与 Copilot, CodeWhisperer 同领域产品的一些区别和特色。定个小目标&#xff0c;通过 DevChat 实现一个简单的 RBAC 模型&#xff0c;小试牛刀一下&#x…

Acer宏碁Aspire A715-75G笔记本工厂模式原厂Windows10预装OEM系统2004带恢复功能

下载链接&#xff1a;https://pan.baidu.com/s/1nJFd25lElc1VAPf_RqSDYA?pwdd05h 提取码&#xff1a;d05h 原装出厂系统自带所有驱动、Office办公软件、出厂主题壁纸、系统属性Acer宏基专属的LOGO标志、 Acer Care Center、Quick Access等预装程序 所需要工具&#xff1a…

kubenetes-kubelet组件

一、kubelet架构 每个节点都运行一个kubelet进程&#xff0c;默认监听10250端口&#xff0c;kubelet作用非常重要&#xff0c;是节点的守护神。 接收并执行 master发来的指令。管理Pod及Pod中的容器。每个kubelet进程会在API Server 上注册节点自身信息&#xff0c;定期向mast…

mysql讲解2 之事务 索引 以及权限等

系列文章目录 mysql 讲解一 博客链接 点击此处即可 文章目录 系列文章目录一、事务1.1 事务的四个原则1.2 脏读 不可重复读 幻读 二、索引三,数据库用户管理四、mysql备份 一、事务 1.1 事务的四个原则 什么是事务 事务就是将一组SQL语句放在同一批次内去执行 如果一个SQ…

常见后缀名总结 为你指点迷津

相信在日常的学习和工作中&#xff0c;大家一定会遇到各种各样的文件类型&#xff0c;他们的后缀名类型各不相同&#xff0c;诸多陌生的文件格式经常让大家不知道他们存在于电脑的意义&#xff0c;想删又没法删&#xff0c;想执行又无法执行。 今天&#xff0c;学长就带领大家一…

笔记:AI量化策略开发流程-基于BigQuant平台(二)

五、模型训练股票预测 完成了数据处理&#xff0c;接下来就可利用平台集成的各算法进行模型训练和模型预测啦。本文将详细介绍“模型训练”、“模型预测”两大模块操作、原理。 模型训练和模型预测是AI策略区别于传统量化策略的核心&#xff0c;我们通过模型训练模块利用训练…

c语言练习11周(6~10)

输入任意字串&#xff0c;将串中除了首尾字符的其他字符升序排列显示&#xff0c;串中字符个数最多20个。 题干 输入任意字串&#xff0c;将串中除了首尾字符的其他字符升序排列显示&#xff0c;串中字符个数最多20个。输入样例gfedcba输出样例gbcdefa 选择排序 #include<s…

每日一题(LeetCode)----数组--长度最小的子数组

每日一题(LeetCode)----数组–长度最小的子数组 1.题目&#xff08; 209.长度最小的子数组&#xff09; 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [numsl, numsl1, ..., numsr-1, numsr] &…

深度解析找不到msvcp120.dll相关问题以及解决方法

​在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“msvcp120.dll丢失”。这个错误通常会导致某些应用程序无法正常运行&#xff0c;给用户带来很大的困扰。那么&#xff0c;如何解决msvcp120.dll丢失的问题呢&#xff1f;本文将为大家介绍…

手机地磁传感器与常见问题

在手机中&#xff0c;存在不少传感器&#xff0c;例如光距感&#xff0c;陀螺仪&#xff0c;重力加速度&#xff0c;地磁等。关于各传感器&#xff0c;虽功能作用大家都有所了解&#xff0c;但是在研发设计debug过程中&#xff0c;却总是会遇到很多头疼的问题。关于传感器&…

BM65 最长公共子序列(二)

动态规划 BM65 最长公共子序列&#xff08;二&#xff09; 这道题是动态规划的典型例题。 思路 题目要求获取最长公共子序列&#xff0c;我们要先求最长公共子序列的长度&#xff0c;然后根据这个长度倒推从而获取这个子序列。注意&#xff1a;子序列不是子串&#xff0c;子…

C语言进阶

数组 在基础篇说过&#xff0c;数组实际上是构造类型之一&#xff0c;是连续存放的。 一维数组 定义 定义格式&#xff1a;[存储类型] 数据类型 数组名标识符[下标]; 下面分模块来介绍一下数组的定义部分的内容。 1、初始化和元素引用&#xff1a; 可以看到数组是连续存储…