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

  1. 与维基百科相比,什么样的文件描述符描述更简单?为什么需要它们?比如说,以壳进程为例,它是如何应用的呢?

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

329174 次浏览
简单地说,当您打开一个文件时,操作系统创建一个条目来表示该文件,并存储关于所打开文件的信息。因此,如果在你的操作系统中打开了100个文件,那么在操作系统中(内核中的某个地方)就会有100个条目。这些条目由整数表示,如(…100、101、102....)。这个条目号是文件描述符。 因此,它只是一个整数,唯一地表示该进程打开的文件。 如果您的进程打开了10个文件,那么您的进程表将有10个文件描述符条目 类似地,当你打开一个网络套接字时,它也由一个整数表示,它被称为套接字描述符。

.我希望你能理解

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

系统调用上覆盖了一个抽象层,即stdio接口。这比基本的系统调用提供了更多的功能/特性。对于这个接口,您获得的不透明句柄是FILE*,它由fopen()调用返回。有很多很多函数使用stdio接口fprintf()fscanf()fclose(),它们让你的生活更容易。在C语言中,stdinstdoutstderr分别是FILE*,在UNIX中分别映射到文件描述符FILE*1、FILE*2和FILE*3。

< p > # EYZ0 < br > 对于内核,所有打开的文件都由文件描述符引用。文件描述符是非负数。< / p > 内核维护了一个包含所有正在使用的打开的文件描述符的表。文件描述符的分配通常是顺序的,它们从空闲文件描述符池中作为下一个空闲文件描述符分配给文件。关闭文件时,文件描述符将被释放,并可用于进一步分配 请看这张图片了解更多细节:

Two Process

当我们想读或写一个文件时,我们用由open ()create ()函数调用返回的文件描述符来标识该文件,并将其用作read ()write ()的参数 按照惯例,UNIX系统shell将文件描述符0与进程的标准输入相关联,文件描述符1与标准输出相关联,文件描述符2与标准错误相关联 文件描述符范围从0到OPEN_MAX。文件描述符max值可以用ulimit -n获得。欲了解更多信息,请参阅APUE Book的第3章

作为其他答案的补充,unix将所有东西都视为文件系统。您的键盘是一个仅从内核角度读取的文件。屏幕是一个只写的文件。类似地,文件夹、输入输出设备等也被认为是文件。当一个文件被打开时,比如当设备驱动程序[设备文件]请求一个open(),或者进程打开一个用户文件时,内核分配一个文件描述符,一个整数,指定对该文件的访问权限,例如它是只读的,只能写的等等。

任何操作系统都有正在运行的进程,比如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)。

关于File Descriptor的更多要点:

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

  2. 0, 1, 2是标准的FD,对应于STDIN_FILENOSTDOUT_FILENOSTDERR_FILENO(在unistd.h中定义),在程序启动时默认代表shell打开。

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

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

# EYZ0

    Linux / Unix中,所有内容都是一个文件。常规文件,目录, 甚至设备都是文件。每个文件都有一个相关的编号,称为文件描述符(FD) 你的屏幕也有一个文件描述符。当程序执行时 输出被发送到屏幕的文件描述符,你可以看到 在显示器上进行程序输出。如果输出被发送到File 描述符的打印机,程序的输出将是 打印出来。

    错误重定向:
    当你在终端上执行一个程序/命令时,总会打开3个文件
    1. 标准输入
    2. 标准输出
    3. # EYZ0 < / ol >
      当程序运行时,这些文件始终存在。如前所述,文件描述符与每一个都相关联 这些文件。
      File ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, 文件描述符
      标准输入STDIN              标准输出STDOUT       标准错误STDERR           李
      < / >
    4. 例如,在搜索文件时,1 通常是获得许可拒绝错误或其他类型的错误。这些错误可以保存到一个特定的文件中。
      示例1 < / >强

$ ls mydir 2>errorsfile.txt
.txt

标准错误的文件描述符是2。
如果没有任何名为mydir的目录,则命令的输出将保存到文件errorfile.txt
使用“2>”,我们将错误输出重定向到名为“errorfile.txt”的文件
因此,程序输出不会充满错误。 我希望你得到了你的答案。

其他答案补充了很好的内容。我只是说说我的看法。

根据维基百科,我们可以肯定地知道:文件描述符是非负整数。我认为最重要的一点是:

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

我们知道最著名的文件描述符是0,1和2。

. 0对应STDIN, 1对应STDOUT, 2对应STDERR

比如说,以壳进程为例,它是如何应用的呢?

检查这段代码

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

第4列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文件类型。TYPEs的一些值是:

REG – Regular File
DIR – Directory
FIFO – First In First Out
但是所有的文件描述符都是 CHR -字符特殊文件(或字符设备文件)

现在,我们可以用lsof -p PID轻松识别STDINSTDOUTSTDERR的文件描述符,或者我们可以看到同样的情况,如果我们使用ls /proc/PID/fd

还要注意,内核所跟踪的文件描述符表与文件表或inodes表并不相同。正如其他一些答案解释的那样,这些是独立的。

fd table

例如,您可能会问自己这些文件描述符的物理位置以及在/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来检查,他们将从c开始,在我的例子中是crw--w----

回想一下大多数Linux之类的操作系统定义了七种类型的文件:

  • 常规文件
  • 目录
  • 字符设备文件
  • 块设备文件
  • 本地域套接字
  • 命名管道(fifo)和
  • 符号链接

文件描述符

  • 对于内核,所有打开的文件都由文件描述符引用。
  • 文件描述符是非负整数。
  • 当我们打开一个现有文件或创建一个新文件时,内核会向进程返回一个文件描述符。
  • 当我们想读或写一个文件时,我们用open或create调优的文件描述符来标识文件,作为读或写的参数。 每个UNIX进程有20个文件描述符和它的处理,编号从0到19
  • 当流程开始时,前三个已经打开 0:标准输入 1:标准输出 2:标准错误输出
  • 当父进程派生一个进程时,子进程继承父进程的文件描述符

除了以上所有简化的回答。

如果使用bash脚本处理文件,最好使用文件描述符。

例如< p >: 如果你想读写文件“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

文件描述符只是任何开放资源的引用。只要您打开一个资源,内核就假定您将对其进行一些操作。所有通过程序和资源的通信都发生在一个接口上,这个接口由文件描述符提供。

由于一个进程可以打开多个资源,一个资源可能有多个文件描述符。< br > 你可以通过简单地运行, ls -li /proc/<pid>/fd/这里pid是进程的进程id

我不知道内核代码,但我将在这里添加我的两分,因为我已经思考了一段时间,我认为它会有用

当您打开一个文件时,内核返回一个文件描述符来与该文件进行交互。

文件描述符是您正在打开的文件的API的实现。内核创建这个文件描述符,将其存储在一个数组中,并将其提供给您。

例如,该API需要一个允许您读取和写入文件的实现。

现在,再想想我说过的话,记住所有东西都是文件——打印机、监视器、HTTP连接等等。

这是我在阅读https://www.bottomupcs.com/file_descriptors.xhtml后的总结。

这里提供的所有答案都很棒,这是我的版本

文件描述符是非负整数,充当“文件”或I/O资源(如管道、套接字或数据流)的抽象句柄。这些描述符帮助我们与这些I/O资源进行交互,并使使用它们变得非常容易。I/O系统对于用户进程来说是一个字节流(I/O流)。Unix进程使用描述符(小的无符号整数)来引用I/O流。与I/O操作相关的系统调用以一个描述符作为参数。

有效的文件描述符范围从0到可配置的最大描述符数(ulimit, /proc/sys/fs/file-max)。内核为FD表的std输入(0),std输出(1)和std错误(2)分配desc.。如果文件打开失败,fd返回-1。# EYZ0

当进程成功请求打开一个文件时,内核返回一个文件描述符,该描述符指向内核全局文件表中的一个条目。文件表项包含文件的inode、字节偏移量和该数据流的访问限制(只读、只写等)等信息。