重定向原理和缓冲区

news2025/1/20 14:50:45

文章目录

  • 重定向
  • 缓冲区

正文开始前给大家推荐个网站,前些天发现了一个巨牛的 人工智能学习网站, 通俗易懂,风趣幽默,忍不住分享一下给大家。 点击跳转到网站。

重定向

内核中为了管理被打开的文件,一定会存在描述一个文件的file结构体,这个结构体中大概有什么呢?
我们知道文件=文件内容 + 文件属性,所以file结构体中一定会存在打开文件的各种属性,其次它也一定存在自己的读写方法,也就是方法集,文件的内容是存在在磁盘中的,所以file中又一个属于内核级别的文件缓冲区,所以当我们读写文件的时候,是需要把文件的内容拷贝到文件缓冲区中的,然后如果读文件就把缓冲区中的内容拷贝到我们自己的定义的缓冲区中,如果写或者修改的话就把内容拷贝到内核缓冲区中,然后刷新到磁盘,所以我们在应用层进行的数据读写的本质就是将内核缓冲区的内容进行来回拷贝。不管是读文件还是写文件都需要先把内容拷贝到文件缓冲区。

fd的分配规则
进程是默认打开了0,1,2文件描述符的,我们在打开一个文件fd就是3,如果我们关闭了1,然后在进行打开文件,那么新打开的文件fd就是1,所以文件描述符的分配规则是遍历文件fd表,寻找最小的没有被使用的位置,然后分配给打开的文件。

重定向原理

重定向的就是把本来应该打印到显示器的内容打印了文件中,而往显示器打印本质就是想显示器文件打印,因为C语言中的标准输入输出本质就是封装了fd0和1,所以我们利用文件描述符的分配规则就可以自己实现一个简单的重定向功能。
如果我们要实现一个输出重定向的话,我们只需要把fd1关了,然后再打开一个文件,然后我们往显示器中打印内容,就会往fd1中打印,但是现在文件1指向的是我们自己新打开的文件。输入同理。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
    close(1);
    int fd = open("log.txt",O_WRONLY | O_CREAT | O_TRUNC , 0666);
    if(fd < 0) return -1;

    printf("hello printf\n");
    fflush(stdout);
    close(fd);
    return 0;
}

在这里插入图片描述
所以通过这种方式我们就可以自己实现一个输出重定向。但是我们每次都要自己关闭1号文件描述符,这样太麻烦了,那么如果存在一种可以把我们打开的文件的fd内容覆盖1号下标的内容就可以实现这个技术,所以重定向的本质就是修改文件描述符表。对于用户来说,fd是不变的,但是fd指向的内容改变了。系统中有一个函数dup2就可以实现这个功能。
在这里插入图片描述
dup2就可以让oldfd覆盖到newfd。对于被覆盖的文件,OS会自动帮你关闭的,所以我们通过这种方式也可以实现重定向功能。
以输入重定向为例:
dup2(fd,0)就可以实现下图的效果。
在这里插入图片描述
所以重定向的本质就修改文件描述符指向的内容,命令行级别的只需要对字符串进行特定的解析,然后调用dup2函数就可以实现重定向功能。程序替换时不会影响重定向的,因为程序替换只会替换代码和数据,对于进程的PCB是不影响的,所以对于PCB指向的文件fd

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
    int fd = open("log.txt",O_WRONLY | O_CREAT | O_TRUNC , 0666);

    if(fd < 0) return -1;
    //使用dup2
    dup2(fd,1);

    printf("hello hello\n");
    close(fd);
    return 0;
}

在这里插入图片描述
如果是对于追加和输入重定向的话,我们只需要控制一些打开文件时,打开的方式就可以实现,和输出同理。

我们知道标准输入和标准输出是我们平时使用必不可少的东西,所以系统会帮我们自动打开,这都没毛病,但是标准错误是什么东西??

#include <stdio.h>
int main()
{
    printf("i am printf\n");
    perror("i am perror ");
    return 0;
}

如果我们直接运行:
在这里插入图片描述
如果我们重定向一下:
在这里插入图片描述
可以通过这种方式把标准输出和标准错误打印的东西分开打印,perror和cerr都是向标准错误中打印,我们可以通过重定向把标准输出和标准错误打印的东西分别打印到不同的文件方便调试。

打印到同一个文件
在这里插入图片描述
打印到不同文件:
在这里插入图片描述

缓冲区

什么是缓冲区?
缓冲区的本质就是一快内存,用来存放数据的。

为什么要有缓冲区?
缓冲区的主要作用就是提高效率,谁使用缓冲区就提供谁的效率,因为有缓冲区的存在,我们在写一些东西的时候一定会涉及I/O操作,就一定会访问硬件,所以通过缓冲区累计一部分数据后再进行发送会远远比每次都进行I/O的效率要高很多。可以提高发送的效率。

缓冲区能够缓存一定的数据,就一定会存在自己的刷新策略:

  1. 全缓冲(缓冲区满了,在进行刷新)
  2. 行缓冲(行刷新)
  3. 无缓冲(立即刷新)

这三种是一般的策略,用户也可以通过fflush这样的函数来进行强制的刷新,并且在进程结束的时候,一般都要进行缓冲区的刷新的。一般对于显示器文件来说是行刷新,对于普通的磁盘文件是全刷新的。

我们可以通过一个样例来证明一下这个缓冲区的存在:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>

int main()
{
    printf("hello printf\n");
    fprintf(stdout, "hello fprintf\n");
    fputs("hello fputs\n", stdout);

    char buffer[1024] = "hello write\n";
    write(1,buffer,strlen(buffer));
    pid_t id = fork();
    return 0;
}

这个代码很简单就不解释了,我们先直接运行一下,然后在重定向一下,看结果:

在这里插入图片描述
那么为什么会出现这个现象呢?

  1. 当我们直接运行的时候我们调用的所有接口都是向显示器文件打印,而显示器文件的刷新方式是行刷新,而我们所有的打印后面都有‘\n’,所以在fork()之前,我们的数据都已经被刷新了,包括write系统调用。
  2. 当我们重定向的时候,本质已经是向磁盘文件中打印了,不是向显示器文件打印了,所以刷新方式已经变成了全缓冲。
  3. 全缓冲就意味着缓冲区很大,当我们fork的时候,我们实际写入的数据很少,不足以把缓冲区打满,所以当fork的时候数据仍然在缓冲区中。
  4. 我们可以看到,重定向之后,只有C语言的接口打印了两次,而write系统调用只打印了一次,所以我们目前说的缓冲区是C语言提供的,和操作系统没有任何关系。也侧面证明了exit和_exit的区别,一个C语言提供的,程序退出刷新缓冲区,一个系统调用,不刷新缓冲区,所以这个缓冲区一定是C语言提供的。
  5. C/C++提供的缓冲区,里面保存的一定是我们用户的数据,只要我们不刷新,这个数据就属于我们用户,但是如果我们把缓冲区的数据写入了OS中,那么这部分数据就不属于我们了,而是属于OS。
  6. 当进程退出时一般都要刷新缓冲区,即使没有达到刷新的条件。而刷新的本质即使清空缓冲区,清空也是写入。所以当我们重定向向文件中打印的时候,系统调用会先先打,而C语言提供的接口打印的东西都还在C语言提供的缓冲区中,当我们fork创建子进程后,子进程会继承父进程的大部分数据,当子进程或者父进程退出时,退出的一方要刷新缓冲区,也就是写入,由于父子之间具有独立性,就要发生写时拷贝,这时,就出现了上面C语言接口打印两次的情况。

我们之前说过文件也有自己的文件缓冲区,也就是内核缓冲区,所以C语言的缓冲区和内核缓冲区的关系就是我们平时先把数据拷贝到C语言的缓冲区,根据刷新的机制在刷新到文件缓冲区,然后OS根据自己的安排定时刷新到磁盘。所以文件读写的本质就是来回拷贝。
在这里插入图片描述
从用户缓冲区拷贝到文件缓冲区的过程就是我们平时说的刷新。
我们在使用C语言的I/O接口是,都会接触FILE结构体,就连默认打开的3个流都是FILE指针类型。
在这里插入图片描述
因此C语言提供的缓冲区就是在FILE结构体中。所以FILE中不仅封装了fd文件描述符,还封装了缓冲区。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1602617.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【热门话题】常见分类算法解析

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 常见分类算法解析1. 逻辑回归&#xff08;Logistic Regression&#xff09;2. 朴…

【Spring进阶系列丨第十篇】基于注解的面向切面编程(AOP)详解

文章目录 一、基于注解的AOP1、配置Spring环境2、在beans.xml文件中定义AOP约束3、定义记录日志的类【切面】4、定义Bean5、在主配置文件中配置扫描的包6、在主配置文件中去开启AOP的注解支持7、测试8、优化改进9、总结 一、基于注解的AOP 1、配置Spring环境 <dependencie…

1.SCI各模块

1.学会“抄” 写论文&#xff0c;一定要学会“抄”&#xff01;这样才能事半功倍&#xff0c;尤其是对于初次写作的新手&#xff0c;否则写作过程一定会让你痛不欲生&#xff0c;而且写出来的东西就是一坨shi&#xff0c;不仅折磨自己&#xff0c;也折磨导师。 写论文与建大楼…

SparkUI 讲解

目录 Executors Environment Storage SQL Exchange Sort Aggregate Jobs Stages Stage DAG Event Timeline Task Metrics Summary Metrics Tasks &#x1f490;&#x1f490;扫码关注公众号&#xff0c;回复 spark 关键字下载geekbang 原价 90 元 零基础入门 Spar…

OpenCV从入门到精通实战(二)——文档OCR识别(tesseract)

导入环境 导入必要的库 numpy: 用于处理数值计算。 argparse: 用于处理命令行参数。 cv2: OpenCV库&#xff0c;用于图像处理。 import numpy as np import argparse import cv2设置命令行参数 ap argparse.ArgumentParser() ap.add_argument("-i", "--imag…

Appium的使用:混合APP切换上下文

网上别的文章说要把移动端的webview设置成调试模式,才能看到下图信息。 但我这里是直接在Android Studio新建了一个空白活动,然后放的webview控件,写的webview代码,直接部署到模拟器上,在确定adb可以连接到模拟器后,在桌面浏览器输入chrome://inspect/#devices后就可以看…

3 xgboost

xgboost比赛以及工程利器。目前存在大量有关算法文档。 XGBoost&#xff08;eXtreme Gradient Boosting&#xff09;是一种基于决策树集成的机器学习算法&#xff0c;被广泛应用于分类、回归和排名等任务。XGBoost 在 Kaggle 等数据科学竞赛中取得了很好的表现&#xff0c;被认…

每日练习——leetcode402. 移掉 K 位数字和17. 电话号码的字母组合

目录 402. 移掉 K 位数字 题目描述 解题思路 代码实现 17. 电话号码的字母组合 题目描述 解题思路 代码实现 402. 移掉 K 位数字 题目描述 给你一个以字符串表示的非负整数 num 和一个整数 k &#xff0c;移除这个数中的 k 位数字&#xff0c;使得剩下的数字最小。请…

阿里云4核8G云服务器价格多少钱?700元1年

阿里云4核8G云服务器价格多少钱&#xff1f;700元1年。阿里云4核8G服务器租用优惠价格700元1年&#xff0c;配置为ECS通用算力型u1实例&#xff08;ecs.u1-c1m2.xlarge&#xff09;4核8G配置、1M到3M带宽可选、ESSD Entry系统盘20G到40G可选&#xff0c;CPU采用Intel(R) Xeon(R…

JVM 方法调用之方法分派

JVM 方法调用之方法分派 文章目录 JVM 方法调用之方法分派1.何为分派2.静态分派3.动态分派4.单分派与多分派5.动态分派的实现 1.何为分派 在上一篇文章《方法调用之解析调用》中讲到了解析调用&#xff0c;而解析调用是一个静态过程&#xff0c;在类加载的解析阶段就确定了方法…

黑马点评(四) -- 分布式锁

1 . 分布式锁基本原理和实现方式对比 分布式锁&#xff1a;满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁&#xff0c;只要大家使用的是同一把锁&#xff0c;那么我们就能锁住线程&#xff0c;不让线程进行&#xff0c;让…

​宁德时代:用一块电池玩转两个万亿赛道

2022 到 2023 连续两年&#xff0c;被称为国内储能行业的大储&#xff08;发电侧、电网侧&#xff09;元年和中储&#xff08;工商业&#xff09;元年&#xff0c;整个储能行业可谓是异常火爆&#xff0c;众多资本或企业纷纷涌入该赛道。 对于行业从业者来说&#xff0c;所从事…

第十六篇:springboot案例

文章目录 一、准备工作1.1 需求说明1.2 环境搭建1.3 开发规范1.4 思路 二、部门管理2.1 查询部门2.2 删除部门2.3 新增部门2.4 修改部门2.5 RequestMapping 三、员工管理3.1 分页查询3.2 删除员工3.3 新增员工3.3.1 新增员工3.3.2 文件上传 3.4 修改员工3.4.1 页面回显3.4.2 修…

MySQL基础知识——MySQL事务

事务背景 什么是事务&#xff1f; 一组由一个或多个数据库操作组成的操作组&#xff0c;能够原子的执行&#xff0c;且事务间相互独立&#xff1b; 简单来说&#xff0c;事务就是要保证一组数据库操作&#xff0c;要么全部成功&#xff0c;要么全部失败。 注&#xff1a;MyS…

【Java探索之旅】掌握数组操作,轻松应对编程挑战

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; Java编程秘籍 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一、数组巩固练习1.1 数组转字符串1.2 数组拷贝1.3 求数组中的平均值1.4 查找数组中指…

Node Version Manager(nvm):轻松管理 Node.js 版本的利器

文章目录 前言一、名词解释1、node.js是什么&#xff1f;2、nvm是什么&#xff1f; 二、安装1.在 Linux/macOS 上安装2.在 Windows 上安装 二、使用1.查看可安装的node版本2.安装node3. 查看已安装node4.切换node版本5.其它 总结 前言 Node.js 是现代 Web 开发中不可或缺的一部…

书生·浦语大模型实战营之Lagent AgentLego 智能体应用搭建

书生浦语大模型实战营之Lagent & AgentLego 智能体应用搭建 Lagent 简介 Lagent 是一个轻量级开源智能体框架&#xff0c;旨在让用户可以高效地构建基于大语言模型的智能体。同时它也提供了一些典型工具以增强大语言模型的能力。 Lagent 目前已经支持了包括 AutoGPT、R…

【系统分析师】应用数学部分

文章目录 1、图论应用1.1 最小生成树1.2 最短路径1.3 网络与最大流量 2、运筹方法2.1 关键路径法2.2 线性规划2.3 动态规划2.4 预测与决策2.4.1 囚徒困境2.4.2 实例&#xff1a;商业竞争 2.5 状态转移矩阵2.6 排队论2.7 决策2.7.1 决策2.7.2不确定型决策2.7.3 决策树2.7.4 决策…

4.17号驱动

中断子系统 1. 中断工作原理 1.1 异常处理流程 保存现场(cpu自动完成) 保存cpsr寄存器中的值&#xff0c;到spsr_寄存器中 修改cpsr寄存器中的值 修改状态位(T位) 根据需要禁止相应的中断位(I/F) 修改对应模式位 保存函数的返回地址到lr寄存器中 修改pc指向异常向量表 …

Realsense D455 调试

1 Realsense D455 配置&#xff1a; RGB&#xff1a;彩色相机&#xff0c;FOV&#xff08;h&#xff0c;v&#xff09;&#xff08; 90*65 &#xff09;红外点阵发射&#xff1a;位于上图中RGB右边&#xff0c;发射特定模式的红外光&#xff0c;通常是一种点阵图案&#xff0c…