用简单的术语解释什么是文件描述符?


383
  1. 与Wikipedia相比,对文件描述符的更简单描述是什么?为什么需要它们?说,以shell进程为例,它如何应用?

  2. 进程表是否包含多个文件描述符。如果是,为什么?


3
那么stdin stdout stderr等的概念呢?我有一个实例,例如说打开了浏览器进程,它已经打开了一些用于显示html的临时文件。进程使用相同的fd进行读取/写入?此外,进程表.......它还具有fd0指针fd1指针fd2指针.....这样的条目,这是否意味着所有这些文件都在RAM中?为什么还有指针?
Nishant

43
当您打开文件时,操作系统会创建一个指向该文件的流,并将该流连接到已打开的文件,描述符实际上代表了该流。同样,OS也创建了一些默认流。这些流将连接到您的终端而不是文件。因此,当您在终端中编写内容时,它将进入标准输入流和OS。当您在终端上写入“ ls”命令时,操作系统会将输出写入stdout流。stdout流已连接到您的监视器终端,因此您可以在此处看到输出。
2011年

1
关于浏览器示例,浏览器没有必要保持打开文件的状态。这取决于浏览器的实现,但是在大多数情况下,浏览器会打开临时文件,写入文件并关闭文件,因此即使打开了网页也不必打开文件。描述符仅保存文件的信息,而不必将文件保存在RAM中。从描述符读取数据时,操作系统将从硬盘读取数据。在文件描述符的信息,只是表示该文件对硬盘等位置
Tayyab

5
文件描述符到文件不是一对一的映射。我可以将同一个文件打开()4次,并获得4个不同的文件描述符。可以使用每种方法(取决于传递给open()的标志)进行读取,写入或两者。至于文件是驻留在RAM还是磁盘上-内核及其各种缓存对您都是隐藏的。最终,什么是高速缓存将与磁盘上的内容匹配(用于写入),并且如果数据已经在高速缓存中,则内核将不会返回磁盘以进行读取。
Beano

7
这是一篇易于理解的好文章bottomupcs.com/file_descriptors.xhtml
Krishan Gopal,

Answers:


561

简而言之,当您打开文件时,操作系统会创建一个代表该文件的条目,并存储有关该打开文件的信息。因此,如果操作系统中打开了100个文件,则操作系统中(内核中的某个位置)将有100个条目。这些条目由整数表示,例如(... 100,101,102 ....)。该条目号是文件描述符。因此,它只是一个整数,唯一表示操作系统中打开的文件。如果您的进程打开10个文件,那么您的进程表将有10个文件描述符条目。

同样,当您打开网络套接字时,它也由整数表示,称为套接字描述符。我希望你明白。


7
同样,这就是为什么如果一次打开很多文件,文件描述符就会用完的原因。这将阻止* nix系统运行,因为它们/proc始终打开描述符来填充内容。
Spencer Rathbun'5

8
@ErbenMo:不,可能不一样。当您打开文件时,操作系统将分配一个可用的FD,当您关闭它时,操作系统将释放该FD,并将该FD分配给此后打开的另一个文件。它的操作系统跟踪已打开文件的方式,与特定文件无关。
Tayyab 2014年

49
因此,这只是一个整数,唯一表示操作系统中已打开的文件。 ”这是不正确的。该整数唯一表示进程中打开的文件。例如,文件描述符0将在一个进程中表示一个打开的文件,而在另一个进程中表示一个完全不同的打开的文件。
基思·汤普森

15
@Tayyab:我相信你错了。文件描述符0、1和2是每个运行进程的标准输入,标准输出和标准错误。一个成功的初始调用open()将为您提供文件描述符3,即使另一个正在运行的进程碰巧有一个文件描述符3。也请参见POSIX的定义open():文件描述符当前未针对该进程打开。” (强调)。
基思·汤普森

17
@KeithThompson:是的,你是对的。实际上,它与抽象级别有关。实际上维护了两个表,其中第一个是每个进程的表,第二个是系统范围的表。每个进程表中的FD(即fdtable)在系统范围内不是唯一的。但是,它映射到包含系统范围内唯一条目的v节点表。因此,当您调用fopen()和fileno()函数检查描述符时,您可以在2个不同的进程中获得相同的FD号,因为它返回的fdtable索引是每个进程的。感谢您提出来!!
Tayyab '16

116

文件描述符是一个不透明的句柄,在用户和内核空间之间的接口中使用它来标识文件/套接字资源。因此,当您使用open()socket()(系统调用以连接到内核的接口)时,系统会为您提供文件描述符,该文件描述符是整数(实际上是进程u结构的索引-但这并不重要)。因此,如果您想直接与内核接口,使用系统调用read()write()close()等你用手柄的文件描述符。

系统调用上覆盖了一层抽象,即stdio接口。这提供了比基本系统调用更多的功能。对于此接口,您获得的不透明句柄是FILE*,由fopen()调用返回。有迹象表明,用很多很多的功能stdio接口fprintf()fscanf()fclose(),这是那里让您的生活更轻松。在C中,stdinstdout,和stderrFILE*,这在UNIX分别映射到文件描述符012


6
我个人认为此答案比标记为答案的答案要好。已投票。
塔里克

101

从马口中听到:APUE(Richard Stevens)。
对于内核,所有打开的文件均由文件描述符引用。文件描述符是一个非负数。

当我们打开现有文件或创建新文件时,内核将文件描述符返回到进程。 内核维护着所有正在使用的打开文件描述符的表。文件描述符的分配通常是顺序的,它们将作为空闲文件描述符池中的下一个空闲文件描述符分配给文件。当我们关闭文件时,文件描述符将被释放并可用于进一步分配。
有关更多详细信息,请参见此图像:

两道工序

当我们想要读取或写入文件时,我们使用open()create()函数调用返回的文件描述符来标识文件,并将其用作read()write()的参数
按照惯例,UNIX System Shell将文件描述符0与进程的Standard Input关联,将文件描述符1与Standard Output关联,并将文件描述符2与 Standard Error
文件描述符的范围是0到OPEN_MAX。可以使用获取文件描述符最大值ulimit -n。有关更多信息,请阅读《 APUE手册》的第3章。


1
由于0、1、2与进程的“ stdin”,“ stdout”和“ stderr”相关联,我们可以针对不同的进程同时使用这些描述符吗?
塔里克

@Tarik:文件描述符是每个进程的。要查看此信息,请下载osqueryosqueryi <<< echo '.all process_open_files'在bash shell中执行。
Ben Creasy

29

其他答案增加了很多东西。我只加我的2美分。

根据Wikipedia,我们可以确定:文件描述符是非负整数。我认为最重要的遗漏是:

文件描述符绑定到进程ID。

我们知道最著名的文件描述符是0、1和2。0对应于STDIN,1 对应于,2对应STDOUTSTDERR

说,以Shell进程为例,它如何应用?

查看此代码

#>sleep 1000 &
[12] 14726

我们创建了一个ID为14726(PID)的进程。使用,lsof -p 14726我们可以得到如下信息:

COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF    NODE NAME
sleep   14726 root  cwd    DIR    8,1     4096 1201140 /home/x
sleep   14726 root  rtd    DIR    8,1     4096       2 /
sleep   14726 root  txt    REG    8,1    35000  786587 /bin/sleep
sleep   14726 root  mem    REG    8,1 11864720 1186503 /usr/lib/locale/locale-archive
sleep   14726 root  mem    REG    8,1  2030544  137184 /lib/x86_64-linux-gnu/libc-2.27.so
sleep   14726 root  mem    REG    8,1   170960  137156 /lib/x86_64-linux-gnu/ld-2.27.so
sleep   14726 root    0u   CHR  136,6      0t0       9 /dev/pts/6
sleep   14726 root    1u   CHR  136,6      0t0       9 /dev/pts/6
sleep   14726 root    2u   CHR  136,6      0t0       9 /dev/pts/6

第四列FD和第二列TYPE对应于文件描述符和文件描述符类型。

FD的一些值可以是:

cwd – Current Working Directory
txt – Text file
mem – Memory mapped file
mmap – Memory mapped device

但是实际文件描述符在以下位置:

NUMBER – Represent the actual file descriptor. 

数字后面的字符“ 1u”表示打开文件的模式。r表示读取,w表示写入,u表示读写。

TYPE指定文件的类型。TYPE的一些值是:

REG – Regular File
DIR – Directory
FIFO – First In First Out

但是所有文件描述符都是CHR –字符特殊文件(或字符设备文件)

现在,我们可以找出文件描述符STDINSTDOUTSTDERR易与lsof -p PID,或,我们可以看到,如果我们相同ls /proc/PID/fd

还要注意内核跟踪的文件描述符表与文件表或索引节点表不同。正如其他答案所解释的,这些是分开的。

fd表

您可能会问自己,这些文件描述符在物理上在哪里以及/dev/pts/6例如存储在什么位置

sleep   14726 root    0u   CHR  136,6      0t0       9 /dev/pts/6
sleep   14726 root    1u   CHR  136,6      0t0       9 /dev/pts/6
sleep   14726 root    2u   CHR  136,6      0t0       9 /dev/pts/6

好吧,/dev/pts/6纯生活在记忆中。这些不是常规文件,而是所谓的字符设备文件。您可以使用以下方法进行检查:在我的情况下ls -l /dev/pts/6,它们将以开头ccrw--w----

只是想回想大多数像OS这样的Linux,它定义了七种文件类型:

  • 常规文件
  • 目录
  • 角色设备文件
  • 阻止设备文件
  • 本地域套接字
  • 命名管道(FIFO)和
  • 符号链接

1
谢谢。确实,必须指出每个过程都是很重要的!它有助于更​​好地可视化事物。
Nishant

1
您在回答中提到的由OS定义的文件类型确实有助于理解较低级别的文件。
Rohan Bhale

20

有关以下几点的更多要点File Descriptor

  1. File Descriptors(FD)是(0, 1, 2, ...)与打开的文件关联的非负整数。

  2. 0, 1, 2是与相对应的标准FDSTDIN_FILENOSTDOUT_FILENO并且STDERR_FILENO(在中定义unistd.h)在程序启动时默认代表Shell打开。

  3. FD按顺序分配,这意味着可能的最低未分配整数值。

  4. 可以在/proc/$pid/fd(基于Unix的系统上)看到针对特定进程的FD 。


16

作为其他答案的补充,unix将所有内容都视为文件系统。您的键盘是一个从内核角度只读的文件。屏幕是只写文件。类似地,文件夹,输入输出设备等也被视为文件。每当打开文件时,例如,当设备驱动程序(用于设备文件)请求open()或进程打开用户文件时,内核就会分配文件描述符,该整数指定了对该文件的访问权限,因此该文件为只读,仅写等。[供参考:https : //en.wikipedia.org/wiki/Everything_is_a_file ]


文件描述符还可以引用文件系统中不存在的内容,例如匿名管道和网络套接字。
kbolino '18 -10-7

12

文件描述符(FD):

  • Linux / Unix中,所有内容都是文件。常规文件,目录甚至设备都是文件。每个文件都有一个关联的编号,称为文件描述符(FD)。
  • 您的屏幕上还有一个文件描述符。执行程序时,输出将发送到屏幕的File Descriptor,您将在监视器上看到程序输出。如果将输出发送到打印机的文件描述符,则程序输出将已经被打印。

    错误重定向:
    无论何时在终端上执行程序/命令,始终会打开3个文件
    1. 标准输入
    2. 标准输出
    3. 标准错误。

    每当运行程序时,这些文件始终存在。如前所述,文件描述符与每个文件相关联。
    文件                                        文件描述符
    标准输入STDIN 0
    标准输出STDOUT 1
    标准错误STDERR 2

  • 例如,在搜索文件时,通常会出现权限被拒绝错误或其他某种错误。这些错误可以保存到特定文件。
    例子1

$ ls mydir 2> errorsfile.txt

标准错误的文件描述符为2。
如果没有名为mydir的目录,则命令的输出将保存到文件errorfile.txt中。
使用“ 2>”将错误输出重定向到名为“ errorfile”的文件。 txt”,
因此程序输出不会出现错误。

我希望你能得到答案。


5

任何操作系统都有正在运行的进程(p),例如p1,p2,p3等。每个过程通常会不断使用文件。

每个过程都由一个过程树(或过程表,换句话说)组成。

通常,操作系统用数字表示每个进程中每个文件(即,在每个过程树/表)。

该过程中使用的第一个文件是file0,第二个是file1,第三个是file2,依此类推。

任何这样的数字都是文件描述符。

文件描述符通常是整数(0、1、2,而不是0.5、1.5、2.5)。

鉴于我们经常将流程描述为“流程表”,并且鉴于表具有行(条目),我们可以说每个条目中的文件描述符单元用于表示整个条目。

以类似的方式,当您打开网络套接字时,它具有套接字描述符。

在某些操作系统中,文件描述符可能用完了,但是这种情况非常少见,普通计算机用户不必为此担心。

文件描述符可能是全局的(进程A以0开头,结束于1;进程B以2开头,结束于3)等等,但是据我所知,通常在现代操作系统中,文件描述符不是全局的,实际上是特定于进程的(进程A以0开始,以5结束,进程B以0开始,以10结束)。


在此处阅读有关FD的更多信息:unix.stackexchange.com/questions/358022/…–

1
好答案:)
humble_wolf

5

文件描述符

  • 对于内核,所有打开的文件都由文件描述符引用。
  • 文件描述符是一个非负整数。
  • 当我们打开现有文件或创建新文件时,内核将文件描述符返回到进程。
  • 当我们要读取或写入文件时,我们将带有通过open或create重新调整的文件描述符的文件标识为read或write的参数。
  • 每个UNIX进程都有20个文件描述符,并以0到19的方式对其进行处理,但是许多系统将其扩展到63。
  • 流程开始时,前三个已经打开0:标准输入1:标准输出2:标准错误输出
  • 当父进程派生一个进程时,子进程将继承父进程的文件描述符

1

除了所有简化的响应之外。
如果要使用bash脚本处理文件,最好使用文件描述符。
例如:-
您要读写文件“ test.txt”。
使用文件描述符,如下所示

FILE=$1 # give the name of file in the command line
exec 5<>$FILE # '5' here act as the file descriptor
# Reading from the file line by line using file descriptor
while read LINE; do
    echo "$LINE"
done <&5

# Writing to the file using descriptor
echo "Adding the date: `date`" >&5 
exec 5<&- # Closing a file descriptor

-5

文件描述符是文件的描述符。它们提供文件链接。在他们的帮助下,我们可以读取,写入和打开文件。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.