Linux重定向和缓冲区

文章目录

  • 知识回顾
    • &取地址重定向
  • 重定向底层
    • 文件描述符分配规则
    • dup2
    • 标准输出和标准错误的区别
  • 缓冲区
  • 缓冲区总结

知识回顾

我们在之前有了解过输出重定向>, >>,可以让echo命令本来是打印到屏幕上而变成了把这些数据写到文件中,并且可以追加或者覆盖文件内容进行写入,输入<可以让文件中的内容重定向到屏幕上
在这里插入图片描述
echo后面只跟跟字符串,就把字符串打印到了屏幕上
echo后面加上加字符串再加>后跟文件,就把对应的字符串写入了文件中,并且会把文件中之前的数据覆盖
echo后面加上字符串再加>>后跟文件,也是把字符串写入了文件中,但是不同的是它不会把文件中原有的数据进行覆盖
在这里插入图片描述

< 把文件中的内容输出到了屏幕上
在这里插入图片描述

&取地址重定向

#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
  const char* msg1 = "hello normal message\n";
  const char* msg2 = "hello error message\n";
  fwrite(msg1, 1, strlen(msg1), stdout);
  fwrite(msg1, 1, strlen(msg1), stdout);
  fwrite(msg1, 1, strlen(msg1), stdout);
  fwrite(msg1, 1, strlen(msg1), stdout);
  fwrite(msg1, 1, strlen(msg1), stdout);
  fwrite(msg2, 1, strlen(msg2), stderr);
  fwrite(msg2, 1, strlen(msg2), stderr);
  fwrite(msg2, 1, strlen(msg2), stderr);      
  fwrite(msg2, 1, strlen(msg2), stderr);
  fwrite(msg2, 1, strlen(msg2), stderr);
  return 0;
}


在这里插入图片描述
解释:
在这里插入图片描述
这句话其实是一个简写,写全的话应该是./mytest 1>normal.log 2>error.log,这个命令意为执行可执行文件./mytest并把要输出到1号文件描述符的数据重定向到文件normal.log,把要输出到二号文件描述符的数据重定向到文件error.log
那么此时如果我就是想把要写入到文件1,2号文件描述符的数据都重定向到一个文件呢?
在这里插入图片描述
./mytest >log.txt 2>&1这句话的意思是:标准输出的输出到文件log.txt,标准错误也输出到文件log.txt
区别:

在这里插入图片描述
语法规定重定向到同一个文件时,后面的要加符号&

重定向底层

文件描述符分配规则

Linux进程默认会打开三个文件描述符标准输入0,标准输出1,标准错误2,对应的物理设备一般是键盘,显示器,显示器。那么我们在进行输出时底层默认是打开文件标准输出或标准错误进行写入,也就是输出到键盘上,那么是如何把数据不输出到显示器而是文件中的呢?
其实是把默认的1或2文件给关闭,再把要被写入的文件放到文件描述符1或2的位置,那么在底层系统仍会向1或2文件进行输出,此时却不是再向显示器进行输出了,而是刚放到1或2处文件进行写入。怎么把文件放到1位置的呢?这就和底层的文件描述符分配规则有关了
在打开一个文件放到files_struct表中的时候,系统会默认从下表0位置开始找,当找到第一个没有放文件的位置时,就会把该文件放到这,并把该位置的文件描述符分配给这个文件,
一句话理解文件描述符的分配规则:在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。
如下图所示:
在这里插入图片描述

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

int main()
{
  close(1);
  umask(0);
  int fd = open("myfile", O_WRONLY|O_CREAT, 0644);
  if(fd < 0)
  {
    perror("open");
    return 1;
  }
  printf("fd: %d\n", fd);
  printf("hello Linux\n");
  fflush(stdout);
  close(fd);
                                                  
  return 0;

上面使我们自己实现的,也符合我们的预期,但是略显挫,因此库里面给我们提供了相应的重定向的接口。

dup2

在这里插入图片描述
先打开一个文件,产生新的文件描述符,然后再把新的文件描述符放到想要重定向到的文件描述符那里
返回值:
在这里插入图片描述
成功返回新产生的描述符,失败返回-1
作用:在这里插入图片描述

dup2的作用就是让旧的描述符指针内容被新的描述符指针内容拷贝,这句话听起来好像产生的结果是,最终这两个位置描述符都变成旧的描述符指针的内容?其实不然,这里会使我们产生混淆,系统这里是把原来已有的文件描述符称为了新的,而刚打开的文件产生的文件描述符是旧的,但我们可以这么理解新的就是先产生的,旧的就是后产生的,注意这里的文件描述符的拷贝并不是数组下标之间的拷贝,而是文件描述符在特定文件描述符数组下标里面的指针内容在进行拷贝
函数要求:
1.如果新的描述符是不明确的,就会调用失败并且原有的文件不会被关闭
2.如果新的文件描述符是明确的,并且两个参数的描述符相同,那么什么也不做并返回旧的文件描述符
参数:
oldfd:新产生的文件描述符
newfd:原来已有的文件描述符

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

int main()
{
  umask(0);
  int fd = open("myfile2", O_WRONLY|O_CREAT, 0644);
  if(fd < 0)
  {
    perror("open");
    return 1;
  }
  dup2(fd, 1);
  close(fd);//把原来的打开的文件给关掉,当然也可以不关
  const char* msg = "abcdefg\n";
  write(1, msg, strlen(msg));
  printf("%d\n", fd);
  return 0;
}          
                                                     

本来应该打印到屏幕打印的数据此时已经打印到了我们的文件里
在这里插入图片描述
上层在进行写入或者打印时,还是会向1号文件描述符里面输入,他以为是在向屏幕上打印,其实我们已经在底层将1号文件描述符换成了我们新打开的这个文件描述符

标准输出和标准错误的区别

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

int main()
{
  umask(0);
  int fd = open("myfile2", O_WRONLY|O_CREAT, 0644);
  if(fd < 0)
  {
    perror("open");
    return 1;
  }
  dup2(fd, 1);
  close(fd);//把原来的打开的文件给关掉,当然也可以不关
  perror("i am stderror\n");                          
  const char* msg = "abcdefg\n";
  write(1, msg, strlen(msg));
  printf("%d\n", fd);
  return 0;
}

在这里插入图片描述
perror(“i am stderror\n”);
perror在底层默认是向2号文件描述符标准错误打印,因此我们把1号文件描述符换掉,并不影响perror向屏幕的打印,只会影响原来要想1号文件描述符打印的函数调用。
为什么要有标准错误呢?
因为我们不想让错误和正常的输出混淆,把错误打印到一个专门保存错误的文件,便于我们查看错误。

缓冲区

此外这里还有一个疑问

echo后面什么都不跟时,为什么会打印出一个换行符,而且我们再重定向追加时,字符串会进行换行打印?
这是因为我们在输入结束时会按下换行键,换行符是有效字符自然就会被写入

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

int main()
{
  char buf[1024];
  ssize_t s = read(0, buf, sizeof(buf));
  if(s > 0)
  {
    buf[s] = 0;
    write(1, buf, strlen(buf));
    write(2, buf, strlen(buf));         
    write(1, buf, strlen(buf) - 1);
    write(2, buf, strlen(buf) - 1);
  }
  return 0;
}

在这里插入图片描述
上面这部分代码就可以证明换行符\n也被写入到了文件中

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

int main()
{
  close(1);
  umask(0);
  int fd = open("myfile", O_WRONLY|O_CREAT, 0644);
  if(fd < 0)
  {
    perror("open");
    return 1;
  }
  printf("fd: %d\n", fd);
  printf("hello Linux\n");
  fflush(stdout);
  close(fd);
                                                  
  return 0;
}

在这里插入图片描述

如果不加fflush(stdout),结果如下:
在这里插入图片描述
为什么文件myfile中没有内容?fflush()的作用是什么?
在这里插入图片描述

将对应缓冲区当中的内容立即刷新出来,fflush(stdout)就是把标准输出的缓冲区内容立即刷新,为什么会有缓冲区呢?缓冲区的作用是为了减少对磁盘的读写次数,提高计算机的运行效率。系统调用时需要时间的,程序频繁地使用系统调用会降低程序的运行效率,库函数访问文件时会根据不同的需要,设置不同类型的缓冲区,从而减少直接调用IO系统调用的次数,也就提高了效率,调用printf时,输出的结果一般会被标准库缓存起来,等到缓冲区满了再一次性写入到文件,在缓冲区没满的时候要想及时的输出就可以使用fflush(stdout)强制把缓存内容进行输出。

#include <stdio.h>                     
#include <unistd.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(msg1), 1, stdout);
 write(1, msg2, strlen(msg2));
 fork();
 return 0;
}

在这里插入图片描述
通过上述例子我们可以发现一个现象,当直接运行该可执行程序打印到屏幕上时,fwrite,printf,write函数各被执行了一次,但是当把该进程重定向到文件时,却发现printf和fwrite函数被执行了两次,而write函数只被执行了一次,这是为什么呢?
同一段代码既然会被执行两次,那么一定与fork函数有关
首先在向文件写入时库函数会先把要输出的,放到用户层的缓冲区,等到缓冲区满了或者进程结束时才会一起刷新到内核缓冲区,而系统调用write会直接把数据放到内核缓冲区,因此在fork之前write函数就已经被刷新输出了,而printf,fprintf,fwrite等还在用户缓冲区里面等着,当执行fork时,创建子进程,此时子进程和父进程的数据是一样的,共用一个缓冲区,但是当进程退出时要进行缓冲区刷新,此时无论是父进程缓冲区刷新还是子进程缓冲区刷新,就会使得共用一份数据的父子进程数据不一样,此时就会发生写时拷贝,再次创建一个缓冲区,因此在用户层的数据就被刷新了两次。
那么为什么直接向屏幕输出时,fork后数据不会被打印两次呢?
因为向屏幕打印时,是行刷新,就是遇到\n就会刷新,因此在执行fork之前,用户层的缓冲区内就已经被刷新了,执行fork后进程退出不会发生,应用层的缓冲区内没有数据被修改,因为没有数据了,所以就不会发生写时拷贝。

缓冲区总结

缓冲区分为内核缓冲区和应用层缓冲区,对于库函数会先把数据写入到应用层缓冲区,而系统调用一般是直接写到内核缓冲区,当写到应用层缓冲区又会分三种情况:直接刷新,行刷新,全缓冲。直接刷新就是不在应用层缓冲区等待,只要是有数据就直接刷新到内核缓冲区,对于行刷新就是只要遇到\n就向内核缓冲区进行刷新,全缓冲就是等缓冲区写满了才向内和缓冲区进行刷新。对于向屏幕打印时采用的是行缓冲,向文件写入时采用的是全缓冲对于fflush函数就是直接刷新向内核缓冲去写入,因此我们可以断定,该函数一定在底层会调用write函数。想文件写入采用全缓冲是为了减少IO的调用次数,提高效率。
在这里插入图片描述
库函数先写入到用户层缓冲区再通过底层调用的write函数写入到内核缓冲区
用于层的缓冲区,是语言级别的缓冲区,是在堆上malloc出来的,并且每一个进程都会有一个缓冲区

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

操作系统 day09(线程)

线程 为什么引入线程 在没引入进程之前&#xff0c;系统中的各个程序只能串行的执行&#xff0c;比如&#xff1a;只能先听歌&#xff0c;再聊QQ。引入进程之后&#xff0c;各个程序可以并发执行&#xff0c;比如&#xff1a;一边听歌&#xff0c;一边聊QQ。但是现在QQ可以一…

使用篇(一):Ai绘图-Stable Diffusion WebUI

1.介绍 1.1 概述 Stable Diffusion Web UI是一个基于Stable diffusion AI模型的AI绘画软件。它是一个多功能的AI绘画软件&#xff0c;支持以下几个功能&#xff1a; 用户可以输入一堆关键词或一句话来生成图片。 它使用了图像加噪去噪过程中的生成模型—— Duffusion&#xff…

Xilinx FPGA SPIx4 配置速度50M约束语句(Vivado开发环境)

qspi_50m.xdc文件&#xff1a; set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design] set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design] set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design] set_property CONFIG_VOLTAGE 3.3 [curren…

【Unity细节】Json序列化时出现:An item with the same key has already been added. Key:

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 &#x1f636;‍&#x1f32b;️收录于专栏&#xff1a;unity细节和bug &#x1f636;‍&#x1f32b;️优质专栏 ⭐【…

【Unity细节】如何让组件失活而不是物体失活

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 &#x1f636;‍&#x1f32b;️收录于专栏&#xff1a;unity细节和bug &#x1f636;‍&#x1f32b;️优质专栏 ⭐【…

揭秘!2024年热门的图标设计工具

在这个瞬息万变的世界里&#xff0c;设计师们对创新和实用的工具的渴望日益热切。我们需要时刻紧跟潮流&#xff0c;发掘和尝试最新&#xff0c;最有价值的图标设计工具&#xff0c;才能在剧烈的市场竞争中引人注目。以下是我们细心挑选的2024年图标设计工具的热门推荐&#xf…

OpenAI开发者大会掀起风暴:GPT模型价格狂降50%,应用商店即将亮相,AI技术将引爆全球!

OpenAI首届开发者大会召开了&#xff01; 关键信息&#xff1a; GPT-4升级版GPT-4 Turbo来了&#xff0c;上下文窗口达到128k&#xff0c;为GPT-4的4倍&#xff1b;OpenAI还降低了几乎所有模型的API使用价格&#xff0c;整体便宜了一半多&#xff1b;GPT-4系列的多模态能力向B…

2023年11月PHP测试覆盖率解决方案

【题记&#xff1a;最近进行了ExcelBDD PHP版的开发&#xff0c;查阅了大量资料&#xff0c;发现PHP测试覆盖率解决方案存在不同的历史版本&#xff0c;让我花费了蛮多时间&#xff0c;为了避免后人浪费时间&#xff0c;整理本文&#xff0c;而且网上没有给出Azure DevOps里面P…

ElementUI之el-progress动态修改进度条里面文本颜色与进度条色块统一

1.效果&#xff1a; 2.实现方式 通过行内style样式动态给整个progress赋颜色 再在样式里给进度条文字单独设置颜色为默认继承父级颜色就ok啦 <el-progress class"custom-progress" stroke-linecap"square" :style"{color:item.color}" :colo…

cpu 支持内存带宽与内存最大长度的关系《鸟哥的 Linux 私房菜》

鸟哥的 Linux 私房菜 -- 计算机概论 -- 計算机&#xff1a;辅助人脑的好工具 同理&#xff0c;64 位 cpu 一次接受内存传递的 64bit 数据&#xff0c;内存字节地址用 64 位记录&#xff0c;最多能记录2^64个字节2^64Bytes2^34GB17179869184GB2^24TB&#xff0c;理论上&#xff…

使用 AIGC ,ChatGPT 快速合并Excel工作薄

职场数据处理&#xff0c;数据分析汇报与统计的过程中&#xff0c;经常会遇到这样的一个问题那就是需要统计的数据源在多个文件中&#xff0c;多个工作薄中&#xff0c;如果要进行数据处理&#xff0c;汇总的时候会很不方便 例如&#xff1a; 如果要汇总6个月的数据可能就得需…

CSS实现透明度效果的两种方法—— opacity 和 rgba()

在实际开发过程中&#xff0c;为了给用户呈现一些效果&#xff0c;我们需要控制元素的透明度。CSS 提供了 opacity 属性和 rgba() 函数给我们控制透明度&#xff0c;接下来通过一个例子来感受一下两种方法的区别。 <style>.transparentBox {display: inline-block;width…

文生图模型评测之PickScore

文章目录 1. 简介2. 构建数据集2.1 Pick-a-Pic web App2.2 Pick-a-Pic Dataset3. PickScore3.1.1模型结构和损失函数3.2 模型训练3.3 验证模型4. 作用4.1 作为验证模型的验证集4.2 用于模型选择5. 小结论文链接:Pick-a-Pic: An Open Dataset of User Preferences for Text-to-…

LinuxMySql

结构化查询语言 DDL&#xff08;数据定义语言&#xff09; 删除数据库drop database DbName; 创建数据库create database DbName; 使用数据库use DbName; 查看创建数据库语句以及字符编码show create database 43th; 修改数据库属性&#xff08;字符编码改为gbk&#xff09;…

Python教程之字典(Dictionary)操作详解

文章目录 前言一、创建字典二、访问字典里的值三、访问字典里的值四、删除字典元素五、字典键的特性六、字典内置函数&方法七、字典练习代码关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Py…

docker---dockerfile相关知识

第 3 章 Docker 高级实践 在这一部分我们主要来介绍一些Docker的高级内容&#xff1a; Dockerfile 和 Docker compose 3.1 Dockerfile Dockerfile我们从下面的几个方面来介绍&#xff1a; Dockerfile简介 Dockerfile快速入门 Dockerfile详解 Dockerfile简单 实践 3.1.1 Docke…

JavaEE平台技术——预备知识(Maven、Docker)

JavaEE平台技术——预备知识&#xff08;Maven、Docker&#xff09; 1. Maven2. Docker 在观看这个之前&#xff0c;大家请查阅前序内容。 &#x1f600;JavaEE的渊源 &#x1f600;&#x1f600;JavaEE平台技术——预备知识&#xff08;Web、Sevlet、Tomcat&#xff09; 1. M…

Canal

canal译意为水道/管道/沟渠&#xff0c;主要用途是基于 MySQL 数据库增量日志解析&#xff0c;提供增量数据订阅和消费。 1.canal 工作原理 canal 模拟 MySQL slave 的交互协议&#xff0c;伪装自己为 MySQL slave &#xff0c;向 MySQL master 发送dump 协议MySQL master 收到…

mac 安装 selenium + chrome driver

前言 使用 selenium 模拟浏览器渲染数据&#xff0c;需要依赖各浏览器的驱动才能完成&#xff0c;因此需要单独安装chrome driver 查看本地 chrome 浏览器的版本 可以看到我这里已经是 arm 架构下最新的版本了 下载对应的 chrome driver 访问下面的地址&#xff1a; Chrome…

Redis7--基础篇2(Redis的十大数据类型及常用命令)

1. Redis的十大数据类型及常用命令 Redis是key-value键值对类型的数据库&#xff0c;我们所说的数据类型指的是value的数据类型&#xff0c;key的数据类型都是字符串。 1.1 字符串&#xff08;String&#xff09; string是redis最基本的类型&#xff0c;一个key对应一个val…