什么是文件描述符以及重定向的本质和软硬链接(Linux)
目录
- 1 什么是文件?什么是文件操作?
- 认识系统接口open
- 什么是文件描述符
- 认识Linux底层进程如何打开的文件映射关系
- 重定向的本质
- 理解软硬链接
- 扩展问题
1 什么是文件?什么是文件操作?
文件 = 文件内容 + 文件属性(文件属性也是数据,即使你创建一个空文件,也要占据空间)
文件操作 = 文件内容操作 + 文件属性操作(有可能,在操作文件的过程中,既改变内容,又改变属性)
不管是学习语言还是学习操作系统,IO流是我们学习过程中不可获取的一个阶段,在这一部分我们会学习打开文件、读写文件等操作。
那所谓的“打开文件”,究竟是在干什么呢?其实就是将文件的属性或内容加载到内容中。
我相信大多数人的入门语言都是c语言,那我们就以c语言的文件操作进行举例;
如何理解printf?为什么调用printf就能往显示屏中打印内容呢?
其实printf是封装了系统的接口,然后由系统接口调用显示屏的驱动方法,然后进行打印
其实不管是C语言也好,Java也好,其他语言也罢,大多数的文件操作都是封装了系统接口。
封装的原因
- 原生系统接口使用成本比较高
- 语言不具备跨平台性(每个操作系统的接口可能不一样,写的代码可能只能在一个平台上跑)
封装是如何解决跨平台的问题的呢?
以c/c++方式举例:穷举所有底层接口 + 条件编译
在Linux系统调用接口中,我们打开文件使用open、关闭文件close、写入write、读取read。那这些接口和C中库函数接口有什么联系呢?我们可以这样理解:C中调用得这些库函数底层一定封装了系统调用接口,可以认为fopen底层调用open,fclose底层调用close,fread底层调用read,fwrite底层调用write。我们在windows中打开文件,windows底层也有一套自己的windows相关的api系统接口,当我们在windows使用C的库函数时,C调用的就是windows下的系统接口。这样在语言层面上就实现了跨平台性。
认识系统接口open
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
pathname: 要打开或创建的目标文件
flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。
O_RDONLY: 只读打开
O_WRONLY: 只写打开
O_RDWR : 读,写打开
这三个常量,必须指定一个且只能指定一个
O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
O_APPEND: 追加写
返回值:
成功:新打开的文件描述符
失败:-1
int main()
{umask(0);int fda = open("loga.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);int fdb = open("logb.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);printf("fda:%d\n",fda);printf("fdb:%d\n",fdb);close(fda);close(fdb);return 0;
}
运行结果:
fda:3
fdb:4
什么是文件描述符
OK,那前面描述怎么多,到底什么是文件描述符呢?其实系统接口open的返回值就是文件描述符。
Linux系统中,把一切都看做是文件,当进程打开现有文件或创建新文件时,OS向进程返回一个文件描述符,文件描述符就是OS为了高效管理已被打开的文件所创建的索引,用来指向被打开的文件,基本上所有执行I/O操作的系统调用都会通过文件描述符。简单来说,文件描述符可以唯一标识一个新打开的文件;类似学号,身份证号可以唯一标识一个人。
OK,引出概念后,我们来解决下面几个问题
上面那个例子中,为什么运行结果是3,4;或者说为什么是从3开始?
因为操作系统默认已经打开0,1,2号文件
0表示:标准输入,键盘
1表示:标准输出,显示器
2表示:标准错误,显示器
认识Linux底层进程如何打开的文件映射关系
** 为什么文件描述符是像0,1,2,3,4,5…,你见过什么样的数据,是这样子的?**
当然是数组的下标就是这样子的啦
首先在一个进程可以打开多个文件(就像我们写代码可以打开多个文件一样),那这么多文件,我们的OS要怎么管理呢?
一个文件被打开,在内核中,要创建该打开的文件的内核数据结构叫做struct file
struct file
{//包含了我想看到的文件大部分内容+属性struct file *next;struct file *prev;
}
再讲这个结构体file用类似链表的数据结构给串起来,所以对被打开的文件的管理,转化为对链表的增删改查
** 那进程如何和打开的文件建立映射关系呢?**
看下图
首先task_struct(进程控制块PCB)里面有一个字段是struct files_struct* files;files_struct里面有一个文件描述符表,其实就是一个array[]数组,里面存着结构体file的地址;当我们用户要打开一个文件时,先给你创建struct file这个结构体,初始化file内部属性还有函数指针指向的对应的方法,并且在当前进程的文件描述符表里分配一个没有被使用的下标,将新文件的地址(也就是结构体file的地址)给文件描述符表;然后会将这个新文件对应的文件描述符表的下标返回给用户,后面用户在调用read,write,一定传入了对应的文件描述符(fd),根据这个文件描述符就能找到对应的文件,对它做相应的操作。
那肯定有人问了,0,1,2 对应stdin,stdout,stderr 对应 键盘,显示器,显示器(这些都是硬件啊)也用你上面的struct file 来标识吗?
是的,在Linux下一下皆文件
在Linux下一切皆文件,结构体struct file里面有函数指针,指向的就是对应的IO方法(read,write),像磁盘啊,显示器啊,键盘啊,这些硬件,调用他们要调用对应设备的驱动方法,所以struct file的函数指针,指向这些对应设备的驱动方法。比如说标椎输出2号文件,结构体的函数指针就是指向显示器的驱动方法,将数据写到2号文件,也就是显示到显示屏上。
所以一切皆文件,举个例子,把显示屏看做文件,我们往显示屏打印数据,不就是往文件里写东西而已。
你可以验证一下上面的理论,先把标准输出1号文件关闭了,再打开log.txt文件,根据fd的分配规则,新的fd值一定是1;所以此时调用printf();本来printf是打印到1号文件,也就是显示屏;但是现在打印到log.txt文件里取了(当然这个过程也叫做重定向)
文件描述符的分配规则:从头遍历数组fd_array[],找到一个最小的,没有被使用的下标,分配给新的文件
int main()
{close(1);//根据fd的分配规则,新的fd值一定是1int fd = open("log.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);if(fd < 0){perror("oppen");return 1;}//printf-》stdout->1->虽然不在指向对应的显示器了,但是已经指向了log.txt的底层struct file 对象//本来应该要往显示器打印,最终却变成了向指定文件打印 -> 重定向printf("fd:%d\n",fd);//为什么必须要fflush一下fflush(stdout);close(fd);
}
重定向的本质
修改文件描述符fd下标 对应的struct file * 的内容 (将其换成目标文件的地址)。
重定向具体操作
int dup2(int oldfd,int newfd);//duplicate重定向
int main()int fd = open("log.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
if(fd < 0)
{perror("open");return 0;
}
dup2(fd,1)>=0
//本来应该要往显示器打印,最终却变成了向指定文件打印 -> 重定向
fprintf(stdout,"打开文件:%d",fd);
fflush(stdout);
close(fd);
return 0;
}
理解软硬链接
在讲软硬链接之前,我们得先了解一下什么是inode。
我们刚才说的,打开的文件是内存级别的,实际上大量的文件还在磁盘上,这些文件和多,很杂,我们的文件系统就要对这些进行管理。
那么一个硬件(磁盘)怎么跟我们的OS产生联系呢?
把磁盘想象成为线性结构,当做500GB大小的数组,然后对磁盘的管理,转化为了对数组空间的管理。但是一次性管理500GB大小的数组不太可能,所以就对500GB的数组进行分区,一个区100GB,一个区还是太大,在对区进行分组;一个组10G;组内在细分成块,然后内核对这些块进行描述,比如有什么属性,属于哪个组;然后就转为对这些块的管理。
Linux 系统中就有一个名为 superblock 的 “硬盘地图”。 Linux 并不是把文件内容直接写入到 superblock 中,而是在里面记录着整个文件系统的信息。superblock里面就有一个叫inode的。
文件= 内容 + 属性——都是数据——都要存储
Linux采用的是将内容和属性数据分开存储的方案
内容一般放在(一块块)block中(4kb),属性数据一般放在inode中(inode说人话就是磁盘上的另一份空间,一般这份空间是128字节)
Linux 把每个文件的权限与属性记录在 inode("索引节点:index node ") 中,而且每个文件占用一个独立的 inode 表格,该表格的默认大小为 128 字节。
里面记录着如下信息 :
-
文件的访问权限(read、write、execute)
-
该文件的所有者与所属组(owner、group)
-
该文件的大小(size)
-
该文件的创建或内容修改时间(ctime)
-
该文件的最后一次访问时间(atime)
-
该文件的修改时间(mtime)
-
文件的特殊权限(SUID、SGID、SBIT)
-
该文件的真实数据地址(point)。
在 Linux 系统中 ,inode 号才是文件的唯一标识而非文件名。文件名只是为了方便人们的记忆和适用
那我现在知道了,inode就是文件的唯一标识,用来存储文件的属性的,那软硬链接到底是什么呢?
硬链接(hard link) : 可以将它理解为一个 “指向原始文件 inode 的指针”,系统不为它分配独立的 inode 和 文件。所以,硬链接文件与原始文件其实是同一个文件,只是名字不同。我们每添加一个硬链接,该文件的 innode 连接数就会增加 1 ; 而且只有当该文件的 inode 连接数为 0 时,才算彻底被将它删除。因此即便删除原始文件,依然可以通过硬链接文件来访问。需要注意的是,我们不能跨分区对文件进行链接。所以硬链接可以用来防止误删。
软链接(symbolic link) : 等同于 Windows 系统下的快捷方式。仅仅包括所含链接文件的路径名字。因此能链接目录,也能跨文件系统链接。但是,当删除原始文件后,链接文件也将失效。
软硬链接的区别:软链接是一个独立文件,有自己独立的inode和inode编号;硬链接,不是一个独立的文件,它和目标文件使用同一个inode
扩展问题
-
什么是硬链接数??
此时的inode编号,不是就一个“指针”的概念吗?硬链接数本质就是给文件inode属性中的一个计数器count,标识有几个文件名和我的inode建立了映射关系。简而言之,就是有几个文件名,指向我的inode(文件本身)
-
既然软链接是一个独立的文件,那它的文件内容是什么呢??
保存的是指向文件的所在路径
-
为什么创建普通文件,默认硬链接数是1;而创建目录,默认硬链接数是2呢??
普通文件的文件名,本身就和自己的inode具有映射关系,所以是1;
默认的空目录,有两个默认文件.和…,.表示当前目录,也指向这个目录,所以目录自己指向自己的inode,.也指向目录的inode,所以是2;而…指向的是上级目录的inode
相关文章:

什么是文件描述符以及重定向的本质和软硬链接(Linux)
目录 1 什么是文件?什么是文件操作?认识系统接口open 什么是文件描述符认识Linux底层进程如何打开的文件映射关系重定向的本质理解软硬链接扩展问题 1 什么是文件?什么是文件操作? 文件 文件内容 文件属性(文件属性…...

LVM逻辑卷元数据丢失恢复案例 —— 筑梦之路
Lvm常见的故障主要是pv出现异常,有以下几种情况 一个是pv所在的磁盘发生了lvm的元数据损坏一个是系统无法识别到pv所在的磁盘一个是系统异常,断电等导致重启后盘符发生变化,也就是系统识别的磁盘uuid发生变化,但是wwid还是可以对应…...

Java技术规范概览
Java技术规范 目录概述需求: 设计思路实现思路分析1.Java JSR的部分2.JSR-000373.JSR-0000394.JSR-000337 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy,skip hardness,make a bet…...

【OpenMMLab AI实战营第二期】二十分钟入门OpenMMLab笔记
OpenMMlab 主页:openmmlab.com 开源地址:https://github.com/open-mmlab 学习视频地址:https://www.bilibili.com/video/BV1js4y1i72P/ 概述 开源成为人工智能行业发展引擎 时间轴 theano:2007 Caffe:2013 Ten…...

docker-compose单机容器集群编排
docker-compose dockerfile模板文件可以定义一个独立的应用容器,如果需要多个容器就需要服务编排。服务编排有很多技术方案 docker-compose开源的项目实现对容器集群的快速编排 docker-compose将所管理的容器分为三层,分别为工程,服务&#…...
CentOS7 安装Gitlab
1、安装依赖 sudo yum install -y curl openssh-server ca-certificates tzdata perl libsemanage-devel 2、安装邮件服务工具 sudo yum install -y postfix 3、配置GitLab 软件源镜像 curl -fsSL https://packages.gitlab.cn/repository/raw/scripts/setup.sh | /bin/bash …...

Mysql InnoDB的Buffer Pool
Buffer Pool 在MySQL服务器启动的时候就向操作系统申请了⼀⽚连续的内存,他们给这⽚内存起了个名,叫做Buffer Pool(中⽂名 是缓冲池)。 默认情况下Buffer Pool只有128M⼤⼩,最⼩值为5M,通过修改配置文件设…...

SMTP简单邮件传输协议(C/C++ 发送电子邮件)
SMTP是用于通过Internet发送电子邮件的协议。电子邮件客户端(如Microsoft Outlook或macOS Mail应用程序)使用SMTP连接到邮件服务器并发送电子邮件。邮件服务器还使用SMTP将邮件从一个邮件服务器交换到另一个。它不用于从服务器下载电子邮件;相…...

uploads靶场通关(1-11关)
Pass-01(JS校验) 看题目我们准备好我们的php脚本文件,命名为1.php 上传该php文件,发现上传失败 方法一:将浏览器的JavaScript禁用 然后就能上传了 方法二: 查看源码,发现只能上传以下形式的文…...

6.1黄金探底回升是否到顶,今日多空如何布局
近期有哪些消息面影响黄金走势?今日黄金多空该如何研判? 黄金消息面解析:周三(5月31日)黄金期货价格攀升,美国国债收益率下降推动金价升至一周最高收盘位。美市尾盘,现货黄金收报1962.42美元/盎司,上升3…...

自定义ViewGroup实现流式布局
目录 1、View的绘制流程 2、自定义ViewGroup构造函数的作用 3、onMeasure 方法 3.1、View的度量方式 3.2、onMeasure方法参数的介绍 3.3、自定义ViewGroup onMeasure 方法的实现 4、onLayout方法 5、onDraw方法 6、自定义View的生命周期 7、自定义流式布局的实现 扩展ÿ…...

Git版本控制
目录 版本控制 概念 为什么需要版本控制? 常见的版本控制工具 Git 1、安装 2、了解基本的Linux命令 3、配置git 用户名和邮箱 4、git 工作模式 5、git 项目管理 6、git 分支 托管平台 远程仓库 Gitee 关联多个远程库 Git服务器 Git GUI 版本控制 概…...

若依之权限处理
若依之权限处理 若依前后端不分离版本使用的是shiro进行权限控制,本文主要是对shiro在若依中的使用进行分析。 RBAC权限模型 RBAC是指基于角色的访问控制。其基本思想是,对系统的各种权限不是直接授予具体的用户,而是在用户集合与权限集合…...

华为OD机试真题 Java 实现【矩阵最大值】【2023 B卷 100分】,附详细解题思路
一、题目描述 给定一个仅包含0和1的N*N的二维矩阵,请计算二维矩阵的最大值。 计算规则如下: 1、每行元素按下标顺序组成一个二进制数(下标越大越排在低位),二进制数的值就是该行的值。矩阵各行值之和为矩阵的值。 2、允许通过向左或向右整体循环移动每行元素来改变各元…...

ModuleNotFoundError: No module named ‘transformers_modules.chatglm-6b_v1‘的解决方案
大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…...

MMPretrain代码课
安装注意事项 训练时需要基于算法库源码进行开发,所以需要git clone mmpretrain仓库。如果只调用,则pip install 即可。 from mmpretrain import get_model, list_models,inference_model分别用于模型的获取、例举、推理 此时还没加载预训练权重 tor…...

Selenium自动化程序被检测为爬虫,怎么屏蔽和绕过
Selenium 操作被屏蔽 使用selenium自动化网页时,有一定的概率会被目标网站识别,一旦被检测到,目标网站会拦截该客户端做出的网页操作。 比如淘宝和大众点评的登录页,当手工打开浏览器,输入用户名和密码时,…...
Nvidia Jetson Orin:开发技巧
Jetson PXXX定义 P2180 -> Jetson TX1 P3310 -> Jetson TX2 P3489 -> Jetson TX2i P3448 -> Jetson Nano devkit P3448-0020 -> Jetson Nano production module P2888 -> Jetson Xavier P2888-0060 -> Jetson Xavier-8GB P3701 -> Jetson AGX Orin D…...

为什么需要 git 和 相关的小知识
为什么需要git和相关的小知识 先看一个实际需求,引出Git 问题: 公司五一活动计划 ● 先说一个最简单的情况,比如你做了公司五一活动计划书(如图) 解决方案: 版本管理工具(Git) 一句话: Git 是目前最流行的分布式版本控制软件 Git 是怎么来的? Git…...

(详解)vue中实现主题切换的三种方式
目录 一、背景 二、实现思路 方法1:定义全局的CSS变量 方法2:切换已定义好的css文件 方法3:切换顶级CSS类名 (需使用css处理器,如sass、less等) 一、背景 在我们开发中我们会遇到像是需要切换程序风格、主题切换啦这种应用场景。 参考大佬…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...

算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...

iview框架主题色的应用
1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题,无需引入,直接可…...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...

【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅
目录 前言 操作系统与驱动程序 是什么,为什么 怎么做 system call 用户操作接口 总结 前言 日常生活中,我们在使用电子设备时,我们所输入执行的每一条指令最终大多都会作用到硬件上,比如下载一款软件最终会下载到硬盘上&am…...

Linux中《基础IO》详细介绍
目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改,实现简单cat命令 输出信息到显示器,你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...