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

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

强制刷新

相关推荐
少妇的美梦9 分钟前
logstash教程
运维
chen94532 分钟前
k8s集群部署vector日志采集器
运维
chen94539 分钟前
aws ec2部署harbor,使用s3存储
运维
轻松Ai享生活5 小时前
5 节课深入学习Linux Cgroups
linux
christine-rr5 小时前
linux常用命令(4)——压缩命令
linux·服务器·redis
三坛海会大神5556 小时前
LVS与Keepalived详解(二)LVS负载均衡实现实操
linux·负载均衡·lvs
東雪蓮☆6 小时前
深入理解 LVS-DR 模式与 Keepalived 高可用集群
linux·运维·服务器·lvs
qq_264220896 小时前
LVS负载均衡群集和LVS+Keepalived群集
运维·负载均衡·lvs
乌萨奇也要立志学C++6 小时前
【Linux】进程概念(二):进程查看与 fork 初探
linux·运维·服务器
雨落Liy6 小时前
Nginx 从入门到进阶:反向代理、负载均衡与高性能实战指南
运维·nginx·负载均衡