【Linux】基础IO认识(2)

基础IO认识(2)

  • 1、补充系统调用
    • 1、1、read调用
    • 1、2、stat
  • 2、重定向
    • 2、1、文件描述符的分配规则
    • 2、2、实现重定向(dup2)
  • 3、缓冲区的理解
    • 3、1、缓冲区典型实例
    • 3、2、缓冲区代码形式展示
  • 4、深化和实践利用
    • 4、1、在shell中加入重定向
    • 4、2、简单实现库的封装

1、补充系统调用

上一篇文章中已经介绍了打开还有关闭的系统调用,但是还有一些的调用没有讲到,现在就简单的讲解一下吧。

1、1、read调用

系统调用也包括了read的选项。
在这里插入图片描述
从指定文件描述符描述的文件中读取,读取到buffer中,buffer期望读取多少呢?根据count个字节来判断。

1、2、stat

stat的系统调用的作用就是类似于status。能够获取指定文件的属性。
在这里插入图片描述
图上的介绍就是,stat调用能够通过路径或者是文件描述符来获取到struct stat的结构体。
struct stat这又是什么呢?那肯定是关于文件属性的结构体啊,我们man 2 stat的时候能够看到。

其中这结构体中的变量都代表一个文件的各种属性,换句话说全部属性也都在这里了
并且这个结构体在这个函数中是一个输出型参数,当这个函数调用结束之后,能够根据所指定的文件的属性来填充其中的内容。
上面的系统调用参数想要试用的话也很简单,根据上面的参数需求,下面来演示一下。
在这里插入图片描述
在这里插入图片描述

2、重定向

2、1、文件描述符的分配规则

进程会查询自己的文件描述表,分配最小的没有被使用的fd(没有实现的难度,就相当于是遍历数组,找最小下表)。

2、2、实现重定向(dup2)

如果我们知道了上面的文件描述符的分配规则的情况下的话,如果我们关闭了fd=1的情况,并且在下面printf或者是fprintf的时候还是照样的是一个默认,另一个指向的还是stdout的话(加上fflush),此时展现的结果就是会创建一个文件,并且在文件中有着我们原本需要打印的内容,那这不就是换一种意义上的重定向的含义吗。代码如图
在这里插入图片描述
其中C语言中的这两个函数其实指向本质应该是这样:fprintf/printf->stdout->struct FILE->stdout->_fileno==1。所以底层的变化,关close(1)的时候,在上层上的C语言函数中,不能够直接看出变化,所以在上层调用的时候就还是会照样的继续去访问原本的fd==1的下标,然后继续在其中写入的话,就会在我们创建的文件中开始写入原本向现实其中显示的内容,这样不就是展示出了重定向的操作了
这样看来的话,重定向在上层没有需要操作的,需要改变的就只有OS内部fd文件操作符的变化。
可是当我们不加上fflush的话,写不进去,这又是为什么呢?缓冲区和这个又有什么关系呢?
其实是因为在C语言级别的stdin,stdout以及stderr的FILE结构体中,也包含了语言层面上的文件缓冲区,我们刚刚介绍的文件操作函数也并不是直接通过系统调用直接存放在OS中的内核文件缓冲区中,而是存放在了语言层面上的缓冲区中,也就是说,如果不刷新的话,只会存在语言缓冲区中,不会真正的写到文件内核缓冲区中,也就不会在进程结束的时候,展示出我们新写的文件。所以我们要fflush让语言层面的缓冲层刷新到系统层面上然后对文件进行写或者别的操作。
这么看的话,重定向是不是有点粗糙,每次的重定向都还需要关闭文件,然后再打开才能够实现重定向吗?
当然不是了!介绍系统调用接口dup2

要知道dup2不只是两个整数之间的拷贝,而是文件下标是代表的文件内容的拷贝。当然哪一个是old哪一个是new就需要看函数解释( 我就简单的搜一下这个调用的介绍,想要更深入的了解到话,还是自行搜搜吧)
在这里插入图片描述
看完函数解释知道newfd是oldfd的拷贝,所以要重定向的话,最后留下来的是oldfd的,所以重定向位置的文件的操作符是oldfd,原本指向显示器的fd= =1的文件操作符拷贝了oldfd的文件操作符。所以应该选2。
在这里插入图片描述
这样的话原本指向1的显示器的文件操作符的话,现在就是指向了oldfd。虽然这样的话可能会导致多个文件操作符指向同一个文件,但是不用担心,因为在struct file中会存在int ref_count,类似于引用计数。当然,原本有被指向的文件可能在ref_count == 0的时候就会自行进行关闭。

3、缓冲区的理解

缓冲区不只存在一个地方,首先得知道缓冲区有内核级缓冲区也还有用户级缓冲区但是所有的缓冲区,都有两个效果。首先就是解耦,还有一个就是提高效率
举个例子来简单化的效果的话就像是,我们平时见到的超市一样,我们需要的时候直接去超市里面拿就可以了,我们不回去跑那么远,还要去工厂中去拿,所以这种方便也就是像缓冲区一样给系统的速度增加,同时这种情况也减少了程序之间的耦合性质,我们不需要管到底怎么样才能将我们的数据存到数据之上,我们只需要把我们的数据按照键盘输入就行了。就像是,我们不需要管到底怎么样把商品弄到超市,我们只需要在超市拿就行了。
可是,我们进行数据的拷贝到缓冲区的时候难道不是会增加时间吗?怎么会减少时间的呢?我们直接把数据拷贝到文件缓冲区中这样不应该是更快吗,为什么要多花一次时间拷贝到语言层面上的缓冲区呢?
其实不然,这里的提高效率是提高谁的效率?是提高商品和超市的效率吗?不是的,提高的是客户和商品之间的效率,换句话说也就是提高使用者的效率,使用者之需要交到缓冲区就可以,并不需要等待OS进行交换数据的时间,这样的话,使用者就能够继续使用,这样的话,就能够提高使用者使用的效率。
除此之外还
不建议
多次的调用系统调用,因为系统调用也是需要消耗时间的,所以放在语言层的缓冲区,就会积攒一些之后然后再开始刷新缓冲区,启动系统调用,这是提高IO刷新的效率。
再说一下,其实对于语言来说,并不是一定要做到给我们提供缓冲区的,但是提供缓冲区的话,就能够大大的方便操作者使用的效率了,所以说,这也就是为什么C语言在当时,包括现在,流行了那么多年的原因,随着我们学习的深入,我们也逐渐理解到C语言中为我们使用者考虑到的细节,优化使用者的使用感受,也提高了系统的效率。
缓冲区的实现方式也有挺多种可能的,就比如说,只要有数据就使用fflush,刷新到OS的缓冲区,还有一种就是行刷新,现实就是这么刷新的,还有一种就是全缓冲,一般对应的就是普通文件的刷新,只有当缓冲区都写满的时候才会进行刷新。当然这都是在进程正常进行的时候才会有的状态,如果进程停止了的话,系统会自动刷新。 还有就是我们能够强制进程中的缓冲区直接刷新,最简单的例子就是使用fflush。值得注意的是这是在Linux操作系统上的缓冲区刷新规则,如果在windows上的话可能还会有所不一样

3、1、缓冲区典型实例

下面请看前提的代码
在这里插入图片描述
这样的代码下,如果我们不加上fork的话,我们最后实现的是什么呢?
在这里插入图片描述
实现出来是这样的,如果我们不是指向显示器的情况呢?反而是指向文件的重定向操作呢?
在这里插入图片描述
这样的结果反而是这样的,那如果说,我们加上fork这样的创造出来的子进程的话,又会是什么样子呢?
在这里插入图片描述
出现的反而是这个样子。这究竟是怎么一回事呢?答案就是问题出现在缓冲区刷新的定义不同导致的。 对于显示器来说,是一行的刷新,但是对于文件来说是全缓冲的刷新方式。利用了重定向,也就导致了刷新策略的不同,这样的话就导致了结果的不一样。还有就是可以看出区别就是,打印两遍的几乎都是库函数,只有系统调用的才打了一次。
知道不一样了之后再来细致的分析一下。其实对于fork来说,复制的是父进程的几乎所有的信息,所以在对于重定向到文件中的可运行程序来说,调用C语言实现的文件操作还没有刷新到OS中的缓冲区,还是在语言层面上的缓冲区中,所以对于子进程来说还会复制其中存留的消息,所以在最后fork完了结束之后的时候,才会显示出两遍的C语言函数的打印。

3、2、缓冲区代码形式展示

在这里插入图片描述
在这里插入图片描述

简单截图一下,这就是我们C语言中的FILE结构体,其中也包含之前所讲到的_fileno的标志符号。中间的一大部分也是为了维护语言级别的缓冲区而存在的大量的定义。这样的话,我就能够实现C语言对于库函数调用然后实现数据的格式化形式再交给系统,让系统进行操作下面的后续问题。

4、深化和实践利用

4、1、在shell中加入重定向

实现重定向,就需要我们能够读取得到重定向的方式,在此之前的shell中我们能够读取到我们用户的输入的指令。想要读到重定向的话也就特别简单,因为重定向的操作符也就只有三个,所以遍历一遍的话也就能够找到重定向的操作符了。当然了,找到重定向的操作符之后,需要我们去寻找后面的文件名,此时也需要注意,跳过合适的空格,找到真正的文件名称。类似于我们的那个之前shell中就有的寻找cwd名字时候的宏函数操作,这里的宏函数的优点也是显而易见的,1、能够直接使用函数内临时变量不需要进行传址操作,更能够让人读懂。2、宏函数在处理的时候会直接替换,这样的话能够减少变量创建,缩短一点时间。

void CheckRedir(char cmd[])
{// > >> <// "ls -a -l -n >  myfile.txt"int pos = 0;int end = strlen(cmd);while(pos < end){if(cmd[pos] == '>'){if(cmd[pos+1] == '>'){cmd[pos++] = 0;pos++;redir_type = App_Redir;SkipSpace(cmd, pos);filename = cmd + pos;}else{cmd[pos++] = 0;redir_type = Out_Redir;SkipSpace(cmd, pos);filename = cmd + pos;}}else if(cmd[pos] == '<'){cmd[pos++] = 0;redir_type = In_Redir;SkipSpace(cmd, pos);filename = cmd + pos;}else{pos++;}}
}

这样的操作就是找到重定向符号的种类,同时确定重定向后面的文件的名字,能够帮助我们找到对应的文件。这里我们在找到符号之后就把那个位置设置为0,是因为在后一步的SplitCommand中,需要找到的结尾也是和0相同的ASCII码的值,这样能够稍微写的优雅一点。
还需要修改的地方就是执行我们命令的时候。

void ExecuteCommand()
{pid_t id = fork();if(id < 0) Die();else if(id == 0){//重定向设置if(filename != NULL){if(redir_type == In_Redir){int fd = open(filename, O_RDONLY);dup2(fd, 0);}else if(redir_type == Out_Redir){int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);dup2(fd, 1);}else if(redir_type == App_Redir){int fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0666);dup2(fd, 1);}else{}}// childexecvp(gArgv[0], gArgv);exit(errno);}else{// fahterint status = 0;pid_t rid = waitpid(id, &status, 0);if(rid > 0){lastcode = WEXITSTATUS(status);if(lastcode != 0) printf("%s:%s:%d\n", gArgv[0], strerror(lastcode), lastcode);}}
}

需要进行重定向的设置,通过调用dup2的系统调用,实现我们上层能够看到的文件的重定向的操作,关于dup2的函数的参数的设置,还有功能,已经在刚刚讲过了,可以回头看看复习一下。
这样的话,我们就能够通过自己实现的shell实现读取操作,也能够实现重定向操作了。

4、2、简单实现库的封装

这一步的简单说明主要还是介绍,缓冲区的设置和定义的,并没有别的需求上的条件。所以主要关注缓冲区在其中的意义就行了。
实现库的封装实际上是挺难的,所以为了防止这么多了的情况下,还要讲不少的内容。所以期待下一篇吧!

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

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

相关文章

模拟火车世界5/Train Sim World 5 (容量289GB)百度网盘下载

版本介绍 Build.15665692|容量289GB|官方简体中文|支持键盘.鼠标.手柄 游戏介绍 来《模拟火车世界5》里&#xff0c;全世界的铁路尽属于你&#xff01;在标志性的特色城市中&#xff0c;驾驶列车穿越铁轨&#xff0c;飞驰在 3 条全新线路上&#xff0c;用新的角色迎接新的挑战…

教师薪酬管理系统的设计与实现

摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;老师信息因为其管理内容繁杂&#xff0c;管理数量繁多导致手工进行处理不能满足广…

【FreeRL】Rainbow_DQN的实现和测试

文章目录 前言环境1 PER note2 C51 note3 Noisy note4 Rainbow note其他 前言 具体代码实现见&#xff1a;https://github.com/wild-firefox/FreeRL/blob/main/DQN_file/DQN_with_tricks.py 将其中所有的trick都用上即为Rainbow_DQN。 效果如下&#xff1a;&#xff08;学习曲…

vue 案例使用

el-switch 按键的使用 <el-switchclass"switchStyle" v-model"boolValue" :active-value"1" :inactive-value"0" active-text"ON" inactive-text"OFF" active-color"#13ce66" inactive-color&qu…

明星御用剪辑师亲授:PR剪辑技巧大全

在这个视频内容大爆炸的时代&#xff0c;一个好的剪辑工具就如同一位得力的助手&#xff0c;能让你在视频制作的道路上事半功倍。今天&#xff0c;就让我来为大家揭秘几款PR剪辑工具&#xff0c;它们各具特色&#xff0c;能够帮助你轻松应对各种剪辑需求。让我们开始吧&#xf…

kali——tshark的使用

目录 前言 使用方法 tshark提取流量为文档 前言 tshark 是一个命令行的网络分析工具&#xff0c;它用于捕获和分析网络流量。它支持多种网络协议&#xff0c;包括 TCP、UDP、ICMP 等。Tshark 可以用于调试网络问题、进行安全审计、分析应用程序性能等。 在 Kali Linux 中&…

绿咖啡豆缺陷检测系统源码分享

绿咖啡豆缺陷检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer…

ubuntu虚拟机装载共享文件夹导致的诡异错误

最近使用vmware station 15 安装了 ubuntu22.04 的虚拟机。在装载共享文件夹不久后便会出现诡异的错误。目前在网络上好像没有人把这归结到装载共享文件夹的问题上&#xff0c;故以供参考。 第一次&#xff1a; 在装载之后大概第二次开机&#xff0c;出现报错界面。 提示蓝牙…

驱动器磁盘未格式化恢复实战

驱动器磁盘未格式化的深度剖析 在日常的数字生活中&#xff0c;驱动器作为数据存储的重要载体&#xff0c;承载着用户无数的珍贵资料。然而&#xff0c;当遇到“驱动器中的磁盘未被格式化”的提示时&#xff0c;这份平静往往会被瞬间打破。这一状况不仅让用户感到困惑和焦虑&a…

精选评测!分享5款AI写论文最好用的软件排名

写作是一项既费时又费力的任务&#xff0c;尤其是对于科研人员来说&#xff0c;撰写论文更是一项必不可少的挑战。幸运的是&#xff0c;现在有多款免费的AI写作工具可以大大简化这一过程。小编精心挑选了5款免费的AI写作工具&#xff0c;旨在帮助大家提高写作效率&#xff0c;让…

word文档的读入(8)

如何读取答题卡中的选择题答案&#xff0c;并把所有的信息导入到Excel表格中&#xff5e; 在初始化了字典中的字段并获取了标准答案和学生答案后&#xff0c;现在只需使用if语句将学生答案studentAnswerOne和标准答案value进行比较。选择题一道题2分&#xff0c;答案正确时&…

pgsql的威胁操作--危险行为

pgsql的威胁操作--危险行为 要在PostgreSQL中列出所有数据库并删除指定的数据库 进入容器Pgsql docker exec -it 04d438beab57 /bin/bash 登录pgsql 使用以下命令登录到PostgreSQL psql -U postgres -h localhost -p 5432 列出数据库详细信息 postgres# \l 这将显示所有…

C语言代码练习(第二十六天)

今日练习&#xff1a; 数据的交换输出输入 n 个数&#xff0c;找出其中最小的数&#xff0c;将它与最前面的数交换后输出这些数 输入一个英文句子&#xff0c;将每个单词的第一个字母改成大写字母 输入一个十进制数 N &#xff0c;将它转换成 R 进制数输出 数据的交换输出输入 …

34.贪心算法1

0.贪心算法 1.柠檬水找零&#xff08;easy&#xff09; . - 力扣&#xff08;LeetCode&#xff09; 题目解析 算法原理 代码 class Solution {public boolean lemonadeChange(int[] bills) {int five 0, ten 0;for (int x : bills) {if (x 5) // 5 元&#xff1a;直接收下…

【Git】将本地项目上传到git | 在IDEA的提交记录中更改 提交的用户名

一:将本地项目上传到git 1:在​gitee​上创建以自己项目名称命名的空项目【注意项目名称与本地项目的文件夹名称一致】 2:进入想上传的项目的文件夹,然后右键点击 3:查看用户名及邮箱 $ git config user.name $ git config user.email4: 配置你的用户名及邮箱【如果有…

李宏毅2023机器学习HW15-Few-shot Classification

文章目录 LinkTask: Few-shot ClassificationBaselineSimple—transfer learningMedium — FO-MAMLStrong — MAML Link Kaggle Task: Few-shot Classification The Omniglot dataset background set: 30 alphabetsevaluation set: 20 alphabetsProblem setup: 5-way 1-sho…

【代码随想录训练营第42期 Day59打卡 - 图论Part9 - Bellman-Ford算法

目录 一、Bellman-Ford算法 定义 特性 伪代码实现 二、经典题目 题目&#xff1a;卡码网 94. 城市间货物运输 I 题目链接 题解&#xff1a; Bellman-Ford算法 三、小结 一、Bellman-Ford算法 定义 Bellman-Ford算法是一个迭代算法&#xff0c;它可以处理包含负权边的…

Zabbix的安装与基本使用(主机群组、应用集、监控项、触发器、动作、媒介)

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 一、环境准备 &#xff08;1&#xff09;实验基本设置&#xff1a; 主机名IP地址角色Mater192.168.1.10监控端node1192.168.1.11被监控端 # 网络自…

『功能项目』制作提示主角升级面板【56】

我们打开上一篇55事件中心处理怪物死亡的项目&#xff0c; 本章做的事情是制作提示主角升级的界面&#xff0c;当主角升级时就会被显示出来点击确认即可消失 首先在unity编辑场景制作 在确认按钮对象上添加事件 点击Button将Panel添加至事件框选 在事件函数中选择gameobject.S…

Linux操作系统入门(五)

————————————————————————————————————————— 至此&#xff0c;大部分Linux操作系统的文件操作指令已经总结完成&#xff0c;最后还需进行vim编辑器的使用 使用方法&#xff1a;在FinalShell终端中输入"vim [文件]",以下图…