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 系统层面上理解文件


一个文件其实可以划分成俩个板块

  1. 属性
  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语言缓冲区导致的

  1. 都知道C语言的缓冲区是遇到 \\n、\\r、fflush、程序结束、缓冲区满了 才会看时机吧数据刷新给内核中的缓冲区,且C缓冲区对显示器是行刷新,对普通文件是全刷新
  2. 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文件系统

  1. EXT2
  2. EXT3
  3. fs
  4. usb-fs
  5. sysfs、proc
  6. ……

每一个分区所使用的管理系统不一定是一样的这个要看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详解

Linux基础IO——文件系统(上)

[Linux] 基础 IO

[Linux] 基础 IO

[Linux] 基础 IO

Linux系统编程:基础IO 上简单复习C语言文件接口 | 学习系统文件接口 | 认识文件描述符 | Linux下,一切皆文件 | 重定向原理