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

Linux重定向与缓冲区

目录

文件描述符的分配规则

重定向

使用 dup2 系统调用

FILE


文件描述符的分配规则

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main()
{int fd = open("myfile", O_RDONLY);if(fd < 0){perror("open");return 1;}printf("fd: %d\n", fd);close(fd);return 0;
}

输出发现是 fd: 3

关闭0或者2,再看

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main()
{close(0);//close(2);int fd = open("myfile", O_RDONLY);if(fd < 0){perror("open");return 1;}printf("fd: %d\n", fd);close(fd);return 0;
}

发现是结果是: fd: 0 或者 fd 2 可见,文件描述符的分配规则:在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。

重定向

那如果关闭1呢?看代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>int main()
{close(1);int fd = open("myfile", O_WRONLY|O_CREAT, 00644);if(fd < 0){perror("open");return 1;}printf("fd: %d\n", fd);fflush(stdout);close(fd);exit(0);
}

此时,我们发现,本来应该输出到显示器上的内容,输出到了文件 myfile 当中,其中,fd=1。这种现象叫做输出重定向。常见的重定向有:>, >>,<。

那重定向的本质是什么呢?

实际上我们的上层是不会变的,也就是说操作系统还是只看fd,但是呢fd对应的文件结构体内容变了,指向了其它文件的地址就会发生这种效果。

使用 dup2 系统调用

函数原型如下:

#include <unistd.h>int dup2(int oldfd, int newfd);
//int fd = open(filename,O_CREAT | O_WRONLY | O_TRUNC,0666);int fd = open(filename,O_CREAT | O_WRONLY | O_APPEND,0666);dup2(fd,1);printf("hello world\n");fprintf(stdout, "hello world\n");

printf是C库函数当中的IO函数,一般往 stdout 中输出,但是stdout底层访问文件的时候,找的还是fd:1, 但此时,fd:1 下标所表示内容,已经变成了log.txt的地址,不再是显示器文件的地址,所以,输出的任何消息都会往文件中写入,进而完成输出重定向。

FILE

  • 因为IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过fd访问的。
  • 所以C库当中的FILE结构体内部,必定封装了fd。

来段代码在研究一下:

#include <stdio.h>
#include <string.h>int main()
{const char *msg0="hello printf\n";const char *msg1="hello fwrite\n";const char *msg2="hello write\n";printf("%s", msg0);fwrite(msg1, strlen(msg0), 1, stdout);write(1, msg2, strlen(msg2));fork();return 0;
}

运行出结果:

hello printf
hello fwrite
hello write

但如果对进程实现输出重定向呢? ./hello > file , 我们发现结果变成了:

hello write
hello printf
hello fwrite
hello printf
hello fwrite

我们发现 printf fwrite (库函数)都输出了2次,而 write 只输出了一次(系统调用)。为什么呢?肯定和 fork有关!

  • 一般C库函数写入文件时是全缓冲的,而写入显示器是行缓冲。
  • printf fwrite 库函数会自带缓冲区(进度条例子就可以说明),当发生重定向到普通文件时,数据的缓冲方式由行缓冲变成了全缓冲。
  • 而我们放在缓冲区中的数据,就不会被立即刷新,甚至fork之后
  • 但是进程退出之后,会统一刷新,写入文件当中。
  • 但是fork的时候,父子数据会发生写时拷贝,所以当你父进程准备刷新的时候,子进程也就有了同样的 一份数据,随即产生两份数据。
  • write 没有变化,说明没有所谓的缓冲。、

综上: printf fwrite 库函数会自带缓冲区,而 write 系统调用没有带缓冲区。另外,我们这里所说的缓冲区, 都是用户级缓冲区。其实为了提升整机性能,OS也会提供相关内核级缓冲区,不过不在我们讨论范围之内。 那这个缓冲区谁提供呢? printf fwrite 是库函数, write 是系统调用,库函数在系统调用的“上层”, 是对系统 调用的“封装”,但是 write 没有缓冲区,而 printf fwrite 有,足以说明,该缓冲区是二次加上的,又因为是 C,所以由C标准库提供。

相关文章:

  • Java—类与对象(一)
  • 【NLP 计算句子之间的BLEU和ROUGE分数】
  • 图像识别与 OCR 应用实践
  • 学术论文的科研流程概述 视频会议记录
  • GpuGeek全栈AI开发实战:从零构建企业级大模型生产管线(附完整案例)
  • stm32 ADC单通道转换
  • day20-线性表(链表II)
  • C++(2)
  • 牛顿迭代公式
  • MySQL中的索引下推技术(ICP)
  • 通用软件项目技术报告 - 导读IV(终)
  • MATLAB实现振幅调制(AM调制信号)
  • 构建现代化WPF应用:数据驱动开发与高级特性解析
  • Jenkins里构建一个简单流水线
  • 【常用算法:排序篇】6.归并排序双刃剑:逆序数秒算与搜索引擎海量数据排序
  • Virtualized Table 虚拟化表格 el-table-v2 表头分组 多级表头的简单示例
  • 机器学习基础课程-5-课程实验
  • 使用Docker部署MongoDB
  • AI时代的弯道超车之第七章:如何用AI赋能创业?
  • springboot项目启动报错:找不到或无法加载主类
  • 商务部新闻发言人就暂停17家美国实体不可靠实体清单措施答记者问
  • 国务院关税税则委员会公布公告调整对原产于美国的进口商品加征关税措施
  • 石家庄推动城市能级与民生福祉并进
  • 多元史料下的“西狩”叙事——《“庚子西狩”中外资料六种》解题
  • 马克龙称法英正与乌克兰商议“在乌部署欧洲军队”
  • 体坛联播|穆勒主场完成拜仁谢幕战,山西车队再登环塔拉力赛