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 0x10void 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);    //打印2printf("-------------------------------\n");    Print(THREE);    //打印3printf("-------------------------------\n");    Print(FOUR);    //打印4printf("-------------------------------\n");    Print(FIVE);    //打印5                                                                                                                                      printf("-------------------------------\n");    Print(ONE | TWO | THREE | FOUR | FIVE);   //打印12345  printf("-------------------------------\n");    Print(ONE | THREE | FIVE); //打印135return 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

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

在这里插入图片描述

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

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

相关文章

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

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

【入门Flink】- 10基于时间的双流联合(join)

统计固定时间内两条流数据的匹配情况&#xff0c;需要自定义来实现——可以用窗口&#xff08;window&#xff09;来表示。为了更方便地实现基于时间的合流操作&#xff0c;Flink 的 DataStrema API 提供了内置的 join 算子。 窗口联结&#xff08;Window Join&#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…

SOME/IP 协议介绍(四)RPC协议规范

RPC协议规范 本章描述了SOME/IP的RPC协议。 传输协议绑定 为了传输不同传输协议的SOME/IP消息&#xff0c;可以使用多种传输协议。SOME/IP目前支持UDP和TCP。它们的绑定在以下章节中进行了解释&#xff0c;而第[SIP_RPC_450页&#xff0c;第36页]节讨论了选择哪种传输协议。…

【Go入门】面向对象

【Go入门】面向对象 前面两章我们介绍了函数和struct&#xff0c;那你是否想过函数当作struct的字段一样来处理呢&#xff1f;今天我们就讲解一下函数的另一种形态&#xff0c;带有接收者的函数&#xff0c;我们称为method method 现在假设有这么一个场景&#xff0c;你定义…

Linux驱动开发——PCI设备驱动

目录 一、 PCI协议简介 二、PCI和PCI-e 三、Linux PCI驱动 四、 PCI设备驱动实例 五、 总线类设备驱动开发习题 一、 PCI协议简介 PCI (Peripheral Component Interconnect&#xff0c;外设部件互联) 局部总线是由Intel 公司联合其他几家公司一起开发的一种总线标准&#…

前端开发引入element plus与windi css

背景 前端开发有很多流行框架&#xff0c;像React 、angular、vue等等&#xff0c;本文主要讲vue 给新手用的教程&#xff0c;其实官网已经写的很清楚&#xff0c;这里再啰嗦只是为了给新手提供一个更加简单明了的参考手册。 一、打开element plus官网选则如图所示模块安装命令…

Nginx缓存基础

1 nginx缓存的流程 客户端需要访问服务器的数据时&#xff0c;如果都直接向服务器发送请求&#xff0c;服务器接收过多的请求&#xff0c;压力会比较大&#xff0c;也比较耗时&#xff1b;而如果在nginx缓存一定的数据&#xff0c;使客户端向基于nginx的代理服务器发送请求&…

华为L410上制作内网镜像模板02

原文链接&#xff1a;华为L410上制作离线安装软件模板02 hello&#xff0c;大家好啊&#xff0c;今天给大家带来第二篇在内网搭建Apache服务器&#xff0c;用于安装完内网操作系统后&#xff0c;在第一次开机时候&#xff0c;为系统安装软件的文章&#xff0c;今天给大家介绍在…

Linux之基础开发工具gdb调试器的使用(三)

文章目录 一、Linux调试器-gdb使用1、安装gdb2、背景3、Debug和release4、区分Debug和release 二、Linux调试器-gdb命令演示1、显示指定行之后的代码&#xff08;自动记录最后一条指令&#xff09;2、断点1、打印断点2、查看断点3、删除断点4、使能&#xff08;禁用/开启&#…

StartUML的基本使用

文章目录 简介和安装创建包创建类视图时序图 简介和安装 最近在学习一个项目的时候用到了StartUML来构造项目的类图和时序图 虽然vs2019有类视图&#xff0c;但是也不是很清晰&#xff0c;并没有生成uml图&#xff0c;但是宇宙最智能的IDE IDEA有生成uml图的功能 下面就简单介…

Flowable 外部表单

内置表单需要在每个节点中去配置&#xff0c;当如果多个节点使用同一套表单属性就要配置多次比较麻烦&#xff0c;修改的时候也要修改多次&#xff0c;外部表单可以定义一次&#xff0c;然后其它节点都去引用同一个表单属性。 外部表单需要定义一个.form后缀的文件。 外部表单…

关于值传递和引用传递的问题记录

目录 1. 问题概述 1.1 测试 1.2 结果 2. ArrayList和Arrays.ArrayList 1. 问题概述 最近忙着写论文很久没更新了&#xff0c;趁现在有时间简单记录一下最近遇到的一个坑。 对于Java中的List<>类型的对象&#xff0c;按我以前理解是引用传递&#xff0c;但有一点要注…

Excel中使用数据验证、OFFSET实现自动更新式下拉选项

在excel工作簿中&#xff0c;有两个Sheet工作表。 Sheet1&#xff1a; Sheet2&#xff08;数据源表&#xff09;&#xff1a; 要实现Sheet1中的“班级”内容&#xff0c;从数据源Sheet2中获取并形成下拉选项&#xff0c;且Sheet2中“班级”内容更新后&#xff0c;Sheet1中“班…

SMART PLC MODBUSTCP速度测试

SMART PLC MODBUSTCP通信详细介绍请参看下面文章链接: S7-200SMART PLC ModbusTCP通信(多服务器多从站轮询)_matlab sumilink 多个modbustcp读写_RXXW_Dor的博客-CSDN博客文章浏览阅读6.4k次,点赞5次,收藏10次。MBUS_CLIENT作为MODBUS TCP客户端通过S7-200 SMART CPU上的…

ARM寄存器及功能介绍/R0-R15寄存器

1、ARM 寄存器组介绍 ARM 处理器一般共有 37 个寄存器&#xff0c;其中包括&#xff1a; &#xff08;1&#xff09; 31 个通用寄存器&#xff0c;包括 PC&#xff08;程序计数器&#xff09;在内&#xff0c;都是 32 位的寄存器。 &#xff08;2&#xff09; 6 个状态寄存器…

【中间件篇-Redis缓存数据库02】Redis高级特性和应用(慢查询、Pipeline、事务、Lua)

Redis高级特性和应用(慢查询、Pipeline、事务、Lua) Redis的慢查询 许多存储系统&#xff08;例如 MySQL)提供慢查询日志帮助开发和运维人员定位系统存在的慢操作。所谓慢查询日志就是系统在命令执行前后计算每条命令的执行时间&#xff0c;当超过预设阀值,就将这条命令的相关…

spring boot 中@Value读取中文配置时乱码

1.spring boot 读取application.properties 该文件是iso8859编码 如果是直接写中文 读取时会乱码 显示成?? 必须得转ascii码才能正常显示 其他方法测试也不行 Value("${apig.order.tiaokong.qianzi}") private String apigOrderTiaokongQianzi;

LabVIEW中NIGPIB设备与驱动程序不相关的MAX报错

LabVIEW中NIGPIB设备与驱动程序不相关的MAX报错 当插入GPIB-USB设备时&#xff0c;看到了NI MAX中列出该设备&#xff0c;但却显示了黄色警告指示&#xff0c;并且指出Windows没有与您的设备相关的驱动程序。 解决方案 需要安装能兼容的NI-488.2驱动程序。 通过交叉参考以下有…

链表的实现(文末附完整代码)

链表的概念及结构 链表是一种物理存储结构上非连续、非顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次序实现的 我们在上一篇文章所学习的顺序表是连续存储的 例如&#xff1a; 顺序表就好比火车上的一排座位&#xff0c;是连续的 而链表就好比是火车…