一些前置知识:

文件 = 属性 + 内容

文件 分为 打开的文件、未打开的文件

打开的文件:由进程打开,本质是 进程与文件 的关系;维护的文件对象先加载文件属性,文件内容一般按需加载

未打开的文件:在永久性存储介质 —— 磁盘上;也需要被管理

C语言文件接口

fopen

打开文件并指定打开方式,返回 FILE* 文件对象指针

r:只读

w:写入、但在写入之前会清空文件;  > 重定向也是使用 w 方法写入文件

a:追加,在文件结尾写入; >> 追加方法

fwrite

向已打开的 FILE 文件对象写入 size 字节的内容

stdin、stdout、stderr

C程序默认启动时,会打开 3 个标准输入输出流,分别是 键盘文件 和 2 个显示器文件

fprintf

向指定文件对象 中写入

文件相关系统调用接口

未打开的文件存储在 磁盘上,磁盘是 外设,访问磁盘上的文件 —— 相当于在 访问硬件;

几乎所有的库,只要是 访问硬件设备相关,必定封装了系统调用

因为 操作系统 通过 硬件驱动 管理硬件,并向上层提供 系统调用接口;

open

参数解读

pathname:文件路径

int flags:系统提供了设置好的多个 宏 标志位,可以 通过 位或运算 进行组合,以实现不同的 文件打开方法(bit 位级别的 标志位传递方式、有点类似位图了)

mode:以 8 进制的方式 设置被打开文件的权限,但还要经过 umask 权限掩码的计算( 最终权限 = 起始权限 & (~umask)

返回值:一个较小的非负整数,文件描述符 file descriptor

int fd = open("test.txt", O_WRONLY | O_CREAT, 0666);
if( fd < 0 )
{perror("open error");return 1;
}

close

int fd = open("test.txt", O_WRONLY | O_CREAT, 0666);
if( fd < 0 )
{perror("open error");return 1;
}close(fd);

write

int fd = open("logggggg.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
int size_write = write(fd, "aaaaaaa", 3);	
close(fd);

默认从文件开始写,但不会清空原有内容,而是覆盖

小结

C文件相关库函数 封装了系统调用 open、write、close

C库中使用 自定义类型 FILE 类型的文件对象描述打开的文件,而系统调用中 使用 文件描述符 fd

不论什么语言,底层都是这种 封装系统调用的 模式,向用户提供 库函数,以便二次开发

文件描述符 fd

在 Linux 中,文件描述符 就是一个 小整数

当磁盘文件被加载到内存,内核为该进程创建 PCB,接着创建文件描述符表 struct files_struct,其中有 存放文件对象指针的 指针数组 struct file* fd_array[ ] ,接着打开 3 个默认的标准输入输出流 stdin、stdout、stderr,接着创建一个 struct file 自定义数据结构 描述被打开文件的属性;如果一个进程打开多个文件,那就创建多个文件对象 struct file,用 双链表指针 互相组织起来 —— 对文件的操作就变成了对该文件对象的 增删改查

通过从进程创建 到 打开文件捋一遍之后,文件描述符 fd 就是 文件描述符表 中 指针数组的 下标。

既然已经有文件描述符表来组织管理 打开的文件对象 struct file,那各个 struct file 还用双链表结构组织是否多此一举?

非也,考虑了如果进程崩掉了之类的意外情况。

通过 文件描述符表 结构,将 内核中的 进程管理模块 与 文件管理模块 解耦

注意:除了 内核 默认打开的 3 个标准输入输出流文件,其余打开文件对象的指针 struct file* 依次按照文件描述符表的 空闲的 元素位置 进行分配。

用 系统调用 操作 fd

例 read:从文件中读取 count 字节数据

char buff[100];
ssize_t s = read(0, buff, sizeof(buff));
buff[s] = '\0';
printf("echo : %s\n", buff);

从文件描述符为 0 的文件中读取 sizeof(buff) 大小的数据,并写入 buff 开始的缓冲区中:

根据运行结果,回车也被写入,因为此时 bash 内的缓冲区为 行缓冲方式(见后文描述)

小结

为什么默认打开 stdin、stdout、stderr?

因为 操作系统启动时,键盘、显示器就已经被 操作系统 识别并打开了,在 其他进程被创建时,只需要将这三个 已经被打开的 文件对象struct file 的地址 填进 文件描述符表 中,使用就 ok。

Linux 中的文件相关 系统调用 只认文件描述符,不同于 C 库中封装的自定义类型 FILE(但 FILE 中一定封装了 文件描述符)

stdout 和 stderr 都指向显示器文件;

一个文件对象 struct file 可以被多个进程使用,例如 3 个默认的 stdin、stdout、stderr 可以被多个进程同时使用;所以 struct file 中会包含一个属性:引用计数

引用计数:记录有几个文件描述符 指向自己;调用 close 会使该文件对象的 引用计数 减 1,并且将 这个进程的 文件描述符表 中的 对应文件描述符下标 的指针数组 的元素 置空,以供其他打开的文件使用; 当被 close 的文件对象的引用计数为 1时,调用 close 会释放掉这个 文件对象。

重定向

本质:对进程的 文件描述符表 中的地址 进行内核级别的 拷贝。

文件描述符的分配规则:从文件描述符表的 0 下标开始,寻找空闲位置存放 新文件的 文件对象指针。

输出重定向

示例:将 本来写入显示器文件(文件描述符 1)的内容,写入 文件描述符为 5 的文件

操作系统提供了 系统调用接口,主要介绍 dup2 :完成 拷贝文件描述符下标对应的 文件对象指针 的工作

int fd = open("fdddddddd.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666 );const char* str = "hello world forever!";//dup2(1, fd);dup2(fd, 1);
write(1, str, strlen(str));

追加重定向

与输出重定向一样,只是打开文件时 的 flags 参数,再按位或 O_APPEND,就是向文件中追加内容,而不是 清空掉原文件内容 再写入

int fd = open("fdddddddd.txt", O_WRONLY | O_CREAT | O_APPEND, 0666 );const char* str = "hello world forever!";//dup2(1, fd);dup2(fd, 1);
write(1, str, strlen(str));

输入重定向

示例:本来要从 stdin 中读取数据,可以使用 dup2 系统调用接口,拷贝 某个文件描述符对应的 文件对象指针,转为 从 另一个文件中读取数据

int fd = open("fdddddddd.txt", O_CREAT | O_APPEND | O_RDONLY, 0666 );                                                                                                                                    char buff[100];dup2(fd, 0);
ssize_t s = read(0, buff, sizeof(buff));
buff[s] = '\0';printf("%s", buff);

成功将 fddddddd.txt 重定向到 0 号文件描述符,使得本应从 stdin 读取数据存入 buff,转为 从 fdddddd.txt 中读取数据 存入 buff。

:如果把 stdout、stdin、stderr 给重定向,覆盖掉了,有办法找到这三个文件,再重定向回来即可恢复

为什么 stdout、stderr 都指向显示器,应用场景

在分离正常日志与错误日志时非常有用:

另外,命令行输出重定向时,默认被重定向的是 1号 文件描述符 stdout,可以省略不写。

:程序替换 exce 接口功能,并不影响进程对 文件的访问。

小结

硬件设备都可以被进程,以 open 打开访问,因为 Linux 下一切皆文件:

1、内核为 进程创建 PCB —— task_struct,其中包含指针 指向文件描述符表

2、文件描述符表 这个指针数组中 存放着 struct file* —— 文件对象的指针

3、对于所有外设(键盘、显示器、磁盘等硬件)来说,进程在 打开它们并创建对应的 struct file 时,struct file 中有个自定义类型的指针 指向 struct operation_func

4、struct operation_func 中有相应的 读、写 的函数指针,不同外设共用相同的 struct operation_func 结构(一般情况下);读、写和其他方法的 函数指针指向相应的外设的 具体底层驱动的 函数方法实现,但这些对上层来说,是不用关心的;相同的函数指针 却指向 不同的方法实现,就像面向对象中的 多态一样

5、struct operation_func 结构中 有 读、写等方法的函数指针,不同外设的 底层驱动的 方法实现,就会用来 初始化这些 函数指针。

对于 C 库中的 stdin、stdout、stderr:

内核在创建进程时就已经把 0、1、2 这三个描述符指向同一个终端设备

C 库只是随后把这三个描述符包装成 FILE* 变量(stdin、stdout、stderr),并加了一层缓冲而已。

在 Linux 中,键盘输入、屏幕输出都被抽象成同一个“终端设备文件”,这个终端设备文件既提供读接口(用户敲的字符)又提供写接口(送到屏幕的文字),因此 0、1、2 三个文件描述符实际上都指向同一个 inode(同一个 struct file),只是分别用于读、写、写。

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

力扣164:最大间距

力扣164:最大间距题目思路代码题目 给定一个无序的数组 nums&#xff0c;返回 数组在排序之后&#xff0c;相邻元素之间最大的差值 。如果数组元素个数小于 2&#xff0c;则返回 0 。 您必须编写一个在「线性时间」内运行并使用「线性额外空间」的算法。 思路 这道题的思路…

Redis类型之Hash

1.hash常用操作 这里还是要强调&#xff0c;redis的类型指的是value的类型。故而这里的hash是把key这一层组织完成以后&#xff0c;到了value这一层&#xff0c;value的其中一种类型还可以是hash。1.1 HSET 和 HGETHSET&#xff1a;设置hash类型的keyHSET key field value [fie…

Apache Pulsar性能与可用性优化实践指南

Apache Pulsar性能与可用性优化实践指南 一、技术背景与应用场景 随着微服务、实时计算和大数据平台的普及&#xff0c;消息系统承担了海量数据的传输与解耦任务。Apache Pulsar作为新一代分布式消息与流处理系统&#xff0c;拥有多租户、持久化存储和灵活一致性的特点&#xf…

工单分类微调训练运维管理工具原型

简述需求进展之前&#xff0c;我尝试用Longformer模型来训练工单分类系统&#xff0c;但问题很快就暴露出来&#xff1a;Longformer训练时间长得让人抓狂&#xff0c;每次训练只能针对一个租户的数据&#xff0c;无法快速适配多个租户的需求。切换一个使用相同标签的租户还能够…

@CacheConfig​​当前类中所有缓存方法详解

CacheConfig​​当前类中所有缓存方法详解在 Spring Cache 抽象中&#xff0c;CacheConfig 是一个​​类级别注解​​&#xff0c;用于为​​当前类中的所有缓存方法&#xff08;如 Cacheable、CachePut、CacheEvict&#xff09;提供默认配置​​。其核心作用是​​避免在每个方…

正确使用SQL Server中的Hint(10)—Hint简介与Hint分类及语法(1)

9.5. 正确使用Hint 9.5.1. Hint简介 与Oracle等其他关系库类似,SQL Server中,也提供了诸多Hint用于支持SQL调优,那就是通过正确应用Hint技术,可以指示CBO为SQL语句产生和选择最合理而高效的查询计划。Hint确实可以做到很容易的对CBO产生影响,但因为多数场景中,CBO都能为…

Redis的分布式序列号生成器原理

Redis 分布式序列号生成器的核心原理是利用 Redis 的原子操作和高性能特性&#xff0c;在分布式系统中生成全局唯一、有序的序列号。其设计通常结合业务需求&#xff08;如有序性、长度限制、高并发&#xff09;&#xff0c;通过 Redis 的原子命令&#xff08;如 INCR、INCRBY&…

2025年SEVC SCI2区,基于深度强化学习与模拟退火的多无人机侦察任务规划,深度解析+性能实测

目录1.摘要2.问题定义3.SA-NNO-DRL方法4.结果展示5.参考文献6.算法辅导应用定制读者交流1.摘要 无人机&#xff08;UAV&#xff09;因其高自主性和灵活性&#xff0c;广泛应用于侦察任务&#xff0c;多无人机任务规划在交通监控和数据采集等任务中至关重要&#xff0c;但现有方…

汽车娱乐信息系统域控制器的网络安全开发方案

引言1.1 项目背景随着汽车行业的快速发展和智能化、网联化的趋势日益明显&#xff0c;汽车娱乐信息系统&#xff08;In-Vehicle Infotainment System&#xff0c;IVIS&#xff09;已经成为现代汽车的重要组成部分。汽车娱乐信息系统不仅提供了丰富的多媒体功能&#xff0c;如音…

【论文阅读】Deep Adversarial Multi-view Clustering Network

摘要多视图聚类通过挖掘多个视图之间的共同聚类结构&#xff0c;近年来受到了越来越多的关注。现有的大多数多视图聚类算法使用浅层、线性嵌入函数来学习多视图数据的公共结构。然而&#xff0c;这些方法无法充分利用多视图数据的非线性特性&#xff0c;而这种特性对于揭示复杂…

Redis - 使用 Redis HyperLogLog 进行高效基数统计

文章目录引言HyperLogLog 工作原理Spring Boot 集成 Redis1. 添加依赖2. 配置 Redis 连接3. Redis 配置类HyperLogLog 实战应用1. 基础操作服务类2. 网站日活跃用户统计3. 性能测试与误差分析应用场景分析适用场景不适用场景性能优化技巧与传统方案对比结论引言 在数据分析和监…

後端開發技術教學(三) 表單提交、數據處理

上回&#xff1a;後端開發技術教學(二) 條件指令、循環結構、定義函數 -CSDN博客 必要資源&#xff1a; trae中文版下載網址: TRAE - The Real AI Engineer phpStudy 2018 : phpStudy - Windows 一键部署 PHP 开发环境 小皮出品 目錄 一、表單提交 1.1 get & post 1.…

Python训练Day39

浙大疏锦行 图像数据的格式&#xff1a;灰度和彩色数据模型的定义显存占用的4种地方 模型参数梯度参数优化器参数数据批量所占显存神经元输出中间状态 batchisize和训练的关系 一、 图像数据的介绍 图像数据&#xff0c;相较于结构化数据&#xff08;表格数据&#xff09;他的特…

十八、MySQL-DML-数据操作-插入(增加)、更新(修改)、删除

DML数据操作添加数据更新(修改)数据删除数据总结代码&#xff1a; -- DML:数据操作语言-- -- DML:插入数据-insert -- 1.为tb_emp表的username,name&#xff0c;gender 字股插入值insert into tb_emp(username,name,gender,create_time,update_time) values (Toki,小时,2,now()…

Linux 安装 JDK 8u291 教程(jdk-8u291-linux-x64.tar.gz 解压配置详细步骤)​

一、准备工作 ​下载 JDK 安装包​ 去 Oracle 官网或者可信的镜像站下载&#xff1a; ​jdk-8u291-linux-x64.tar.gz​ &#xff08;这是一个压缩包&#xff0c;不是安装程序&#xff0c;解压就能用&#xff09; ​jdk-8u291-linux-x64.tar.gz​下载链接&#xff1a;https://pa…

蓝桥杯----锁存器、LED、蜂鸣器、继电器、Motor

(七)、锁存器1、原理蓝桥杯中数据传入口都是P0&#xff0c;也就是数码管段选、位选数据、LED亮灭的数据、蜂鸣器启动或禁用的数据&#xff0c;外设启动或者关闭都需要通过P0写入数据&#xff0c;那么如何这样共用一个端口会造成冲突嘛&#xff0c;答案是肯定的。所以蓝桥杯加入…

AI热点周报(8.3~8.9):OpenAI重返开源,Anthropic放大招,Claude4.1、GPT5相继发布

名人说&#xff1a;博观而约取&#xff0c;厚积而薄发。——苏轼《稼说送张琥》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录一、OpenAI的"开源回归"&#xff1a;时隔5年的战略大转弯1. GPT-OSS系列&a…

《Kubernetes部署篇:基于x86_64+aarch64架构CPU+containerd一键离线部署容器版K8S1.33.3高可用集群》

总结&#xff1a;整理不易&#xff0c;如果对你有帮助&#xff0c;可否点赞关注一下&#xff1f; 更多详细内容请参考&#xff1a;企业级K8s集群运维实战 一、部署背景 由于业务系统的特殊性&#xff0c;我们需要针对不同的客户环境部署基于containerd容器版 K8S 1.33.3集群&a…

Linux抓包命令tcpdump详解笔记

文章目录一、tcpdump 是什么&#xff1f;二、基本语法三、常用参数说明四、抓包示例&#xff08;通俗易懂&#xff09;1. 抓所有数据包&#xff08;默认 eth0&#xff09;2. 指定接口抓包3. 抓取端口 80 的数据包&#xff08;即 HTTP 请求&#xff09;4. 抓取访问某个 IP 的数据…

抖音、快手、视频号等多平台视频解析下载 + 磁力嗅探下载、视频加工(提取音频 / 压缩等)

跟你们说个安卓上的下载工具&#xff0c;还挺厉害的。它能支持好多种下载方式&#xff0c;具体多少种我没细数&#xff0c;反正挺全乎的。​ 平时用得最多的就是视频解析&#xff0c;像抖音、快手、B 站上那些视频&#xff0c;想存下来直接用它就行&#xff0c;连海外视频的也能…