Linux学习之路 -- 文件 -- 文件描述符
前面介绍了与文件相关的各种操作,其中的各个接口都离不开一个整数,那就是文件描述符,本文将介绍文件描述符的一些相关知识。
目录
<1>现象
<2>原理
文件fd的分配规则和利用规则实现重定向
<1>现象
我们可以先通过printf把文件描述符打印出来,我们可以多打开几个文件,看看文件描述符有什么特别,再解释原理
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>#define filename "file.txt"
int main()
{int fd1 = open("log1.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);int fd2 = open("log2.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);int fd3 = open("log3.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);int fd4 = open("log4.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);int fd5 = open("log5.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);printf("fd1:%d\n",fd1);printf("fd2:%d\n",fd2);printf("fd3:%d\n",fd3);printf("fd4:%d\n",fd4);printf("fd5:%d\n",fd5);return 0;
}
运行结果

可以看见,文件的描述符从3开始增加,在3之前的0 1 2 其实已经被用掉了。看到这些数字,其实我们可以联想到数组的下标(因为文件描述符不能小于0),那么我们就可以假设一个文件描述符就代表该文件在某一数组中的位置(至于这个数组是什么,后面详谈),那是谁用了0 1 2 呢?其实是标准输入、输出和错误三个文件。
<2>原理
前面介绍过文件的几种系统调用的接口,我们不难发现,这些接口其实和C语言库中的文件操作函数非常相似,实际上C语言的文件操作函数就是封装系统调用接口。既然C语言底层是系统调用接口,那我们在使用C语言的文件函数,这些系统调用接口所需要文件描述符在哪里呢? 当然是在FILE 这个结构体当中,这个结构体是C标准库自己封装的一个结构体。
既然FILE这个结构体中含有文件描述符,那我们就可以通FILE来查看标准输入、输出和错误的文件的文件描述符是多少。下面演示一下代码
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>#define filename "file.txt"
int main()
{printf("%d\n",stdin->_fileno); // _fileno是FILE结构体中文件描述符的成员名printf("%d\n",stdout->_fileno);printf("%d\n",stderr->_fileno);
}
运行结果

这就印证了0 1 2 这三个文件描述符是一开始就被标准输入、输出和错误占用了。
文件描述符的具体含义
前面说过,打开文件的进程,所以研究文件的各种操作,就是研究进程与磁盘数据的关系。而我们在日常运行进程时,我们可能要打开很多的文件,对于这些文件,系统自然是需要管理的。而在linux中,OS就通过创建了一个file的结构体(别的系统也有可能是其他的数据结构)来描述一个被打开的文件,file这个结构体中包含了很多内容,这里比较重要的是文件属性、方法集和缓冲区。而这些文件的file又可以通过指针链接起来。进程PCB中有一个结构体指针,该结构体指针指向的结构体是用于描述该进程打开的所有文件,它也称为文件描述表,其中就包含了每个文件的file指针。

所以当我们需要打开一个新文件时,先在磁盘中找到对应的位置,然后生成新的struct file,初始化struct file中的数据,并将其指针填入struct file* fd_array[] 中,最后再将对应fd_array的下标返回给上层,我们就可以使用返回的fd进行文件操作。而我们如果要关闭文件时,就释放对应的struct file。
题外话
在linux中我们把所有的东西都看成文件,底层的硬件设施也可以看成文件。对于这些硬件肯定有读方法和写方法,这些硬件的读写方式肯定是不一样的。但我们可以用一个struct file来描述这些读写方法,并将这些不同的读写方法重名成相同的函数名,这样我们在调用硬件的一些函数时,就可以忽略底层的差异,直接使用上层的接口
文件fd的分配规则和利用规则实现重定向
直接先说规则:最小的没有被使用的数组下标,会分配给最新打开的文件。我们用一段代码演示一下上述的规则。

从运行结果上来看,我们关闭了文件描述符为0的文件,然后重新创建了一个log1.txt文件(该文件之前不存在)。这个新建文件的文件描述符为零,不是3,说明上述的分配规则是正确的。
输出重定向
我们先用一段代码实现一下输出重定向,再解释一下原理
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>#define filename "file.txt"
int main()
{close(1);int fd = open("log1.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);printf("fd:%d\n",fd);return 0;
}
运行结果

这里我们可以发现,运行代码时,显示器上并没有显示出对应的文件描述符。而log1.txt文件里面出现了对应的文件描述符。这就是我们所熟悉的输出重定向,因为printf这个函数底层只认fileno (文件描述符在struct file里面的成员名)。而这里我们把标准输出文件关闭了,给打开的log1.txt分配的文件描述符就是1,printf只会朝文件描述符为1的文件里面打印。所以才会出现上述现象。以此类推,我们也可以写出输入重定向,下面演示一下
先创建一个long.txt文件,向里面写入一些内容(这里为了方便演示,我只输入了一段数字),然后关闭stdin(也就是标准输入),然后系统给long.txt分配文件描述符为0,使用scanf读取数据,而scanf只认文件描述符为0的文件,所以scanf会从long.txt中读取数据。
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>#define filename "file.txt"
int main()
{close(0);int fd = open("long.txt",O_RDONLY);int a = 0;scanf("%d",&a);printf("%d\n",a);return 0;
}
运行结果

同理,如果我们需要实现追加重定向,我们只需要在原来输出重定向的基础上,把打开文件的方式中的O_TRUNC修改成O_APPEND即可,其余的均不变。、
dup2函数
像上述实现重定向的方式颇为繁琐,有没有更好的方式呢?当然是有的。这里文件描述符代表的数组里面存的是struct file的指针,如果我们要实现重定向,只要将数组对应下标的内容进行交换即可。而这里由于stdout可以不被使用,所以我们可以把新打开文件对应的指针,直接拷到stdout对应的数组下标中即可。

这里有专门的系统调用来帮我们实现这一功能,这个接口就是dup2(另外两个暂时不学)。
![]()
这个函数会把对应数组下标的内容拷贝到另外一个数组下标的对应内容上,而在这里就是把oldfd对应的数组下标内容拷贝到newfd对应数组下标的内容里面,所以最后只剩下oldfd对应数组下标内容。
如果要实现重定向操作,我们就不需要再关闭文件描述符,只需要使用dup2这个接口即可。
以上就是全部内容,如果文中有不对之处,还望各位大佬指正,谢谢!!!
相关文章:
Linux学习之路 -- 文件 -- 文件描述符
前面介绍了与文件相关的各种操作,其中的各个接口都离不开一个整数,那就是文件描述符,本文将介绍文件描述符的一些相关知识。 目录 <1>现象 <2>原理 文件fd的分配规则和利用规则实现重定向 <1>现象 我们可以先通过prin…...
JDK动态代理和Cglib动态代理区别
1.如果目标类实现了接口,将会使用JDK动态代理,否则会使用Cglib动态代理; 2.JDK代理使用自己的字节码生成工具生成代理对象,而Cglib会使用ASM字节码生成工具去生成; 3.JDK动态代理是通过反射的方式去实现代理对象的所有方法,通过…...
牛客 | 字符金字塔
请打印输出一个字符金字塔,字符金字塔的特征请参考样例 #include <stdio.h> #include <string.h> using namespace std; int main() {char c;scanf("%c", &c);for (int i 1; i < (c - 64); i)//第一个循环决定了有多少行{//c:67 第三…...
【计算机科学速成课】笔记三——操作系统
文章目录 18.操作系统问题引出——批处理设备驱动程序多任务处理虚拟内存内存保护Unix 18.操作系统 问题引出—— Computers in the 1940s and early 50s ran one program at a time. 1940,1950 年代的电脑,每次只能运行一个程序 A programmer would write one at…...
用js代码实现贪吃蛇小游戏
js已经学了大部分了,现在就利用我所学的js知识试试做贪吃蛇小游戏吧 以下部分相关图片以及思路笔记均出自渡一陈老师的视频 首先制作简单的静态页面,添加贪吃蛇移动的背景和相关图片,比如开始游戏等等 将各个功能均封装在函数中࿰…...
微信小程序+esp8266温湿度读取
本文主要使用微信小程序显示ESP8266读取的温湿度并通过微信小程序控制LED灯。小程序界面如下图所示 原理讲解 esp8266 通过mqtt发布消息,微信小程序通过mqtt 订阅消息,小程序订阅后,就可以实时收到esp8266 传输来的消息。 个人可免费注册五个微信小程序账号,在微信小程序官…...
软考中级-软件设计师(十)网络与信息安全基础知识
一、网络概述 1.1计算机网络的概念 计算机网络的发展:具有通信功能的单机系统->具有通信功能的多机系统->以共享资源为目的的计算机网络->以局域网及因特网为支撑环境的分布式计算机系统 计算机网络的功能:数据通信、资源共享、负载均衡、高…...
推荐一个好用的命令行工具ShellGPT
ShellGPT 配置安装常用功能聊天写命令并执行 高级功能函数调用角色管理 总结 这两天突然想到,现有的很多工具都在被大模型重构,比如诞生了像perplexity.ai 这种新交互形式的搜索引擎,就连wps也推出了AI服务,甚至都可以直接生成ppt…...
Prompt提示词教程 | 提示工程指南 | 提示词示例 入门篇
在上一节中,我们介绍并给出了如何赋能大语言模型的基本示例。如果还没看而且是刚入门的同学建议看下,有个基本概念。 Prompt提示词教程 | 提示工程指南 | 提示工程简介https://blog.csdn.net/HRG520JN/article/details/138523705在本节中,我…...
uniapp + uView动态表单校验
项目需求:动态循环表单,并实现动态表单校验 页面: <u--form label-position"top" :model"tmForm" ref"tmForm" label-width"0px" :rulesrules><div v-for"(element, index) in tmForm…...
【Linux】HTTPS
欢迎来到Cefler的博客😁 🕌博客主页:折纸花满衣 🏠个人专栏:Linux 目录 👉🏻HTTPS协议概念👉🏻加密为什么要进行加密 👉🏻常见的加密方式对称加密…...
语音识别--使用YAMNet识别环境音
⚠申明: 未经许可,禁止以任何形式转载,若要引用,请标注链接地址。 全文共计3077字,阅读大概需要3分钟 🌈更多学习内容, 欢迎👏关注👀【文末】我的个人微信公众号…...
前端JS必用工具【js-tool-big-box】,邮箱,手机,身份证号,ip地址等正则验证方法学习
这一小节,我们针对前端npm包 js-tool-big-box 的使用做一些讲解,主要是针对项目中,邮箱,手机号,身份证号,ip地址,url格式,邮政编码等验证的方法使用。 目录 1 安装和引入 2 邮箱验…...
notepad++安装 hex-editor插件
打开notepad 点击插件 搜索 hex-editor,点击右侧 安装install 安装成功后,在已安装插件中就有显示了...
Ubuntu18.04设置SSH密钥登录
我们一般使用 VSCode 、MobaXterm、PuTTY等 SSH 客户端来远程管理 Linux 服务器。但是,一般的密码方式登录,容易有密码被暴力破解的问题。所以,一般我们会将 SSH 的端口设置为默认的 22 以外的端口,或者禁用 root 账户登录。但是即…...
自动化运维管理工具----------Ansible模块详细解读
目录 一、自动化运维工具有哪些? 1.1Chef 1.2puppet 1.3Saltstack 二、Ansible介绍 2.1Ansible简介 2.2Ansible特点 2.3Ansible工作原理及流程 2.3.1内部流程 2.3.2外部流程 三、Ansible部署 3.1环境准备 3.2管理端安装 ansible 3.3Ansible相关文件 …...
零基础代码随想录【Day27】|| 39. 组合总和,40.组合总和II, 131.分割回文串
目录 DAY27 39. 组合总和 解题思路&代码 40.组合总和II 解题思路&代码 131.分割回文串 解题思路&代码 DAY27 39. 组合总和 力扣题目链接(opens new window) 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有…...
实验15 MVC
二、实验项目内容(实验题目) 编写代码,掌握MVC的用法。 三、源代码以及执行结果截图: inputMenu.jsp: <% page contentType"text/html" %> <% page pageEncoding "utf-8" %> &…...
《Python编程从入门到实践》day21
# 昨日知识点回顾 设置背景颜色 在屏幕中央绘制飞船 # 今日知识点学习 12.5 重构:方法_check_events()和_update_screen() 12.5.1 方法_check_events() import sys import pygame from Settings import Settings from Ship import Shipclass AlienInvasion:"…...
上位机图像处理和嵌入式模块部署(树莓派4b镜像烧录经验总结)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 陆陆续续也烧录了好多次树莓派的镜像了,这里面有的时候很快,有的时候很慢。特别是烧录慢的时候,也不知道是自己…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...
Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...
跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...
处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
