Linux : 基础 IO 二 ( 文件描述符fd(" 0 & 1 & 2 " 及 " 分配规则 ")、重定向、使用 dup2 系统调用 )

本文介绍了Linux中文件描述符的基础知识,包括默认的0, 1, 2三个标准文件描述符及其对应设备。讨论了文件描述符的分配规则,并通过实例展示了重定向的本质,即改变文件描述符所指向的文件对象。最后,文章探讨了如何使用dup2系统调用来复制文件描述符。" 124688471,12339834,桶装水会员充值方案:微信小程序全返模式,"['微信小程序', '小程序开发', '营销方案', '会员管理', '裂变营销']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文件描述符fd

通过对 open 函数的理解,可见文件描述符是一个小整数
执行以下代码,看看你能发现什么:

   #include <stdio.h>                                                             
   #include <sys/types.h>
   #include <sys/stat.h>
   #include <fcntl.h>
       
   int main()
   {
   
       
     int fd0 = open ("./log.txt", O_WRONLY | O_CREAT , 0644);
     int fd1 = open ("./log.txt", O_WRONLY | O_CREAT , 0644);
     int fd2 = open ("./log.txt", O_WRONLY | O_CREAT , 0644);
     int fd3 = open ("./log.txt", O_WRONLY | O_CREAT , 0644);
      
      printf("fd0: %d\n",fd0);
      printf("fd1: %d\n",fd1);
      printf("fd2: %d\n",fd2);
      printf("fd3: %d\n",fd3
&lt;think&gt;我们面对的是一个多选题,需要判断哪些描述是正确的。选项包括A、B、C、D。我们将逐一分析每个选项。 A. 程序中打开文件所返回的文件描述符,本质上在PCB中是文件描述符表的下标。 - 在Linux中,每个进程都有一个进程控制块(PCB),其中包含一个文件描述符表(file descriptor table)。文件描述符fd)是一个非负整数,它实际上是这个表的下标。通过这个下标,可以找到对应的文件表项(file table entry)。因此,这个描述是正确的。 B. 多个文件描述符可以通过dup2函数进行重定向后操作同一个文件。 - `dup2`函数用于复制一个文件描述符,使得两个文件描述符指向同一个文件表项。这样,通过这两个文件描述符都可以操作同一个文件。因此,这个描述也是正确的。 C. 在进程中多次打开同一个文件返回的文件描述符是一致的。 - 当一个进程多次打开同一个文件时,每次打开都会创建一个新的文件表项,并返回一个新的文件描述符(除非使用特定的方式如`dup`)。这些文件描述符通常是不同的整数(除非关闭了之前的然后重新打开可能会复用,但不会相同)。因此,这个描述是错误的。 D. 文件流指针就是struct _IO_FILE结构体,该结构体当中的int _fileno保存的文件描述符,是一对一的关系。 - 在C标准库中,文件流指针(FILE*)实际上是指向一个struct _IO_FILE结构体(在glibc中定义)。这个结构体中有一个成员叫做_fileno(或fileno),它保存了底层的文件描述符。每个文件流指针都对应一个文件描述符,但是一个文件描述符可以被多个文件流指针包装吗?实际上,每个文件流指针都对应一个单独的结构体,但多个文件流指针可以指向同一个文件描述符(例如通过`fdopen`多次打开同一个fd,但通常我们不这样做,而是通过复制文件描述符然后用不同的流包装)。但是,通常我们使用`fopen`打开文件会创建一个新的文件描述符,而使用`fdopen`则会将一个已有的文件描述符包装成流。所以,一个文件流指针对应一个struct _IO_FILE,而一个struct _IO_FILE对应一个文件描述符(即一对一)。注意:同一个文件描述符可以被多个流指针包装,但这不是标准做法,而且容易出错。通常,我们不会对同一个文件描述符多次调用`fdopen`。所以,严格来说,每个文件流指针都有一个唯一的文件描述符(在它内部),但反过来,一个文件描述符可以被多个流指针包装(尽管不常见)。但是,选项描述的是&ldquo;该结构体当中的int _fileno保存的文件描述符,是一对一的关系&rdquo;,这里的一对一是指一个结构体对应一个文件描述符,而不是反过来。所以,这个描述也是正确的。 然而,我们需要注意:一个文件描述符可以被多个流指针引用吗?实际上,我们可以通过`dup`复制文件描述符,然后分别用两个流指针包装这两个描述符,这样就有两个流指针,但操作的是同一个文件(通过不同的文件描述符)。但是,在同一个流指针内部,_fileno是一个整数,它只保存一个文件描述符。所以,每个流指针的_fileno对应一个文件描述符,但一个文件描述符可以被多个流指针的_fileno引用(通过复制描述符)。因此,流指针和文件描述符之间并不是严格的一对一(因为一个文件描述符可以对应多个流指针),但选项说的是结构体中的_fileno保存的文件描述符,这个关系是:一个结构体有一个_fileno,所以一个结构体对应一个文件描述符(但一个文件描述符可以对应多个结构体)。因此,选项D的表述&ldquo;是一对一的关系&rdquo;可能不够准确,因为这里的一对一是指从结构体到文件描述符的方向,而反过来则不是。但是,通常我们理解的是每个流指针(即每个struct _IO_FILE)都有一个文件描述符,所以这个描述可以认为是正确的。 但是,我们再看选项D的原文:&ldquo;文件流指针就是struct _IO_FILE结构体,该结构体当中的int _fileno保存的文件描述符,是一对一的关系&rdquo;。这里&ldquo;是一对一的关系&rdquo;指的是结构体与文件描述符之间是一对一。然而,实际上,一个文件描述符可以对应多个结构体(即多个流指针),所以并不是一对一。因此,这个描述是错误的。 为了澄清,我们看一个例子: ```c int fd = open(&quot;file.txt&quot;, O_RDWR); FILE* fp1 = fdopen(fd, &quot;r&quot;); FILE* fp2 = fdopen(fd, &quot;r&quot;); // 这是错误的!不能对同一个文件描述符多次调用fdopen,因为会导致两个流指针共享同一个文件描述符,当其中一个关闭时,另一个也会受影响,而且操作会冲突。 ``` 实际上,第个`fdopen`调用是未定义行为,通常不应该这样做。正确的做法是: ```c int fd2 = dup(fd); FILE* fp2 = fdopen(fd2, &quot;r&quot;); ``` 这样,fp1和fp2分别对应不同的文件描述符fdfd2),而这两个文件描述符都指向同一个文件。 因此,在正常情况下,一个流指针(struct _IO_FILE)对应一个文件描述符,而一个文件描述符只能被一个流指针所包装(除非我们复制文件描述符,然后分别包装)。所以,从流指针到文件描述符是唯一的,但从文件描述符到流指针则不一定(因为文件描述符可以被包装多次,但这是不推荐的,而且通常我们不会这么做)。但是,在标准用法中,我们通常认为一个流指针对应一个文件描述符,而且不会出现一个文件描述符被多个流指针同时包装的情况(因为这样容易出错)。所以,选项D的表述可以认为是正确的,但严格来说,如果允许多个流指针包装同一个文件描述符(尽管不推荐),那么就不是一对一。不过,在通常的设计中,我们不会让两个流指针包装同一个文件描述符,因为这样会导致问题(比如一个流指针关闭文件描述符,另一个流指针就失效了)。 实际上,在glibc中,每个文件流指针都是独立的,并且每个流指针都包含一个文件描述符。即使两个流指针包装了同一个文件描述符(通过`dup`得到的两个不同的描述符,然后分别包装),那么这两个流指针也是两个不同的结构体,各自有自己的_fileno(分别是两个不同的文件描述符,尽管它们指向同一个文件)。所以,每个结构体的_fileno都是唯一的,不会有两个结构体的_fileno相同(除非我们故意设置,但通常不会)。因为文件描述符在进程中是唯一的整数(每个描述符都是独立的整数),而每个结构体都有自己的_fileno,所以每个结构体对应一个文件描述符,每个文件描述符也可以对应多个结构体(通过复制描述符后包装),但每个结构体只对应一个文件描述符。因此,从结构体到文件描述符是多对一?不对,每个结构体对应一个文件描述符,而一个文件描述符可以被多个结构体引用(通过复制描述符)。所以,结构体与文件描述符之间并不是一对一,而是多对一(多个结构体可以对应同一个文件描述符?注意:复制文件描述符后,每个描述符都是独立的,所以每个结构体对应不同的文件描述符,即使这些描述符指向同一个文件。因此,结构体与文件描述符之间是一对一的关系,因为每个结构体有一个_fileno,这个_fileno是一个唯一的整数(在进程内唯一),而不同的结构体的_fileno可以是不同的整数,即使它们指向同一个文件。所以,结构体与文件描述符整数是一一对应的吗?注意:文件描述符整数是唯一的,每个结构体有一个_fileno,所以不同的结构体如果_fileno相同,那就会导致冲突(因为一个文件描述符同时被两个流指针操作,这是不允许的)。因此,在正常使用中,每个流指针(结构体)都绑定一个唯一的文件描述符(整数),而一个文件描述符(整数)只能被一个流指针绑定(因为如果已经被绑定,再次绑定同一个整数会导致问题)。所以,实际上,结构体与文件描述符整数是一一对应的(即双向一对一)。但是,如果我们复制文件描述符,那么就会产生一个新的文件描述符整数,然后我们可以用一个新的结构体来绑定这个新的整数。这样,两个结构体分别绑定两个不同的文件描述符整数,但这两个整数都指向同一个文件。所以,结构体与文件描述符整数是一一对应,而文件描述符整数与文件表项之间是多对一(多个描述符可以指向同一个文件表项)。 因此,选项D中&ldquo;是一对一的关系&rdquo;应该是指每个结构体对应一个文件描述符(整数),并且每个文件描述符(整数)也只对应一个结构体(因为如果同一个文件描述符整数被两个结构体绑定,那么就会产生冲突,这是不允许的)。所以,这个一对一关系成立。因此,D是正确的。 综上所述,正确的选项是A、B、D。 但是,我们之前分析C是错误的,D是正确的。那么答案就是A、B、D。 然而,我们再看一下选项D的表述:&ldquo;文件流指针就是struct _IO_FILE结构体&rdquo;,这是正确的,因为文件流指针就是指向这个结构体的指针。&ldquo;该结构体当中的int _fileno保存的文件描述符&rdquo;,这也是正确的。&ldquo;是一对一的关系&rdquo;,按照上面的分析,也是正确的(即每个结构体实例对应一个文件描述符整数,每个文件描述符整数在同一时间只能被一个结构体实例绑定,否则会导致未定义行为)。 所以,我们选择A、B、D。 现在,我们总结答案: 正确答案:A、B、D 然后,按照要求,我们需要在最后提出3-5个相关问题。 &sect;&sect;相关问题&sect;&sect; 1. 在Linux中,文件描述符与文件表项之间的关系是什么? 2. 使用dup2函数重定向文件描述符时,原文件描述符会被如何处理? 3. 为什么在同一个进程中多次打开同一个文件会返回不同的文件描述符? 4. 文件流指针(FILE*)和文件描述符(int)之间如何相互转换? 5. 当多个文件描述符指向同一个文件表项时,它们的文件偏移量是共享的吗?&lt;/think&gt;### 正确答案分析 根据Linux文件系统机制和C标准库实现原理,以下描述是正确的: **A. 正确** 文件描述符本质上是进程控制块(PCB)中文件描述符表的下标索引。每个进程的文件描述符表存储在PCB中,`open()`返回的整数fd就是该表的下标,通过它可访问文件表项。 **B. 正确** `dup2(oldfd, newfd)` 可将newfd重定向到oldfd指向的文件。重定向后,两个fd共享相同的文件表项,可同时操作同一文件(如并发读写)。 **D. 正确** 文件流指针(`FILE*`)本质是`struct _IO_FILE`结构体(glibc定义)。其`_fileno`成员存储底层文件描述符,两者严格一对一绑定(如`fopen()`创建流时自动分配fd,`fileno()`可获取)。 以下描述**错误**: **C. 错误** 多次打开同一文件会生成独立的文件描述符(如第一次open返回3,第次返回4),每个fd对应独立的文件表项(共享inode但偏移量独立)。 --- ### 完整答案 **正确选项:A, B, D**
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值