Linux基础IO ——文件(上)
Posted 风起、风落
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux基础IO ——文件(上)相关的知识,希望对你有一定的参考价值。
文章目录
1. 预备知识
文件= 内容+属性
对应文件的操作,对内容的操作,对属性的操作
当文件没有被操作的时候,一般在磁盘中
当对文件进行操作的时候,一般在内存中,因为冯诺依曼体系规定
当我们对文件进行操作的时候,文件需要提前加载到内存中,提前加载的是属性
当我们对文件进行操作的时候,文件需要提前加载到内存中,不只有你在load,内存中一定存在大量的不同文件属性
- 打开文件本质就是将需要的属性加载到内存中,OS内部一定会同时存在大量的被打开的文件,操作系统就会通过先描述,在组织的方式管理这些被打开的文件
- 先描述,构建在内存中的文件结构体 struct file 文件属性,struct
file*next,表明被打开的文件 每一个被打开的文件,都要在OS内对应 文件对象的struct
结构体,可以将所有的struct结构体通过某种数据结构链接起来,在OS内部,对被打开的文件进行管理,就会转换为对链表的增删查改
结论:文件被打开,OS要为被打开的文件,创建对应的内核数据结构,struct file结构体 ,该结构体包含各种属性,各种链接关系
2. 回忆C接口
fopen
FILE *fopen(const char *path, const char *mode);
第一个参数为 打开文件对应的路径
第二个参数为 打开文件对应的权限
如果打开成功,返回FILE指针,否则返回NULL
创建myfile.c文件
E>#include<stdio.h>
#define LOG "log.txt"
E>int main()
FILE*fp= fopen(LOG,"w");
//默认写方式 打开文件,如果文件不存在,就创建它
if(fp==NULL)
perror("fopen");//报错
return 1;
fclose(fp); //关闭文件
return 0;
fputs
int fputs(const char *s, FILE *stream);
你想要写的字符串,写入特定的流当中,成功返回字符串字符个数,失败返回-1
#include<stdio.h>
#define LOG "log.txt"
int main()
FILE*fp= fopen(LOG,"w");
//默认写方式 打开文件,如果文件不存在,就创建它
if(fp==NULL)
perror("fopen");//报错
return 1;
//进行文件操作
const char*msg="bbb\\n";
fputs(msg,fp);
fclose(fp); //关闭文件
return 0;
将msg字符串中的数据写入fp流中
fprintf
int fprintf(FILE *stream, const char *format, …);
指定文件流,向文件打印
指定文件流fp,而fp打开的文件为log.txt,所以将msg数据打印到log.txt文件中
因为Linux中一切皆文件,所以也可以传入stdout(标准输出流)中,stdout也对应一个文件,即显示器文件
运行可执行程序,结果显示到显示器了
snprintf
int snprintf(char *str, size_t size, const char *format, …);
通过格式化流的方式,把字符串信息自定义格式化到字符串缓冲区中,并规定大小
将msg中的数据打印到buffer字符串中,同时使用fputs将buffer中的数据写入刚刚打开的文件log.txt中
追加方式—— a
追加,不会清空文件,而是每一次写入都是从文件尾部写入的
修改myfile.c文件内容
#include<stdio.h>
#define LOG "log.txt"
int main()
FILE*fp= fopen(LOG,"a");
//默认写方式 打开文件,如果文件不存在,就创建它
if(fp==NULL)
perror("fopen");//报错
return 1;
//进行文件操作
const char*msg="bbb\\n";
int cnt=1;
while(cnt)
fputs(msg,fp);
cnt--;
fclose(fp); //关闭文件
return 0;
多次运行可执行程序,发现可追加
以读方式—— r
char *fgets(char *s, int size, FILE *stream);
从特定的文件流中按行所取对应的文件,将读到的内容放到缓冲区中
修改myfile.c文件内容
#include<stdio.h>
#define LOG "log.txt"
int main()
FILE*fp= fopen(LOG,"r");
if(fp==NULL)
perror("fopen");//报错
return 1;
//进行文件操作
while(1)
char line[128];
if(fgets(line,sizeof(line),fp)==NULL)
break;
else
printf("%s",line);
fclose(fp); //关闭文件
return 0;
从fp中读取到line中,如果当前读取返回NULL说明读取失败 ,返回break
此时运行可执行程序,即可看到对应文件中的内容
3.操作系统如何进行读写文件操作
open
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
第一个参数代表 文件路径+文件名
第二个参数 代表 文件对应的选项(选项的问题后面会提)
如果打开成功了,就会返回新的文件描述符,如果打开失败,返回 -1
操作系统是如何让用户给自己传递标志位的
1. 我们怎么做
通过写一个 函数 若 int XXX (int flag) ,传递参数flag flag=1/2/3 ,将flag进行赋值
2. 系统怎么做
操作系统存在系统调用接口 int YYY (int flag),flag作为一个整数,有32个比特位,可以用一个比特位表示一个标志位
,一个int 就可以同时传递至少32个标记位
此时的flag 就可以看作数据类型 位图 看待
理解标记位的问题
创建test.c文件
#include<stdio.h>
//分别代表从右向左的每个比特位
#define ONE 0x1
#define TWO 0x2
#define THREE 0x4
#define FOUR 0x8
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");
int main()
printf("-------------------------------------\\n");
print(ONE); //打印one
printf("-------------------------------------\\n");
print(ONE|TWO); //打印one two
printf("-------------------------------------\\n");
print (ONE|TWO|THREE);//打印 one two three
printf("-------------------------------------\\n");
print (ONE|TWO|THREE|FOUR); //打印 one two three four
printf("-------------------------------------\\n");
return 0;
通过整数flag 一次传递多个标志位
0000 0000
将最后的四个比特位通过宏的方式分别记录下来
再与flag 按位与 ,若为真,则说明传递的flag 是 这4个比特位中的其中一个
执行可执行程序,此时分别打印出了 one two three four的运行结果
类比上述 open的第二个参数flag ,存在多个标志位,同通过宏来实现,每一个标志位都代表不同的值
新创建文件权限不正确
- O_CREAT :文件不存在就打开,不存在就创建一个文件
- O_WRONLY: 以写方式打开文件
在myfile.c文件中重新输入代码
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
#define LOG "log.txt"
int main()
int fd=open(LOG, O_WRONLY| O_CREAT);//打开一个文件,若文件不存在则重新创建一个
if(fd==-1)//说明打开失败
printf("fd:%d,errno:%d,errstring:%s\\n",fd,errno,strerror(errno));//打印出错误信息
else
printf("fd :%d\\n", fd);
close(fd); //关闭文件
return 0;
运行可执行程序,发现
假设log.txt文件不存在,通过创建文件并打开文件,发现新文件的权限不正常
因为在Linux中创建一个文件需要有对应的权限的
int open(const char *pathname, int flags, mode_t mode);
所以在文件不存在时,一般采用有三个参数接口的open
mode代表权限
修改myfile.c文件的内容
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#define LOG "log.txt"
int main()
int fd=open(LOG, O_WRONLY| O_CREAT,0666);//打开一个文件,若文件不存在则重新创建一个
if(fd==-1)//说明打开失败
E> printf("fd:%d,errno:%d,errstring:%s\\n",fd,errno,strerror(errno));//打印出错误信息
else
printf("fd :%d\\n", fd);
close(fd); //关闭文件
return 0;
0666 :拥有者 所属组 other都有读写权限
此时log.txt文件拥有正常的权限
但是输入的是666 ,显示的却是664,即other没有写权限
因为创建一个文件时,默认权限受到umask的影响
解决 umask的权限
使用 man 2 umask
查看
#include <sys/types.h>
#include <sys/stat.h>
mode_t umask(mode_t mask);
可以影响当前进程启动时,属于自己的umask,采取就近原则,因为自己设置离的更近所以使用自己设置的umask
而不是系统的umask
修改myfile.c文件的内容
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#define LOG "log.txt"
int main()
umask (0);//将权限掩码设置成0
int fd=open(LOG, O_WRONLY| O_CREAT,0666);//打开一个文件,若文件不存在则重新创建一个
if(fd==-1)//说明打开失败
printf("fd:%d,errno:%d,errstring:%s\\n",fd,errno,strerror(errno));//打印出错误信息
else
printf("fd :%d\\n", fd);
close(fd); //关闭文件
return 0;
使用umask (0) 将权限掩码设置成0
此时log.txt文件的权限为 666
write
通过 man 2 write
查看文件写入接口
ssize_t write(int fd, const void *buf, size_t count);
fd代表文件描述符
buf代表 缓冲区
count代表 缓冲区大小
write将缓冲区的count大小的数据写入 fd中
返回值代表实际写入多少字节
修改myfile.c文件内容
include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#define LOG "log.txt"
int main()
umask (0);//将权限掩码设置成0
int fd=open(LOG, O_WRONLY| O_CREAT,0666);//打开一个文件,若文件不存在则重新创建一个
if(fd==-1)//说明打开失败
printf("fd:%d,errno:%d,errstring:%s\\n",fd,errno,strerror(errno));//打印出错误信息
else
printf("fd :%d\\n", fd);
const char* msg="hello world";
int cnt=5;
while(cnt)
char line[128];
snprintf(line,sizeof(line),"%s,%d\\n",msg,cnt);//将msg和cnt写入line缓冲区中
write(fd,line,strlen(line)+1);//将line写入fd中
cnt--;
close(fd); //关闭文件
return 0;
若 strlen(line)+1 ,则打开log.txt文件时发现出现乱码,因为数字0在ASCII表中属于不可显示字符
所以为了不出现乱码,所以strlen(line) 不应该+1,因为\\0是c语言的规定,不是文件的规定
修改myfile.c文件内容
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#define LOG "log.txt"
int main()
umask (0);//将权限掩码设置成0
int fd=open(LOG, O_WRONLY| O_CREAT,0666);//打开一个文件,若文件不存在则重新创建一个
if(fd==-1)//说明打开失败
printf("fd:%d,errno:%d,errstring:%s\\n",fd,errno,strerror(errno));//打印出错误信息
else
printf("fd :%d\\n", fd);
const char* msg="hello world";
int cnt=5;
while(cnt)
char line[128];
snprintf(line,sizeof(line),"%s,%d\\n",msg,cnt);//将msg和cnt写入line缓冲区中
write(fd,line,strlen(line));//将line写入fd中 strlen不要+1
cnt--;
close(fd); //关闭文件
return 0;
此时strlen不加1
默认不会对原始文件清空
修改myfile.c文件内容,msg和cnt的数据内容
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#define LOG "log.txt"
int main()
umask (0);//将权限掩码设置成0
int fd=open(LOG, O_WRONLY| O_CREAT,0666);//打开一个文件,若文件不存在则重新创建一个
if(fd==-1)//说明打开失败
printf("fd:%d,errno:%d,errstring:%s\\n",fd,errno,strerror(errno));//打印出错误信息
else
printf("fd :%d\\n", fd);
const char* msg="aaaaa";
int cnt=1;
while(cnt)
char line[128];
snprintf(line,sizeof(line),"%s,%d\\n",msg,cnt);//将msg和cnt写入line缓冲区中
write(fd,line,strlen(line));//将line写入fd中 strlen不要+1
cnt--;
close(fd); //关闭文件
return 0;
O_WRONLY | O_CREAT 默认不会对原始文件清空
O_TRUNC : 将文件做清空
修改myfile.c文件内容
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
文章目录
1 语言层面上的文件(C语言)
1.2 C语言接口介绍
在C语言中想要对文件进行操作那么需要用到一下的接口(fopen,fclose,fwrite,fred)对文件进行读写操作
如图所示 : >
代码实现
写形式打开文件
int main()
FILE * fd=fopen("log.txt","w");//打开文件
const char *str="hello word";
fwrite(str,1,strlen(str),fd);//写文件
fclose(fd);//关闭文件
return 0;
结果:
写/读形式读打开文件
代码:
int main()
FILE * fd=fopen("log.txt","w");//W打开文件
FILE * fd=fopen("log.txt","r");//打开文件
char str[1024];
size_t ret=fread((char*)str,1,1024,fd);
str[ret]='\\0';//c语言中字符串以\\0结尾
printf("%s\\n",str);
fclose(fd);//关闭文件
return 0;
读结果
写结果
追加追加形式写文件
代码
int main()
FILE * fd=fopen("log.txt","a");//打开文件
const char *str="hahahaha~";
fwrite(str,1,strlen(str),fd);
fclose(fd);//关闭文件
return 0;
追加结果:
1.2 系统接口介绍
不知大家是否记得,就是用户是无法访问硬件的(要访问则一定要贯穿整个计算机体系结构),一般系统会暴露接口,则库为了方便使用会进行封装,上述介绍的接口,但其实我们也可以直接调用这些系统接口
如图所示 : >
写的形式打开,不存在则创建文件
int mian()
int fd = open("log.txt",O_CREAT|O_WRONLY,0x644);//打开文件没有则创建权限为644
char * str="hello word";
write(fd,str,strlen(str));
close(fd);
结果
读形式打开,不存在则创建(读一般情况都在吧)
int main()
int fd = open("log.txt",O_CREAT|O_RDONLY,0x644);//打开文件没有则创建权限为644
char str[1024];
ssize_t ret=read(fd,str,1024);
str[ret]='\\0';
printf("%s \\n",str);
close(fd);
结果
追加形式打开
int main()
int fd = open("log.txt",O_WRONLY|O_APPEND);
char * str="hahaha~~~";
write(fd,str,strlen(str));
结果
仔细观察系统接口与C语言的接口大致相同,那以后使用C的接口时你就会想到他的底层是如何运作的。但是他们还是有所区别,系统调用返回的是 一个整形(文件描述符)
而C语言是一个FILE * 的指针,后文介绍文件描述符
1.3 三个默认打开输入输出流
不知道大家还记不记得三个标准输入输出流,分别是标准输入,标准输出,标准错误
(stdin,stdout,stderr)所对应的硬件是键盘,显示器,显示器
问
那么为啥要默认打开这三个输入输出流?
答
都有输入输出的要求,且方便操作(如人生第一个程序hello world和你说输入要打开标准输入,打印时要打开标准输出,从入门到入土)
c语言就有对应的接口fprintf,fsanf他们就需自己指定的流输入输出:
如图所示 : >
计算体系结构图 : >
2 系统层面上理解文件
一个文件其实可以划分成俩个板块
- 属性
- 内容(数据)
问
创建一个文件不写入任何数据,会占用内存吗?
答
占用,上述说过一个文件分为俩部分属性与内容
,而创建文件时显然属性也一定创建了
2.1如何理解linux一切皆文件
大家或多或少动听过linux下一切皆文件,那么这个要如何理解呢?其实只需要理解这六个字先描述在组织
问
当一个进程执行时,是否会打开各种文件,硬件(驱动)?
答
当然会啊,那么那么如果文件和硬件(驱动)打开的多,操作系统(作用: 文件、驱动、进程、内存管理)则需要讲他们管理起来,则管理的本质就是 先描述在组织
问
对文件的操作是??
答
emmm对文件的操作其实就是读写
问
各种硬件不是有不同的读写方式吗?如键盘只有读,我从来没看到过写数据到键盘上的,那么如何组织,如何如题所说一切皆文件呢?
答
确实各种硬件都有不同的读写方式,但是我们可以先描述在组织用
,那么操作系统管理硬件,那么就可以用相同的方式操作
如图所示 : >
stdin对应的是键盘,stdout,stderr对应显示器
2.2 文件描述符 与 FILE
用系统的接口调用返回的一个整数也就是文件描述符,那是如何根据文件描述符来找对对应的文件呢?
代码
int main()
int fd1=open("log1.txt",O_CREAT|O_WRONLY,0x666);
int fd2=open("log2.txt",O_CREAT|O_WRONLY,0x666);
int fd3=open("log3.txt",O_CREAT|O_WRONLY,0x666);
int fd4=open("log4.txt",O_CREAT|O_WRONLY,0x666);
int fd5=open("log5.txt",O_CREAT|O_WRONLY,0x666);
int fd6=open("log6.txt",O_CREAT|O_WRONLY,0x666);
int fd7=open("log7.txt",O_CREAT|O_WRONLY,0x666);
int fd8=open("log8.txt",O_CREAT|O_WRONLY,0x666);
printf("fd1 %d\\n",fd1);
printf("fd2 %d\\n",fd2);
printf("fd3 %d\\n",fd3);
printf("fd4 %d\\n",fd4);
printf("fd5 %d\\n",fd5);
printf("fd6 %d\\n",fd6);
printf("fd7 %d\\n",fd7);
printf("fd8 %d\\n",fd8);
return 0;
结果:
问
嗯居然是连续的,他们不会放在一起吧?为啥第一个文件描述符是从3开始的?
答
连续下标想到了啥?数组呀,没错这些文件底层其实是放在数组中(客观表示),文件描述符为啥从三开始,上述说过编译器会默认打开三个流(输入、输出、错误),自然后面创建的文件描述符只能从3开始
上述说用统一的方式看待硬件实现一切皆文件,但是文件一多就需要管理
,并用一种数据结构组织起来
,这里用的数据结构就是数组
如图所示 : >
问
那么C是如何操作的呢?他的返回值不是一个FILE*吗?
答
其实FILE底层是一个结构体并且里面有俩个重要的部分1:C语言缓冲区 2:file_on(fd)
如图所示 : >
验证_filen 是否是fd
int main()
printf("%d\\n",stdin->_fileno);
printf("%d\\n",stdout->_fileno);
printf("%d\\n",stderr->_fileno);
int fd=open("log.txt",O_CREAT|O_WRONLY,0x666);//系统接口
printf("%d\\n",fd->_fileno);
结果
那么C语言中底层调用文件我们大致就可以猜想出来了(fopen调用open,open返回fd给 fopen ,fopen再把fd写入到 FILE*对象中的_fileon,后面通过接口对文件读写
)
如图所示 : >
2.3 文件描述符的分配规则
文件描述符的分配规则其实很简单,就是数组中最小空余的下标
,上述例子已经可以看出(标准输入、输出、错误占用下标0,1,2,新开文件就在3下标处)
问
标准输入输出错误流可以关闭吗?
答
既然他是默认打开的,注意默认打开,那么也就是说是被打开的,那么就可以被关闭
代码:
int main()
close(0);
int fd=open("log.txt",O_CREAT|O_WRONLY,0x666);
printf("%d\\n",fd);
结果
问
如果问吧标准输给关闭了,是否会打印呢?应该会吧,标准错误也是显示器
答
其实是不会打印的,虽然stderr也是显示器,但是底层要把数据输显示器上,还是通过stdout去输出
,但是stdout中的fileon还是2
,但files_strcuct中的fd 数组下标为2的数据已经被 “抹除”
,那么就找不到显示器文件
如图所示 : >
2.3.2 重定向
重定向:把本该输出到固定位置的数据输出到别出简称重定向
上述说,可以把stdout关闭,但stdout的_fileson还是2
,且文件描述符分配规则是没使用且下标最小的位置
,那么我新打开一个文件
,在输出数据不就是到文件中
了吗,这不就是重定向吗??
验证代码:
int main()
close(1);//关闭显示器文件
int fd = open("log.txt",O_WRONLY);//已经存在
printf("hello world");//输出到stdout中
return 0;
结果:
正常情况下我们不会像上面的操作来完成重定向,但是我们确实需要重定向的需求,C语言中有个接口可以帮助我们实现重定向dup2
接口介绍
接口使用
int main()
int fd = open("log.txt",O_WRONLY);//已经存在
dup2(fd,1);
printf("hello world");
return 0;
结果
2.4 创建一个进程的新理解
其实上述中所画的图是有一点错误的,其实FILE*其实是在代码也就是数据,他是在内存中的,根本不可能直接访问files_struct,他们其实用是PCB进行互相访问
如图所示 : >
2.5 缓冲区的理解
上述在介绍文件描述符与FILE*时提到过FILE 是一个结构体,它里面包含了俩个重要的部分 1. 缓冲区 2. files_on, 缓冲区(对IO数据的临时存储的区域
)想必大家已经耳熟能详了吧!
怪异现象(程序结束后调用fork)
int main()
const char *msg0="hello printf\\n";
const char *msg1="hello fwrite\\n";
const char *msg2="hello write\\n";
printf("%s",msg0);
fwrite(msg1, strlen(msg0), 1, stdout);
write(1,msg2,strlen(msg2));
fork();
输出结果 : >
那么把输出的结果重定向到文件中的时候 : >
问
这个应该就是所谓的怪异现象吧,C语言的接口所输出的数据重定向后输出了俩份,这个是为啥呢?
答
确实这就是所谓的怪异现象,其实这都是看似没有用处的fork 与 C语言缓冲区导致的
- 都知道C语言的缓冲区是遇到
\\n、\\r、fflush、程序结束、缓冲区满了
才会看时机吧数据刷新给内核中的缓冲区,且C缓冲区对显示器是行刷新,对普通文件是全刷新
fork后父子数据是共享
的,那么fork前父进程输出的数据没有刷新,那么就留在C缓冲区
(FILE 结构体,也就是数据),当父亲将数据刷新时,那么就会写实拷贝
这就是为啥输出到屏幕上是正常的,但是重定向后却变得怪异起来
解决怪异现象(提前全刷新,防止写实拷贝)
int main()
const char *msg0="hello printf\\n";
const char *msg1="hello fwrite\\n";
const char *msg2="hello write\\n";
printf("%s",msg0);
fwrite(msg1, strlen(msg0), 1, stdout);
write(1,msg2,strlen(msg2));
fflush(stdout);//提前进行全刷新
fork();
结果
问
为啥要存在C语言缓冲区 ? 不是有内核系统自带的缓冲区吗?直接刷新的系统的不香吗?
答
我觉得是提高效率,听个故事,现在是早上,你爹现在每5分钟给你2元,并会给你5次,2元只可以买一个包子,那么你是会拿到钱直接去买包子再回来等,还是等钱全部到手再去统一一次性买呢?
刷新策略如图所示 : >
2.6 文件系统
上述介绍的都是打开的文件,那么下面来介绍一下没有打开呆在磁盘上的文件吧!!!
这里有一个问题就是打开的文件要管理,那么没打开的文件需要进行管理吗?
那一定要管理呀,不然你如果需要打开文件去哪里找?搜索整个磁盘吗?
硬件部分:
机械硬盘,固态硬盘(ssd),光盘,磁带。现在磁带光盘应该已经退出时代的舞台了,现在基本用的都是机械硬盘或者是固态硬盘。由于ssd的工艺太高,那么我就介绍一下机械硬盘吧!
如图所示 : >
问
那如何吧数据写入固态硬盘中?open(“盘片/磁道/扇区/文件名”,……)?
答
你不觉得这个很麻烦吗?且不通用吧,假如你有钱了换个存储器呢?ssd那不是又要改。所以一切都需要抽象一下,直接把底层看成一个线性结构,像磁带一样只是卷在一起了
(数组等),一个盘对应一个数组,可以把他抽象为多维数组(in t arr[盘面][磁道][扇区]
,如何存储器都是如此),底层维护这种关系的是对应存储器的驱动
补充1 :
都知道,需要执行一个程序一定要把程序的加载到内存中,那么程序底层不过就是代码数据,那这些都是存在磁盘中的。内存IO大小是4字节,且他也是有自己基础单位是1字节
补充2 :
当存储器用统一的方式看待后,管理又是一个难题,假设存储器的大小为2个T,这时就是一个巨大的数组。分区不知道大家是否听过 (分区:把大空间的盘分为多个小空间的盘,方便操作系统使用和管理
),没错这里就要进行分区。
//stata 文件 的截图
2. 6.2 文件系统
LINUX文件系统
- EXT2
- EXT3
- fs
- usb-fs
- sysfs、proc
- ……
每一个分区所使用的管理系统不一定是一样
的这个要看OS
EXT2文件系统介绍
inode
每一个文件,目录,都有一个Inode,且Inode中存放的是文件的属性信息,但是不包括文件名,查看inode 在ls后面+i选项
问
通过上面的EXT2文件系统现在可以知道一个文件在磁盘上是如何存放的
(inode table 存放文件的属性,Data blokcks 存放数据
),但是他们是如何联系的呢?
答
创建一个文件那么就会申请Inode存文件的属性信息,在申请blocks存放数据内容,他们如何管理,每个inode里面有一个数组存的是blocks的编号
(存放内容的block)
如图所示 : >
问
我好奇Block bitmap 和 Inode bitmap 是计数让我们知道Inode和blocks的使用情况?用一个整形来计数吗?
答
其实计数也不是不行但是,这里用了却更加妙,这里使用的位图
(用一个bit位来标识状态),相比计数他可以很快的找到那个Inode和blocks使用或者没有使用
如图所示 : >
那么现在要操作一个文件我是如何找他的呢?
不知道大家是否还记得,目录结构其实是一个树状结构
的,且上面也说过目录也是有自己的Inode(目录也是文件,也有自己的属性),那么他的内容是啥
呢?当然是里面文件的文件喽(与文件对应的Inode)
,毕竟一个当父亲(目录)不可能不记得自己儿子(文件)的名字(文件名)和生日(inode)
树状结构如图所示 :>
目录Inode与blocks与文件的关系 如图所示 :>
问
目录也是文件,那我要找当前目录不是要找上级目录………
答
没错正是如此,他会一直一直的往上找知道根目录,执行过一次后就会缓存路径,现在可以解释一个现象了,就是当你第一次开机执行命令的时候是不是都比平时慢一点(自己验证下吧)?
问
文件名是否是一个文件的标识?理解ls后就可以看到存在磁盘上文件的属性信息?
答
当然不是表示一个文件的是Inode。
ls后,直接区当前目录找文件,并通inode返回文件的属性
现在理解一下创建一个文件需要的操作
1.inode bitmap 和 blocks bitmap申请所需大小个数,2. 存储到inode中的block组中当前文件内容所需block的编号 3. 把文件名和inode的映射关系存到目录的block中
如图所示 :>
2.6.3 软硬链接
想要进行软硬链接就需要用到命令 ln
软硬链接的理解
如果用C++来理解软连接是指针而硬连接是引用
,也就是说软连接有自己的空间,而硬链接就是本体
如图so是软ha是硬 : >你会发现硬的inode和文件是一样的,而软的inode是不同的,那么就说inode也有对应的block,他的block中存的就是文件的路径(类似windows中的快捷方式)
怪异现象(创建文件硬链接是1,创建目录确实2)
是否还记得ls的一个选项就是-a显示全部包括隐藏的文件?你会发现有一个 .的文件
他就是代表当前文件夹,..的文件
代表上级目录。当查看 .文件的inode
时发现居然是一样的
完结🎉🎉🎉🎉
上面就是博主总结的IO博客,如果对你有所帮助,请给我点个赞吧,点赞,点赞,点赞,给大家跳个舞!!!
以上是关于Linux基础IO ——文件(上)的主要内容,如果未能解决你的问题,请参考以下文章
Linux系统编程:基础IO 上简单复习C语言文件接口 | 学习系统文件接口 | 认识文件描述符 | Linux下,一切皆文件 | 重定向原理