【Linux从入门到精通】文件描述符详解

  

文章目录

一、引言 

二、引入文件描述符fd

2、1 观察fd的值

2、2 fd保存的位置

三、详解文件描述符fd

3、1 为什么要有文件描述符呢

3、2 到底什么是文件操作符呢

四、文件描述符的使用

4、1 验证文件描述符

4、1、1 验证stdin、stdout、stdout

4、1、2 验证fd值的大小顺序

4、2 输入输出重定向

4、2、1 dup2的使用 

 五、Linux下一切皆文件


🙋‍♂️ 作者:@Ggggggtm 🙋‍♂️

👀 专栏:Linux从入门到精通  👀

💥 标题:文件描述符💥

 ❣️ 寄语:与其忙着诉苦,不如低头赶路,奋路前行,终将遇到一番好风景 ❣️ 

一、引言 

  在Linux操作系统中,文件描述符是一种用于访问文件或输入/输出资源的抽象概念,它是为了更有效地管理和操作文件、设备、套接字等资源而引入的。文件描述符的作用和重要性在操作系统和编程中具有深远意义。

  通过 文件操作(C语言vs系统调用)上篇文章 文件操作(C语言vs系统调用) 对系统调用 open的讲解后,我们知道文件描述符是一个整数。当时它代表的含义是什么呢?又有什么意义呢?本篇文章会对文件描述符进行详细解释。

二、引入文件描述符fd

2、1 观察fd的值

  我们学习系统调用后,知道open调用完之后,会返回一个整型的值,该值就是文件描述符。那我们不妨打印出来观察一下fd值的大小和规律。代码如下:

#include<stdio.h>    
#include<sys/types.h>    
#include<sys/stat.h>    
#include<fcntl.h>    
#include<unistd.h>    int main()    
{    int fd1 = open("log1.txt", O_WRONLY|O_CREAT|O_APPEND, 0666); //rw-rw-rw-    printf("open success, fd: %d\n", fd1);    int fd2 = open("log2.txt", O_WRONLY|O_CREAT|O_APPEND, 0666); //rw-rw-rw-    printf("open success, fd: %d\n", fd2);    int fd3 = open("log3.txt", O_WRONLY|O_CREAT|O_APPEND, 0666); //rw-rw-rw-    printf("open success, fd: %d\n", fd3);    int fd4 = open("log4.txt", O_WRONLY|O_CREAT|O_APPEND, 0666); //rw-rw-rw-    printf("open success, fd: %d\n", fd4);    close(fd1);    close(fd2);    close(fd3);    close(fd4);                                                                                                                                              return 0;    
} 

  上述代码就是打开了四个文件,然后打印他们的文件描述符fd的值。我们看输出结果:

  我们惊奇的发现,文件描述符的值好像是有规律的!从上述打印的结果观察出,fd的值是从3开始,依次往上加一递增的。那么问题来了:为什么从3开始呢?有0、1、2吗?

  我们在学习C语言时,可能会听说过:当我们运行程序时,系统会默认帮我们打开标准输入、标准输出、标准错误。那标准输入、标准输出、标准错误到底是什么呢?

  首先我们要知道Linux下,一切皆文件。难道显示器和键盘也是文件吗?答案是是的!!(后面我们也会对Linux下一切皆文件进行讲解)没错,当程序运行起来时,系统会默认帮我们打开三个标准文件的!而这三个文件分别对应的fd值就是0、1、2

2、2 fd保存的位置

  我们知道当程序运行起来时,系统会默认帮我们打开三个标准文件后,那么这三个文件描述符fd的只会被保存起来的。如果不保存起来,怎么向标准输出,也就是显示器打印值呢?或者又怎么从标准输入,也就是键盘读取值呢?所以他们的fd值一定会被保存起来的。那么问题来了,这个值保存在了哪里了呢?

  上述调用fopen时,返回值为FILE* ,FILE是什么类型呢?FILE就是一个结构体!fd其实就是保存在了一个结构体,该结构体就是一个文件结构体FILE(struct file)。过海结构体中包含的文件的大部分信息。后面我们也会给出具体的详细解释。

三、详解文件描述符fd

3、1 为什么要有文件描述符呢

  到这里会一直有疑问:到底什么是文件描述符呢(就是一个整数吗)?为什么要有文件描述符呢?接下来会详细解释。

  首先,一个进程可能会打开很对文件。上面的代码中就打开了四个文件,如果想的话甚至可能会更多。内存中不仅仅只有一个进程吧!那么就很有可能在系统中打开了很多文件。那么操作系统要不要对这些文件进行管理呢?必须要进行管理!!!怎么管理呢?先描述,后组织!!!

  怎么描述呢?我们都知道,Linux操作系统使用C语言写的。那么问题就转换成了C语言用什么来描述一个对象呢?不就是结构体吗!关键问题来了:怎么进行组织呢?这就与文件描述符有关系了。接着往下看。

  当一个程序运行起来加载到内存后,创建对应的数据结构,就会变成一个进程。其中关键是进程控制块PCB。在Linux内核中,PCB为task_struct。其中,task_struct中就包含了一个文件结构体指针files_struct* fs。改指针就是指向的文件结构体。重点是该文件结构体中有一个指针数组file* fd_arry[](文件映射表),该指针数组指向的就是我们所打开的文件。我们所新打开的文件fd的值是文件描述表中最小的为空的位置具体可结合下图理解:

  我们在对上述的组织过程进行从头到尾的详细阐述一下:首先进程调用fwrite -> 找到所传入的FILE*  -> 找到FILE中的fd -> 调用内部封装的write(系统调用) -> 找到进程的task_struct -> 找files_struct* fs -> 找到 files_struct -> 扎到files_struct中的file* fd_array[] -> fd_array[fd] -> 该位置的值是一个指针,该指针指向的就是file -> 找到该文件后,再进行操作。

  相信到这里,你就会对为什么要引入文件操作符fd就会清楚了。一个很重要的原因就是便于管理文件和操作

3、2 到底什么是文件操作符呢

  刚开始我们只知道文件操作符是一个整数,其具体到表的含义并不知道。通过对上述的了解后,我们也就知道了文件描述符是一个下标,是用来标识和操作文件或者输入输出设备的整数。每个打开的文件(包括标准输入、标准输出和标准错误输出)都会被分配一个唯一的文件描述符文件描述符的使用主要有以下几个方面:

  1. 文件操作:通过文件描述符,我们可以对文件进行读取、写入、定位等操作。例如,可以使用文件描述符来打开、关闭、读取、写入文件。
  2. 输入输出重定向:文件描述符可以用于在程序运行时动态地将输入输出重定向到其他文件。例如,可以将程序的输出重定向到文件中,或者将文件作为程序的输入。
  3. 管道通信:文件描述符可以用于实现进程间的通信,其中最常见的方式是使用管道。通过创建管道,并使用文件描述符将数据从一个进程传递给另一个进程。

  接下来下面我们会对上述的三个使用进行讲解的。

四、文件描述符的使用

4、1 验证文件描述符

4、1、1 验证stdin、stdout、stdout

  首先我们要验证三个标准:stdin、stdout、stdout中是否包含文件描述符fd。首先stdin、stdout、stdout是数据类型什么类型的呢?结合我们所学的文件操作函数,数据类型不就是FILE*吗。验证代码如下:

#include<stdio.h>    
#include<sys/types.h>    
#include<sys/stat.h>    
#include<fcntl.h>    
#include<unistd.h>    int main()    
{    //_fileno就是文件描述符fdprintf("%d\n%d\n%d\n",stdin->_fileno,stdout->_fileno,stderr->_fileno);    int fd1 = open("log1.txt", O_WRONLY|O_CREAT|O_APPEND, 0666); //rw-rw-rw-    printf("open success, fd: %d\n", fd1);     close(fd1);    return 0;    
}

  输出结果如下:

4、1、2 验证fd值的大小顺序

  我们上述提到:新打开的文件fd的值是文件描述表中最小的为空的位置。怎么证明呢?不要忘记了可以通过close结合fd,进行关闭系统默认打开的标准文件。验证代码如下:

  #include<stdio.h>    #include<sys/types.h>    #include<sys/stat.h>    #include<fcntl.h>    #include<unistd.h>    int main()    {    close(0)    int fd1 = open("log1.txt", O_WRONLY|O_CREAT|O_APPEND, 0666); //rw-rw-rw-    printf("open success, fd: %d\n", fd1);                                                                                                                 close(fd1);                                                                                                                                           return 0;                                                                            }  

  上述代码就是把fd值为0(标准输入)文件关闭,我们再新创建文件,在观察它的fd值。结果如下:

  确实,值为0了。我们在通过下述代码再次验证:

#include<stdio.h>    
#include<sys/types.h>    
#include<sys/stat.h>    
#include<fcntl.h>    
#include<unistd.h>    int main()    
{    close(0);                                                                                                                                                int fd1 = open("log1.txt", O_WRONLY|O_CREAT|O_APPEND, 0666); //rw-rw-rw-    printf("open success, fd: %d\n", fd1);    int fd2 = open("log2.txt", O_WRONLY|O_CREAT|O_APPEND, 0666); //rw-rw-rw-    printf("open success, fd: %d\n", fd2);    close(fd1);    close(fd2);     return 0;    
}

  输出结果如下: 

 

  确实,我们只关闭了fd值为0(标准输入)文件关闭,同时打开两个文件。其中一个fd的值为0,另一个为3。我们所述的是正确的。

4、2 输入输出重定向

 我们平常的输入是从标准输入(键盘)读取,输出往标准输出(显示器)打印!输入和输出重定向就是不再从标准输入读取,向标准输出打印了,就是从指定的文件读取,或只输入到指定文件。什么原理呢?

  首先,我们知道默认是输出到标准输出(显示器)上。我们能不能关闭标准输出,然后打开一个我们想输出到指定的文件,这是新打开的文件fd的值不就是我们刚刚关闭的标准输出文件的fd的值嘛!!! 根据我们上述详解文件描述知道,操作系统內部只认识文件标识符fd,并不认识所谓的stdin、stdout。此时新打开的文件不就称为了标准输出吗!!!我们可结合下图理解:

  通过上述讲解,思路是有了。我们再看代码实现:

#include<stdio.h>    
#include<sys/types.h>    
#include<sys/stat.h>    
#include<fcntl.h>    
#include<unistd.h>    
#include<string.h>    int main()    
{    close(1);    int fd = open("log.txt", O_WRONLY | O_APPEND | O_CREAT);    if(fd<0)     {    perror("open");    return 1;    }    printf("fd: %d\n", fd); // stdout->FILE{fileno=1}->log.txt    printf("fd: %d\n", fd);    printf("fd: %d\n", fd);    printf("fd: %d\n", fd);    printf("fd: %d\n", fd);                                                                                                                                  printf("fd: %d\n", fd);    fprintf(stdout, "hello fprintf\n");    const char *s = "hello fwrite\n";    fwrite(s, strlen(s), 1, stdout);    printf("fd: %d\n", fd);fflush(stdout);close(fd);return 0;
}

   我们再来看输出结果:

  确实不在输出到屏幕上,而是我们制定的文件。这不就是输出重定向吗。 

4、2、1 dup2的使用 

  dup2函数的作用有两个方面:一是复制文件描述符,二是重新分配文件描述符。

  1. 复制文件描述符: dup2可以用来复制一个已打开的文件描述符,创建一个新的文件描述符,使其指向相同的文件、管道或套接字。这种方式可以实现重定向输入、输出或错误流的功能。
  2. 重新分配文件描述符: 使用dup2可以将一个文件描述符重新指定为指定的文件、管道或套接字。如果新的文件描述符已经被打开,则系统会自动关闭它。

  这里我们主要讲解复制文件描述符。

  在每次输入重定向前,都需要调用close关闭标准输出,未免会有点麻烦。有没有其他的方法呢?答案是有的。我们可以看一下dup系列的系统调用接口。如下图:

  这里我们就讲解dup2的使用。 具体如下图:

   一共是有两个参数:oldfd、newfd, newfd是拷贝oldfd得到的。

  我们想要的效果是:把新打开的文件fd值为3的地址拷贝到fd值为1的地址处。那么结合我们上面说到newfd是拷贝oldfd得到的。那么dup2的使用方法不就是dup2(3,1)。我们再看输出重定向的代码:

#include<sys/types>
#include<sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>int main() {int fd = open("file.txt", O_WRONLY | O_CREAT, 0644);if (fd < 0) {perror("open");return -1;}// 复制文件描述符int newfd = dup2(fd, stdout); // 将标准输出重定向到文件 dup2(3,1);if (newfd < 0) {perror("dup2");return -1;}printf("Hello, World!\n"); // 输出将被重定向到文件close(fd);return 0;
}

  运行结果如下:

  输入重定向的原理与输出重定向的原理相同,本篇文章就不再做过多解释。 

 五、Linux下一切皆文件

  在Linux中,"一切皆文件"(Everything is a file)是一个重要的概念,用于描述Linux操作系统中所有资源和设备都以文件的形式进行访问和处理。

  这个概念可以理解为,无论是硬盘上的文件、网卡、设备、进程等,都被抽象为文件的形式存在。在Linux系统中,通过文件系统(File System)来管理和访问这些资源。

具体来说,"一切皆文件"可以被解释为:

  1. 文件:在Linux中,普通的文件就是我们常见的文本文件、二进制文件等。它们被组织成一个层次结构的目录树,通过路径来定位和访问。

  2. 目录:目录也是一种文件,它包含了其他文件和目录的信息。通过目录,可以组织和管理文件的层次结构。

  3. 设备文件:Linux将硬件设备(如磁盘、网络接口等)和虚拟设备(例如打印机,输入设备)都看作是文件来处理。通过设备文件,可以读取和写入设备的数据。

  4. 进程:在Linux系统中,每个正在运行的进程都有与之关联的文件。通过读取和写入相应的文件,可以与进程进行通信和交互。

  通过将所有资源都抽象为文件,Linux提供了一套统一的接口,可以使用相同的命令和工具来访问和管理不同的资源。这种统一性使得Linux操作系统更加灵活和强大,同时也方便了开发者和系统管理员进行各种操作和配置。

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

OIer常用的表情包(更新中)

日常用的图片…虽然和csdn的并没有什么关系…但OIer的自嘲精神还是要有的…

c语言——完数的计算

完数即所有因子之和等于其本身值 列入&#xff0c;28124714&#xff0c;28所有的因子为1&#xff0c;2&#xff0c;4&#xff0c;7&#xff0c;14 而这五个因子之和恰好也是28. //完数的计算 /*完数即所有因子之和等于其本身值 列入&#xff0c;28124714&#xff0c;28所有的…

快速了解SpringBoot注解的使用

文章目录 容器功能--注解Spring 注入组件的注解Component、Controller、Service、Repository案例演示 Configuration应用实例传统方式应用实例使用SpringBoot 的Configuration 添加/注入组件 Configuration 注意事项和细节 Import应用实例 ConditionalConditional 介绍应用实例…

干部年龄大 计算机水平,各级别公务员“晋升年龄表”来了,超过这个年龄,以后基本上没戏...

原标题&#xff1a;各级别公务员“晋升年龄表”来了&#xff0c;超过这个年龄&#xff0c;以后基本上没戏 距离2021年公务员报名考试已经过去一段时间了&#xff0c;只能说今年的考公没有那么容易&#xff0c;因为据说今年共有150万人报名&#xff0c;比去年整整多了10.8万人&a…

excel不显示0_【208期】根据身份证号计算退休年龄时长高亮提醒,EXCEL做到了

导读 应亲们要求&#xff0c;以后会多上实例&#xff0c;今天给大家分享一期关于EXCEL根据身份证号以及退休年龄&#xff0c;动态自动计算距离到期年限&#xff0c;以及高亮提醒的实战案例。 实例要求&#xff1a; 根据身份证号以及退休年龄&#xff0c;用excel公式自动计算出距…

年龄大了学计算机,年龄大了就“不值钱”的专业,30岁开始走下坡路,学生要慎重选择...

文/香橙聊教育 目前为止&#xff0c;我国有12个学科门类&#xff0c;98个专业类&#xff0c;500多个专业。 这些专业有很多是有年龄限制的&#xff0c;也就是说&#xff0c;到了一定的年龄就开始走下坡路&#xff0c;就像初二是初中阶段的分水岭是一个道理。 这些专业的学生在工…

计算机专业退休有退休金,我参加工作42年,国家公务员退休,二级警督,退休工资为什么按2014年10月份的工资计算机退休费...

咨询我 帮助人数&#xff1a;3463719 退休年龄 根据1978年6月国务院颁发的《关于工人退休、退职的暂行办法》和《关于安置老弱病残干部的暂行办法》(国发[1978]104号)规定&#xff0c;下列几种情况可以办理退休&#xff1a; (1)男性干部、工人年满60周岁&#xff0c;女干部年满…

计算退休年龄js怎么写,自定义年龄计算退休时间js怎么实现

最近在研究js&#xff0c;看到一个工具比较新颖&#xff0c;在线计算退休时间、退休年龄的工具&#xff0c;如下&#xff1a;http://www.chinawe.net/tools/tuixiunianling/ 可以自定义时间和退休年龄&#xff0c;还能得出工作天数。 这个其实就是通过js计算出来的&#xff0c…

Mysql:如果知道一个用户的出身日期,如何统计他的退休年龄是那一年

业务需求 今天获得一个统计报表的业务&#xff1a; 在一个企业用户表中&#xff08;详细字段不写&#xff09;&#xff0c;现需要统计所有员工的退休日期形成一个定时任务&#xff0c;在定时任务中&#xff0c;如果员工接近还有60天&#xff0c;定时任务会短信发名单给人事部。…

计算机领域男的多大年龄退休,2022年后男性几岁退休 2022退休时间表

近年来&#xff0c;有关于延迟退休的新闻报道&#xff0c;一直在不少。有一种说法是&#xff0c;在2022年以后&#xff0c;延迟退休政策将会出台&#xff0c;这对于近几年来&#xff0c;将要退休的人群来说&#xff0c;无疑是非常关心的&#xff0c;那么2022年后男性几岁退休&a…

计算机专业多大退休,60%仍在使用Windows 7的计算机下个月正式退休

实际上&#xff0c;计算机的普及发生在最近十年中。Microsoft Windows 7于2009年10月22日发布&#xff0c;距今天已经整整十年了。因此&#xff0c;许多人首先接触了Windows 7中的计算机。今天&#xff0c;这个伴随着许多人的操作系统即将完成其使命&#xff0c;并将于2020年1月…

退休人员计算工具

一&#xff0c;背景介绍 平时工作中需要计算退休年龄&#xff0c;分为公务员&#xff0c;参公&#xff0c;事业三类。 以前都是手工计算的&#xff0c;因为人会不断更新流动&#xff0c;每次需要这些数据的时候&#xff0c;都需要重新计算一遍。 这样存在几个问题&#xff1a; …

通达OA SQL注入漏洞【CVE-2023-4165】

通达OA SQL注入漏洞【CVE-2023-4165】 一、产品简介二、漏洞概述三、影响范围四、复现环境POC小龙POC检测工具: 五、修复建议 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损…

百度地图标注公司那家好

目前传统的商业模式已经无法满足创业者的发展需求&#xff0c;想要有更好更快的发展可以通过网络来进行推广&#xff0c;腾讯地图标注这是一种全新的推广模式&#xff0c;现在每个人几乎都在用手机来进行。上网&#xff0c;在手机上下载各种各样的地图来寻找自己的目的地&#…

如何在UI自动化测试中加入REST API的操作

1、问题 当我们描述一个“好的自动化测试用例”时&#xff0c;经常出现标准是&#xff1a; 精确 自动化测试用例应该测试一件事&#xff0c;只有一件事。与测试用例无关的应用程序的某个部分中的错误不应导致测试用例失败。 独立 自动化测试用例不应该受测试套件中任何其他…

将QQ和微信的保存路径由C盘转移到其他盘

将QQ和微信的保存路径由C盘转移到其他盘 &#xff08;1&#xff09;QQ中的文件通过右键点击QQ头像——》选择系统设置——》选择文件管理——》选择更改目录 完成以后&#xff0c;需要重启一下QQ即可。 &#xff08;2&#xff09;右键点击微信——》选择设置——》选择文件…

pc版微信聊天记录备份迁移

1. 备份:定期备份文件夹 C:\Users\${username}\Documents\WeChat Files (${username}为微信安装的windows用户名) 2. 恢复&#xff1a; &#xff08;1&#xff09;. 新机器安装成功微信后先别启动 &#xff0c;将 备份的WeChat Files 文件夹复制到 C:\Users\${username}\Doc…

固态硬盘的合盘及数据转移

场景 1.在C盘空间不足时&#xff0c;我们会想到把其他的盘的数据分给C盘&#xff0c;这样就会涉及到数据转移的问题。 2.在硬盘分区合并的时候&#xff0c;有的两分区中间会有一个恢复分区&#xff0c;导致不能合并的情况。 解决 分盘 1.右键我的电脑&#xff0c;点击管理 …

6.2.0在线编辑:GrapeCity Documents for Word (GcWord) Crack

GrapeCity Word 文档 (GcWord) 支持 Office Math 函数以及转换为 MathML GcWord 现在支持在 Word 文档中创建和编辑 Office Math 内容。GcWord 中的 OMath 支持包括完整的 API&#xff0c;可处理科学、数学和通用 Word 文档中广泛使用的数学符号、公式和方程。以下是通过 OMa…

男宝宝起名的三大思路

男宝宝起名的三大思路 最近有个哥们正在为一件事情烦恼&#xff0c;开始的时候咱们都觉得挺奇怪的&#xff0c;这哥们现在可以说是家庭事业双丰收了&#xff0c;到底还有什么事儿能让他这么的烦恼呢?询问之下&#xff0c;这个哥们儿向我们吐了苦水&#xff0c;不为别的&#x…