【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、特殊情况

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

强制刷新

相关推荐
工程师老罗1 天前
龙芯2k0300 PMON取消Linux自启动
linux·运维·服务器
千百元1 天前
centos如何删除恶心定时任务
linux·运维·centos
skywalk81631 天前
网站证书自动续订失败的问题解决,原来是续订指令certbot renew出错,导致crontab定时任务续订失败
运维·服务器·证书·certbot
额呃呃1 天前
游戏服务器和一般高性能服务器的区别
运维·服务器·游戏
liwulin05061 天前
【JAVA】创建一个不需要依赖的websocket服务器接收音频文件
java·服务器·websocket
2501_906150561 天前
开源问卷平台DWSurvey开发配置记录
运维·开源·jenkins
gaize12131 天前
服务器分类及区别划分!多样化服务器用途体系架构及层次分类
运维·服务器·架构
鲁正杰1 天前
【运维部署】现代化内网穿透与文件共享方案 (Rust)
运维·开发语言·rust
oMcLin1 天前
如何在Manjaro Linux上配置并优化Caddy Web服务器,确保高并发流量下的稳定性与安全性?
linux·服务器·前端
济6171 天前
linux(第七期)--gcc编译软件-- Ubuntu20.04
linux·运维·服务器