• Linux重定向原理与系统调用dup2


    🧸🧸🧸各位大佬大家好,我是猪皮兄弟🧸🧸🧸
    在这里插入图片描述

    一、重定向原理

    在这里插入图片描述
    因为文件类的接口都会去调用系统调用,因为对文件操作属于是文件管理,这部分内容是需要操作系统去完成的,因此在系统中,他是只认识fd这个数字的,并不知道FILE*这些,这些是上层封装出来的

    ①输出重定向

    输出重定向的本质就是关掉stdout,也就是1号文件描述符,然后打开一个文件去占用这个文件描述符,然后输出即可

    int main()
    {
    	char buffer[SIZE]="zhupi";
    	close(1);//unistd.h
    	int fd = open("log.txt",O_WRONLY|O_TRUNC|O_CREAT,0666);
    	//打开文件,写|覆盖|没有则创建,权限为0666,真实权限需要集合权限掩码 0666&umask
    	if(fd<0) ...
    	printf("fd:%d\n",fd);
    	cout<<buffer<<endl;
    	close(fd);
    	return  0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    ②输入重定向

    输入重定向就是关掉stdin,也就是0号文件描述符,然后打开文件,占用0号文件描述符的位置,然后键盘输入,或者其他输入进去即可

    int main()
    {
    	char buffer[64];
    	close(0);
    	int fd = open("log.txt",O_RDONLY)
    	if(fd<0) ...
    	printf("fd:%d\n",fd);
    	fgets(buffer,sizeof buffer,stdin);
    	printf(buffer);
    	printf("\n");
    	close(fd);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    二、重定向的系统调用dup2

    在这里插入图片描述
    在这里插入图片描述

    int dup2(int oldfd,int newfd);
    
    • 1

    dup2的作用是把oldfd拷贝到newfd,比如给log.txt创建到3的位置,然后将3拷贝给1,所以3是oldfd,1是newfd(这只是dup2的做法,其实我们close掉1,再open文件log.txt也是一样的效果)
    在这里插入图片描述

    dup2输出重定向

    int main(int argc,char*argv[])
    {
    	printf("%d\n",argc);
    	if(argc!=2)
    	{
    		return 2;
    	}
    	int fd = open("log.txt",O_WROLNY|O_CREAT|O_TRUNC,0666);
    	if(fd<0)
    	{
    		perror("open");
    		return 1;
    	}
    	dup2(fd,1);//old是fd,new是1,把fd拷贝给1
    	fprintf(stdout,"%s\n",argv[1]);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    当我们自己先close(1),再open log.txt,最后如果close(fd)的时候,发现并没有输出进log.txt,这是因为缓冲区的原因,还没有刷新进log.txt就被关了,这个到后面缓冲区的时候再谈,而dup2(fd,1)就不会,这是dup2的一种特性

    三、如何理解一切皆文件

    一切皆文件时Linux的设计哲学,体现在软件设计层面
    那Linux是C写的,如何用C实现面向对象,甚至是运行时多态?

    在这里插入图片描述
    也就是用结构体来体现类的成员变量,然后存函数指针来找成员函数
    所以说底层不同的硬件,对应不同的操作方法就可以了,每个设备实现的read和write不同

    所以设计了一个struct_file来描述,在上层没有任何的硬件差别,看待所有文件的格式,都统一成了对file_struct的操作,所以在操作系统之上,Linux就有一切皆文件,Linux的这种管理模式,称作VFS(virtual file system虚拟文件系统)

    在这里插入图片描述

    四.缓冲区

    ①常见的缓冲区刷新策略

    1.立即刷新
    2.行刷新(行缓冲)
    3,满刷新(全缓冲)

    ②缓冲区的认识

    一般而言,行缓冲的设备一般是显示器
    因为显示器是给用户看的,一方面照顾效率,一方面照顾用户体验
    全缓冲的设备一般是磁盘文件
    因为刷新到磁盘文件,因为用户是不需要马上看到的,更在乎的是效率

    但是所有的设备,都倾向于全缓冲,因为缓冲区满了才刷新就意味着需要更少的IO操作->更少的访问外设,提高效率,其他属性层略是结合具体情况做的妥协(都倾向于缓冲区)

    例:
    在这里插入图片描述
    因为向显示器打印是行缓冲,所以都能够正常打印,但当向文件中打印的时候,因为刷新策略是全缓冲(满刷新),所以在程序结束之前或者说缓冲区满之前并不会刷新到文件,所以子进程进行对父进程的拷贝,所以C的打印接口打印了两次,那么write系统调用为什么只打印一次呢?因为缓冲区是C标准库提供的,缓冲区并不是OS的,所以系统调用并不会用这套缓冲区的刷新策略,而且,父子进程所指向的缓冲区在打印之前也是指向的同一个,因为有写时拷贝。C的打印接口其实就是拷贝一份给缓冲区,然后缓冲区去调用系统调来刷新

    上面我们指的缓冲区是用户级缓冲区,这个缓冲区是C标准库提供的,除此之外,还有内核级缓冲区,其实调用write,也不是直接写到外设上的,而是每个file结构体都有对应的内核缓冲区。

    需要注意的是:
    1.我们需要避免在全缓冲策略下在还未刷新数据前关闭文件描述符导致数据并未刷新
    2.通过关闭1号文件描述符,再让打开的文件取占位,就可以达到行刷新的目的

    在这里插入图片描述

  • 相关阅读:
    学习太极创客 — MQTT(八)ESP8266订阅MQTT主题
    BootStrap-前端框架
    《DREEAM Guiding Attention with Evidence for Improving Document-Level Relation Extraction》阅读笔记
    学会这些分析定位BUG小技巧,你离跨入中级测试还远吗?
    php+vue3实现点选验证码
    elform-item动态prop
    JavaScript代码在网页的使用方式及注意事项
    电脑系统升级Win11变卡顿?如何解决!
    UE5 运行时生成距离场数据
    解决虚拟机磁盘满了,无法上传文件,给虚拟机扩容问题
  • 原文地址:https://blog.csdn.net/zhu_pi_xx/article/details/128178412