相较于维基百科,文件描述符的更简化描述是什么?它们为什么是必须的?以shell进程为例,它如何应用?
进程表是否包含多个文件描述符?如果是,为什么?
相较于维基百科,文件描述符的更简化描述是什么?它们为什么是必须的?以shell进程为例,它如何应用?
进程表是否包含多个文件描述符?如果是,为什么?
/proc
中的文件描述符,这可能导致系统无法继续运行。请注意,文件描述符是一种操作系统资源,用于识别打开的文件。 - Spencer Rathbunopen()
初始化调用将会给你返回文件描述符3。请参阅 open()
的 POSIX 定义:"open()函数将返回命名文件的文件描述符,该文件描述符是当前未打开的最低文件描述符,对于该进程而言"(强调添加)。 - Keith Thompsonopen()
或socket()
(系统调用与内核进行接口),您将获得一个文件描述符,它是一个整数(实际上是进程u结构中的索引-但这并不重要)。因此,如果您想直接与内核进行接口,使用系统调用read()
,write()
,close()
等,你需要使用的句柄就是文件描述符。stdio
接口。它提供比基本系统调用更多的功能/特性。对于这个接口,您获得的不透明句柄是一个FILE*
,由fopen()
调用返回。有许多使用stdio
接口的函数,如fprintf()
,fscanf()
,fclose()
,这些函数旨在使您的编程更加容易。在C语言中,stdin
,stdout
和stderr
都是FILE*
,在UNIX中分别映射到文件描述符0
,1
和2
。ulimit -n
命令获得。更多信息请参阅APUE书籍的第三章。osqueryi <<< echo '.all process_open_files'
来验证这一点。 - Ben Creasy>
来突出引用的段落。 - undefined其他答案已经提供了很好的内容,我只想补充我的看法。
据维基百科所知:文件描述符是一个非负整数。我认为最重要的一点是缺失了以下内容:
文件描述符与进程ID绑定。
我们知道最常用的文件描述符是0、1和2。0对应于STDIN
,1对应于STDOUT
,2对应于STDERR
。
以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-字符特殊文件(或字符设备文件)。
现在,我们可以使用lsof -p PID
轻松地识别STDIN
、STDOUT
和STDERR
的文件描述符,或者如果我们使用ls /proc/PID/fd
,我们也可以看到相同的内容。
请注意,内核跟踪的文件描述符表与文件表或inode表不同。正如其他答案所解释的那样,它们是分开的。
您可能会问自己这些文件描述符在物理上位于哪里,例如/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 的操作系统定义了七种类型的文件:
这些标准流对于重定向和管道非常重要。例如,如果提供的目录不存在,命令ls
会将错误输出到终端:
$ ls ./non-existent
ls: ./nonexistent: No such file or directory
errors.log
,通过使用标准错误的输出重定向元字符2>
:$ ls ./non-existent 2> errors.log
errors.log
,而不是屏幕”。现在errors.log
包含了之前发送到屏幕的输出。|
)元字符来实现这一点。$ ls -l | wc -l
ls -l
命令的标准输出发送到wc -l
命令的标准输入,然后将结果发送到屏幕上。文件描述符
(File Descriptors,简称FD) 是与已打开文件相关联的非负整数 (0, 1, 2, ...)
。
0, 1, 2
是标准FD,对应于在程序启动时由shell默认打开的 STDIN_FILENO
、STDOUT_FILENO
和 STDERR_FILENO
(在 unistd.h
中定义)。
FD按顺序分配,意味着分配的整数值越小,则优先级越高。
特定进程的FD可以在 Unix 系统上的 /proc/$pid/fd
中查看。
除了其他答案之外,unix 将一切都视为文件系统。从内核的角度来看,您的键盘是一个只读文件。屏幕则是一个只写文件。同样地,文件夹、输入输出设备等也被认为是文件。每当打开一个文件时,例如当设备驱动程序(用于设备文件)请求open()或进程打开用户文件时,内核会分配一个文件描述符,即指定该文件访问权限的整数,如只能读取,只能写入等。[参考:https://en.wikipedia.org/wiki/Everything_is_a_file]
open()
或create()
等函数返回的文件描述符来标识文件,并将其提供给read()
或write()
。文件描述符是非负整数,作为“文件”或I/O资源(如管道、套接字或数据流)的抽象句柄。这些描述符帮助我们与这些I/O资源交互,并使处理它们变得非常容易。对于用户进程而言,I/O系统可视为一系列字节(I/O流)。Unix进程使用描述符(小的无符号整数)来引用I/O流。与I/O操作相关的系统调用会将描述符作为参数。
有效的文件描述符范围从0到可配置的最大描述符号码(ulimit、/proc/sys/fs/file-max)。内核为FD表中的标准输入(0)、标准输出(1)和标准错误(2)分配描述符号。如果文件打开不成功,fd返回-1。
当进程成功请求打开一个文件时,内核会返回一个文件描述符,该描述符指向内核全局文件表中的一个条目。文件表条目包含诸如文件的inode、字节偏移和该数据流的访问限制(只读、只写等)之类的信息。