linux文件系统与基础io(代码片段)

燕麦冲冲冲 燕麦冲冲冲     2022-12-11     624

关键词:

文件的宏观理解

1⃣️文件在哪里呢?
“狭义”上在磁盘,“广义”上一切皆文件。
主要研究“狭义”,磁盘为外设,那么对文件的所有操作,本质都是对外设的输入输出,简称IO
2⃣️文件是什么?
空文件占不占用磁盘空间?依旧占用。
文件 = 属性 + 内容
所有的文件操作,无外乎就是两种操作,对属性和对内容。
3⃣️从系统角度看内存
文件操作代码-》可执行程序-〉从磁盘加载到内存-》变成进程
对文件的操作,本质都是进程对文件的操作!

C语言提供的对文件操作接口是用户层的,真正起作用的是系统调用接口

一些c文件接口的使用注意事项⚠️

FILE* fp = fopen(./log.txt”, “a”);
if(fp == NULL)
	perror(“fopen”);

const char* msg = “hello world\\n”;
fwrite(msg, strlen(msg), 1, fp);
fclose(fp);

1⃣️使用fwrite的时候,需要把字符串的’\\0’写入文件吗?
不加,因为‘\\0’是C的规定,用于标识字符串结束,而与文件无关。

2⃣️使用fopen的时候,反复使用a模式在文件中追加了几次后,再使用w模式写入,此时文件是啥样的?
以w模式写入,会直接清空掉以前所有的内容,再重新写入。

3⃣️filename前不加./时也会在当前路径下创建,当前路径是怎么找到的?
每个进程,都有一个内置的属性cwd,标识该进程认为自己所处的路径。

如何理解一切皆文件📁

1⃣️任何C程序,都默认会打开三个“文件”📁, 分别叫做标准输入(stdin),标准输出(stdout),标准错误(stderr)。分别为键盘文件,显示器文件、显示器文件📁
明明这三个都是硬件,那么为什么还被叫做文件?
Linux下一切皆文件。

2⃣️一切皆文件如何实现的?
所有的外设硬件,无外乎就是read和write
(1)不同的硬件,对应的读写方式肯定是不一样的。
(2)OS需要管理大量的文件——先描述,再组织

OS管理一个个file结构体对象形成的链表,每个对象代表一种文件,其文件的读写方式由函数指针指向。

3⃣️为什么C程序会默认打开stdin、stdout、stderr?仅仅是C吗?
(1)便于语言进行上手使用。
这三个文件对应着键盘和显示器文件,要调用如scanf、printf、perrpor等库函数必须需要打开这些文件,这些库函数的底层也一定会涉及到这几个硬件文件。
(2)几乎任何语言都是这样的。

open等系统调用接口的使用示例
open
返回值:file descriptor 文件描述符
对比C语言的返回值:FILE* 文件指针
参数:第一个为文件的路径,第二个为打开文件的方式,第三个为创建新文件时为其赋予的权限,若文件已存在就不传。
第二个参数是利用宏定义的整型参数,每种权限在其二进制的某一位上为1,判断是否满足某一权限,就拿这个权限&该参数。

	1 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<fcntl.h>
  5 #include<unistd.h>
  6 #include<string.h>
  7 int main()
  8 
  9   int fd = open("./log.txt", O_WRONLY|O_CREAT, 0644);
 10   if(fd < 0)
 11   
 12     perror("open");
 13     return 1;
 14   
 15   const char* msg = "hello system call!\\n";                                                                                       
 16   write(fd, msg, strlen(msg));
 17   close(fd);
 18   return 0;
 19 

为什么每种语言需要封装一个自己的接口来调用系统接口呢?
兼容自身语法特性,系统调用使用成本高,而且不具备可移植性。
上述代码是在Linux系统下使用的,Windows肯定不可兼容。而库函数可以自动根据平台,选择自己底层对应的文件接口。

文件描述符详解
其本质是数组的下标,且默认打开的三个文件就占了0、1、2,那么之后创建的文件的描述符会排在后面,除非提前关闭默认的。

(1)用户层看到的fd,本质是系统中维护进程和文件对应关系的数组的下标。
(2)对进程来讲,对所有的文件进行操作,统一使用一套接口(一组函数指针),那么就做到了一切皆文件📁
(3)所谓默认打开的三个文件,其实是有底层系统支持的。默认一个进程在运行的时候,就打开了0,1,2
(4)系统中,分配文件描述符的规则:最小的、未被使用的进行分配。

输出重定向的原理
将本来需要打印到屏幕的,写入到文件里。

通过关闭fd为1的stdout文件,再创建一个新文件,此时新文件的fd为1,再调用printf函数,理论上可以向新文件内写入,但是执行后,却看不到文件中有任何内容。

  1 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<fcntl.h>
  5 #include<unistd.h>
  6 #include<string.h>
  7 int main()
  8 
  9   close(1);
 10   int fd = open("log.txt", O_CREAT|O_WRONLY, 0644);
 11   printf("log.txt's fd is %d\\n", fd);
 12   printf("hello world!\\n");
 13   close(fd);                                                                                                                      
 14   return 0;
 15 

穿插知识点:C语言FILE*与fd的耦合原理
FILE是一个结构体,其中有个成员变量int _fileno,即为C语言的文件描述符。

printf不带\\n时,输出的内容会暂存于缓冲区内,这个缓冲区在哪里呢?
FILE结构体内还有缓冲区相关内容。

总结:struct FILE 内部包含1、底层对应的文件描述符。2、应用层C语言提供的缓冲区数据。

所以解决方案是在执行close(fd)前,利用fflush(stdout)进行刷新C语言FILE结构体内的缓冲区即可。原因是打印的信息会暂存于缓冲区,如果还未等刷新就关闭文件,缓冲区内的数据就没机会刷新了。

输出重定向也只会更改下标为1指向的stdout显示器文件。

为什么上述代码明明带了\\n却未提前刷新?
因为显示器文件刷新策略是行刷新,但普通文件的刷新策略是要将缓冲区写满才刷新,即为全缓冲刷新策略。

重要知识点再复习

重定向基本原理

通过演示重定向未能成功,证明用户层缓冲区和内核层缓冲区的刷新机制不同。

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4 #include<sys/stat.h>
  5 #include<fcntl.h>
  6 
  7 int main()
  8 
  9   close(1);
 10   int fd = open("log.txt", O_CREAT|O_WRONLY, 0644);
 11   if(fd < 0)
 12     perror("open");
 13     return 1;
 14   
 15   fprintf(stdout, "hello world! fd:%d\\n", fd);
 16   close(fd);                                                                                                                                                         
 17   return 0;
 18 

文件系统的大致框架

为什么存在不同的缓冲区?

如果用户层直接把数据拷贝至内核缓冲区效率较低,而且使得用户与内核得以区别开来。

系统接口和C接口的混用

下述示例中利用三个c接口向显示器上打印三个字符串,再利用系统接口向显示器上打印一个字符串,最后fork创建子进程。

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4 #include<sys/stat.h>
  5 #include<fcntl.h>
  6 #include<string.h>
  7 int main()                                                                                                                         
  8 
  9   printf("I am printf\\n");
 10   fprintf(stdout, "I am fprintf\\n");
 11   fputs("I am fputs\\n", stdout);
 12   const char str[] = "I am write\\n";
 13   write(1, str, strlen(str));
 14   fork();
 15   return 0;
 16 

编译运行程序

把该可执行程序从定向到新文件中

出现两次的是c接口,一次是系统接口。
显示器文件写入策略——行缓冲刷新。——第一种情况的解释
普通文件写入策略——全缓冲刷新。——第二种情况的解释
执行完C语言的三个接口后,结果会暂存于C语言缓冲区内。fork结束后有两个进程,此时程序即将退出,会刷新C语言的缓冲区的那三行到普通文件中,刷新缓冲区本质是对内存进行写入(设置为无效区域),子进程进行写时拷贝,于是出现了两次C接口执行内容,一次系统接口的执行内容也证明了系统缓冲区的存在。

利用dup2接口实现直接重定向

之前关闭显示器文件实现的是模拟重定向,操作繁琐。
原理是修改一号文件描述符对应的指针指向。

int dup2(int oldfd, int newfd);		//重定向目标+新位置 

new是old的一份拷贝。
(1)输出重定向——把要打印到显示器的内容写入文件

  7 int main()
  8 
  9   int fd = open("log.txt", O_CREAT|O_WRONLY, 0644);
 10   if(fd < 0)
 11   
 12     perror("open");
 13     return 1;                                                                                                              
 14   
 15   dup2(fd, 1);
 16   const char str[] = "hello world\\n";
 17   write(1, str, strlen(str));
 18   close(fd);
 19   return 0;
 20 

(2)输入重定向——把文件的内容打印到显示器

  8 int main()
  9 
 10   int fd = open("log.txt", O_RDONLY);
 11   if(fd < 0)
 12   
 13     perror("open");
 14     return 1;
 15   
 16   dup2(fd, 0);
 17   char buffer[1024];
 18   ssize_t num = read(0, buffer, sizeof(buffer) - 1);                                                                       
 19   if(num > 0)
 20   
 21     buffer[num] = '\\0';
 22     printf("echo: %s\\n", buffer);
 23   
 24   return 0;
 25 

程序替换的时候,会不会影响重定向对应的数据结构

不会。程序替换时只涉及到磁盘与内存的交互,还有子进程的页表映射关系的修改,并不会与文件相关的数据结构有关。

文件:打开的+未打开的

打开的:属性与操作方法的表现就是struct file
未打开的:在磁盘上,未被加载到进程。
两者类似于进程与程序的关系。

重要知识点再复习

关于Linux的两点

1、什么是Linux及其特点
一款操作系统,但为企业级,特点开源,因开源而健壮稳定成本低,进而被企业所选择。
2、Linux怎么来的
计算机的发明投入大量成本用于实验,要回本就要将计算机商业化,最早主要是在硅谷进行。于是计算机被许多人使用,但直接操作硬件麻烦,于是操作系统随之而来。unix被一个大学生模仿并开源,社区大佬共同维护,诞生了Linux。

Linux怎么用

命令行操作(多用命令,不用死记)
界面操作(较少使用)

Linux权限管理

分为两类
人:拥有者、所属组、其他
事物属性:rwx
更改权限:(1)chmod u/g/o +/- rwx (2)chmod 777

目录的权限:进入一个目录需要x权限。
粘滞位:目录可以让多用户rwx,所以为了防止他人误删文件,需要设置粘滞位。

Linux软件包管理器yum

yum install
yum remove
编译源代码可能因移植性问题导致报错。
但需要注意更新yum源。

Linux编辑器vim使用

多分析自己那一块效率低下,去学习和使用快捷键

Linux调试器gdb使用

虽然以后用得少而且考得简单,但是现阶段需要多用。

Linux项目自动化构建工具make/Makefile

make是一条命令
Makefile是一个文件,格式为:依赖关系+依赖方法
可维护文件之间的编译结构。

磁盘上的文件系统

机械硬盘的物理结构是一个个的圆盘,而固态硬盘却不是,为了OS便于管理,将这些物理结构抽象成一个数组,对应各个内存区域。由于内存大小很大,可以分成多个区域,为一小块区域设计管理文件的方案,并直接将管理方案复用到其他区域,这就是磁盘上的文件系统。

管理方案

内存分区其实还会被进一步划分成一块块的,叫做块组

boot block是启动块,要是被刮花了就启动不了了,所以十分重要。
super block包含所有块组的文件系统信息,并不是每一个块组都有,是做了冗余备份。
group descriptor table是组内信息,所有块组都有。

一个文件对应一个inode(包括目录)。
inode是一个文件的所有属性集合,但不包括文件名,inode也是数据,需要占据空间。
真正标识文件的不是文件名而是inode。
inode是可以和特定的数据块产生关联。

如何定位文件?
通过路径和目录定位。
目录也是文件,有inode,也有数据块。
目录的inode对应的数据块存放:目录下存放的文件名及其inode的映射关系

inode bitmap的意义是标识inode的使用情况,以便创建新文件的时候快速索引到未使用位置处。
block bitmap标识data blocks的使用情况。

touch一个空文件后,ext*文件系统做了哪些工作?
通过inode bitmap索引到未使用的区域,创建一个inode并填入文件信息,将文件名与其inode到映射关系存储到该文件的目录的数据块中。
如果向文件中写入,通过目录中的数据块找到文件的inode,为该inode分配数据块(通过block bitmap索引),写入数据。
这也能说明了OS为什么不允许同一目录下存在文件名相同的文件。

Linux下属性和内容是分离的,属性由inode保存,内容由data blocks保存

软硬链接🔗

软链接的建立

显示出inode

软链接就是一个普通文件,有自己独立的inode,类似于一个桌面上的快捷方式。保存的是指向所链接文件的路径。

硬链接的建立

硬链接拥有与链接文件相同的inode,没有自己独立的inode,类似C++的引用&(别名)。
本质是在该目录的数据块中创建了硬链接名与inode的映射关系,并没有创建新文件。

第三列数字代表的是硬链接数,可以发现普通文件也至少会有一个,是当前目录的数据块中存放了一组文件名与inode的映射关系。

为什么目录硬链接数默认就是2呢?

简单来说就是目录自己和该目录内的路径是一个东西,类似原理还有上一路径的…

这样设置是为了方便路径的快速转换或者设置相对路径。

文件的三个时间

文件被修改的频率不高,而文件被访问频率较高,所以为了提高效率,单纯访问文件其实并不会修改access时间,如cat
touch文件名,可以刷新所有时间。

使用价值

通过时间之间的比对,在编译代码的过程中判断哪些代码不需要再被重复编译了。

linux基础io(代码片段)

目录系统文件IOopenwritereadclose文件描述符fdFILE重定向使用dup2系统调用文件系统磁盘磁盘和内存交互磁盘的分区与格式化Ext2文件系统的存储方案inode软硬链接软连接硬链接文件的三个时间系统文件IOopenopen接口的作用是打开文件。... 查看详情

《linux从0到99》九基础io(代码片段)

基础IO1.回顾c语言文件操作接口2.系统调用文件操作系统01open函数02read函数03write函数04lseek函数05close函数3.文件描述符0&1&2文件描述符的分配规则(最小未分配原则)4.文件描述符与文件流指针的区别01文件流指针的本... 查看详情

linux下的基础io(代码片段)

目录一.C语言文件IO操作(库函数)1.打开文件fopen 2.写入二进制文件fwrite和读取二进制文件fread3.关闭文件fclose 二.系统文件IO(系统调用接口)1.打开文件open系统调用2.读文件read和写文件write3.关闭文件close  三.文件描述符1.系统管理... 查看详情

linux入门基础io(代码片段)

基础IO✔回顾C文件的接口✔系统文件I/O✔文件描述符文件描述符的分配规则重定向✔FILE缓冲区fclose和close✔dup2系统调用✔理解文件系统inode硬链接软链接文件的三个时间✔回顾C文件的接口在学习C语言时我们了解了一些C语言的对... 查看详情

linux入门基础io(代码片段)

基础IO✔回顾C文件的接口✔系统文件I/O✔文件描述符文件描述符的分配规则重定向✔FILE缓冲区fclose和close✔dup2系统调用✔理解文件系统inode硬链接软链接文件的三个时间✔回顾C文件的接口在学习C语言时我们了解了一些C语言的对... 查看详情

linux基础io(代码片段)

文章目录一.C文件IO相关操作二.系统文件IOopen函数返回值重定向使用dup2系统调用给简易shell中增加重定向功能理解文件系统理解软硬链接三.动态库和静态库制作动静态库一.C文件IO相关操作只有文件名但不带路径的话,默认在... 查看详情

linux基础io(代码片段)

文章目录一.C文件IO相关操作二.系统文件IOopen函数返回值重定向使用dup2系统调用给简易shell中增加重定向功能理解文件系统理解软硬链接三.动态库和静态库制作动静态库一.C文件IO相关操作只有文件名但不带路径的话,默认在... 查看详情

linux文件系统与基础io(代码片段)

文件的宏观理解1⃣️文件在哪里呢?“狭义”上在磁盘,“广义”上一切皆文件。主要研究“狭义”,磁盘为外设,那么对文件的所有操作,本质都是对外设的输入输出,简称IO2⃣️文件是什么?空文... 查看详情

嵌入式基础(代码片段)

...录操作网络一些命令入门LinuxGCCMakefile函数通用Makefile使用文件IO系统调用函数怎么进入内核?Linux软件架构Linux启动过程如何理解Bootloader与Kernel文件系统概念虚拟文件系统、根文件系统和文件系统VFS:根文件系统其他文件系统ub... 查看详情

嵌入式基础(代码片段)

...录操作网络一些命令入门LinuxGCCMakefile函数通用Makefile使用文件IO系统调用函数怎么进入内核?Linux软件架构Linux启动过程如何理解Bootloader与Kernel文件系统概念虚拟文件系统、根文件系统和文件系统VFS:根文件系统其他文件系统ub... 查看详情

linux篇第九篇——基础io(系统文件io+文件描述符+重定向+文件系统+软硬链接)(代码片段)

...识,IO相信大家都不陌生,我们在C/C++中对文件进行读写的操作,也就是文件IO,这篇博客我也会带大家回顾一下。这篇博客还会介绍系统中的文件IO调用的接口,还有文件系统相关的内容和概念,文... 查看详情

linux基础io-io接口,文件描述符,重定向(代码片段)

【Linux】基础IO文章目录【Linux】基础IO一、C语言中文件IO操作1.C语言中的开关读写文件1.1.fopen()1.2.fclose()1.3.fwrite()1.4.fread()2.stdin&&stdout&&stderr3.三个标准流和IO接口二、系统文件IO1.系统级别的开关读写文件1.1.open()1.2.close... 查看详情

linux文件与文件描述符的介绍(代码片段)

基础IO(上)前言C语言文件IO相关操作接口回顾写文件:读文件:三个默认打开的文件回顾:打开文件的方式系统文件IO相关操作接口openclosewriteread代码调用文件描述符fd分配规则文件描述符fd与系统的对应关系... 查看详情

linux基础io(代码片段)

文章目录C语言文件IOC语言文件接口汇总什么是当前路径?默认打开的三个流系统文件I/Oopenopen的第一个参数open的第二个参数open的第三个参数open的返回值closewriteread文件描述符fd文件描述符的分配规则重定向重定向的原理dup2... 查看详情

linux练习生基础io(详细)(代码片段)

...IO的部分,将围绕以下内容进行梳理讲解:复习C文件IO相关操作认识文件相关系统调用接口认识文件描述符,理解重定向对比fd和FILE,理解系统调用和库函数的关系理解文件系统中inode的概念认识软硬链接,对比区... 查看详情

linux基础io(代码片段)

...,心有惊雷,生似静湖。基础IO前言一、C语言中文件的IO操作fopen函数打开文件fread函数读文件fwrite函数写文件stdin&stdout&stderr二、系统I/O操作open函数open的第一个形参open的第二个形参open的第三个形参open函数的返回值... 查看详情

13linux下的基础io(代码片段)

...用接口是无法移植到Windows下的。文章目录一、C语言中的文件接口二、系统文件I/O2.1.系统调用接口open2.2.文件描述符fd(filedescriptor)2.3.补充内容--函数指针访问硬件2.4.重定向的实现原理三、FILE四、dup重定向4.1.使用dup2完... 查看详情

《linux从0到99》九基础io(代码片段)

基础IO1.回顾c语言文件操作接口2.系统调用文件操作系统01open函数02read函数03write函数04lseek函数05close函数3.文件描述符0&1&2文件描述符的分配规则(最小未分配原则)4.文件描述符与文件流指针的区别01文件流指针的本... 查看详情