【Linux操作系统】文件描述符fd

一文细说Linux虚拟文件系统原理 为了实现 一切对象皆是文件 这个目标,Linux 内核提供了一个中间层:虚拟文件系统(Virtual File System)。如果大家使用过面向对象编程语言(如C++/Java等)的话,应该对 接口 这个概念并不陌生。而虚拟文件系统类似于面向对象中的接口,定义了一套标准的接口。开发者只需要实现这套接口,即可以使用操作文件的接口来操作对象。如下图所示:上图中的蓝色部分就是虚拟文件系统所在位置。从上图可以看出,虚拟文件系统为上层应用提供了统一的接口。 阅读详情

       🔥🔥 欢迎来到小林的博客!!
      🛰️博客主页:✈️林 子
      🛰️博客专栏:✈️ Linux之路
      🛰️社区 :✈️ 进步学堂
      🛰️欢迎关注:👍点赞🙌收藏✍️留言

系统文件I/O函数介绍

我们C语言有fopen,fwrite,fread等接口来进行文件访问。其根本原因还是进行了一层系统调用,使用了系统提供的接口。所以我们可以直接使用系统的接口来进行文件的访问操作。

再此之前需要先介绍俩个函数。

open

在这里插入图片描述

int open(const char *pathname, int flags, mode_t mode);

其中 pathname,代表要写入的字符串,flags,就是要对文件进行的操作,mode_t mode 是一个八进制的权限值。如果文件打开失败会返回-1,打开成功则返回文件的fd值

write

在这里插入图片描述

ssize_t write(int fd, const void *buf, size_t count);

fd就是文件的fd值,buf就是要写入的字符串。count就是要写入的字符数量。而返回值是实际写入的数量。

所以我们可以写这样一份代码:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>


int main() {   int fd = open("logo",O_WRONLY | O_CREAT,0664);//O_WRONLY写,O_CREAT如果文件不存在就创建   if(fd < 0) //打开文件失败,open会返回-1   {
    perror("fail\n");
    return 1;   }
     int count = 5;   const char* str = "hello linux\n";   int len = strlen(str);   while(count--)
    write(fd,str,len); // 往fd的位置写文件


  close(fd);

  return 0; }

如何运行看看结果。

在这里插入图片描述

我们可以发现,再运行这个程序之后。我们就会文件里写入程序中指定的内容。

那么再为大家介绍一个系统接口。

read

在这里插入图片描述

使用方法和write一样,只不过write是写,read是读。

代码:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>


int main()
{
  int fd = open("logo",O_RDONLY);
  if(fd < 0) //打开文件失败,open会返回-1
  {
    perror("fail\n");
    return 1;
  }
  
  int count = 5;
  const char* str = "hello linux\n";
  int len = strlen(str);
  char buff[1024] = {0};
  while(read(fd,buff,len) > 0) //把文件的数据读到buff中
  {
    printf("%s",buff);//打印读取的数据
  }
  close(fd);

  return 0;
}

而close,就是关闭文件的意思。open打开文件,close关闭文件。


open函数返回值

在认识返回值之前,先来认识一下两个概念: 系统调用 和 库函数。

  1. 上面的 fopen fclose fread fwrite 都是C标准库当中的函数,我们称之为库函数(libc)。
  2. 而, open close read write lseek 都属于系统提供的接口,称之为系统调用接口

在这里插入图片描述

系统调用接口和库函数的关系,一目了然。

所以,可以认为,f系列的函数,都是对系统调用的封装,方便二次开发。


文件描述符fd

通过对open函数的学习,我们知道了文件描述符就是一个小整数

open打开文件时会返回这个文件描述符,那么我们来看看这个文件描述符是多少呢?

那我们就用下面这段代码来观察每个打开文件的文件描述符。

#include<stdio.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>

int main()
{
  int fd1 = open("./log.txt",O_WRONLY | O_CREAT);
  int fd2 = open("./log1.txt",O_WRONLY | O_CREAT);
  int fd3 = open("./log2.txt",O_WRONLY | O_CREAT);
  int fd4 = open("./log3.txt",O_WRONLY | O_CREAT);
  int fd5 = open("./log4.txt",O_WRONLY | O_CREAT);
  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;
}

编译后我们发现对应的fd(文件描述符)值是 3 4 5 6 7

在这里插入图片描述

我们可以看到是一个连续的整数数列。那么这下就有点好奇了,为什么文件描述符从3开始? 而不是从0 或者1 开始。

答案是,每个进程执行时,会默认打开三个文件。 这三个文件就是 stdin(标准输入) , stdout(标准输出) , stderr(标准错误) 。 而它们三个对应的fd值分别就是 0 , 1 , 2。所以后续打开的文件会从3开始。

所以我们还可以这样子输入输出:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
  const char* print = "print\n";
  char buff[1024]; 
  write(1,print,strlen(print)); //把 print写到显示器

  read(0,buff,1024); //把 键盘的字符串 读到buff里
  buff[strlen(buff)-1] = '\0';  //因为输入会录入回车。把回车换成\0
  printf("input:%s\n",buff);

  return 0;
}

运行结果:

在这里插入图片描述

为什么文件描述符从0开始?

现在知道,文件描述符就是从0开始的小整数。为什么从0 开始? 我们不妨思考一下,从0开始的,连续的整数。像什么? 是不是很像数组的下标? 没错,fd值对应的就是数组下标!!

在每个进程控制块(PCB) 中, 都有一个指针*files指针指向一张表files_struct 。这是因为当进程打开文件时,操作系统需要创建相应的数据结构来描述这些文件。 而在 files_stuct表中,有一个最重要的部分,那就是一个指针数组!这个数组中的每一个元素都是一个指向当前进程已经打开了的文件的指针!所以本质上,文件描述符就是这个数组的下标,只要拿着文件描述符,就可以找到对应的已打开的文件

在这里插入图片描述

fd的本质是内核中是进程和文件关联的数组的下标。

而我们一个进程可以打开多个文件。所以 **进程:文件 = 1 : n **。进程和文件是一对多的关系。

所以我们也可以直接往 1(标准输出)里面写 ,一样可以在显示器打印结果。

#include<stdio.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>

int main()
{
  char buff[] = "hello file\n";
  write(1,buff,sizeof(buff)-1);
  return 0;
}

我们编译运行一下。

在这里插入图片描述

那我们再来玩一个好玩的,我们把标准输入关掉。然后在打开文件,文件描述符还是3 吗?

#include<stdio.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>

int main()
{
  close(0);
  int fd1 = open("./log.txt",O_WRONLY | O_CREAT);
  printf("fd1 = %d\n",fd1);
  return 0;
}

我们会发现新打开的文件,它的fd值是0。 也就是我们把 标准输入关掉之后, 0 这个下标的位置空出来了,而这个时候打开文件那么就会往最前面的空位找。

在这里插入图片描述

结论:文件描述符的分配规则是从头开始,找下第一个空位分配。

重定向的实现原理

那再玩一个好玩的,把文件描述符1关掉呢?会发生什么?

#include<stdio.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>

int main()
{
  close(1);
  int fd1 = open("./log.txt",O_WRONLY | O_CREAT);
  printf("fd1 = %d\n",fd1);
  return 0;
}

我们编译运行

在这里插入图片描述

我们发现运行没有打印任何信息,但是! 我们在log.txt 发现了 fd1 = 1这个信息。这本是我们要打印在屏幕上的信息,为什么输出到了文件中呢?

这是因为printf函数不管你是标准输出,还是你自己打开的文件。它只负责往文件描述符为1的文件里写入。当我们关掉了标准输出,打开了新的文件log.txt时,printf函数还是理所当然的往fd为1的文件写入。然而此时文件描述符为1的文件早已不是我们的标准输出了,而是我们新打开的文件,所以就写入了我们新打开的文件。

在这里插入图片描述

而重定向的原理也是如此。

不同的文件,怎么输入输出到不同的设备?

我们都知道每个 file文件都是write和read接口。那么它怎么知道我要从键盘读,它怎么知道我要从键盘读取?

这就要用到多态的原理了。首先我们得有个虚函数表,存放的是对应的read/write函数的函数指针。这样文件就可以通过函数指针找到对应的外设进行该外设的读写操作。

在这里插入图片描述

文件描述符的验证

C语言中的FILE* 结构体一定包含了文件描述符, 为什么这么说呢? 因为任何语言,你想要使用外设设备。都必须要经过操作系统的同意!而你的标准输入输出会访问键盘和显示器, 所以也必须经过操作系统的同意! 因为操作系统不信任任何人,只相信自己,所以操作系统会提供一层系统调用接口。 通过调用系统调用接口,由操作系统去访问外设。 所以 C语言中的FILE* 文件结构体一定包含了文件描述符。怎么验证呢?我们可以访问结构体成员**_fileno** 来获取文件描述符,因为这个成员存取的就是文件描述符。

#include<stdio.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>

int main()
{
  FILE* f = fopen("./log.txt","w");
  printf("fd1 = %d\n",f->_fileno);
  return 0;
}

随后编译输出

在这里插入图片描述

输出的新打开文件的文件描述符是3。

文件描述符的继承

如果当父进程fork出了一个子进程之后,那么子进程会继承父进程的文件描述符吗?

答案是,一定会! 因为子进程是以父进程的模板创建的。子进程会单独复制一份file_struct 出来! 而不是和父进程共享。

所以,我们也可以解释一下,为什么每个进程执行的时候都会默认打开标准输入,标准输出,标准错误三个文件? 这一切都是因为这些进程继承了父进程的file_struct。而每个执行的进程的父进程都是bash,bash是命令行解析器。输入命令时,我们需要标准输入流,而标准输出用来返回输入命令的结果。标准错误用来返回报错。

一个进程可以打开多个文件,一个文件也可以被多个进程打开。在这里会用到一个引用计数的计数。文件每被一个进程打开,就会计数一次。被一个进程关闭,就会减少一次。直到为0时操作系统才会真正关闭这个文件。

【HCIE】FRR+BFD+OSPF与BGP联动 FRR+BFD+OSPF与BGP联动 阅读详情

相关推荐

海思MIPI屏调试实战:从参数计算到点亮屏幕的完整指南

本文提供了一份海思平台MIPI屏幕调试的完整实战指南。文章以Hi3516DV300调试800x1280分辨率屏幕为例,详细讲解了从参数计算、配置文件修改到最终点亮屏幕的全流程,并分享了常见问题排查与进阶优化技巧,旨在帮助嵌入式开发者高效解决MIPI屏调试难题。

weixin_29190573的博客 736

文件描述符fd

1

work_hard423的博客 2408

OpenCV 常用的API总结(迅速查询)

OpenCV基本操作 openCV安装 读取图片(IO操作) 绘制集合图形 图像加法以及混合操作 OpenCV图像处理 图像几何变换 形态学操作 图像平滑 直方图 边缘检测 模板匹配 傅里叶变换 轮廓检测 图像分割 图像特征提取 SIFT检测关键点 视频读写............

Peyzhang的博客 3936

linux中fd的几点理解——一切皆文件

fd 全称是 file descriptor,文件描述符,又称句柄。当我们在打开一个文件,创建一个 socket, 创建一个 epoll 时,返回值往往都用一个变量 int fd 来表示。

weixin_38184628的博客 5597

Plant Simulation新手入门:5步搞定生产线物流仿真(附模型库使用技巧)

本文为Plant Simulation新手提供了从零开始构建生产线物流仿真的五步入门指南。通过理念重塑、流程规划、对象连接、参数设置到数据分析的完整流程,帮助用户快速掌握这一强大仿真工具的核心逻辑。文章特别强调了模型库的高效使用技巧,助力工程师在虚拟环境中精准模拟和优化生产系统,提升规划效率。

efc123456的博客 335

别再手动巡检了!用Zabbix 5.0 + SNMPv2自动监控华为S系列交换机(附完整命令集)

本文详细介绍了如何利用Zabbix 5.0与SNMPv2实现华为S系列交换机的自动化监控,告别低效的手动巡检。通过配置SNMPv2c安全基线、核心功能启用及Zabbix Server端深度配置,构建7×24小时监控体系,实时预警接口状态、流量波动等关键指标,大幅提升运维效率与故障响应速度。

weixin_30940783的博客 629

【Linux学习】基础IO——系统调用 | 文件描述符fd | 重定向

介绍了基础IO中,文件操作系统调用的运用,文件描述符fd的本质,重定向的原理,包括输出重定向,输入重定向和追加重定向,以及对Linux下一切皆文件的认识。

weixin_63726869的博客 2463

在Ubuntu 20.04上,用EtherCAT Igh 1.6.2驱动高创BD3E伺服电机(附完整C代码)

本文详细介绍了在Ubuntu 20.04实时内核环境下,使用EtherCAT Igh 1.6.2主站驱动高创BD3E伺服电机的完整实现方案。从实时系统配置、伺服电机PDO映射到精确控制线程设计,提供了完整的C代码样例和实际项目中的调试技巧,帮助开发者实现工业自动化中的高精度运动控制。

weixin_30772105的博客 111

Linux文件描述符fd的理解

​ 最后总结几点规则把最小未使用的fd下标给用户使用。系统默认占用的关闭后也可占用​ 2.**close()是系统调用接口,fclose()是C库函数。​ 3.printf/cout 默认往标准输出1号文件描述符去打印的,cerr/perror同理,默认往标准错误2号文件描述符去打印的​ 4.重定向的原理是:更改文件描述符fd,从而更改打印方向等。​ 5.文件描述符fd本质是文件结构体数组下标。​ 6.任何文件的前三号文件描述符0、1、2都是先默认被系统占用的。

专业而不失有趣的C++编程老师一枚~ 1693

LangChain实战:Plan and Execute代理在智能预算规划中的应用

本文详细介绍了如何利用LangChain框架中的Plan and Execute代理实现智能预算规划。通过拆解复杂财务问题、调用多工具链协作,该代理能自动完成从个人月度预算到家庭年度综合预算的智能规划,并具备消费习惯学习和多目标优化等进阶功能,帮助用户实现精准财务管理和决策优化。

weixin_42530732的博客 382

【2026最新】3分钟Windows极速部署OpenClaw,完整可复制命令+全链路避坑指南

很多新手在Windows环境部署OpenClaw时,总会遇到各种玄学问题:WSL环境配置失败、Docker镜像拉取超时、容器启动后无法访问、权限报错、数据丢失……网上大量教程要么是Linux环境的,要么是早已过时的旧版本配置,照着操作不仅没法3分钟部署,反而要花几小时踩坑填坑。

2601_94871597的博客 494

Linux操作系统——文件描述符fd(二)

烂笔头不如好记性

weixin_53811859的博客 275

YOLOv11改进策略【SPPF】| AIFI : 基于Transformer的尺度内特征交互,在降低计算成本的同时提高模型的性能

本文记录的是基于AIFI模块的YOLOv11目标检测改进方法研究。AIFI是RT-DETR中高效混合编码器的一部分,利用其改进YOLOv11模型,

Limiiiing的博客 2240

【 Redis底层机制解读:linux操作系统文件描述符FD 】

Redis底层机制解读:linux操作系统文件描述符FD

u012632105的博客 296

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Linux操作系统3-文件与IO操作2(文件描述符fd与文件重定向)

C++文件描述符fd,文件的重定向,理解Linux下一切皆文件

yzc的博客 904

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

【Linux操作系统】进程与内存文件的关系及文件描述符fd、重定向的理解

我们如果想访问文件,第一步都是要打开它,想要修改文件,都要通过执行代码以进程的方式完成修改3.我们的CPU只能访问内存,因此文件必须被加载到内存中才能访问4.一个进程可以打开多个文件,在一定时间内,系统会存在多个进程,但可能同时会存在更多被打开的文件,我们的系统是如何对这些文件进行管理的呢?5.系统中是不是所有的文件都被进程打开了(内存文件)?并不是,没有被打开的文件在磁盘中(磁盘文件)

liuty0125的博客 1295

Linux操作系统~系统文件IO,什么是文件描述符fd?什么是vfs虚拟文件系统

我们的输入输出最终都是访问硬件,OS是操作系统的管理者我们使用的都是语言层面上的接口,所以的“语言”上的操作,都必须贯穿OS。然而操作系统不相信任何人,所以访问操作系统都是需要通过系统调用的接口的。因此几乎所有的语言fopen,fclose,fread,fwrite,fgets,fputs,fgetc,fputc等底层一定需要使用OS提供的系统调用上面的 fopen fclose fread fwrite 都是C标准库当中的函数,我们称之为库函数(libc)。而,

Zebra的博客 2086

文件描述符FD

File descriptor 简称fd,当应用程序打开/新建一个文件时,内核会返回给应用程序一个文件描述符对应这个打开/创建的文件,fd本质是一个非负整数,是一个索引值,对应于该应用程序所维护该进程所打开的文件记录表中元素的索引值。理论来说内存越大,可打开的文件描述符数量就越多,但是实际情况是内核会有系统级限制,以及用户级限制,不让某一个应用程序进程消耗掉所有的文件资源,可以使用。这里就引入了文件描述符,通过文件描述符我们能够定位到具体的文件,也能够基于文件描述符相关的元信息获得文件的相关信息。

张豫湘的博客 810

【Linux】关于socket的fd

socket的fd是什么? fd 是(file descriptor),这种一般是BSD Socket的用法,用在Unix/Linux系统上。 fd全称是file descriptor,是进程独有的文件描述符表的索引。 在Unix/Linux系统下,一个socket句柄,可以看做是一个文件,在socket上收发数据,相当于对一个文件进行读写,所以一个socket句柄,通常也用表示文件句柄的...

michaelwoshi的博客 8962

linux下面的fd限制

如果不考虑内存大小的限制,在linux下面,fd (即file descriptor)的数量来自2个限制(阈值)。其一:是操作系统的限制。这个限制主要是在linux内核中,我们知道,用户程序的fopen操作最后都通过system call进入到linux kenrel。linux kernel会对此进行检查,防止某个用户占用太多的系统资源。现在的内核都可...

weixin_34232363的博客 842

什么是句柄和文件描述符

什么是句柄 句柄就是一个标识符,只要获得对象的句柄,我们就可以对对象进行任意的操作。 句柄不是指针,操作系统用句柄可以找到一块内存,这个句柄可能是标识符,map的key,也可能是指针,看操作系统怎么处理的了。fd算是在某种程度上替代句柄吧;Linux 有相应机制,但没有统一的句柄类型,各种类型的系统资源由各自的类型来标识,由各自的接口操作。 在操作系统层面上,文件操作也有类似于FILE...

sinat_41852207的博客 1023

Linux 句柄是什么 ?

1.句柄就是一个标识符,只要获得对象的句柄,我们就可以对对象进行任意的操作。 2.句柄不是指针,操作系统用句柄可以找到一块内存,这个句柄可能是标识符,map的key,也可能是指针,看操作系统怎么处理的了。 fd算是在某种程度上替代句柄吧; Linux 有相应机制,但没有统一的句柄类型,各种类型的系统资源由各自的类型来标识,由各自的接口操作。 3.ht

990

理解Linux的文件描述符FD与Inode

链接 理解Linux的文件描述符FD与Inode - 知乎

Nysanier的博客 522

什么是文件描述符(File Descriptor,FD)

如果你想深入了解文件描述符以及系统编程的相关知识,可以学习编程狮(W3Cschool)上的相关课程。总之,文件描述符是操作系统中管理打开文件和资源的重要机制,通过合理使用文件描述符,可以实现高效的文件操作和进程间通信。

爱编程的鱼的博客 1032
上一篇: 【Linux】环境变量
下一篇: 【C++】模板template
林   子
博客等级 码龄4年 2671粉丝 64原创
评论 44
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林 子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值