当前位置: 首页 > news >正文

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

目录

​编辑 系统接口进行文件访问

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、特殊情况
               进程退出,系统会自动刷新
               强制刷新 

http://www.lryc.cn/news/454375.html

相关文章:

  • Unity WebGL使用nginx作反向代理处理跨域,一些跨域的错误处理(添加了反向代理的配置依旧不能跨域)
  • 视频转文字免费的软件有哪些?6款工具一键把视频转成文字!又快又方便!
  • 解决DHCP服务异常导致设备无法获取IP地址的方法
  • Python机器学习模型的部署与维护:版本管理、监控与更新策略
  • 免费送源码:Java+ssm+JSP+Ajax+MySQL SSM汽车租赁管理系统 计算机毕业设计原创定制
  • Vivado viterbi decoder license
  • 【FastAdmin】PHP的Trait机制:代码复用的新选择
  • 小红书制作视频如何去原视频音乐,视频如何去原声保留背景音乐?
  • netty之Netty使用Protobuf传输数据
  • 【力扣 | SQL题 | 每日四题】力扣2082, 2084, 2072, 2112, 180
  • 快速了解Java中的15把锁!
  • TypeScript 封装 Axios 1.7.7
  • 【数据结构】【链表代码】移除链表元素
  • 作文-杭州游记
  • 降压芯片TPS54821
  • YOLO v1详解解读
  • 【动态规划-最长公共子序列(LCS)】【hard】【科大讯飞笔试最后一题】力扣115. 不同的子序列
  • 深入理解 JavaScript 中的 void`运算符和 yield*表达式
  • 第四节——从深层剖析指针(让你不再害怕指针)
  • openpnp - 吸嘴校正失败的opencv参数分析
  • 【Python】Marmir 使用指南:Python 驱动的电子表格生成器
  • 深入理解 JavaScript 事件循环机制:单线程中的异步处理核心
  • Stream流的终结方法(二)——collect
  • 【C语言系统编程】【第一部分:操作系统知识】1.1.操作系统原理
  • 基于Java+VUE+echarts大数据智能道路交通信息统计分析管理系统
  • leetcode-42. 接雨水 单调栈
  • ThinkPHP和PHP的区别
  • clientWidth,offsetWidth,scrollHeight
  • SVN版本回退
  • IDEA关联Tomcat