当前位置: 首页 > news >正文

【操作系统和计网从入门到深入】(四)基础IO和文件系统

前言

在这里插入图片描述
这个专栏其实是博主在复习操作系统和计算机网络时候的笔记,所以如果是博主比较熟悉的知识点,博主可能就直接跳过了,但是所有重要的知识点,在这个专栏里面都会提到!而且我也一定会保证这个专栏知识点的完整性,大家可以放心订阅~# 基础IO

1. 文件描述符预备工作

Linux系统下一切皆文件

1.1 复习C文件接口相关细节

#include <stdio.h>
#include <stdlib.h>
// 复习C语言文件接口
int main()
{FILE *fp = fopen("log.txt", "w");if (fp == NULL){perror("fopen");return 1;}// 文件操作fclose(fp);return 0;
}

这个文件在哪里创建?

我们实验发现,程序在哪里被执行,log.txt就会在哪里被创建,log.txt是相对路径。

就是在工作目录下创建。

1.2 用C语言相关文件接口模拟实现一个cat命令

// 模拟实现一个cat命令
int main(int argc, char *argv[])
{if (argc != 2){printf("args error!\n");return 1;}FILE *fp = fopen(argv[1], "r"); // 打开这个文件if (fp == NULL){perror("fopen");return 2;}// 读取文件里面的内容char line[64];while (fgets(line, sizeof(line), fp) != NULL) // 按照行读取{fprintf(stdout, "%s", line);}fclose(fp);return 0;
}

三个自动打开的文件描述符,很熟悉了,不再赘述。

1.3 学习系统调用

open

如果打开成功 — 返回文件描述符,如果打开失败,返回-1。

O_WRONLY只负责写,如果没有这个文件,是打不开的!

我们带上O_CREAT就能创建了

但是我们发现, 创建出来的这个文件的权限怎么是个奇怪的东西呢?所以,不像我们C接口创建出来的那么整齐

所以,光光创建是不够的!

一般涉及到文件的创建的时候,我们会传递第三个参数,表示权限。

如果这个文件已经有了

我们就使用两个参数的open就行了 不需要三个参数的,带上O_RDONLY选项 — read only

关闭文件:fclose

int close(int fd);

现在想要往里面写东西了。

write函数!

如果我们往已经有东西的文件里面,再写入一个短一点的字符串。

所以这个是不会帮我们清空文件的。

想要系统帮我们清空还要带上一个选项O_TRUNC

int fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);

这样才会帮我们清空。

那如果我想要往文件中追加呢?

O_TRUNC换成O_APPEND

现在,我们来认识一下 读文件的接口 read

read 的返回值我们到进程通信再说 现在我们先不关心

read是不会给我们加 \0 的

1.4 文件描述符这个intFILE*的关系

FILE是一个结构体,是C语言提供的。

C中文件相关库函数内部一定会调用系统调用! 那么在系统角度,认FILE,还是认 fd ? 系统只认fd

FILE结构体里面必定封装了fd!

// 文件描述符和FILE*
int main()
{printf("stdin: %d\n", stdin->_fileno);printf("stdout: %d\n", stdout->_fileno);printf("stderr: %d\n", stderr->_fileno);return 0;
}
yufc@ALiCentos7:~/Src/Review/operatingSys/Unit4$ ./test
stdin: 0
stdout: 1
stderr: 2
yufc@ALiCentos7:~/Src/Review/operatingSys/Unit4$ 

同样!先描述再组织!在内核中,OS内部要为了管理每一个被打开的文件,构建struct file{}

1.5 struct file{}

用双链表组织起来。

struct file
{struct file* next;struct file* prev;// 后面的字段 ...// 包含了一个被打开的文件的几乎所有的内容, 不仅仅包含属性
};

所以本质是存在一个数组的!

struct file* array[32]

所以fd的本质就是一个数组下标。

2. 正式开始学习文件描述符

fd的分配规则是,最小的,没有被占用的文件描述符。

然后012是被打开的,这个很熟,所以下一个打开的文件就是3。

2.1 输入重定向和输出重定向

如果我把1文件描述符关了,然后打开一个文件,那么这个新打开的文件的fd就是1,这个很好理解。

所以原本要打印到stdout的东西会打印到新打开的文件中去。

// 输出重定向
int main()
{close(1);int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC);assert(fd >= 0);printf("fd: %d\n", fd);printf("hello world!\n");fflush(stdout); // 不加这个是没有输出的close(fd);return 0;
}

这里fflush(stdout)其实没有刷新屏幕,其实刷新的是log.txt,因为里面文件描述符是1,而现在1不是显示器,而是log.txt

至于这个代码里面,为什么如果不加fflush(stdout);,会没有输出

因为重定向到文件里面,磁盘文件是全缓冲的(第三节复习缓冲区的时候会讲),所以printf之后在缓冲区里面,所以没有输出,然后按道理来说,程序结束会自动刷新,但是你都close了,肯定就刷新不了了。

所以要不不加close,不用fflush也能有结果

加了close,那么fflush也要加,不然结果被close清理掉了

输入重定向也是一个道理。

当然,重定向不是这样实现的!我们这种方式仅仅只是利用了文件描述符的特点而已。有没有一种方式,可以让我们的不用关闭别人的,也能完成重定向呢?肯定是有的!

2.2 dup2

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

记住:最终想要输出到哪里,哪里的fd就是第一个参数。

  • 如果oldfd不是有效的文件描述符,则调用失败,并且newfd未关闭
  • 如果oldfd是有效的文件描述符,而newfd的值与oldfd相同,则dup2()不执行任何操作,并且
    返回newfd

至于为什么,我们上面那种先close的重定向方法,最后close之后,就不能成功重定向,而左边代码的方法 没问题。
这其实是dup2的一个特性,涉及到缓冲区的概念。

2.3 如何理解一切皆文件(VFS)

3. 缓冲区

缓冲区在哪里?我们写一个代码看看。

这个代码分别调用了C语言的输出函数和系统的输出函数,打印一句话。

// 缓冲区
int main()
{// C语言printf("hello printf\n");fprintf(stdout, "hello fprintf\n");const char *s = "hello fputs\n";fputs(s, stdout);// 系统调用const char *ss = "hello write\n";write(1, ss, strlen(ss)); // 写到fd=1的文件上->stdoutfork();return 0;
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们先把现象解释一下:

首先

write 只打印了一次 其他的打印了两次

为什么?我们下节课再讲!讲清楚之后,我们可以也可以回答一些尚未解答的现象了!

为什么会这样,我们现在来解释!

关于缓冲区的认识:

  • 一般而言,行缓冲的设备文件 – 显示器

  • 一般而言,全换从的设备文件 – 磁盘文件

  • 所有设备,永远都倾向于全缓冲! 缓冲区满了才刷新->需要更少的IO操作->更少次的外设访问->提高效率!

当和外部设备进行IO的时候,数据量的大小不是主要矛盾, 和外设预备IO的过程才是最耗费时间的

其他刷新策略是,结合具体情况做的妥协!

为什么fork()之后拷贝一份?

  1. 如果向显示器打印,刷新策略是行刷新,那么最后执行 fork的时候,一定一定是函数执行完了 && 数据已经刷新了

  2. 如果对应的程序做了重定向,本质是向磁盘文件打印 — 隐性的刷新策略变成了全缓冲!此时代码的已经没有意

    义了,所以fork的时候,函数执行完了,但是数据没有刷新!现在数据在,当前进程的C标准库中!

  3. 这部分数据,属不属于父进程的数据?肯定是的!fork之后,父子各自执行自己的退出。进程退出是需要刷新缓冲区的!

  4. 那么现在的一个问题,刷新这个动作,算不算“写”?算的,从缓冲区刷新出去,相当于写到显示器里

  5. 此时会有写时拷贝!

  6. 所以!C的接口会出现两份的数据!

4. 文件系统和inode

4.1 背景知识1

int main()
{// Cprintf("hello printf\n");fprintf(stdout, "hello fprintf\n");perror("hello perror"); // stderr// 系统调用const char* s1 = "hello write(stdout)\n";const char* s2 = "hello write(stderr)\n";write(1, s1, strlen(s1));write(2, s2, strlen(s2));// C++std::cout << "hello cout" << std::endl;std::cerr << "hello cerr" << std::endl;return 0;
}

这个代码直接运行,肯定是这样的。

但是如果重定向一下呢?

1,2都是显示器文件,但是他们 两个是不同的显示器文件! 我们可以认为,同一个显示器文 件,被打开了两次!

一般而言,如果程序运行有可能 有问题的话,建议使用stderr来打 印! 如果是常规打印,建议用stdout 打印。

然后区分之后,我们可以这么运行,可以把正确的和错误的分开打印到文件里面去。

可以理解成,把fd为2的放到err.txt里面去。

另外cat还有一个用法:

cat < log.txt > back.txt

这个表示,把log.txt的内容交给cat,cat准备向显示器打印,但是此时再次重定向到back.txt上,所以最终就是,log.txt的内容完成一次拷贝到back.txt上!

4.2 背景知识2

学习文件系统要掌握的背景知识:

  1. 我们以前学习的都是被打开的文件,那们有没有没有被打开的文件?当然存在,在磁盘里 2. 我们学习磁盘级别的文件,我们侧重点在哪里呢?

  2. 单个文件的角度 — 这个文件在哪里?这个文件多大?这个文件的其他属性是什么? 站在系统的角度, 一共有多少个文件?各自属性在哪里?如何快速找到?我还可以存储多少 个文件?如何快速找到制定的文件?

  3. 如何进行对磁盘文件进行分门别类的存储,又来支持更好的存取?

所以,我们先要了解磁盘

磁盘具体构造,寻址方式,可以看看以前的课件/ppt。

一个重要概念:虽然磁盘的基本单位是扇区(512字节) 但是操作系统(文件系统)和磁盘进行IO的基本单位是:4kb

为什么?

  1. 太小了,有可能会导致多次的IO,进而导致效率降低
  2. 如果OS使用和磁盘一样的大小,万一磁盘基本大小变了的话,OS的源代码要不要改呢? 所以硬件和软件(OS)进行解耦。

4.3 文件系统构造

4.4 如果文件特别大怎么办

一个block放不下怎么办?

在data block中,不是所有的datablock只能存文件数据,也可以存其他块的块号!

我们通过索引找到一个块之后,可以通过这个块继续找到下面的块这样就解决了要存大文件的问题

找到一个文件的步骤:inode 编号 -> 分区特定的bg -> inode -> 属性 -> 内容

现在的问题是,inode编号是怎么得到的?

在Linux文件属性中国呢,是没有文件名这个东西的。

  1. 在一个目录下,可以保存很多文件,但是这些文件名是不能重复的!
  2. 目录是文件吗?是 -> 所以目录也有自己的inode,也有自己的datablock!

一个文件的文件名,是存在datablock里面存的!

datablock里面存了:文件名和inode编号的映射关系!

下面我们要回答三个问题:

  1. 创建文件,系统做了什么
  2. 删除文件,系统做了什么?
  3. 查看文件,系统做了什么?

为什么删除总比拷贝快很多?因为删除的时候,不用把内容这部分东西真的删掉,只需要把位图标记改了就行了。

所以,删了的东西能恢复吗?肯定是可以的,只是我们不会而已 我们只要找到原来的inode,找到磁盘的位置(删除日志)只要它还没被覆盖就一定能找到。

一道面试题:

为什么还有空间,但是一直不能创建文件呢?可能就是因为inode申请不下来文件无法创建。

如果创建出来,也只有个文件名没有inode这个时候一写就会失败,一写就会失败的,无法写入。

相关文章:

【操作系统和计网从入门到深入】(四)基础IO和文件系统

前言 这个专栏其实是博主在复习操作系统和计算机网络时候的笔记&#xff0c;所以如果是博主比较熟悉的知识点&#xff0c;博主可能就直接跳过了&#xff0c;但是所有重要的知识点&#xff0c;在这个专栏里面都会提到&#xff01;而且我也一定会保证这个专栏知识点的完整性&…...

四.Winform使用Webview2加载本地HTML页面并互相通信

Winform使用Webview2加载本地HTML页面并互相通信 往期目录本节目标核心代码实现HTML代码实现的窗体Demo2代码效果图 往期目录 往期相关文章目录 专栏目录 本节目标 实现刷新按钮点击 C# winform按钮可以调用C# winform代码显示到html上点击HTML按钮可以调用C# winform代码更…...

如何有效清理您的Python环境:清除Pip缓存

Python是一个广泛使用的高级编程语言&#xff0c;以其强大的库和框架而闻名。然而&#xff0c;随着时间的推移和不断安装新的包&#xff0c;Python环境可能会变得混乱不堪&#xff0c;尤其是pip缓存可能占用大量的磁盘空间。本文将向您展示如何有效地清理pip缓存&#xff0c;保…...

Jira 母公司全面停服 Server 产品,用户如何迁移至极狐GitLab

Jira 母公司即将全面停服旗下部分 Server 端产品的销售和服务支持&#xff01; Jira 母公司 Atlassian 在几年前确定了公司的战略为“全面上云”&#xff0c;为此做出了停止 Server 产品的销售和支持。整个时间线从 2021 年 2 月 2 日开始&#xff0c;直到今年 2 月 15 日&…...

Docker安装配置OnlyOffice

OnlyOffice 是一款强大的办公套件&#xff0c;你可以通过 Docker 轻松安装和部署它。本文将指导你完成安装过程。 步骤 1&#xff1a;拉取 OnlyOffice Docker 镜像 首先&#xff0c;使用以下命令从 Docker Hub 拉取 OnlyOffice Document Server 镜像&#xff1a; sudo docke…...

启动低轨道卫星LEO通讯产业与6G 3GPP NTN标准

通讯技术10年一个大跃进&#xff0c;从1990年的2G至2000年的3G网路&#xff0c;2010年的4G到近期2020年蓬勃发展的5G&#xff0c;当通讯技术迈入融合网路&#xff0c;当前的 5G 技术不仅可提供高频宽、低延迟&#xff0c;同时可针对企业与特殊需求以 5G 专网的模式提供各式服务…...

PICO Developer Center 创建和调试 ADB 命令

PICO 开发者中心概览 ADB 是一个轻量级的 Android 调试桥(Android Debug Bridge&#xff0c;简称 ADB)&#xff0c;用于与 Android 设备进行通信和调试。ADB提供了许多有用的功能&#xff0c;使开发人员能够轻松地管理和调试设备上的应用程序。 你可以使用 PDC 工具来调试系统…...

【VRTK】【PICO】如何快速创建一个用VRTK开发的PICO项目

【背景】 每次新建一个VRTK的PICO项目总是做一些重复工作,于是就想着搞成一个基本的包,把基本的设置都放进去,今后新做项目直接导这个包就行了。 完整资源包请见本篇博客的绑定资源。 【内容简介】 这个包是我为了快速开发基于VRTK的PICO应用设置的基础项目包。每次开发…...

国产操作系统:VirtualBox安装openKylin-1.0.1虚拟机并配置网络

国产操作系统&#xff1a;VirtualBox安装openKylin-1.0.1虚拟机并配置网络 openKylin 操作系统目前适配支持X86、ARM、RISC-V三个架构的个人电脑、平板电脑及教育开发板&#xff0c;可以满足绝大多数个人用户及开发者的使用需求。适用于在VirtualBox平台上安装openKylin-1.0.1…...

本地git切换地区后,无法使用ssh访问github 22端口解决方案

问题 由于放假回家&#xff0c;发现之前一直使用正常的git&#xff0c;与github无法通讯&#xff0c;pull和push都无法连接。报错如下&#xff1a; connect to host github.com port 22: Connection timed out fatal: Could not read from remote repository. 原因 可能是所…...

Chat2DB:AI赋能的多数据库客户端工具,开源领航未来数据库管理

Chat2DB&#xff1a;开源多数据库客户端的AI革新 Chat2DB使用教程:Chat2DB使用教程_哔哩哔哩_bilibili 引言&#xff1a; 随着企业数据的快速膨胀&#xff0c;数据库管理的复杂性也在增加。此时&#xff0c;一个能够跨越数据库边界、并且集成先进的AI功能的工具&#xff0c;不…...

SQL Server修改数据字段名的方法

1. ALTER TABLE语句修改 这是一种最常用的数据库更改字段的方法&#xff0c;使用Alter Table语句来更改数据库字段的名称。 一般格式如下&#xff1a; ALTER TABLE 表名 RENAME COLUMN 原字段名 TO 新字段名; 例如&#xff0c;修改字段名字段名从UserName到Uname&#xff1a;…...

Flutter编译报错Connection timed out: connect

背景&#xff1a;用Android Studo 创建了Flutter项目&#xff0c;编译运行报错java.net.ConnectException: Connection timed out: connect 我自己的环境&#xff1a; windows11 Android Studio Flutter 截图如下&#xff1a; 将错误日志展开之后&#xff1a; Exception…...

PG DBA培训26:PostgreSQL运维诊断与监控分析

本课程由风哥发布的基于PostgreSQL数据库的系列课程&#xff0c;本课程属于PostgreSQL Diagnosis and monitoring analysis&#xff0c;学完本课程可以掌握PostgreSQL日常运维检查-风哥PGSQL工具箱&#xff0c;风哥专用PGSQL工具箱介绍&#xff0c;风哥专用PGSQL工具箱使用&…...

运维之道—生产环境安装Redis

目录 1.前言 2.环境准备 2.1 安装gcc依赖 3.部署安装 3.1 下载redis安装包 3.2 解压并编译安装redis 3.3 配置redis ​编辑3.4 启动redis并测试 4. 总结 1.前言 大家好,运维之道的系列文章继续进行,我们今天整理的是Redis生产环境的安装,Redis的安装以及生产环境的…...

人工智能数学验证工具LEAN4【入门介绍3】乘法世界-证明乘法的所有运算律

视频链接&#xff0c;创作不易记得投币哦&#xff1a; import Game.Levels.Multiplication.L08add_mul World "Multiplication" Level 9 Title "mul_assoc" namespace MyNat Introduction " We now have enough to prove that multiplication is a…...

Armv8-M的TrustZone技术简介

TrustZone技术是适用于Armv8-M的可选安全扩展,旨在为各种嵌入式应用提供改进的系统安全基础。 TrustZone技术的概念并不新鲜。该技术已经在Arm Cortex-A系列处理器上使用了几年,现在已经扩展到Armv8-M处理器。 在high level上,TrustZone技术适用于Armv8-M的概念与Arm Cort…...

ctfshow-反序列化(web267-web270)

目录 web267 web268 web269 web270 总结 web267 页面用的什么框架不知道 看源码看一下 框架就是一种软件工具&#xff0c;它提供了一些基础功能和规范&#xff0c;可以帮助开发者更快地构建应用程序。比如Yii框架和ThinkPHP框架就是两个流行的PHP框架&#xff0c;它们提供…...

决策树的分类

概念 决策树是一种树形结构 树中每个内部节点表示一个特征上的判断&#xff0c;每个分支代表一个判断结果的输出&#xff0c;每个叶子节点代表一种分类结果 决策树的建立过程 1.特征选择&#xff1a;选取有较强分类能力的特征。 2.决策树生成&#xff1a;根据选择的特征生…...

LateX--插入伪代码类型详解

文章目录 1.算法伪代码流程图----循环带范围1.1.算法伪代码示例图11.2.算法伪代码示例图2 2.算法伪代码流程图----循环不带范围3.算法伪代码流程图---不带行数数字4.参考文献 1.算法伪代码流程图----循环带范围 #需要插入这个宏包 \usepackage[ruled,linesnumbered]{algorithm…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库&#xff0c;特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列&#xff0c;以便知晓哪些列包含有价值的数据&#xff0c;…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

Netty从入门到进阶(二)

二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架&#xff0c;用于…...