【Linux 】文件描述符fd、重定向、缓冲区(超详解)

目录

[​编辑 系统接口进行文件访问](#编辑 系统接口进行文件访问)

[open 接口介绍](#open 接口介绍)

文件描述符fd

重定向

缓冲区

1、缓冲区是什么?

2、为什么要有缓冲区?

3、怎么办?


我们先来复习一下,c语言对文件的操作:

C默认会打开三个输入输出流,分别是stdin, stdout, stderr

  1 #include<stdio.h>
  2 
  3 int main()
  4 {
  5     FILE *fp=fopen("log.txt","w");
  6     if(fp==NULL)
  7     {
  8         perror("error!");
  9         return 1;
 10     }
 11     fprintf(fp,"hello,%d,%s\n",10,"sxh");
 12     fclose(fp);
 13     return 0;
 14 }

系统接口进行文件访问

操作文件,除了上述C接口(当然,C++也有接口,其他语言也有),我们还可以采用系统接口来进行文件访问;

  #include<stdio.h>
  #include<string.h>
   
  #include<sys/types.h>
  #include<sys/stat.h>
  #include<fcntl.h>
  #include<unistd.h>
   
  int main()
  {
      umask(0);
      int fd =open("log.txt",O_WRONLY | O_CREAT |O_TRUNC,0666);
      if(fd<0)
      {   
          perror("open");
          return 1;
      }
      
      //const char *message ="hello Linux!\n";
      const char * message ="aaaa\n";
      write(fd,message,strlen(message));
  
      close(fd);
  }

open("log.txt",O_WRONLY | O_CREAT |O_TRUNC,0666); :以写方式打开,不存在就创建,存在就先清空!相当于c语言中的 "w" 方式;

open 接口介绍

open man 2 open

#includ e <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

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

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

int creat(const char *pathname, mode_t mode);

pathname:要打开或创建的目标文件

flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行"或"运算,构成; 常见的参数有:

O_RDONLY: 只读打开

O_WRONLY: 只写打开

O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限

O_APPEND: 追加写

mode: mode 只有 当 在 flags 中使用 O_CREAT 时才有效 , 否则被忽略.

返回值

成功:新打开的文件描述符

失败:-1

文件描述符fd

我们通过系统调用I/O打开一个文件的返回值,就是文件描述符:

int fd = open("log.txt",O_WRONLY | O_CREAT |O_TRUNC,0666);

这里fd 就是log.txt的文件描述符;

如果我们同时打开三个文件,并把这三个文件的文件描述符打印出来,会发现:

文件描述符是从3开始的,那0、1、2呢?

Linux进程默认情况下会有3个缺省打开的文件描述符:分别是标准输入0, 标准输出1, 标准错误2.

0,1,2对应的物理设备一般是:键盘,显示器,显示器

根据上述的图,我们可以知道:

open干什么?

1、创建file

2、开辟文件缓冲区的空间,加载文件数据(延后)

3、查看进程的文件描述符表

4、file地址,填入对应的表下标中

5、返回下标

重定向

看一下下面的代码:

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 #include<stdlib.h>
  8 const char *filename="log.txt";
  9 
 10 int main()
 11 {
 12     //close(0);
 13     close(1);
 14     //close(2);
 15     int fd =open(filename,O_CREAT | O_WRONLY | O_TRUNC,0666);
 16     if(fd<0)
 17     {
 18         perror("open");
 19         return 1;
 20     }
 21     printf("printf,fd:%d\n",fd);
 22     fprintf(stdout,"fprintf,fd:%d\n",fd);
 23 
 24     fflush(stdout);
 25 
 26 
 27     close(fd);
 28     return 0;
 29 
 30 }

先看现象:

1、如果我们关闭的是0 :打印出来的log.txt的文件描述符是0 ;关闭的是2 ,则打印出来的文件描述符是2 ;关闭的是1 ,则不会显示出来;因为文件描述符1表示的是显示器,而我们把显示器关掉了自然打印不出来;

2、我们会在log.txt中打印出printf和fprintf的内容:

现象解释:

文件描述符规则:查看自己的文件描述表,分配最小的没有使用的文件描述符;

由于我们把显示器给关掉了,那么文件描述符1就是最小的没有使用的,这样log.txt就会被分配1为文件描述符;这其实就是一个重定向操作;

看下面的图:

一开始fd_array[1]指向的是显示器,由于close(1),加上open log.txt这样fd_array[1]指向的就是log.txt;

因为重定向的本质是在内核中改变文件描述符特点下标的内容,与上层无关;

fprintf()/printf() --->stdout --->struct FILE --->stdout ---->_fileno ==1;

所以fprintf()/printf() 看的就是_fileno ==1;由于重定向后log.txt的fd为1 ,自然fprintf()/printf()的内容打印到log.txt中;

如果我们一直用这种手动重定向的方式,比较麻烦;可以用系统调用:dup2()

看一下代码:

  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 #include<stdlib.h>
  8 const char *filename="log.txt";
  9 
 10 int main()
 11 {
 12     int fd = open(filename,O_CREAT | O_WRONLY | O_TRUNC,0666);
 13     //dup2(fd,1);
 14 
 15     printf("hello world,fd:%d\n",fd);
 16     fprintf(stdout,"hello world\n");
 17     fflush(stdout);
 18     close(fd);
 19     return 0;
 20 }

上述的代码运行结果:会直接打印到显示器上;

如果把dup2(fd,1);复原:显示器上不会显示,而是在log.txt上显示

其实本质就是将fd覆盖1 指向下标的内容;

缓冲区

1、缓冲区是什么?

缓冲区就是一段内存空间

2、为什么要有缓冲区?

给上层提供高效的IO体验,间接提高整体效率

3、怎么办?

a、刷新策略

1、立即刷新 fflush(stdout)(c语言的刷新)、fsync(fd)(系统调用的刷新)

2、行刷新 (显示器)

3、全缓冲 (缓冲区写满,才刷新,普通文件)

b、特殊情况

进程退出,系统会自动刷新

强制刷新

相关推荐
天上掉下来个程小白30 分钟前
请求响应-08.响应-案例
java·服务器·前端·springboot
IChen.1 小时前
解决centos 删除文件后但空间没有释放
linux·运维·centos
多恩Stone2 小时前
【vs code(cursor) ssh连不上服务器】但是 Terminal 可以连上,问题解决 ✅
运维·服务器·ssh
何陈陈2 小时前
【Linux】线程池
linux·服务器·开发语言·c++
DuoRuaiMiFa2 小时前
Linux系统性能调优实战指南
linux
沉登c2 小时前
Javascript客户端时间与服务器时间
服务器·javascript
憧憬一下2 小时前
线程池的实现和讲解:解决多线程并发服务器创建销毁线程消耗过大的问题
linux·线程池·c/c++·嵌入式linux
碳治郎AI2 小时前
Bolt.new:终极自动化编程工具
运维·自动化