【Linux】进程控制(文件操作符收尾+重定向)

news2024/11/25 2:42:46

上一回进程与文件系统我们主要看了很多文件描述符的知识

1.如何理解一切皆文件?

 

每个设备被打开时,OS给每个文件创建一个自己的struct file 里面填充自己的属性以及自己的缓冲区,其中还有函数指针,里面保存函数地址,通过这个地址可以找到对应外设的read/write方法

 

所以结合上节课的内容我们再来梳理一下进程是如何打开文件的

首先进程要通过自己PCB中内置的struct files_struct*files指针找到struct files_struct 然后在这个结构体内部找到数组struct file* fd_array[ ]这个进而找到OS为这个进程分配的fd(数组下标),通过下标fd找到数组中对应的内容(file*),通过file*指针找到内存中保存struct file的数据结构,访问这个数据结构,找到那个进程要找的struct file!!!!

 如果这个struct file是键盘文件,那么结构体中会有函数指针,指向键盘的读写方法

 这就是使用C语言来设计面向对象 

我们使用OS的本质:通过进程的方式进行OS的访问

来看操作系统层面:首先明确必须访问fd才能找到文件,任何语言访问外设或文件都必须经历OS

之前我们说fopen的返回值是FILE* 这里的FILE其实是一个结构体,那他和我们上面说的struct file有关系吗? 没有,只是上下层的关系

这个结构体是C语言提供的

所以平常我们在安装VS2019的时候是在安装什么?

安装语言对应的头文件和库

这个FILE结构体一定封装了fd!!!!!但是怎么验证?

 

这个_fileno就是每个文件的fd


 2.重定向

重定向有三种:

  • 输出重定向 >

 刚才程序运行的结果是0 1 2 ,我们输出重定向到log里面

  • 输入重定向 <

指定文件作为命令的输入设备

 此时log是输入设备

相当于直接cat log.txt

  • 追加重定向 >>

输出重定向会清空数据,要是不想被清空就使用追加重定向

 

 但是这样的理解太过肤浅,我们深入理解一下重定向的本质

上回我们说过,fd的分配是从下标0开始,遇到第一个没被占用的

看一下这样的代码

1.输出重定向的理解

 

 本该打印到显示器上的东西全都到log.txt中

因为printf只认1,根本不考虑哪个文件fd=1

所以重定向的原理:在上层无法感知的情况下,在OS内部更改进程fd

上面的操作相当于 1>log.txt

也就是log.txt占用了fd=1

2.输入重定向的理解

  

 和刚才其实是一样的scanf函数只认0,他是从0获取数据写到 a b中

所以你关闭了0文件,log.txt分配的fd就是0

自然会从log.txt中拿数据

3.追加重定向的理解

此时open的选项把O_TRUNC改成O_APPEND


现在我想让你把写到stdout的内容打印到log.txt,把写入stderr的内容打印到err.txt怎么搞?

 

 OK,理解一下这条指令

首先运行a.out 把log.txt的fd分配成1,把err.txt的fd分配成2,此时printf只认1,所以hello stdout写入到log.txt fprintf只认2,所以把格式化之后的字符串写入到err.txt中

那如果现在我想把两个字符串都打印到显示器上

 首先运行a.out 然后把运行结果(此时应该只有hello stdout)放到log.txt,hello stderr在2文件里,然后把2也写入到显示器文件,这样就可以一起输出啦

其中1>&2是把1重定向成2的地址,也就是用2的地址的fd=1

如果你想把两个字符串汇总到一起写入log.txt

这是把2重定向成1的地址然后./a.out的时候一起把1 的内容重定向到log.txt 


 但是如果每次重定向都要关闭一个再打开一个文件也太挫了

有没有更好的方法?当然

只要看dup2就行,dup想看懂太简单了

 那么请你回答  1 > 一个文件(他的文件描述符为fd)

此时应该如何传参,是fup(1,fd)还是反过来?

应该是dup2(fd,1),这地方确实是有点绕,但是我的理解是:之前我们不是说过要把文件的fd分配成1,所以fd应该是1的一份拷贝


3.缓冲区 

之前我们总是说缓冲区缓冲区,缓冲区到底在哪? 

 

 又看到这个现象完全懵了

现在我们好好解释一下

那么C库的刷新策略有哪些?

1.无缓冲 (直接写给OS,而不是先放C库的缓冲区) 

2.行缓冲(碰到'\n'就刷)

3.全缓冲(缓冲区写满再刷新)

值得注意的是:

显示器采用行缓冲,普通文件采用全缓冲

为什么要有缓冲区?节省调用者的时间,因为系统调用也是花费时间的

缓冲区在哪?在fopen打开文件的时候,会得到FILE结构体,缓冲区就在其中

所以我们来解释上面的问题

首先C库函数会把第一个字符串放在自己的缓冲区,因为是写入到显示器文件采取行刷新,正好字符串末尾有\n直接刷新,即写给操作系统的stdout文件的缓冲区

write是系统调用,直接可以把hello write写到stdout的缓冲区

所以直接在fork之前就完成刷新了,根本没有创建子进程的事情,也就是有没有fork结果都是一样的

在进程退出之前,OS根据自己的刷新策略把stdout缓冲区的内容调用显示器的写方法,把内容显示到屏幕上

(如果没有fork)

 


再来看一下有fork重定向之后的结果是为什么? 

重定向之后 write还是直接写到系统的缓冲区,但是重定向到普通文件,所以C库的刷新策略变成了全缓冲,所以在执行完write语句之后应该是这样的

这部分的数据属于父进程的,是父进程创建的 

然后fork起作用了,父子进程在退出的时候都要刷新,谁先刷新谁先进行写时拷贝,所以hello fprintf字符串被写入了两次(写入stdout的缓冲区)


其实到这里我们的理解还不是特别深刻,自己写一个缓冲区就好了

 

需要用到一个函数,fsync函数,将文件数据同步到硬盘 

 

 

 main.c

  1 #include "mystdio.h"
  2 #include <string.h>
  3 
  4 #define LOG "log.txt"
  5 int main()
  6 {
  7   MY_FILE*fp=my_fopen(LOG,"w");
  8   const char * msg="hello myfwrite";
  9 int cnt=5;
 10 while(cnt)
 11 {
 12 char buf[1024];
 13 snprintf(buf,sizeof(buf),"%s,%d",msg,cnt--);
 14 size_t num=myfwrite(buf,strlen(buf),1,fp);
 15 printf("当前成功写入: %lu个字节\n", num);                                                                             
 16 my_fflush(fp);
 17 }
 18   return 0;
 19 }

mystdio.h

  1 #pragma once
  2 #include <stdio.h>
  3 #define NUM 1024
  4 typedef struct _MY_FILE
  5 {
  6 int fd;
  7 int current;
  8 int flag;
  9 char outputbuffer[NUM];
 10 }MY_FILE;
 11 
 12 MY_FILE* my_fopen(const char *pathname,const char *mode );
 13 size_t myfwrite(const void *ptr,size_t size,size_t nmemb,MY_FILE *stream);
 14 int my_fflush(MY_FILE*fp);                                                                                            
 15 int my_close(MY_FILE*fp);

mystdio.c

 

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

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

相关文章

京东CEO徐雷突然退休,CFO许冉接任成为首位女CEO

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 京东集团重大人事变动&#xff0c;京东集团CEO徐雷将退休&#xff0c;才49岁&#xff0c;CFO许冉将成为京东集团新任CEO。京东将迎来首位女CEO。 徐雷在48岁被任命为京东CEO&#xff0c;49岁退休。…

IS220PRTDH1A固态过载继电器不会产生热量以方便跳闸

IS220PRTDH1A固态过载继电器不会产生热量以方便跳闸 一旦触发动作发生&#xff0c;双金属片冷却并自行重塑&#xff0c;自动重新启动电路。电机在过载未消除的情况下重新启动&#xff0c;并会一次又一次地点火和重新启动。&#xff08;假设自动重启 这种类型的继电器也可以配备…

Python的画图模块turtle使用详解

简介&#xff1a;Turtle是Python语言中一个很流行的简单的绘图工具。你可以把它理解为一个小海龟&#xff0c;只听得懂有限的指令。它在一个横轴为x、纵轴为y的坐标系原点&#xff0c;(0,0)位置开始&#xff0c;它根据一组函数指令的控制&#xff0c;在这个平面坐标系中移动&am…

【中级软件设计师】—(下午题)试题三精讲总结(四十二)

【中级软件设计师】—&#xff08;下午题&#xff09;试题三精讲总结&#xff08;四十二&#xff09; 一、关系 二、UML中的图 A包含B&#xff0c;那么A执行操作前必须要先执行B 试题一&#xff08;2021年下半年&#xff09; 试题2&#xff08;2021年上半年&#xff09; 官方…

【C++初阶】类和对象下篇

⭐博客主页&#xff1a;️CS semi主页 ⭐欢迎关注&#xff1a;点赞收藏留言 ⭐系列专栏&#xff1a;C初阶 ⭐代码仓库&#xff1a;C初阶 家人们更新不易&#xff0c;你们的点赞和关注对我而言十分重要&#xff0c;友友们麻烦多多点赞&#xff0b;关注&#xff0c;你们的支持是我…

板材激光切割机切割穿孔时注意的几个问题

激光切割设备广泛应用于钣金、五金制品、钢结构、汽车配件、广告、工艺品等行业&#xff0c;成为加工行业不可缺少的环节。在厚板加工中穿孔时间占很大比重&#xff0c;随着加工板材越来越厚&#xff0c;板材激光切割机切割穿孔也会相应地增加难度。 激光切割机两种常见的穿孔方…

druid 远程命令执行 (CVE-2021-25646)

漏洞原理 该漏洞主要就是根据Jackson解析特性(解析name为""时)会将value值绑定到对象(JavaScriptDimFilter&#xff0c;type为javascript时指定的)的对应参数(config)上&#xff0c;造成JavaScriptDimFilter中function属性中的javascript代码被执行。攻击者可以构造…

Redis高级数据结构HyperLogLog

HyperLogLog(Hyper[ˈhaɪpə(r)])并不是一种新的数据结构(实际类型为字符串类型)&#xff0c;而是一种基数算法,通过HyperLogLog可以利用极小的内存空间完成独立总数的统计&#xff0c;数据集可以是IP、Email、ID等。 如果你负责开发维护一个大型的网站&#xff0c;有一天产品…

pytorch移植华为mindspore记录

因为某个需求&#xff0c;需要把原来pytorch的神经网络移植到华为的mindspore上 这边记录下遇到的坑 附上mindspore的官方教程&#xff1a; https://mindspore.cn/tutorials/zh-CN/r2.0/advanced/compute_graph.html 这边附上需要移植的网络&#xff0c;以tensorflow和pytorch…

LeetCode 链表OJ分享

目录 删除排序链表中的重复元素回文链表剑指Offer 06.从尾到头打印链表复制带随机指针的链表 删除排序链表中的重复元素 链接: link 题目描述&#xff1a; 题目思路&#xff1a; 本题思路使用双指针&#xff0c;以示例二为例如下图&#xff1a; 如果head->val等于next-&…

ihateniggers:针对Python开发者的Windows远控木马分析

背景 墨菲安全实验室在持续监测开源软件仓库中的投毒行为&#xff0c;5 月 9 日起发现 4 个包含 “ihateniggers” 远程控制木马的 Python 包被 nagogygmail.com 邮箱关联的账号发布到 PyPI 仓库&#xff0c;试图针对Windows系统下 Python 开发者进行攻击。木马利用了discord、…

各种顺序表和链表的实现代码

目录 一、什么是线性表 二、顺序表 2.1什么是顺序表 2.2静态顺序表的代码实现 2.3动态顺序表的代码实现 三、链表 3.1什么是链表 3.2不带头单向不循环链表的代码实现 3.3带头双向循环链表的代码实现 四、顺序表和链表的区别 一、什么是线性表 线性表是n个具有相同特性…

(十五)数据编辑——图形编辑①

数据编辑——图形编辑① 数据编辑包括几何数据和属性数据的编辑。几何数据的编辑主要是针对图形的操作&#xff0c;即图形编辑&#xff0c;包括平行线复制、缓冲区生成、镜面反射、图层合并、结点操作、拓扑编辑等。属性编辑主要包括图层要素属性的添加、删除、修改、复制、粘…

谷歌落子,我们对中国大模型的期待应该是什么?

对中国大模型厂商而言&#xff0c;市场期待的&#xff0c;也恰是这些真正可落地的应用和实践。这些实践可以在社交&#xff0c;在电商&#xff0c;在低代码&#xff0c;在供应链&#xff0c;也更可以在一个个中国产业数字化转型的新洼地。 作者|思杭 皮爷 出品|产业家 在微软G…

裸辞5个月,面试了37家公司,终于.....

上半年裁员&#xff0c;下半年裸辞&#xff0c;有不少人高呼裸辞后躺平真的好快乐&#xff01;但也有很多人&#xff0c;裸辞后的生活五味杂陈。 面试37次终于找到心仪工作 因为工作压力大、领导PUA等各种原因&#xff0c;今年2月下旬我从一家互联网小厂裸辞&#xff0c;没想…

执行增删改查时的结果处理

查询最终走到PreparedStatementHandler类的query方法&#xff0c;执行查询后调用DefaultResultSetHandler类的handleResultSets方法 1.处理返回的普通实体类 DefaultResultSetHandler类的handleResultSets方法 继续本类的handleResultSet方法 通过 handleRowValues 方法来…

设计一个可靠的自动化测试框架需要考虑哪些问题呢?

随着软件开发的日益普及&#xff0c;自动化测试框架逐渐成为了保障软件质量的必备工具。然而&#xff0c;如何设计一个可靠的自动化测试框架并不是一件简单的事情&#xff0c;需要考虑多方面的问题。本文将从需求分析、架构设计、测试用例编写等多个角度&#xff0c;介绍设计一…

【SpringMVC】| SpringMVC拦截器

目录 一&#xff1a;SpringMVC拦截器 1. 拦截器介绍 2. HandlerInterceptor接口分析 3. 自定义拦截器实现权限验证 一&#xff1a;SpringMVC拦截器 SpringMVC 中的 Interceptor 拦截器&#xff0c;它的主要作用是拦截指定的用户请求&#xff0c;并进行相应的预处理与后处理…

C++面向对象(黑马程序员)

内存分区模型 #include<iostream> using namespace std;//栈区数据注意事项&#xff1a;不要返回局部变量的地址 //栈区的数据由编译器管理开辟和释放int* func(int b) //形参数据也会放在栈区 {b 100;int a 10; //局部变量存放在栈区&#xff0c;栈区的数据在函数执…

Qt创建项目 No suitable kits found.

最近因为要写.net6项目&#xff0c;而更新了Visual Studio&#xff0c;后来再使用Qt的时候发现不能编译了。 调整后&#xff0c;发现新建项目显示“No suitable kits found”错误。 网上也看了很多的解决方案&#xff0c;但都不得要领&#xff0c;也都没有解决。 ----------…