Linux文件描述符

Linux文件描述符

Linux 系统中,把一切都看做是文件,当进程打开现有文件或创建新文件时,内核向进程返回一个文件描述符(file descriptor,fd)[1, 4],在windows下面,这玩意儿叫file handle,句柄。

文件描述符(file descriptor)就是内核为了高效管理这些已经被打开的文件所创建的索引,其是一个非负整数(通常是小整数),用于指代被打开的文件,所有执行I/O操作的系统调用都通过文件描述符来实现。同时还规定系统刚刚启动的时候,0是标准输入,1是标准输出,2是标准错误。这意味着如果此时去打开一个新的文件,它的文件描述符会是3,再打开一个文件文件描述符就是4……[2]。

可以简单理解成系统维护的文件描述符表是一个数组,下标就是索引(文件描述符),数组内容就是一个个指向文件的指针(如0 -> stdin,1 -> stdout,2-> stderr)。

掌握它,有助于深入理解 Linux 文件系统、I/O 操作,
以及进程间通信(如管道(pipe)套接字(Socket))的实现,可以去Ubuntu系统中简单完成下面的例子。<( ̄︶ ̄)↗[GO!]

优化界面的Blog:Linux文件描述符

用户程序与内核交互的基本过程

打开文件

当一个用户程序需要访问某个文件时,它会通过系统调用(如 open())请求内核打开该文件。
内核会根据文件路径在文件系统中查找文件,并为该文件分配一个文件描述符。这个文件描述符是一个整数,表示该文件在内核中的唯一标识符。
内核维护着一个叫做 文件表(file table)的数据结构,文件描述符实际上就是对这个表中的一个条目的引用。

使用文件描述符读写文件

用户程序使用文件描述符来进行后续的文件操作。例如:
读取文件:用户程序调用 read(fd, ...) 系统调用,内核通过文件描述符 fd 查找对应的文件,并从磁盘中读取数据,将数据返回给用户程序。
写入文件:用户程序调用 write(fd, ...) 系统调用,内核通过文件描述符 fd 查找文件,向文件中写入数据。
文件描述符使得内核能够识别哪个文件需要被操作,从而实现文件与程序的交互。

文件描述符与文件表

内核通过文件描述符和文件表来管理已打开的文件。每个进程都有一个 文件描述符表,它是一个数组,其中每个索引对应一个文件描述符。这个文件描述符指向内核的文件表项,每个文件表项包含文件的状态信息(例如当前文件指针、文件权限等)。

文件操作的内核层处理

用户程序和内核之间的交互通常通过系统调用来实现,文件描述符是这些系统调用的接口。内核会根据文件描述符执行相应的操作:
打开文件时,内核会创建或查找该文件的内核对象,并更新文件描述符。
对文件进行读写操作时,内核通过文件描述符在文件表中查找文件对象,然后执行 I/O 操作(例如读取磁盘或写入磁盘)。

关闭文件

当用户程序完成文件操作后,它会通过系统调用 close(fd) 来关闭文件描述符。
内核会释放文件描述符所占用的资源,关闭文件的文件表项,并更新进程的文件描述符表。

通过文件描述符交互的具体例子

假设一个程序需要从文件中读取数据并进行处理,下面的示例代码:

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>int main() {int fd = open("hello.txt", O_RDONLY);if (fd == -1) {perror("open");return 1;}char buffer[128];ssize_t bytesRead = read(fd, buffer, sizeof(buffer));if (bytesRead == -1) {perror("read");close(fd);return 1;}write(STDOUT_FILENO, buffer, bytesRead);close(fd);return 0;
}

在这个例子中,文件描述符的作用可以从以下几个步骤看到:

  • 打开文件时,open() 系统调用将返回一个文件描述符(fd),这个文件描述符在内核中表示 example.txt 文件的句柄。
  • 读取文件时,read() 系统调用使用文件描述符 fd 来访问内核中的文件表项,执行 I/O 操作并将文件数据读取到 buffer 中。
  • 写入数据时,通过 write(STDOUT_FILENO, ...) 输出数据,STDOUT_FILENO 是标准输出的文件描述符(通常是 1)。
  • 关闭文件时,close(fd) 系统调用会释放文件描述符所占用的资源,告知内核文件已经关闭。

通过文件描述符,程序可以与操作系统内核进行有效的通信,完成文件系统和其他 I/O 操作。

Linux上查看文件描述符列表

在 Linux 上使用 vim 打开文件时,操作系统通过文件描述符与文件系统进行交互。下面是一个具体例子。

打开文件(使用 Vim)

首先,你使用 vim 打开一个文件(例如 helloworld.cpp):

vim helloworld.cpp

这时,Vim 会启动并打开 helloworld.cpp 文件。在内核中,Vim 会使用文件描述符来与文件系统交互,即读取和编辑 helloworld.cpp 文件的内容。


在新 Shell 中查找 Vim 进程的 PID

接着,你可以打开另一个终端(Shell),通过 pidof 命令获取正在运行的 Vim 进程的进程 ID(PID):

pidof vim

假设返回的 PID 是 40133,说明 Vim 进程的进程号是 40133


查看 Vim 进程的文件描述符

Linux 系统中的每个进程都有一个对应的 /proc/[pid]/fd 目录,里面列出了该进程打开的所有文件的文件描述符。你可以通过以下命令查看 Vim 进程所使用的文件描述符列表:

ll /proc/40133/fd

这里,40133 是你之前通过 pidof vim 命令获得的 Vim 进程的 PID。

ll 命令会列出该目录下的文件,其中每个文件都对应着一个打开的文件描述符(文件句柄)。输出会类似于:

total 0
dr-x------ 2 allen allen  0 Dec 29 15:58 ./
dr-xr-xr-x 9 allen allen  0 Dec 29 15:58 ../
lrwx------ 1 allen allen 64 Dec 29 15:58 0 -> /dev/pts/8
lrwx------ 1 allen allen 64 Dec 29 15:58 1 -> /dev/pts/8
l-wx------ 1 allen allen 64 Dec 29 15:58 19 -> /home/allen/.vscode-server/data/logs/20241229T150828/ptyhost.log
lrwx------ 1 allen allen 64 Dec 29 15:58 2 -> /dev/pts/8
l-wx------ 1 allen allen 64 Dec 29 15:58 20 -> /home/allen/.vscode-server/data/logs/20241229T150828/remoteagent.log
lrwx------ 1 allen allen 64 Dec 29 15:58 21 -> /dev/ptmx
lrwx------ 1 allen allen 64 Dec 29 15:58 22 -> /dev/ptmx
lrwx------ 1 allen allen 64 Dec 29 15:58 23 -> /dev/ptmx
lrwx------ 1 allen allen 64 Dec 29 15:58 4 -> /home/allen/CPP/.helloworld.cpp.swp

这里的输出解释如下:

  • 0,1,2:这是标准输入、标准输出和标准错误,它们通常会指向终端设备(如 /dev/pts/8)。这些文件描述符是系统默认打开的,用于处理进程的 I/O 操作。

  • 4:这个文件描述符指向你用 Vim 打开的文件 helloworld.cpp。可以看到,/home/allen/CPP/.helloworld.cpp.swp 是文件描述符 4 对应的目标文件。

说明:

  • 在 Linux 中,每个进程都会为打开的文件、管道、设备、套接字等分配一个文件描述符,文件描述符的值通常是从 0 开始递增的。
  • 标准输入(0)、标准输出(1)、标准错误(2)是系统自动打开的,而打开的文件 helloworld.cpp 在 Vim 进程中会被分配到文件描述符 3 及以后。

可以看到新打开的 helloworld.cpp 的文件描述符,竟然是4,而不是从3开始,这里面有一番学问,涉及 vim 的原理。因为vim这种编辑器的原理是先打开源文件并拷贝,然后关闭源文件再打开自己的副本,修改完文件保存的时候直接将副本重命名覆盖源文件。所以打开源文件的时候用的文件描述符3,然后打开自己的副本是时候就该用文件描述符4了,然后关闭源文件,文件描述符3就被释放了,我们查看的时候就只剩下了4,这里它指向的是vim创建的副本文件[3](这里有更详细的解释,这里是一个通俗的理解)。


  1. 检查文件描述符的具体信息

可以通过查看符号链接来了解更多细节,例如,可以用 readlink 命令查看文件描述符指向的文件路径:

readlink /proc/40133/fd/4

深入理解 Linux 中的文件描述符及其背后的数据结构

要深入理解 Linux 中的文件描述符及其背后的数据结构,我们需要了解内核如何通过三个核心数据结构来管理文件描述符:

  1. 进程级的文件描述符表(Process File Descriptor Table)
  2. 系统级的打开文件描述符表(System-wide Open File Table)
  3. 文件系统的 i-node 表(File System i-node Table)

这三个数据结构[4]共同工作,使得 Linux 系统能够高效地管理文件 I/O 操作,并确保每个进程对文件的访问是独立且有序的。


1. 进程级的文件描述符表

每个运行中的进程都有一个 进程控制块(PCB),它包含了与进程相关的各种信息。在这个 PCB 中,文件描述符表(也称为 文件描述符数组)是一个非常重要的数据结构。

  • 文件描述符表的功能:每个进程的文件描述符表记录着该进程所打开的文件描述符。文件描述符是一个整数,它对应着进程所打开的文件、套接字、管道等资源。

  • 表的结构:文件描述符表是一个数组,每个文件描述符对应数组中的一个位置。例如,标准输入、标准输出、标准错误默认分别对应文件描述符 0、1、2,而其他文件则由内核为每个进程分配一个较大的文件描述符,如 3、4、5 等。

  • 进程独立性:进程级文件描述符表是进程私有的。不同进程之间是独立的,进程 A 使用文件描述符 3 打开的文件,进程 B 如果也打开了一个文件,可能也会被分配文件描述符 3。它们的文件描述符对应的是不同的文件资源。

进程级文件描述符表的关键点

  • 每个进程都维护一个自己的文件描述符表。
  • 文件描述符表的每个条目对应一个打开的文件或资源。
  • 文件描述符表存储的只是文件描述符与内核内部文件对象的引用。

2. 系统级的打开文件描述符表

系统级的 打开文件描述符表 是内核维护的一个全局数据结构,用来管理系统中所有进程共享的文件资源。每当一个进程打开文件时,内核会在此表中创建一项记录,表示这个文件被打开。

该表中的每项记录包含以下信息

  • 当前文件偏移量:每个文件都有一个当前的读取或写入位置。当进程执行 read()write() 操作时,内核会根据该文件的偏移量进行相应的读写操作。在每次读取时,偏移量会自动更新,也可以通过 lseek() 系统调用显式地修改偏移量。

  • 打开文件时的标识:由 open() 系统调用的 flags 参数指定,如只读、只写、读写等。内核在打开文件时会记录这些标识,用于后续的访问控制。

  • 文件访问模式:当进程通过 open() 打开文件时,内核会记录文件的访问模式(如只读模式 O_RDONLY,只写模式 O_WRONLY,读写模式 O_RDWR)以及其他访问权限(如 O_APPENDO_NONBLOCK 等)。

  • 与信号驱动相关的设置:某些文件(如终端设备)可以通过信号驱动模式进行 I/O 操作。这些设置会记录在系统级的打开文件表中,以便内核在合适的时机处理信号。

  • 与文件的 i-node 关联:系统级的打开文件描述符表项会保存指向文件系统 i-node 表项的指针。i-node 表项包含了该文件的元数据(如文件大小、权限、时间戳等)。

关键点

  • 每个进程打开文件时,系统级打开文件表会创建相应的记录。
  • 所有进程共享系统级的打开文件描述符表,通过这个表来管理文件的偏移量和访问模式等信息。

3. 文件系统的 i-node 表

i-node索引节点(Index Node)的缩写,是文件系统用来存储文件元数据的一种数据结构。每个文件都有一个对应的 i-node,i-node 不存储文件的内容,而是存储与文件相关的各种属性和元数据。

i-node 表包含以下内容

  • 文件类型:例如普通文件、目录文件、符号链接、套接字、FIFO 等。

  • 文件权限:表示文件的访问权限(如读、写、执行权限)。

  • 文件大小:文件的实际大小(字节数)。

  • 时间戳:包括文件的创建时间、修改时间和访问时间(如 ctimemtimeatime 等)。

  • 指向文件数据块的指针:i-node 会存储指向文件实际数据块的指针(对于小文件直接存储指针,对于大文件使用间接块)。这些指针帮助操作系统在磁盘上定位文件内容。

  • 文件锁列表:如果文件被加锁,i-node 会存储一个指向锁信息的指针。

  • 引用计数:记录有多少个进程或文件描述符正在使用该文件。如果引用计数为 0,则表示该文件可以被删除。

i-node 的关键点

  • i-node 存储文件的元数据,而不存储文件的实际内容。
  • 每个文件在文件系统中都有唯一的 i-node。
  • 文件的内容由磁盘上的数据块来存储,而 i-node 中存储的是指向这些数据块的指针。

文件描述符如何协同工作

文件描述符表、系统级的打开文件描述符表和 i-node 表相互协作来管理文件资源:

  1. 进程级文件描述符表 存储进程所打开的文件描述符,它是进程私有的。当进程通过 open() 打开一个文件时,内核会在进程的文件描述符表中分配一个文件描述符,并且该文件描述符指向 系统级的打开文件描述符表 中的一个记录。

  2. 系统级的打开文件描述符表 存储所有打开文件的状态信息,如文件偏移量、访问模式等,并且每个表项都会指向对应文件的 i-node

  3. i-node 表 存储文件的元数据(如权限、大小等)以及文件内容所在的磁盘位置。每个打开的文件都会通过 i-node 来访问文件的实际数据。

当进程进行文件操作时(如 read()write()),操作首先通过进程级的文件描述符表查找对应的文件描述符,然后在系统级的打开文件描述符表中查找该文件的状态信息,并通过 i-node 访问文件的实际数据。


总结

  • 进程级的文件描述符表:每个进程独立维护,记录当前进程打开的文件描述符。
  • 系统级的打开文件描述符表:所有进程共享,记录文件的状态信息和 i-node 引用。
  • 文件系统的 i-node 表:记录文件的元数据和实际数据的位置信息。

这三个数据结构协作,使得 Linux 系统能够高效且灵活地管理文件 I/O 操作,确保进程之间的文件访问独立且有序,并且能够在多进程环境中正确地管理文件资源。

参考

[1、理解Linux的文件描述符FD与Inode]
[2、理解linux中的file descriptor(文件描述符)]
[3、彻底弄懂 Linux 下的文件描述符(fd)]
[4、Linux文件描述符到底是什么?]

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

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

相关文章

cloudns二级免费域名python更新ipv6 dns记录

没找到api&#xff0c;托管到cloudflare也不行。就只能写代码了&#xff08;只写了更新和添加单条ipv6记录&#xff09; 需要修改的地方 请求头的cookies填自己的 data里的zone填自己的 import requests from lxml import etree host#子域名 cookies填自己的 zone自己域名的 …

机器学习作业 | 泰坦尼克号生存的预测任务

泰坦尼克号生存的预测任务 学校作业&#xff0c;我来水一水 环境&#xff1a;pycharmanaconda虚拟环境 文章目录 泰坦尼克号生存的预测任务0.环境搭建参考&#xff1a;1 目的与要求2 任务背景3 任务简介4 模型介绍1.决策树&#xff08;Decision Tree&#xff09;2.朴素贝叶斯…

【pytorch】conda安装pytorch

Step 1 打开官网&#xff1a; https://pytorch.org/get-started/locally/ 进行选择对应版本&#xff1a; 复制图中命令执行。 Step 2 验证是否安装成功。 执行&#xff1a; import torch print(torch.cuda.is_available()) print(torch.cuda.device_count()) print(torch.…

项目开发实践——基于SpringBoot+Vue3实现的在线考试系统(四)

文章目录 一、管理员角色功能实现1、添加教师功能实现1.1 页面设计1.2 前端功能实现1.3 后端功能实现1.4 效果展示2、教师管理功能实现2.1 页面设计2.2 前端功能实现2.3 后端功能实现2.3.1 后端查询接口实现2.3.2 后端编辑接口实现2.3.3 后端删除接口实现2.4 效果展示二、代码下…

基于16QAM的载波同步和定时同步性能仿真,采用四倍采样,包括Costas环和gardner环

目录 1.算法仿真效果 2.算法涉及理论知识概要 3.MATLAB核心程序 4.完整算法代码文件获得 1.算法仿真效果 matlab2022a仿真结果如下&#xff08;完整代码运行后无水印&#xff09;&#xff1a; 仿真操作步骤可参考程序配套的操作视频。 2.算法涉及理论知识概要 载波同步是…

新服务器ubuntu系统相关操作

1、查看驱动:驱动版本535.216.01能够支持cuda12.2,下面直接使用默认安装的cuda。 2、赋予用户管理员权限。 首先有超级用户(root)权限来编辑 /etc/sudoers 文件,visudo 是一个命令,用于安全地编辑 /etc/sudoers 文件。运行: sudo visudo 在 visudo 编辑器中,找到类似…

2、Bert论文笔记

Bert论文 1、解决的问题2、预训练微调2.1预训练微调概念2.2深度双向2.3基于特征和微调&#xff08;预训练下游策略&#xff09; 3、模型架构4、输入/输出1.输入&#xff1a;2.输出&#xff1a;3.Learned Embeddings(学习嵌入)1. **Token Embedding**2. **Position Embedding**3…

python 渗透开发工具之SQLMapApi Server不同IP服务启动方式处理 解决方案SqlMapApiServer外网不能访问的情况

目录 说在前面 什么是 SQLMapAPI 说明 sqlmapApi能干什么 sqlmapApi 服务安装相关 kali-sqlmap存放位置 正常启动sqlmap-api server SqlMapApi-Server 解决外网不能访问情况 说在前面 什么是sqlmap 这个在前面已经说过了&#xff0c;如果这个不知道&#xff0c;就可以…

操作系统论文导读(八):Schedulability analysis of sporadic tasks with multiple criticality specifications——具有多个

Schedulability analysis of sporadic tasks with multiple criticality specifications——具有多个关键性规范的零星任务的可调度性分析 目录 一、论文核心思想 二、基本定义 2.1 关键性指标 2.2 任务及相关参数定义 2.3 几个基础定义 三、可调度性分析 3.1 调度算法分…

技术速递|调用异步功能 - WinForms 在 .NET 9 中的未来发展

作者&#xff1a; Klaus Loeffelmann 排版&#xff1a;Alan Wang 随着 .NET 的不断发展&#xff0c;WinForms 开发者可用的工具也在不断进步&#xff0c;这使得开发更加高效且应用响应更迅速。在 .NET 9 中&#xff0c;我们很高兴引入了一系列新的异步 API&#xff0c;这些 API…

Docker-构建自己的Web-Linux系统-镜像webtop:ubuntu-kde

介绍 安装自己的linux-server,可以作为学习使用&#xff0c;web方式访问&#xff0c;基于ubuntu构建开源项目 https://github.com/linuxserver/docker-webtop安装 docker run -d -p 1336:3000 -e PASSWORD123456 --name webtop lscr.io/linuxserver/webtop:ubuntu-kde登录 …

【每日学点鸿蒙知识】箭头函数、Watch状态变量、H5获取定位数据、前后台切换、长按事件

【每日学点鸿蒙知识】箭头函数、Watch状态变量、H5获取定位数据、前后台切换、长按事件 1、HarmonyOS confirm: () > void () > { }&#xff1f; confirm: () > void () > { }是什么格式。 是一个箭头函数&#xff0c;它的类型是 () > void&#xff0c;表示…

【人工智能机器学习基础篇】——深入详解监督学习之模型评估:掌握评估指标(准确率、精确率、召回率、F1分数等)和交叉验证技术

深入详解监督学习之模型评估 在监督学习中&#xff0c;模型评估是衡量模型性能的关键步骤。有效的模型评估不仅能帮助我们理解模型在训练数据上的表现&#xff0c;更重要的是评估其在未见数据上的泛化能力。本文将深入探讨监督学习中的模型评估方法&#xff0c;重点介绍评估指…

如何使用React,透传各类组件能力/属性?

在23年的时候&#xff0c;我主要使用的框架还是Vue&#xff0c;当时写了一篇“如何二次封装一个Vue3组件库&#xff1f;”的文章&#xff0c;里面涉及了一些如何使用Vue透传组件能力的方法。在我24年接触React之后&#xff0c;我发现这种扩展组件能力的方式有一个专门的术语&am…

点进CSS选择器

CSS 1.直接在标签的style属性进行设置(行内式) //写在数据单元格td标签内的stytle&#xff0c;设置color颜色和font-size字体大小&#xff1b; <td rowspan"3" style"color: red;font-size: 12px;">Web技术与应用</td> 2.写在head标签中的…

Python基于卷积神经网络的车牌识别系统开发与实现

1. 简介 车牌识别是人工智能在交通领域的重要应用&#xff0c;广泛用于高速违章检测、停车场管理和智能交通系统等场景。本系统通过基于卷积神经网络&#xff08;CNN&#xff09;的深度学习算法&#xff0c;结合 Python 和 MySQL 实现车牌的快速识别与管理。 系统特点&#x…

【PDF物流单据提取明细】批量PDF提取多个区域内容导出表格或用区域内容对文件改名,批量提取PDF物流单据单号及明细导出表格并改名的技术难点及小节

相关阅读及下载&#xff1a; PDF电子物流单据&#xff1a; 批量PDF提取多个区域局部内容重命名PDF或者将PDF多个局部内容导出表格&#xff0c;具体使用步骤教程和实际应用场景的说明演示https://mp.weixin.qq.com/s/uCvqHAzKglfr40YPO_SyNg?token720634989&langzh_CN扫描…

运行python程序报错 undefined symbol: ffi_type_uint32 的参考解决方法

文章目录 写在前面一、问题描述二、解决方法参考链接 写在前面 自己的测试环境&#xff1a; Ubuntu20.04 ROS-Noetic 一、问题描述 运行 python 程序出现如下问题&#xff1a; Traceback (most recent call last):File "<string>", line 1, in <module&…

鱼眼相机模型与去畸变实现

1.坐标系说明 鱼眼相机模型涉及到世界坐标系、相机坐标系、图像坐标系、像素坐标系之间的转换关系。对于分析鱼眼相机模型&#xff0c;假定世界坐标系下的坐标点,经过外参矩阵的变换转到相机坐标系&#xff0c;相机坐标再经过内参转换到像素坐标&#xff0c;具体如下 进一步进…

ASP.NET Core Web API Hangfire

ASP.NET Core Web API Hangfire 前言一、安装二、相关代码1.代码片段2.代码片段3.运行效果 三、测试代码1.即发即弃作业2.延迟作业3.重复作业4.延续作业5.页面调度作业 前言 &#x1f468;‍&#x1f4bb;&#x1f468;‍&#x1f33e;&#x1f4dd;记录学习成果&#xff0c;以…