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

这篇具有很好参考价值的文章主要介绍了【Linux从入门到精通】文件描述符详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【Linux从入门到精通】文件描述符详解,Linux从入门到精通,linux,运维,文件描述符,I/O  

文章目录

一、引言 

二、引入文件描述符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的值。我们看输出结果:

【Linux从入门到精通】文件描述符详解,Linux从入门到精通,linux,运维,文件描述符,I/O

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

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

【Linux从入门到精通】文件描述符详解,Linux从入门到精通,linux,运维,文件描述符,I/O

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

2、2 fd保存的位置

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

【Linux从入门到精通】文件描述符详解,Linux从入门到精通,linux,运维,文件描述符,I/O

  上述调用fopen时,返回值为FILE* ,FILE是什么类型呢?FILE就是一个结构体!fd其实就是保存在了一个结构体,该结构体就是一个文件结构体FILE。该结构体中包含的文件的我们进场所说到的缓冲区和文件描述符fd

struct _IO_FILE {
	int _flags;       /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags

	//缓冲区相关
	/* The following pointers correspond to the C++ streambuf protocol. */
	/* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
	char* _IO_read_ptr;   /* Current read pointer */
	char* _IO_read_end;   /* End of get area. */
	char* _IO_read_base;  /* Start of putback+get area. */
	char* _IO_write_base; /* Start of put area. */
	char* _IO_write_ptr;  /* Current put pointer. */
	char* _IO_write_end;  /* End of put area. */
	char* _IO_buf_base;   /* Start of reserve area. */
	char* _IO_buf_end;    /* End of reserve area. */
	/* The following fields are used to support backing up and undo. */
	char *_IO_save_base; /* Pointer to start of non-current get area. */
	char *_IO_backup_base;  /* Pointer to first valid character of backup area */
	char *_IO_save_end; /* Pointer to end of non-current get area. */

	struct _IO_marker *_markers;

	struct _IO_FILE *_chain;

	int _fileno; //封装的文件描述符
#if 0
	int _blksize;
#else
	int _flags2;
#endif
	_IO_off_t _old_offset; /* This used to be _offset but it's too small.  */

#define __HAVE_COLUMN /* temporary */
	/* 1+column number of pbase(); 0 is unknown. */
	unsigned short _cur_column;
	signed char _vtable_offset;
	char _shortbuf[1];

	/*  char* _save_gptr;  char* _save_egptr; */

	_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

三、详解文件描述符fd

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

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

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

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

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

【Linux从入门到精通】文件描述符详解,Linux从入门到精通,linux,运维,文件描述符,I/O

  我们在对上述的组织过程进行从头到尾的详细阐述一下:首先进程调用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*吗。其中封装的 _fileno 就是我们所说的文件描述符fd。验证代码如下:

#include<stdio.h>    
#include<sys/types.h>    
#include<sys/stat.h>    
#include<fcntl.h>    
#include<unistd.h>    
    
int main()    
{    
    //_fileno就是文件描述符fd
    printf("%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;    
}

  输出结果如下:

【Linux从入门到精通】文件描述符详解,Linux从入门到精通,linux,运维,文件描述符,I/O

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值。结果如下:

【Linux从入门到精通】文件描述符详解,Linux从入门到精通,linux,运维,文件描述符,I/O

  确实,值为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;    
}

  输出结果如下: 

 【Linux从入门到精通】文件描述符详解,Linux从入门到精通,linux,运维,文件描述符,I/O

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

4、2 输入输出重定向

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

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

【Linux从入门到精通】文件描述符详解,Linux从入门到精通,linux,运维,文件描述符,I/O

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

#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;
}

   我们再来看输出结果:

【Linux从入门到精通】文件描述符详解,Linux从入门到精通,linux,运维,文件描述符,I/O

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

4、2、1 dup2的使用 

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

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

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

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

【Linux从入门到精通】文件描述符详解,Linux从入门到精通,linux,运维,文件描述符,I/O

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

【Linux从入门到精通】文件描述符详解,Linux从入门到精通,linux,运维,文件描述符,I/O

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

【Linux从入门到精通】文件描述符详解,Linux从入门到精通,linux,运维,文件描述符,I/O

  我们想要的效果是:把新打开的文件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从入门到精通,linux,运维,文件描述符,I/O

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

 五、Linux下一切皆文件

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

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

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

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

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

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

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

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

到了这里,关于【Linux从入门到精通】文件描述符详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 详解—[Linux 文件描述符]

    一、文件描述符的概念  文件描述符 是Linux系统中用于访问文件的一种机制,它是一个 非负整数 ,用于指代 被打开的文件 。 在Linux中,所有执行I/O操作的系统调用都是通过文件描述符完成的。 文件描述符是一个简单的非负整数,用来表明每一个被进程打开的文件。 在程序

    2024年02月04日
    浏览(57)
  • [Linux]文件描述符(万字详解)

    在学习文件描述符前,首先要了解一下Linux系统常用的文件系统接口。 open函数 open函数有两个接口,三个参数的接口是在两个参数的接口的基础上添加了控制创建文件的权限功能,更适合写文件时使用 pathname参数 – 要打开的文件所在的路径 flags参数 – 打开文件的方式 mode参

    2024年02月10日
    浏览(38)
  • Linux - fd文件描述符和文件详解

                                                      ​​​​​​​             ​​​​​​​                                                   感谢各位 点赞 收藏 评论 三连支持                                                 本文

    2024年02月08日
    浏览(47)
  • 【Linux】基础 IO(文件描述符)-- 详解

    1、 文件的宏观理解 文件在哪呢? 从广义上理解,键盘、显示器、网卡、声卡、显卡、磁盘等几乎所有的外设都可以称之为文件,因为 “Linux 下,一切皆文件”。 从狭义上的理解, 文件在 磁盘(硬件) 上放着 ,只有操作系统才能真正的去访问磁盘。磁盘是一种永久存储介

    2024年03月24日
    浏览(49)
  • 【Linux从入门到精通】Linux中的权限管理(实例+详解)

        本篇文章对Linux下的 普通用户 和 超级用户 进行了对比详解。同时有 添加和删除普通用户 的讲解。也详细的解释了 普通文件的权限、拥有者、所属组 设置方法,和对 目录的权限设置方法 。根据需求引出 粘滞位 ,对粘滞位进行详细的解释。解释 每个要点是都带有实例

    2024年02月03日
    浏览(44)
  • [Linux]基础IO详解(系统文件I/O接口、文件描述符、理解重定向)

            hello,大家好,这里是bang___bang_ ,今天和大家谈谈Linux中的基础IO,包含内容有对应的系统文件I/O接口,文件描述符,理解重定向。    目录 1️⃣初识文件 2️⃣ 系统文件I/O接口 🍙open 🍙write 🍙read 🍙close 3️⃣文件描述符 🍙012 🍙内核中文件描述符的探究 🍙分配

    2024年02月12日
    浏览(37)
  • Linux 从入门到精通:curl 命令使用详解

    curl 命令是一个在 Linux 系统中利用 URL 工作的命令行文件传输工具,常用于服务访问和文件下载。curl 支持 HTTP、HTTPS、FTP 等多种协议(默认是 HTTP 协议),可用于模拟服务请求以及上传和下载文件。 1.1 安装命令 并非所有系统都自带 curl 命令,对于简洁版的 Linux 系统,可以使

    2024年02月07日
    浏览(44)
  • 【Linux从入门到精通】上下文概念详解

        上篇文章(进程的基本概念)我们讲解了进程后,还留下了一个上下文数据概念还没有解释。本篇文章会对上下文概念进行详解。在理解上下文概念时,同时会引出很多新的概念。我们都会对此进行详细解释,希望本篇文章会对你有所帮助。  文章目录  一、什么是上下

    2024年02月05日
    浏览(48)
  • 【Linux从入门到精通】线程详解(线程与进程区别)

        本篇文章主要 对线程的概念和线程的控制进行了讲解 。其中我们再次对进程概念理解。同时 对比了进程和线程的区别 。希望本篇文章会对你有所帮助。  文章目录 一、线程概念 1、1 什么是线程 1、2 再次理解进程概念 1、3 轻量级进程 二、进程控制 2、1 创建线程 pt

    2024年02月07日
    浏览(39)
  • 『Linux从入门到精通』第 ⑪ 期 - Linux调试器——gdb使用详解

    🌸作者简介: 花想云 ,在读本科生一枚,致力于 C/C++、Linux 学习。 🌸 专栏简介:本文收录于 C语言初阶专栏 ,本专栏主要内容为本专栏主要内容为Linux的系统性学习,专为小白打造的文章专栏。 🌸 相关专栏推荐: C语言初阶系列 、 C语言进阶系列 、 C++系列 、 数据结构

    2024年02月02日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包