C语言缓冲区与重定向

news2025/1/23 15:10:21

目录

什么是缓冲区?

刷新策略 

模拟实现重定向

标准输出和标准错误有什么区别?


上文提到关闭1号文件(标准输出文件),根据文件描述符分配规则,再打开的文件的描述符就是1,看以下代码:

运行代码后,前10s中file.txt文件被创建出来了,但是没有内容:

过了10秒钟,程序退出了,内容才被刷新到了磁盘文件上

 

 为什么数据没有被马上打印到文件上呢?

因为存在缓冲区,这里的缓冲区是C语言封装的缓冲区。

什么是缓冲区?

缓冲区的本质其实就是一段内存,用来暂时存放数据的地方。

为什么要有缓冲区呢?

联系生活,比如快递的出现,让货物的配送交给物流,这样就可以解放你的时间 ,而且集中配送货物可以提高总体的效率。对于缓冲区而言,缓冲区对于进程而言效果也是类似的,首先缓冲区解放使用缓冲区的进程的时间,比如进程往磁盘写入的时候,可以直接往缓冲区去写入,进程不用等待写入磁盘完成,可以继续执行后续的代码,其次缓冲区集中处理缓冲区中数据的刷新,减少了IO次数,因为外设上IO的效率是很低的(相比其他硬件),故而提高整机的效率。

缓冲区在哪里?

将上面的代码中的printf函数换成系统调用write:

发现,在sleep期间,进程已经把数据打印到了file.txt上,可见write内部并没有缓冲区,printf封装了write,这个缓冲区是C语言的缓冲区。

在C语言的文件描述结构体FILE当中封装了该FILE对应的语言级别的缓冲区,每次打开文件,每个FILE都有对应的fd和属于自己的语言级别缓冲区,写入时先写到缓冲区,当缓冲区需要刷新的时候,内部再调用write刷新到对应文件中,那么何时刷新呢?这取决于C语言的刷新策略。

刷新策略 

1.无缓冲:也就是立即刷新

进程退出时:当进程正常退出前,C语言内部就会一系列工作,包括调用fclose函数刷新缓冲区

用户强制刷新:用户调用fflush函数直接刷新缓冲区

2.行缓冲:逐行刷新,当输入的字符中有\n就直接刷新之前缓冲区中的内容,显示器文件就是这个刷新策略。

3.全缓冲:也即当缓冲区写满了才刷新,块设备对应的磁盘文件是采用全缓冲刷新策略的。

现在就可以解释原本的代码为什么不立即刷新到磁盘上了,首先1号文件标准输出被关闭了,而打开了磁盘文件,printf会往1号文件打印,也就是说标准输出被重定向到了磁盘文件,而标准输出的刷新策略本来是行刷新的,现在改变了刷新策略变成了全缓冲刷新策略,因为缓冲区没有被写满所以在sleep期间也没有看见数据输出到文件中,当进程退出或调用fflush时,缓冲区就被刷新到了磁盘文件上,于是进程退出后数据就才刷新到了文件当中。

模拟实现重定向

在上上篇博客中,实现了一个简易版的shell,现在根据现有知识再模拟实现一下重定向符号输出重定向>,追加重定向>>和输入重定向<添加入minishell吧。

进程程序替换_且随疾风前行->的博客-CSDN博客

实现方法:

先用一些宏标识几种重定向方法,用全局变量记录一下要重定向到的文件,和如何进行重定向,比如下面代码:

 在命令分割之前检查一下命令中是否有重定向符号,并记录之。

然后再在创建子进程之后程序替换之前,将要重定向的文件打开,并用dup2函数重定向之即可。

代码如下:

#include<cstdio>
#include<unistd.h>
#include<cstdlib>
#include<cstring>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

#define NONE_REDIR -1
#define INPUT_REDIR 0
#define OUTPUT_REDIR 1
#define APPEND_REDIR 2
int g_redir_flag=NONE_REDIR;
char* g_redir_filename =NULL;

void CheckRdir(char*command)
{
    char*begin=command;
    while(*begin)
    {
        if(*begin=='>')
        {
            if(*(begin+1)=='>')
            {
                g_redir_flag=APPEND_REDIR;
                *begin='\0';
                begin+=2;
                while(*begin==' ')++begin;
                g_redir_filename=begin;
                break;
            }
            else
            {
                g_redir_flag=OUTPUT_REDIR;
                *begin='\0';
                begin+=1;
                while(*begin==' ')++begin;
                g_redir_filename=begin;
                break;
            }
        }
        else if(*begin=='<')
        {
            g_redir_flag=INPUT_REDIR;
            *begin='\0';
            begin+=1;
            while(*begin==' ')++begin;
            g_redir_filename=begin;
            break;
        }
        else ++begin;
    }
}
int main()
{
    //保持完整的字符串
    char inbuff[1024];
    //保持打散之后的字符串
    char *argv[32];
    char g_envar[64];//保存环境变量
    while(1)
    {
        printf("Please Enter#");
        fflush(stdout);
        memset(inbuff,'\0',sizeof(inbuff));
        if(fgets(inbuff,sizeof inbuff,stdin)==NULL)
            continue;
        inbuff[strlen(inbuff)-1]='\0';

        //检查重定向符号,并记录重定向方法
        CheckRdir(inbuff);

        argv[0]=strtok(inbuff," ");
        int index=1;
        if(strcmp(argv[0],"ls")==0)argv[index++]="--color=auto";
        if(strcmp(argv[0],"ll")==0)argv[0]="ls",argv[2]="-l",argv[1]="--color=auto",index=3;
        while(argv[index++]=strtok(NULL," "));//第二次调用解析原字符串时,传入NULL
        if(strcmp(argv[0],"export")==0&&argv[1]!=NULL)
        {
            strcpy(g_envar,argv[1]);
            if(putenv(g_envar)==0)
                printf("%s export success!\n",argv[1]);
            continue;
        }
        //内建命令
        if(strcmp(argv[0],"cd")==0 && argv[1]!=NULL)
        {
            chdir(argv[1]);
            printf("cd success!\n");
            continue;
        }
        pid_t id=fork();
        if(id==0)//child
        {
            if(g_redir_flag!=NONE_REDIR)
            {
                int fd;
                switch(g_redir_flag)
                {
                    case INPUT_REDIR:
                        fd=open(g_redir_filename,O_RDONLY,0666);
                        dup2(fd,0);
                        break;
                    case OUTPUT_REDIR:
                        fd=open(g_redir_filename,O_WRONLY|O_CREAT|O_TRUNC,0666);
                        dup2(fd,1);
                        break;
                    case APPEND_REDIR:
                        fd=open(g_redir_filename,O_WRONLY|O_CREAT|O_APPEND,0666);
                        dup2(fd,1);
                        break;
                    default:
                        break;
                }
            }
            execvp(argv[0],argv);
            exit(0);
        }
        int status=0;
        pid_t ret=waitpid(id,&status,0);
        if(ret>0)printf("exit code:%d\n",WEXITSTATUS(status));

    }
    return 0;
}

效果大概如下:

在打印的时候标准输出和标准错误都往屏幕上打印,二者有区别吗?

标准输出和标准错误有什么区别?

看如下代码:

 运行程序,标准输出和标准错误都输出到屏幕中:

使用管道重定向时只重定向了标准输出到file.txt文件中,标准错误还是打印到屏幕中

这是因为管道的重定向默认是使用输出重定向,可以显式写出要如何重定向,

比如:

./a.out 1>stdout.txt 2>stderr.txt 让文件输出到标准输出文件的重定向输出到stdout.txt,标准错误输出的重定向输出到stderr.txt 文件

./a.out>all.txt 2>&1表示让输出到标准输入和标准错误都重定向输出到all.txt文件中

之所以有标准输出和标准错误,主要是用于区分哪些是程序的日常输出,哪些是程序运行中产生的错误。

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

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

相关文章

差分数组详解

目录1.概述2.代码实现3.应用本文参考&#xff1a; LABULADONG 的算法网站 1.概述 &#xff08;1&#xff09;差分数组的思想与前缀和算法的非常近似&#xff08;有关前置和算法的具体细节可以参考前缀和算法这篇文章&#xff09;&#xff0c;其主要适用于频繁地对原始数组的某…

为民服务 智慧政务数据可视化大屏一体化系统

为顺应全球发展趋势&#xff0c;以及我国当前经济社会发展进步的需要&#xff0c;加快政府服务信息化、数字化建设紧跟国际步伐的同时也需要开拓引领。今天给大家分享一个基于 数维图 的 SovitChart编辑器 构建大屏可视化场景的案例——智慧政务数据可视化大屏一体化平台。建设…

RabbitMQ 总结二(MQ原理 通信方式 消息应答机制)

目录 MQ的构成 生产者 交换机 队列 消费者 通信方式 Producer -> Broker (包含Exchange) Exchange -> Binding -> Queue -> Consumer 消息应答 为什么引入消息应答 消息自动重新入队 如何进行消息应答 案例Demo MQ的构成 生产者 消费者 交换机和队列…

【学习笔记之Linux】工具之yum

yum是Linux的软件包管理器。   什么是软件包&#xff1f;在Linux中安装软件&#xff0c;可以通过下载程序源码&#xff0c;然后编译得到可执行程序。但是这样非常麻烦&#xff0c;于是就有人把常用的软件编译好之后做成软件包&#xff0c;然后把软件包放在一个服务器上。   …

redis常见面试题

redis常见面试题 redis集群转载于&#xff1a;https://blog.csdn.net/sun_lm/article/details/123467103 redis的几个数据结构的应用场景借鉴于&#xff1a;https://blog.csdn.net/weixin_51299478/article/details/125204374 1. redis的作用 redis的作用主要就是两个&…

数据结构——串

串又称字符串&#xff0c;是由零个或多个字符组成的有限序列&#xff0c;是一种特殊的线性表。由串中若干个连续字符组成的子序列称为子串。 利用字符数组或字符指针表示串&#xff1a; char str1[] { a,b,c,d,\0 }; char str2[] "abcdef"; char* str3 str1; 上…

Java设计模式之单例模式

这一篇&#xff0c;我们来介绍下设计模式最简单的一个模式&#xff0c;单例模式。 二、释义以及实战 2.1 单例模式的定义 单例模式&#xff0c;英文&#xff1a;Singleton Pattern,英文解释&#xff1a;Ensure a class has only instance,and provide a global point of acce…

黑马2022新版SSM框架教程(SpringMVC_day02)

SpringMVC_day02 文章目录SpringMVC_day021&#xff0c;SSM整合1.1 流程分析1.2 整合配置步骤1&#xff1a;创建Maven的web项目步骤2:添加依赖步骤3:创建项目包结构步骤4:创建SpringConfig配置类步骤5:创建JdbcConfig配置类步骤6:创建MybatisConfig配置类步骤7:创建jdbc.proper…

Vue(十二)

1. TodoList案例自定义事件 //App.vue <template><div id"root"><div class"todo-container"><div class"todo-wrap"><!-- addTodo添加自定义事件 --><MyHeader addTodo"addTodo"/><MyList …

Spring AOP详解

1.什么是 Spring AOP&#xff1f; AOP&#xff08;Aspect Oriented Programming&#xff09;&#xff1a;⾯向切⾯编程&#xff0c;它是⼀种思想&#xff0c;它是对某⼀类事情的 集中处理。⽐如⽤户登录权限的效验&#xff0c;没学 AOP 之前&#xff0c;我们所有需要判断⽤户登…

YACC移进规约冲突案例分析(二)output中状态机转移步骤详解

案例 calc.y %union {int ival;const char *sval; } %token <ival> NUM %nterm <ival> exp %token <sval> STR %nterm <sval> useless %left - %left * %% exp:exp exp | exp - exp | exp * exp | exp / exp | NUM ; useless: STR; %%编译 $ biso…

恭喜龙蜥获得中国开源云联盟2022年度中国“最佳开源实践案例”和“杰出开源贡献者”奖项

近日&#xff0c;由工信部中国电子技术标准化研究院主办的 2022 木兰峰会在北京圆满举办&#xff0c;峰会上正式公布了中国开源云联盟(China Open Source Cloud League&#xff0c;简称“COSCL”) 2022 年度评选名单&#xff0c;龙蜥社区荣获中国“最佳开源实践案例”和“杰出开…

仪器设备使用

NI DcpowerSwitchDigitalDMMFgenScope名称直流电源&#xff08;SMU&#xff09;继电器PPMU数字万用表信号发生器示波器版本PXI-4147PXI-2567PXI-6571PXI-4070PXI-4463PXI-5160 1.Scope 示波器是一种电子测量仪器&#xff0c;可以在无干扰的情况下监控输入信号&#xff0c;随后…

Go结构体(struct)

文章目录Struct定义struct构造struct实例struct的值和指针在与函数共用时&#xff1a;匿名字段和嵌套struct嵌套struct的名称冲突问题Struct 是一个值类型的 定义struct type identifier struct {field1 type1field2 type2… } // 或者 type T struct { a, b int }理论上&am…

JAVA多线程初阶(1)

目录JAVA多线程(1)1.Thread类创建与使用1.1 继承Thread类1.2 实现并发关于sleep()1.3 Runnable创建线程1.4 匿名内部类创建线程1.5 lamda表达式创建线程2.多线程提高效率3.Thread类属性和方法3.1 Thread(String name)3.2 isDaemon()3.3 isAlive()3.3 线程的重要方法3.4 中断线程…

数据结构:图

文章目录图内存中存储图数据结构邻接矩阵存储方法用邻接矩阵&#xff08;Adjacency Matrix&#xff09;来表示一个图的缺点&#xff1a;浪费空间优点邻接表存储方法&#xff08;Adjacency List&#xff09;广度优先算法Breadth-First-Search&#xff08;BFS&#xff09;深度优先…

Android——GT库-日志工具

GT库在创造出来初期&#xff0c;里面的日志工具就一直存在的&#xff0c;经历了很久的迭代变更&#xff0c;当目前的最新版本&#xff0c;日志工具已经创造出更高级的调试日志方式了&#xff0c;接下来咋们来看看GT库中的日志工具具体使用方法吧。 使用GT库里的&#xff0c;当然…

web表单设计器的优点体现在哪?

在数字化管理越来越规范的当下&#xff0c;拥有一款优质高效的低代码开发平台&#xff0c;确实能给企业提质增效带来更大的帮助。很多客户朋友会问道&#xff1a;web表单设计器都有哪些特点&#xff1f;为什么能在企业的现代化办公管理中起到巨大的作用&#xff1f;今天&#x…

Linux终端远程工具xshell,xftp,mobasterm

目录 软件介绍 1.xshell 第一步&#xff1a; 第二步&#xff1a; 第三步&#xff1a; 第四步&#xff1a; 第5步&#xff1a; 2.xftp 第一步&#xff1a; 第二部&#xff1a; 第三步&#xff1a; 3.mobasterm 全能终端神器——MobaXterm 第一步&#xff1a; 第二步&a…

C1083无法打开包括文件: “atlbase.h”: No such file or directory

在打开别人的项目的过程中遇到了“atlbase.h”无法打开的问题&#xff0c;在此记录一下。1.下载ATL生成工具与缓解只下载ATL生成工具后面还会报错&#xff0c;直接下载下载ATL生成工具与缓解一步到位。下载的入口在&#xff1a;工具--->获取工具与功能。需要注意的是&#x…