【Linux】零基础深入学习linux文件系统(一)

文章目录

c语言文件函数

写函数:

c 复制代码
  FILE* fp=fopen("myfile","w");
  if(!fp)
  {
    perror("fopen");
  }
  const char* msg="hello jib\n";
  int cnt=5;
  while(cnt--)
  {
    fwrite(msg,strlen(msg),1,fp);
  }
  fclose(fp);

读函数:

c 复制代码
  fp=fopen("myfile","r");
  char buf[1024];
  size_t s=fread(buf,1,sizeof(buf)-1,fp);
  fclose(fp);

详细的C语言文件知识点请看我这篇文章,这里不过多解释了
C语言文件操作知识

🚩系统接口

读写函数:

c 复制代码
  umask(0);
  int fd=open("myfile",O_RDWR|O_CREAT,0644); 
  if(fd<0)
  {
    perror("open");
    return -1;
  }

  char* msg="hello world\n";
  int cnt=5;
  int len=strlen(msg);
  while(cnt--)
  {
    write(fd,msg,len);
  }

  lseek(fd,0,SEEK_SET);
  char buf[1024];
  while(1)
  {
    size_t s=read(fd,buf,sizeof(buf)-1);
    if(s>0)
    {
        buf[s]='\0';
        printf("%s\n",buf);
    }
    else{
      break;
    }
  }


lseek定位文件,写入文件之后指针到了文件末尾,

off_t lseek(int fd, off_t offset, int whence);

fd:文件描述符

offset:偏移量

whence:基准位置 分为:SEEK_SET SEEK_CUR SEEK_END

这里是定位文件开头

🚩open

打开一个文件,

int open(const char *pathname, int flags);

int open(const char *pathname, int flags, mode_t mode);

🚩参数细节:
pathname 要创建或打开的文件名字

flags:打开文件时,可以传入多个参数选项,用下列一个或多个常量构成flags

  • O_RDONLY 只读打开
  • O_WRONLY 只写打开
  • O_RDWR 既可以读可以写

以上三种模式只能选一种

  • O_CREAT

若是要打开文件不存在,则创建这个文件

  • O_APPEND
    追加写,(下文重定向会讲)

返回值:

成功了返回 文件描述符

失败了返回 -1

文件标识符

通过open,我们知道文件标识符就是一个整数

c 复制代码
  int fd=open("myfile",O_RDWR|O_CREAT,0644); 
  if(fd<0)
  {
    perror("open");
    return -1;
  }
  cout<<fd<<endl;


系统分配文件标识符是从0开始的,为什么我新开的文件标识符是3呢?

因为系统自动打开了3个文件:
键盘,显示器,显示器 ,
分别是标准输入,标准输出,标准错误对应的文件标识符 0,1,2

所以输入输出还可以这样,

c 复制代码
  char buf[1024];
  size_t s=read(0,buf,sizeof(buf)-1);
  if(s>0)
  {
    buf[s]='\0';
    write(1,buf,strlen(buf));
    write(2,buf,strlen(buf));
  }


PCB中有files指针,指向一张表files_struct,表中有file数组,数组中每个元素都是指向已打开文件的下标。

所以文件标识符本质就是该数组的下标,只要有这个下标就能找到对应的文件

分配文件标识符

我们关闭0文件试试

c 复制代码
close(0);
  //close(2);
  size_t fd=open("myfile",O_RDWR|O_CREAT);
  if(fd<0)
  {
    perror("open");
  }
  else{
    cout<<fd;
  }

结果系统把0文件标识符给了myfile

说明分配规则是从struct_file寻找到下标最小且未被使用的文件标识符

🚩重定向

如果我们关闭1呢

c 复制代码
close(1);
  size_t fd=open("myfile",O_RDWR|O_CREAT);
  if(fd<0)
  {
    perror("open");
    return 1;
  }
  else{
    cout<<fd;
  }

运行发现无结果,为什么呢?
原本要向显示器打印的,但是显示器被关了,标识符1被myfile占了,但是系统依旧向1中输入,所以最终结果给了文件myfile

我们看到第一个hello的h被覆盖为1了,因为我之前向myfile中已经输入了许多hello world

要想清空文件,只需改变打开的方式

size_t fd=open("myfile",O_RDWR|O_CREAT|O_TRUNC);


或者追加模式

常见的重定向:>,>>,<

> 把内容输出到文件中

>>就是追加模式
< 把内容输入到指令中

比如cat < text.txt 但我们一般省略 <

小总

C语言的I/O函数其实是封装了系统函数,打开权限a就是封装了追加模式的系统函数,各种语言都有自己文件操作函数,但是本质都是封装了系统函数open,write等等

而且必定封装了fd,所有语言级别的文件操作函数都是通过fd完成的,

🚩缓冲区

先来看代码:

c 复制代码
  char* str="hello write\n";
  char* fstr="hello fwrite\n";

  cout<<"cout"<<endl;
  fprintf(stdout,"hello fprintf\n");
  fwrite(fstr,strlen(fstr),1,stdout); 
  write(1,str,strlen(str));

正常输出,如果加入fork()会怎样

正常运行还是没问题,问题来了,当我们向文件输入,我们发现,C语言的文件函数打印了两次,系统函数只打印了一次。

为啥会这样呢??因为有缓冲区
C语言有自己的缓冲区

🚩 缓冲区刷新模式

  • 无缓冲 - - - 直接刷新
  • 行缓冲 - - - 不刷新,遇到\n刷新
  • 全缓冲 - - - 直到缓冲区满了才刷新

进程退出的时候也会刷新缓冲区

显示器刷新正常是行刷新,遇到\n就刷新,但当向文件中刷新时,显示器就变成了全缓冲,即遇到\n不会立刻给你刷新,放入缓冲区里了

但是为什么C语言文件函数打印了两遍呢?

因为fork创建了子进程,子进程会复制当前的缓冲区,相当于C语言当前缓冲区有了两份,刷新了两遍缓冲区

为什么系统函数只打印了1遍呢 ?

因为操作系统内核有自己的缓冲区,也就是系统函数只在系统缓冲区一份,

实际上,C语言的缓冲区要刷新到操作系统内核,然后再刷新系统缓冲区,目前我们认为,刷新到内核的数据就能到达硬件

解释下为什么cout只打印了一次,因为遇到关键词cout会立即刷新缓冲区


缓冲区存在FILE中,里面有打开文件的缓冲区字段和维护信息

所以之前讲的exit(),函数是刷新C语言缓冲区,在调用_exit()
_exit()直接退出,缓冲区也不刷新

相关推荐
DFT计算杂谈1 小时前
Abinit-10.4.7安装教程
linux·数据库·python·算法·matlab
程序猫.1 小时前
Java零基础入门:集合进阶(下)
java·开发语言
KKKlucifer1 小时前
数据分类分级为基的跨域流通权限动态管控技术:构建安全可控的跨域数据流通体系
大数据·数据库·人工智能
Z***G4791 小时前
SpringBoot线程池的使用
java·spring boot·后端
L***d6701 小时前
Spring Boot 整合 Keycloak
java·spring boot·后端
王桑.1 小时前
spring中的设置定时任务工具--springtask
java·spring·java-ee
n***27191 小时前
工作中常用springboot启动后执行的方法
java·spring boot·后端
tgethe1 小时前
MybatisPlus基础部分详解(上篇)
java·spring boot·mybatisplus
我科绝伦(Huanhuan Zhou)1 小时前
MySQL运维必备:24个核心监控参数(含查询语句+异常处理)
运维·数据库·mysql
不会c嘎嘎1 小时前
【C++】深入理解多态:从用法到原理
开发语言·c++