【Linux】开始了解重定向

这篇具有很好参考价值的文章主要介绍了【Linux】开始了解重定向。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【Linux】开始了解重定向,一起学Linux吧!,linux,运维,服务器,学习,算法,c语言
送给大家一句话:

人真正的名字是:欲望。所以你得知道,消灭恐惧最有效的办法,就是消灭欲望。 – 史铁生 《我与地坛》

1 前言

上一篇文章我们复习了C文件IO相关操作,了解了linux下的文件系统调用(open write read ),认识了文件描述符fd值,今天我们来学习重定向和缓冲区,这个缓冲区之前遇到过很多次,比如进度条项目的刷新缓冲区操作。然后我们可以来尝试封装一下系统调用,模拟C语言的文件库。

2 重定向与缓冲区

2.1 文件描述符分配规则

接下来我们来了解重定向!
首先我们来看fd文件描述符的分配规则,我们写一段代码来看:

    1 #include<stdio.h>
    2 #include<sys/types.h>
    3 #include<sys/stat.h>
    4 #include<fcntl.h>
    5 #include<unistd.h>
    6 #include<string.h>
    7 #include<stdlib.h>
    8 
    9 const char* filename = "log.txt";
   10 
   11 
   12 int main()
   13 {
   14 	
   15   int fd = open("myfile", O_RDONLY);
   16   if(fd < 0){
   17   perror("open");
   18     return 1;
   19   }
   20   printf("fd: %d\n", fd);
   21   close(fd);
   22   return 0;    
   23   }

我们运行来看:
【Linux】开始了解重定向,一起学Linux吧!,linux,运维,服务器,学习,算法,c语言
这和我们的预期是一样的,我们文件操作那篇文章讲解了fd 的 0 1 2 分别代表了标准输入,标准输出,标准错误。那么在创建的文件描述符很自然的就使用了3! 那么加入我们关闭012中的文件呢,那么新打开的文件描述符会是3吗???

    1 #include<stdio.h>
    2 #include<sys/types.h>
    3 #include<sys/stat.h>
    4 #include<fcntl.h>
    5 #include<unistd.h>
    6 #include<string.h>
    7 #include<stdlib.h>
    8 
    9 const char* filename = "log.txt";
   10 
   11 
   12 int main()
   13 {
   14 	close(0); 
   15   int fd = open("myfile", O_RDONLY);
   16   if(fd < 0){
   17   perror("open");
   18     return 1;
   19   }
   20   printf("fd: %d\n", fd);
   21   close(fd);
   22   return 0;    
   23 }

来看:
【Linux】开始了解重定向,一起学Linux吧!,linux,运维,服务器,学习,算法,c语言
我们新创建的文件的文件描述符就成了 0 !
再来试试:

  • 关闭 2 close(2) -->新创建的文件的文件描述符就成了 2
  • 关闭 1 close(1) -->就什么也打印不出来(标准输出被关闭自然打印不出来)
  • 关闭 0 2 close(2)close(0) --> 新创建的文件的文件描述符就成了 0

这样我们大致可以总结出来一个结论:
文件描述符的分配规则:进程会查自己的文件描述符表,分配最小的并且没有被使用过的 fd

2.2 重定向的现象

刚才我们看到了文件描述符的分配规则,也发现关闭1 (标准输出)就我们打印出来,我们再来探究一下:如果我们关闭了 标准输出,并打开了一个文件,那么该文件就成为了1 ,来看看会发生什么现象:

  1 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<fcntl.h>
  5 #include<unistd.h>
  6 #include<string.h>
  7 #include<stdlib.h>
  8 
  9 const char* filename = "log.txt";
 10 
 11 
 12 int main()
 13 {
 14 
 15   close(1);
 16 
 17   int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC , 0666);
 18   if(fd < 0){
 19   perror("open");
 20     return 1;
 21   }
 22   printf("fd: %d\n", fd);
 23   fprintf(stdout,"fprintf fd :%d\n",fd);
 24 
 25   fflush(stdout);    
 26   close(fd);
 27   return 0 ;

来看效果:
【Linux】开始了解重定向,一起学Linux吧!,linux,运维,服务器,学习,算法,c语言
我们发现并没有在显示器打印出来,而是在新文件log.txt中打印出来了!!!

  1. 因为我们关闭了1号文件 (标准输出
  2. 然后又打开了一个文件,那么1号下标就成了该新文件的文件描述符。
  3. 又因为stdout是对系统的封装,里面封装了 1 号文件
  4. 那么stdout 的指向没有发生改变(还是1 号文件),所以自然就打印到了log.txt中去了!.

这种技术就叫做 重定向,也就是把本应该打印到显示器的内容打印到了一个其他文件中。
其本质就是在内核中改变文件描述符表特定下标的内容,和上层无关!
【Linux】开始了解重定向,一起学Linux吧!,linux,运维,服务器,学习,算法,c语言

可是如果不加入fflush 呢???结果是log.txt文件里也什么都没有?!这就涉及缓冲区的内容了。
首先 一个文件都有一个方法表和内核文件缓冲区。同样在C语言中 (stdin stdout stderr都是struct FILE* 的指针,)文件结构体里面一定封装了fd描述符,而且也封装了语言级的缓冲区。以往的 printf fprintf都是先讲内容写到语言级的缓冲区里在写到文件内核缓冲区了,所以fflush作为一个系统调用,就是刷新文件内核缓冲区,使其输出到文件中!!!
而为什么不加入fflush 呢结果是log.txt文件里也什么都没有呢??? 就是因为内容写入到文件内核缓冲区里还没有刷新就被close关闭了,所以还没刷新就文件被关闭了,还怎么打印到文件中。而且我们不写fflush 不写close 就可以成功打印到文件中!!!

2.3 重定向的理解

完成重定向的操作肯定不是像我们上面做的那样简单粗暴(又要删除,又要创建新文件),我们有一个系统调用dup2

NAME
       dup, dup2, dup3 - duplicate a file descriptor

SYNOPSIS
       #include <unistd.h>

       int dup(int oldfd);
       int dup2(int oldfd, int newfd);

       #define _GNU_SOURCE             /* See feature_test_macros(7) */
       #include <fcntl.h>              /* Obtain O_* constant definitions */
       #include <unistd.h>

       int dup3(int oldfd, int newfd, int flags);

每次我们使用dup2 就可以实现重定向 ,来看其功能描述。
dup2() makes newfd be the copy of oldfd, closing newfd first if necessary
通过描述可以知道:

  1. 首先文件描述符的拷贝不是对数字的拷贝,而是下标所对应内容(文件结构体指针)的拷贝
  2. 然后是实现了将oldfd的内容拷贝到newfd(多个下标指向一个文件),dup2( fd , 1 )就是将fd指向的文件拷贝到1 (标准输出)里。

这样通过dup2既可以完成重定向:

    1 #include<stdio.h>
    2 #include<sys/types.h>
    3 #include<sys/stat.h>
    4 #include<fcntl.h>
    5 #include<unistd.h>
    6 #include<string.h>
    7 #include<stdlib.h>
    8 
    9 const char* filename = "log.txt";
   10 
   11 
   12 int main()
   13 {
   14 
   15   int fd = open(filename , O_CREAT | O_WRONLY | O_TRUNC);
   16 
   17   dup2(fd,1);
   18 
   19   printf("Hello world!\n");
   20   fprintf(stdout,"Hello world!\n");                                              
   21   close(fd);
   22   return 0;
   23 }                                          

来看效果:
【Linux】开始了解重定向,一起学Linux吧!,linux,运维,服务器,学习,算法,c语言
这样也实现了重定向的功能!!!比简单粗暴的关闭stdout 再打开新文件好多了!!!
我们也可以将O_TRUNC 换成O_APPEND,这样每次都是追加内容,所以我们的命令也有了对应:

  • > 相当于 O_TRUNC 覆盖
  • >> 相当于 O_APPEND 追加

就这么简单!!!

2.4 缓冲区的理解

缓冲区分为:用户级缓冲区 和 内核缓冲区。缓冲区的作用是:解耦和提高使用者效率

类比生活中,缓冲区就是类似一个超市,我们不需要去工厂进行采购,这样十分麻烦,而直接去超市就解决了问题。也可以比作顺丰快递,我们想要寄东西,只需要交给快递站就可以,我们不需要考虑快递怎么到达目的地!
所以我们操作系统与语言层中,我们的printf 和 fprintf就不需要考虑我们如何将内容写入到文件中,这不是他们需要关心的事情!!!

那为什么会拷贝两次呢???为什么会有两个缓冲区, **因为系统调用是有成本的!**操作系统可能正在执行其他任务,所以为了注重用户体验,就需要缓冲区(也就提高printf fprintf 的效率,因为我们实际上还没有将内容打印到文件,只是打印到了缓冲区,可能调用10次pringtf ,但是只需要刷新一次,是不是刷新IO的效率就高了)

  1. 缓冲区可以理解为一段内存空间
  2. 缓冲区是为了给上层通过良好的IO体验(语言 --> 操作系统 --> 磁盘)
  3. 缓冲区的刷新策略是什么呢?
    • 立即刷新 语言层:fflush() , 系统调用:fysnc(int fd) 相当于无缓冲
    • 行刷新 :显示器(配合人的阅读习惯)
    • 全缓冲,缓冲区写满才刷新:普通文件
    • 特殊情况 :进程退出会自动刷新缓冲区

截图内核的刷新策略我们不关心,就针对用户层面来研究。

3 进程与重定向

我们再来与先前的进程控制结合一下,来看:

  1 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<fcntl.h>
  5 #include<unistd.h>
  6 #include<string.h>
  7 #include<stdlib.h>
  8 
  9 const char* filename = "log.txt";
 10 
 11 
 12 int main()
 13 {
 14 
 15   int fd = open(filename , O_CREAT | O_WRONLY | O_TRUNC);
 16 
 17   
 18 
 19   printf("Hello printf!\n");
 20   fprintf(stdout,"Hello fprintf!\n");
 21 
 22   const char *msg = "hello write!\n";
 23   write(1,msg,strlen(msg));                                                                                                                                                   
 24                                                                                                                                            
 25   fork();      
 26   close(fd);
 27	  return 0;
 28 }

我们运行一下来看效果:
【Linux】开始了解重定向,一起学Linux吧!,linux,运维,服务器,学习,算法,c语言

啊???这是什么现象???

  1. 显示器与文件的打印顺序不一样
  2. 打印次数不一样?!
  • 现象 1: 是因为显示器采用行刷新所以每次换行就会打印出来,普通文件采用全缓冲,最后才会打印出来,打印顺序类似入栈出栈。
  • 现象 2 : 按理说我们fork()之后,创建了子进程,子进程会继承父进程的代码与数据。那么为什么显示器只打印了一遍呢???因为显示器采用行刷新,fork的时候,语言缓冲区和内核缓冲区里没有内容,所以子进程不会打印出来。而向文件打印时,fork之前语言缓冲区有内容(printf fprintf ),内核缓冲区有内容(write)。fork后 ,子进程会拷贝一份数据也就语言层的缓冲区被打印了两次,内核缓冲区不会拷贝给子进程(不是用户级,所有用户共享)

缓冲区就在struct file 内部!每个文件都有自己的缓冲区文章来源地址https://www.toymoban.com/news/detail-851411.html

Thanks♪(・ω・)ノ谢谢阅读!!!

下一篇文章见!!!

到了这里,关于【Linux】开始了解重定向的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Linux运维】shell脚本检查服务器内存和CPU利用率

    在管理服务器时候写了一个 shell脚本,在服务上实现每天凌晨3点查系统的指定文件夹下的容量大小,如果超过10G就要删除3天前的内容,还要时刻查询内存和cpu利用率,如果超过80%就要提示用户出现过载 将以上代码保存为一个.sh文件,然后通过crontab在每天凌晨3点运行即可:

    2024年02月09日
    浏览(65)
  • Linux服务器常见运维性能测试(1)综合跑分unixbench、superbench

    最近需要测试一批服务器的相关硬件性能,以及在常规环境下的硬件运行稳定情况,需要持续拷机测试稳定性。所以找了一些测试用例。本次测试包括在服务器的高低温下性能记录及压力测试,高低电压下性能记录及压力测试,常规环境下CPU满载稳定运行的功率记录。 这个系

    2024年02月04日
    浏览(75)
  • Linux本地部署1Panel服务器运维管理面板并实现公网访问

    1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。高效管理,通过 Web 端轻松管理 Linux 服务器,包括主机监控、文件管理、数据库管理、容器管理等 下面我们介绍在Linux 本地安装1Panel 并结合cpolar 内网穿透工具实现远程访问1Panel 管理界面 执行如下命令一键安装 1Panel: 安

    2024年02月04日
    浏览(92)
  • [1Panel]开源,现代化,新一代的 Linux 服务器运维管理面板

    本期测评试用一下1Panel这款面板。1Panel是国内飞致云旗下开源产品。整个界面简洁清爽,后端使用GO开发,前端使用VUE的Element-Plus作为UI框架,整个面板的管理都是基于docker的,想法很先进。官方还提供了视频的使用教程,本期为大家按照本专栏的基本内容进行多方面的测评。

    2024年02月07日
    浏览(89)
  • C++ Webserver从零开始:基础知识(三)——Linux服务器程序框架

    目录 前言 一.服务器编程基础框架 C/S模型 主要框架 二.I/O模型 阻塞I/O 非阻塞I/O 异步I/O 三.两种高效的事件处理模式 Reactor Proactor 四.模拟Proactor模式 五.半同步/半异步的并发模式 六.有限状态机 七.其他提高服务器性能的方法 池 数据复制 上下文切换和锁         这一章是

    2024年02月22日
    浏览(51)
  • 华为云云耀云服务器L实例评测 | Linux系统宝塔运维部署H5游戏

    本章节内容,我们主要介绍华为云耀服务器L实例,从云服务的优势讲起,然后讲解华为云耀服务器L实例资源面板如何操作,如何使用宝塔运维服务,如何使用运维工具可视化安装nginx,最后部署一个自研的H5的小游戏(6岁的小朋友玩的很开心😁)。 前端的同学如果想把自己

    2024年02月07日
    浏览(56)
  • Linux服务器常见运维性能测试(3)CPU测试super_pi、sysbench

    最近需要测试一批服务器的相关硬件性能,以及在常规环境下的硬件运行稳定情况,需要持续拷机测试稳定性。所以找了一些测试用例。本次测试包括在服务器的高低温下性能记录及压力测试,高低电压下性能记录及压力测试,常规环境下CPU满载稳定运行的功率记录。 这个系

    2024年02月02日
    浏览(52)
  • Linux运维:深入了解 Linux 目录结构

    💖The Begin💖点点关注,收藏不迷路💖 1、根目录表示法: 2、路径分隔符: 3、目录结构: 4、文件系统: 5、特殊目录: / 根目录: 是系统中所有其他目录和文件的起点。根目录是整个文件系统的顶级目录。 /: 根目录,一般根目录下只存放目录,不要存放文件,/etc、/bi

    2024年03月19日
    浏览(58)
  • 【树莓派烤肉 001】从 0 开始用自己的树莓派搭建服务器:运维篇

    没错,我承认这个标题写的很好吃…… 话说我家里那只树莓派 4B 8GB 放了两年了,除了吃灰还是吃灰~ 于是我想用它做一些非同寻常的事情,比如搭服务器。之前建网站都是用的 GitHub Pages,这次我们整个真实的 LAMP 环境。话不多说,下面开始搭建运行环境~ (不用担心树莓派

    2024年02月09日
    浏览(36)
  • 从零开始学习Linux运维,成为IT领域翘楚(二)

    文件系统组织结构 登录系统后,在当前命令窗口下输入命令: 用户管理概述 Linux是一个多用户、多任务的操作系统。 用户账号和用户组 用户概念 用户组概念    用户组(group)就是具有相同特征的用户(user)的集合体;比如有时我们要让多个用户具有相同的权限,比如查

    2024年02月01日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包