Linux | 文件描述符
文章目录
- Linux | 文件描述符
- 1. 文件描述符概述
- 2. 与文件描述符关联的数据结构
- 2.1 进程级的文件描述符表(`struct files_struct`)
- 2.2 文件描述符表项(`struct fdtable`)
- 2.3 文件对象(`struct file`)
- 2.4 索引节点(`struct inode`)
- 3. 数据结构之间的关系
- 4. 文件描述符相关系统调用
- 4.1 `open` 函数
- 4.2 `read` 函数
- 4.3 `write` 函数
- 4.4 `close` 函数
- 5. 代码示例及现象观察
- 5.1 示例代码
- 5.2 代码解释
- 5.3 编译和运行
- 5.4 现象观察
- 6. 标准文件描述符示例
Linux | 文件描述符
1. 文件描述符概述
在 Linux 系统里,文件描述符(File Descriptor)是一个非负整数,它是对文件或者 I/O 设备(如键盘、显示器等)的抽象表示。当进程打开一个现有文件或者创建一个新文件时,内核会为该进程分配一个文件描述符,进程通过这个文件描述符来对文件进行各种操作,例如读取、写入、关闭等。
每个进程都有一个对应的文件描述符表,文件描述符就是该表的索引。系统默认会为每个进程打开三个标准的文件描述符:
- 标准输入(
stdin):文件描述符为 0,通常关联键盘,用于读取用户输入。 - 标准输出(
stdout):文件描述符为 1,通常关联显示器,用于输出信息。 - 标准错误(
stderr):文件描述符为 2,也关联显示器,主要用于输出错误信息。
2. 与文件描述符关联的数据结构
2.1 进程级的文件描述符表(struct files_struct)
每个进程都有一个 files_struct 结构体,它管理着该进程打开的所有文件描述符。这个结构体包含一个指针数组,数组的索引就是文件描述符,每个数组元素指向一个 file 结构体。
struct files_struct {atomic_t count;struct fdtable __rcu *fdt;struct fdtable fdtab;// 其他成员...
};
count:表示当前结构体的引用计数。fdt:指向一个fdtable结构体,该结构体包含了文件描述符表的具体信息。
2.2 文件描述符表项(struct fdtable)
fdtable 结构体包含了文件描述符数组以及其他相关信息,它是 files_struct 结构体中 fdt 指针所指向的对象。
struct fdtable {unsigned int max_fds;struct file __rcu **fd; /* current fd array */unsigned long *close_on_exec;unsigned long *open_fds;unsigned long *full_fds_bits;struct rcu_head rcu;// 其他成员...
};
max_fds:表示当前文件描述符表的最大容量。fd:是一个指向file结构体指针的数组,数组的索引就是文件描述符。
2.3 文件对象(struct file)
file 结构体代表一个打开的文件,它包含了文件的状态信息、文件偏移量、操作函数指针等。
struct file {//.....atomic_long_t f_count; /*引用计数,管理文件对象的生命周期*/struct mutex f_pos_lock; /*保护文件未知的互斥锁*/loff_t f_pos; /*当前文件位置(读写位置)*///..... struct path f_path; /*记录文件路径*/struct inode *f_inode; /*指向与文件相关联的inode对象的指针,该对象用于维护文件元数据,如文件类型、访问权限等*/const struct file_operations *f_op;/*指向文件操作函数表的指针,定义了文件支持的操作,如读、写、锁定等*/unsigned int f_flags; /*表示文件的打开标志,如`O_RDONLY`、`O_WRONLY`等*/fmode_t f_mode; /*表示文件的访问模式*/ // 其他成员...
};
2.4 索引节点(struct inode)
inode 结构体代表文件在磁盘上的元数据,包含了文件的权限、大小、创建时间、数据块位置等信息。
struct inode {umode_t i_mode;unsigned short i_opflags;kuid_t i_uid;kgid_t i_gid;unsigned int i_flags;const struct inode_operations *i_op;struct super_block *i_sb;struct address_space *i_mapping;// 其他成员...
};
i_mode:表示文件的权限和类型。i_uid和i_gid:分别表示文件的所有者用户 ID 和组 ID。i_op:指向一个inode_operations结构体,该结构体包含了对inode进行各种操作的函数指针。i_sb:指向文件所在的超级块结构体。
3. 数据结构之间的关系
当一个进程调用 open 系统调用打开一个文件时,内核会进行以下操作:
- 在内核中创建一个
file结构体,用于表示这个打开的文件,并初始化其成员变量,如f_flags、f_mode等。 - 从进程的
files_struct结构体中找到一个空闲的文件描述符,该文件描述符对应的fdtable数组元素指向新创建的file结构体。 - 根据文件路径找到对应的
inode结构体,并将file结构体中的f_inode指针指向该inode结构体。
这样,进程就可以通过文件描述符访问对应的 file 结构体,进而通过 file 结构体中的 f_op 指针调用相应的操作函数对文件进行读写等操作。
4. 文件描述符相关系统调用
4.1 open 函数
open 函数用于打开或创建一个文件,并返回一个文件描述符。其原型如下:
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
pathname:要打开或创建的文件的路径。flags:打开文件的方式,例如O_RDONLY(只读)、O_WRONLY(只写)、O_RDWR(读写)等。mode:如果创建新文件,指定文件的权限,例如0666表示所有用户都有读写权限。
4.2 read 函数
read 函数用于从文件描述符对应的文件中读取数据。其原型如下:
#include <unistd.h>ssize_t read(int fd, void *buf, size_t count);
fd:文件描述符。buf:用于存储读取数据的缓冲区。count:要读取的字节数。
4.3 write 函数
write 函数用于向文件描述符对应的文件中写入数据。其原型如下:
#include <unistd.h>ssize_t write(int fd, const void *buf, size_t count);
fd:文件描述符。buf:要写入的数据的缓冲区。count:要写入的字节数。
4.4 close 函数
close 函数用于关闭一个文件描述符,释放相关资源。其原型如下:
#include <unistd.h>int close(int fd);
fd:要关闭的文件描述符。
5. 代码示例及现象观察
5.1 示例代码
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>#define BUFFER_SIZE 1024int main() {int fd;char buffer[BUFFER_SIZE] = "Hello, File Descriptor!";char read_buffer[BUFFER_SIZE];// 打开一个文件,如果文件不存在则创建,以只写模式打开fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);if (fd == -1) {perror("open");return 1;}// 从数据结构角度看,此时内核创建了 file 结构体,从进程的 files_struct 找到空闲 fd 指向该 file 结构体,file 结构体的 f_inode 指向文件对应的 inode 结构体// 向文件中写入数据ssize_t bytes_written = write(fd, buffer, strlen(buffer));if (bytes_written == -1) {perror("write");close(fd);return 1;}printf("Written %zd bytes to test.txt\n", bytes_written);// 关闭文件if (close(fd) == -1) {perror("close");return 1;}// 以只读模式打开文件fd = open("test.txt", O_RDONLY);if (fd == -1) {perror("open");return 1;}// 从文件中读取数据ssize_t bytes_read = read(fd, read_buffer, BUFFER_SIZE);if (bytes_read == -1) {perror("read");close(fd);return 1;}read_buffer[bytes_read] = '\0';printf("Read %zd bytes from test.txt: %s\n", bytes_read, read_buffer);// 关闭文件if (close(fd) == -1) {perror("close");return 1;}return 0;
}
5.2 代码解释
- 打开文件:使用
open函数以只写模式打开test.txt文件,如果文件不存在则创建,同时截断文件内容。如果打开成功,会返回一个文件描述符。从数据结构角度,内核会创建一个file结构体,从进程的files_struct中找到一个空闲的文件描述符,让该文件描述符对应的fdtable数组元素指向新创建的file结构体,并且file结构体中的f_inode指针会指向文件对应的inode结构体。 - 写入数据:使用
write函数将字符串"Hello, File Descriptor!"写入文件。此操作会通过文件描述符找到对应的file结构体,然后根据file结构体中的f_op指针调用相应的写入函数。 - 关闭文件:使用
close函数关闭文件描述符,释放资源。此时会减少file结构体的引用计数f_count,当引用计数为 0 时,内核会释放file结构体。 - 再次打开文件:以只读模式打开
test.txt文件,重复上述打开文件的流程。 - 读取数据:使用
read函数从文件中读取数据,并将其存储在read_buffer中。同样是通过文件描述符找到file结构体,调用f_op中的读取函数。 - 输出结果:将读取到的数据输出到终端。
5.3 编译和运行
将上述代码保存为 file_descriptor.c,然后使用以下命令编译和运行:
gcc -o file_descriptor file_descriptor.c
./file_descriptor
5.4 现象观察
- 运行程序后,会在当前目录下创建一个
test.txt文件,并将字符串"Hello, File Descriptor!"写入该文件。 - 程序会输出写入和读取的字节数,以及读取到的内容。
6. 标准文件描述符示例
#include <unistd.h>int main() {const char *message = "Hello, Standard Output!\n";// 使用标准输出文件描述符(1)写入数据write(1, message, strlen(message));return 0;
}
在这个示例中,我们直接使用标准输出的文件描述符(1)将字符串 "Hello, Standard Output!" 输出到终端。编译并运行该程序,你会看到字符串显示在终端上。标准文件描述符对应的 file 结构体在进程启动时就已经初始化好,分别关联到标准输入、标准输出和标准错误设备。
相关文章:
Linux | 文件描述符
文章目录 Linux | 文件描述符1. 文件描述符概述2. 与文件描述符关联的数据结构2.1 进程级的文件描述符表(struct files_struct)2.2 文件描述符表项(struct fdtable)2.3 文件对象(struct file)2.4 索引节点&…...
蓝桥杯-洛谷刷题-day5(C++)(为未完成)
1.P1328 [NOIP2014 提高组] 生活大爆炸版石头剪刀布 i.题目 ii.代码 #include <iostream> #include <string> using namespace std;int N, Na, Nb; //0-"剪刀", 1-"石头", 2-"布", 3-"蜥", 4-"斯"࿱…...
LVS 负载均衡集群(NAT模式)
一、环境准备 四台主机(一台 LVS、两台 RS、一台客户端) 1.1.LVS 主机 LVS 主机(两块网卡) 第一块:NAT模式(内网) 第二块:添加网卡(仅主机模式)࿰…...
开源的轻量级分布式文件系统FastDFS
FastDFS 是一个开源的轻量级分布式文件系统,专为高性能的分布式文件存储设计,主要用于解决海量文件的存储、同步和访问问题。它特别适合以中小文件(如图片、视频等)为载体的在线服务,例如相册网站、视频网站等。 FastD…...
解决 DeepSeek 官网服务器繁忙的实用方案
解决 DeepSeek 官网服务器繁忙的实用方案 大家在使用 DeepSeek 时,是不是经常遇到官网服务器繁忙,等半天都加载不出来的情况?别担心,今天就给大家分享一个用 DeepSeek 硅基流动 Cherry Studio 解决这个问题的实用方案ÿ…...
Terraform 最佳实践:Top 10 常见 DevOps/SRE 面试问题及答案
1. 如何高效管理 Terraform 状态? 使用远程后端,如 S3 或 GCS,存储 Terraform 状态文件。这可以支持协作并确保团队工作时状态的一致性。使用 DynamoDB 或 GCS 锁定状态以防止同时修改状态。 示例: backend "s3" {bu…...
嵌入式八股文面试题(二)C语言算法
相关概念请查看文章:C语言概念。 1. 如何实现一个简单的内存池? 简单实现: #include <stdio.h> #include <stdlib.h>//内存块 typedef struct MemoryBlock {void *data; // 内存块起始地址struct MemoryBlock *next; // 下一个内…...
#渗透测试#批量漏洞挖掘#LiveBos UploadFile 任意文件上传漏洞
免责声明 本教程仅为合法的教学目的而准备,严禁用于任何形式的违法犯罪活动及其他商业行为,在使用本教程前,您应确保该行为符合当地的法律法规,继续阅读即表示您需自行承担所有操作的后果,如有异议,请立即停止本文章读。 目录 漏洞背景 漏洞成因 影响评估 检测方案 …...
ds-download-link 插件:以独特图标选择,打造文章下载链接
源码介绍 “ds-download-link”插件为 WordPress 网站提供了在文章编辑器中添加下载链接的功能,每个下载链接都支持图标选择,并能将这些链接以美观的样式展示在文章前端页面。以下是该插件的主要特性和功能: 后台功能 在文章编辑器下方添加…...
介绍下SpringBoot在分布式架构中,如何实现读写分离
在分布式架构中,Spring Boot 可以通过多种方式实现读写分离,以提升系统性能和扩展性。以下是常见的实现方法: 1. 使用多数据源 通过配置多个数据源,将读操作和写操作分别路由到不同的数据库实例。 实现步骤: 配置多…...
判断函数是否为react组件或lazy包裹的组件
function Modal(){return <p>123</p> } 实参里填入函数名,是false 实参里填入标签形式的函数,是true isValidElement(Modal)//false isValidElement(<Modal></Modal>)//true 官方说明 isValidElement – React 中文文档 但是官方并不建议用isValidE…...
【大数据安全分析】大数据安全分析技术框架与关键技术
在数字化时代,网络安全面临着前所未有的挑战。传统的网络安全防护模式呈现出烟囱式的特点,各个安全防护措施和数据相互孤立,形成了防护孤岛和数据孤岛,难以有效应对日益复杂多变的安全威胁。而大数据分析技术的出现,为解决这些问题带来了新的曙光。 大数据分析在网络安全…...
PHP 中的除以零错误
除以零错误(Division by zero)是指数字除以零的情况, 这在数学上是未定义的。在 PHP 中,处理这种错误的方式取决于 PHP 版本: PHP 7: 使用 / 运算符会产生一个警告 (E_WARNING) 并返回 false。 使用 intd…...
【QT】控件 -- 多元素类 | 容器类 | 布局类
🔥 目录 一、多元素类1. List Widget -- 列表2. Table Widget -- 表格3. Tree Widget -- 树形 二、容器类1. Group Box -- 分组框2. Tab Widget -- 标签页 三、布局类1. 垂直布局【使用 QVBoxLayout 管理多个控件】【创建两个 QVBoxLayout】 2. 水平布局【使用 QHBo…...
数据结构——【二叉树模版】
#思路 1、二叉树不同于数的构建,在树节点类中,有数据,左子结点,右子节点三个属性,在树类的构造函数中,添加了变量maxNodes,用于后续列表索引的判断 2.GetTreeNode()函数是常用方法,…...
centos7 curl#6 - Could not resolve host mirrorlist.centos.org; 未知的错误 解决方案
问题描述 centos7系统安装完成后,yum安装软件时报错“curl#6 - “Could not resolve host: mirrorlist.centos.org; 未知的错误”” [root192 ~]# yum install vim -y 已加载插件:fastestmirror Determining fastest mirrors Could not retrieve mirro…...
【前端发展路径】技术成长路径、职业方向分支、行业趋势与建议、学习资源推荐
前端开发是一个快速发展的领域,技术栈和职业路径也在不断演进。以下是前端开发的典型发展路径,分为技术成长和职业方向两个维度,供参考: 一、技术成长路径 1. 初级阶段(入门) 核心技能: HTML/CSS:语义化标签、布局(Flex/Grid)、响应式设计、CSS 预处理器(Sass/Less…...
NO.15十六届蓝桥杯备战|while循环|六道练习(C++)
while循环 while语法形式 while 语句的语法结构和 if 语句⾮常相似,但不同的是 while 是⽤来实现循环的, if 是⽆法实现循环的。 下⾯是 while 循环的语法形式: //形式1 while ( 表达式 )语句; //形式2 //如果循环体想包含更多的语句&a…...
kotlin标准库里面也有很多java类
Kotlin 标准库中确实存在许多与 Java 类直接关联或基于 Java 类封装的结构,但这并不是“问题”,而是 Kotlin 与 JVM 生态深度兼容和互操作性的体现。以下从技术原理和设计哲学的角度详细解释: 一、Kotlin 与 JVM 的底层关系 Kotlin 代码最终…...
Flutter 双屏双引擎通信插件加入 GitCode:解锁双屏开发新潜能
在双屏设备应用场景日益丰富的当下,移动应用开发领域迎来了新的机遇与挑战。如何高效利用双屏设备优势,为用户打造更优质的交互体验,成为开发者们关注的焦点。近日,一款名为 Flutter 双屏双引擎通信插件的创新项目正式入驻 GitCod…...
01、单片机上电后没有正常运行怎么办
单片机上电后没有运转, 首先要检查什么? 1、单片机供电是否正常? &电路焊接检查 如果连最基本的供电都没有,其它都是空谈啊!检查电路断路了没有?短路了没有?电源合适吗?有没有虚焊? 拿起万用表之前,预想一下测量哪里?供电电压应该是多少?对PCB上电压测量点要…...
使用 EMQX 接入 LwM2M 协议设备
LwM2M 协议介绍 LwM2M 是一种轻量级的物联网设备管理协议,由 OMA(Open Mobile Alliance)组织制定。它基于 CoAP (Constrained Application Protocol)协议,专门针对资源受限的物联网设备设计,例…...
【Elasticsearch】bool查询
Elasticsearch 的bool查询是构建复杂查询条件的核心工具之一。它允许通过布尔逻辑组合多个查询子句,以实现精确的搜索需求。bool查询支持四种主要的子句类型:must、should、filter和must_not。每种子句类型都有其特定的作用和行为。 1.bool查询的基本结构…...
Redis 常见面试题汇总(持续更新)
文章目录 01、Redis 支持哪些数据类型?02、谈谈对 Redis 的 AOF 机制的 rewrite 模式的理解?03、请列举几个 Redis 常见性能问题和解决方案04、Redis 使用的最大内存是多少?内存数据淘汰策略有哪些?05、请谈谈 Redis 的同步机制。…...
蓝桥杯备赛 Day13.1走出迷宫
链接:走出迷宫 题目描述 小明现在在玩一个游戏,游戏来到了教学关卡,迷宫是一个N*M的矩阵。 小明的起点在地图中用“S”来表示,终点用“E”来表示,障碍物用“#”来表示,空地用“.”来表示。 障碍物不能通…...
全面解析鸿蒙(HarmonyOS)开发:从入门到实战,构建万物互联新时代
文章目录 引言 一、鸿蒙操作系统概述二、鸿蒙开发环境搭建三、鸿蒙核心开发技术1. **ArkUI框架**2. **分布式能力开发**3. **原子化服务与元服务** 四、实战案例:构建分布式音乐播放器五、鸿蒙开发工具与调试技巧六、鸿蒙生态与未来展望结语 引言 随着万物互联时代…...
使用 mkcert 本地部署启动了 TLS/SSL 加密通讯的 MongoDB 副本集和分片集群
MongoDB 是支持客户端与 MongoDB 服务器之间启用 TLS/SSL 进行加密通讯的, 对于 MongoDB 副本集和分片集群内部的通讯, 也可以开启 TLS/SSL 认证. 本文会使用 mkcert 创建 TLS/SSL 证书, 基于创建的证书, 介绍 MongoDB 副本集、分片集群中启动 TLS/SSL 通讯的方法. 我们将会在…...
P3372 【模板】线段树 1【题解2】
本题题解分两篇 此篇为第贰篇,用树状数组做 第壹篇:P3372 【模板】线段树 1【题解1】 本文讲解树状数组解决区间修改区间查询 其它树状数组相关文章: 树状数组讲解单点修改/查询树状数组解决区间修改单点查询 P3372 【模板】线段树 1 题…...
使用 EDOT 监测由 OpenAI 提供支持的 Python、Node.js 和 Java 应用程序
作者:来自 Elastic Adrian Cole Elastic 很自豪地在我们的 Python、Node.js 和 Java EDOT SDK 中引入了 OpenAI 支持。它们为使用 OpenAI 兼容服务的应用程序添加日志、指标和跟踪,而无需任何代码更改。 介绍 去年,我们宣布了 OpenTelemetry…...
kotlin中expect和actual关键字修饰的函数作用
在 Kotlin 多平台编程中,expect 和 actual 关键字用于定义跨平台的抽象和具体实现。这种机制允许开发者声明一个平台无关的接口或函数签名(使用 expect),然后在每个目标平台上提供具体的实现(使用 actual)。…...
