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

Linux 基础IO——重定向和缓冲区

目录

一、重定向

1、重定向的本质

2、使用 dup2 系统调用 

(1)输出重定向 

(2)追加重定向 

  (3) 输入重定向 

​ 二、缓冲区

1.理解缓冲区

2.缓冲区刷新问题

3.为什么要有缓冲区?

4.这个缓冲区在哪里?


一、重定向

什么是重定向,是本来写到显示器上的内容写入到文件当中。

其中, fd = 1 。这种现象叫做输出重定向。常见的重定向有:>,>>,<

1、重定向的本质

重定向的本质其实就是在 OS 内部更改 fd 对应的内容指向。也就是本该写入到1号文件描述符的内容都写到了log.txt中。

 1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <sys/types.h>
  4 #include <sys/stat.h>
  5 #include <fcntl.h>
  6 
  7 int main()
  8 {
  9   close(0);
 10   int fd =open("log.txt",O_RDONLY);
 11   if(fd<0)
 12   {
 13     perror("open");
 14     return 1;
 15   }
 16   printf("fd:%d\n",fd);
 17   char buffer[64];
 18   fgets(buffer,sizeof buffer,stdin);                                                                                                         
 19   printf("%s",buffer);
 20   return 0;
 21 }

 

本应该从键盘读取的内容,现在从 log.txt 中读取。这就叫作输出重定向。 

 

 

本应该从显示器上读取,现在从log.txt中读取,这是输入重定向。 

 

这是追加重定向。

2、使用 dup2 系统调用 

 

这个oldfd和newfd我们该怎么区分呢?

很明显依靠函数原型,我们就能认为 dup2(1, fd),因为 1 是先打开的,而 fd 是后打开的.可实际上并不是这样的,文档中说 newfd 是 oldfd 的一份拷贝,这里拷贝的是文件描述符对应数组下标的内容,所以数组内容最终应该和 oldfd 一致。

换而言之,这里就是想把让 1 不要指向显示器了,而指向 log.txt,fd 也指向 log.txt。所以这里的 oldfd 对应 fd,newfd 对应 1,所以应该是 dup2(fd, 1)。

我们最后的文件描述符要跟oldfd一致。

dup(3,1)是把三号文件描述符的内容拷贝到一号文件描述符里。

(1)输出重定向 

  •   < 就是 dup2(fd, 0),且 open 文件的方式是 O_RDONLY;

(2)追加重定向 

 

  (3) 输入重定向 

无非是在追加重定向基础上更改

 二、缓冲区

1.理解缓冲区

先看一段代码

  • 当我们去掉换行,关闭1号文件描述符时,显示器和重定向到文件都显示不了内容。
  • 只有write的时候,也关闭了1号文件描述符,显示器和重定向到文件却能显示。

这是为什么呢?

这是因为存在缓冲区,C库函数接口没带"\n",没有打印到显示器上,先写到了缓冲区上 。

这个缓冲区一定不在操作系统内部!不是系统级别的缓冲区!

write系统调用接口,使用系统内核的缓冲区,进程结束的时候直接刷新到磁盘上。

而printf/fprintf/fwrite...等C库函数,并没有把数据写入到系统级别的缓冲区中,C语言会提供一个缓冲区(这叫应用层缓冲区,也是用户级缓冲区),会先把数据写入到用户级缓冲区里。再调用write系统调用接口,在合适的时候把用户级缓冲区的内容写入到系统缓冲区里。

可这段代码中,执行到close(1)时,还没有调用write讲数据写入到1号文件描述符,就把对应的显示器文件描述符关闭了,此时的数据还在用户级缓冲区,刷新时显示不出来。

 

我们再来看一段代码

 结果如下:

同样一个程序,向显示器打印输出 4 行文本,而向普通文件(磁盘)上打印输出 7 行文本。其中,printf 和 fwrite (库函数)都输出了 2 次,而 write 只输出了一次(系统调用),为什么呢?

2.缓冲区刷新问题

我们先给大家讲解缓冲区刷新

  1. 当进程退出的时候,直接刷新。
  2. 无缓冲区——直接刷新。
  3. 行缓冲区——不刷新,直到遇到了\n才刷新。
  4. 全缓冲区——直到缓冲区满了才刷新。
  • 我们的显示器文件的刷新方案就是行刷新,所以在printf函数遇到\n会直接刷新到显示器中。
  • 讲数据写入到普通文件是全缓冲区刷新,数据写满缓冲区才刷新。或者写完了数据,进程退出了也会刷新。 

我们再重回刚刚的问题:

在向显示器打印输出是是行刷新,遇到了\n就刷新,所以我们在上面写入的4个接口的内容能直接打印到显示器上。

而写入到文件时全刷新,当缓冲区满了或者进程结束了才刷新。

这段代码依次往下执行,printf/fprintf/fwrite这三个是C库函数写入到C缓冲区中,等待合适的时候调用write系统调用接口写入系统缓冲区中,执行到write时,因为write是直接写道系统缓冲区,所以被直接刷新到显示器上,所以优先打印出hello write 

再往下执行到fork时,此时数据还在C缓冲区中,但是 fork 的时候,父子数据会发生写时拷贝,所以当你父进程准备刷新的时候,子进程也就有了同样的一份数据,随即产生两份数据。 从而导致C库函数被打印了两次。

3.为什么要有缓冲区?

  •  解决了效率问题,在printf/fwrite把数据放入缓冲区中直接返回了。
  • 语言配合的格式化(输入、输出的转换),我们写在显示器上,123,这个123是字符1字符2字符3。我们写int a=10; printf("hello%d\n",a); 打印的是整数。这个时候就需要缓冲区进行类型的转化。

4.这个缓冲区在哪里?

我们之前说文件操作系统离不开FILE struct,FILE结构体里面还有对应打开文件的缓冲区字段和维护信息。

每一个文件都对应一个语言缓冲区。

我们打开10个文件,有10个文件描述符,也就有10个对应的语言缓冲区。 

FILE对象是属于用户还是操作系统呢?

属于用户,因为语言都属于用户,FILE内有语言缓冲区。


http://www.kler.cn/a/549458.html

相关文章:

  • 大疆无人机需要的kml文件如何制作kml导出(大疆KML文件)
  • Instagram与小红书的自动化运营
  • Vite入门指南
  • github用户名密码登陆失效了
  • Mac上搭建宝塔环境并部署PHP项目
  • Ubuntu 连接 air pods
  • ios中常见的设计原则和设计模式
  • ARINC 429详解
  • CSS Grid 网格布局,以及 Flexbox 弹性盒布局模型,它们的适用场景是什么?
  • VS Code User和System版区别【推荐使用System版本】and VSCode+Keil协同开发之Keil Assistant
  • 【ISO 14229-1:2023 UDS诊断(会话控制0x10服务)测试用例CAPL代码全解析③】
  • ffmpeg-cli-wrapper操作ffmpeg的工具
  • Anaconda +Jupyter Notebook安装(2025最新版)
  • 问卷数据分析|SPSS实操之量表描述性统计
  • 服务器硬件知识--------linux系统初识and安装
  • 探索Zephyr在汽车领域的应用潜力与挑战
  • 【LLM】13:大模型算法面试题库
  • kubectl exec 实现的原理
  • 力扣 470. 用 Rand7() 实现 Rand10() 拒绝采样 等概率随机数生成
  • SOCKET建立简单的tcp服务端与客户端通信