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

Nis*_*ant 351 unix operating-system file-descriptor

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

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

Tay*_*yab 517

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

类似地,当您打开网络套接字时,它也由一个整数表示,它被称为套接字描述符.我希望你明白.

  • "*所以它只是一个整数,唯一代表操作系统中打开的文件.*"这是不正确的.该整数唯一地表示进程*中打开的文件*.例如,文件描述符0将表示一个进程中的一个打开文件和另一个进程中完全不同的打开文件. (38认同)
  • @KeithThompson:是的你是对的.实际上它关于抽象的程度.实际上维护了两个表,其中第一个是每个进程,第二个是系统范围的.每个进程表中的FD(即fdtable)在系统范围内不是唯一的.但是,它映射到包含系统范围唯一条目的v节点表.因此,当您调用fopen()和fileno()函数来检查描述符时,您可以在2个不同的进程中获得相同的FD编号,因为它返回fdtable的索引,即每个进程.谢谢你提出来!! (14认同)
  • @Tayyab:我相信你错了.文件描述符0,1和2是*每个*运行进程的标准输入,标准输出和标准错误.成功初始调用`open()`会给你文件描述符3,即使另一个正在运行的进程碰巧有一个文件描述符3.参见[open()`的POSIX定义](http://pubs.opengroup .org/onlinepubs/9699919799/functions/open.html):"open()函数应返回指定文件的文件描述符,该文件描述符是该进程**当前未打开的最低文件描述符**." (重点补充). (12认同)
  • @ErbenMo:不,它可能不一样.当您打开文件时,操作系统将分配一个可用的FD,当您关闭它时,操作系统会释放FD,并可能将该FD分配给之后打开的另一个文件.它的操作系统跟踪打开的文件,它与特定文件无关. (8认同)
  • 此外,如果您一次打开大量文件,这就是您可以用完文件描述符的原因.这会阻止*nix系统运行,因为它们会一直打开`/ proc`中的描述符. (7认同)
  • @Tayyab:您应该编辑此答案以添加评论中出现的更多详细信息。 (4认同)
  • 严格地说,进程表不具有文件描述符,文件描述符保存在进程的u结构中,因此对于进程是唯一的. (3认同)
  • 错误的答案。文件对象不是文件描述符,也不是编号为 1...100。文件描述符是每个进程的指示器,将数字(描述符)映射到文件对象。因此,多个进程可以引用同一个文件对象,但具有不同的描述符(和编号)。 (3认同)

Bea*_*ano 106

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

系统调用上覆盖了一层抽象,即stdio接口.这提供了比基本系统调用更多的功能/特性.对于此接口,您获得的opaque句柄是a FILE*,由fopen()调用返回.有许多许多功能使用stdio界面fprintf(),fscanf(),fclose(),这是那里让您的生活更轻松.在C中,stdin,stdout,和stderrFILE*,这在UNIX分别映射到文件描述符0,12.

  • 我个人认为这个答案比标记为答案的答案要好.Upvoted. (5认同)

She*_*mar 93

从马的嘴里听到它:APUE(理查德史蒂文斯).
对于内核,文件描述符引用所有打开的文件.文件描述符是非负数.
当我们打开现有文件或创建新文件时,内核会向进程返回文件描述符. 内核维护一个正在使用的所有打开文件描述符的表.文件描述符的分配通常是顺序的,它们作为免费文件描述符池中的下一个空闲文件描述符分配给文件.当我们关闭文件时,文件描述符被释放并可用于进一步分配.
有关详细信息,请参见此图片:
两个过程

当我们想要读取或写入文件时,我们使用open()create()函数调用返回的文件描述符来标识文件,并将其用作read()write()的参数.
按照惯例,UNIX系统shell将文件描述符0与进程的标准输入,文件描述符1与标准输出和文件描述符2与标准错误相关联.
文件描述符的范围从0到OPEN_MAX.
有关更多信息,请阅读APUE Book的第3章.

  • @Tarik:文件描述符是每个进程的。要查看此内容,请下载 [osquery](https://osquery.io/) 并在 bash shell 中执行 `osqueryi <<< echo '.all process_open_files'`。 (2认同)

pro*_*sti 21

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

根据维基百科我们肯定知道:文件描述符是一个非负整数.我认为最重要的缺失就是:

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

我们知道大多数着名的文件描述符是0,1和2. 0对应于STDIN,1对STDOUT和2对STDERR.

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

看看这段代码

#>sleep 1000 &
[12] 14726
Run Code Online (Sandbox Code Playgroud)

我们创建了一个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
Run Code Online (Sandbox Code Playgroud)

第4列FD和下一列TYPE对应于文件描述符和文件描述符类型.

FD的一些值可以是:

cwd – Current Working Directory
txt – Text file
mem – Memory mapped file
mmap – Memory mapped device
Run Code Online (Sandbox Code Playgroud)

但真正的文件描述符在:

NUMBER – Represent the actual file descriptor. 
Run Code Online (Sandbox Code Playgroud)

数字后面的字符即"1u"表示文件打开的模式.r用于读取,w用于写入,u用于读取和写入.

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

REG – Regular File
DIR – Directory
FIFO – First In First Out
Run Code Online (Sandbox Code Playgroud)

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

现在,我们可以识别文件描述符STDIN,STDOUT并且STDERR很容易lsof -p PID,或者我们可以看到相同的文件描述符ls /proc/PID/fd.

另请注意,内核跟踪的文件描述符表与files表或inode表不同.正如其他一些答案所解释的那样,这些是分开

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
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 谢谢。事实上,重要的是要指出它是每个进程!它有助于更​​好地可视化事物。 (2认同)
  • 您在回答中提到的操作系统定义的文件类型确实有助于在较低级别上理解文件。 (2认同)

小智 17

更多关于File Descriptor:

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

  2. 0, 1, 2是标准的FD的对应STDIN_FILENO,STDOUT_FILENOSTDERR_FILENO(在定义的unistd.h默认代表壳的打开程序启动时).

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

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


Bal*_*alu 15

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


Har*_*thi 13

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

\n

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

\n

有效文件描述符的范围从 0 到可配置的最大描述符编号(ulimit、/proc/sys/fs/file-max)。内核分配 desc。对于 FD 表的标准输入 (0)、标准输出 (1) 和标准错误 (2)。如果文件打开不成功,fd 返回-1。 FD

\n

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

\n


小智 12

文件描述符

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


Abh*_*mal 7

文件描述符(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”,
因此程序输出不会出现错误。

我希望你能得到答案。


swa*_*ina 6

文件描述符只不过是任何开放资源的引用。一旦您打开一个资源,内核就会假设您将对其进行一些操作。通过程序和资源进行的所有通信都通过接口进行,并且该接口由文件描述符提供。

由于一个进程可以打开多个资源,因此一个资源可能具有多个文件描述符。
您只需运行即可查看链接到该进程的所有文件描述符, ls -li /proc/<pid>/fd/这里 pid 是您的进程的进程 ID


小智 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 结束)。


小智 5

除了上述所有简化的响应之外。

如果您在 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
Run Code Online (Sandbox Code Playgroud)