与维基百科相比,文件描述符的更简化描述是什么?为什么需要它们?比如说,以shell进程为例,它是如何应用的呢?
进程表是否包含多个文件描述符。如果是,为什么?
13 回答
简而言之,当您打开一个文件时,操作系统会创建一个条目来表示该文件并存储有关该打开文件的信息。因此,如果您的操作系统中打开了 100 个文件,那么操作系统中将有 100 个条目(在内核中的某个位置)。这些条目由整数表示,例如 (...100, 101, 102...)。这个条目号是文件描述符。所以它只是一个整数,唯一地代表进程打开的文件。如果您的进程打开 10 个文件,那么您的进程表将有 10 个文件描述符条目。
同样,当你打开一个网络套接字时,它也用一个整数来表示,它被称为套接字描述符。我希望你明白。
文件描述符是一个不透明的句柄,用于用户和内核空间之间的接口,以识别文件/套接字资源。因此,当您使用open()
或socket()
(与内核接口的系统调用)时,您会得到一个文件描述符,它是一个整数(它实际上是进程 u 结构的索引——但这并不重要)。因此,如果您想直接与内核交互,使用系统调用read()
,write()
等close()
。您使用的句柄是一个文件描述符。
系统调用上覆盖了一层抽象,即stdio
接口。这提供了比基本系统调用更多的功能/特性。对于此接口,您获得的不透明句柄是 a FILE*
,由fopen()
调用返回。有许多功能使用stdio
界面fprintf()
, fscanf()
, fclose()
,它们可以让您的生活更轻松。在 C 语言中stdin
,stdout
、 和stderr
are ,在 UNIX 中分别映射到FILE*
文件描述符0
和。1
2
从马的嘴里听到:APUE(理查德史蒂文斯)。
对于内核,所有打开的文件都由文件描述符引用。文件描述符是一个非负数。
当我们打开现有文件或创建新文件时,内核会向进程返回一个文件描述符。 内核维护一个所有打开的文件描述符的表,这些描述符正在使用中。文件描述符的分配通常是连续的,它们作为空闲文件描述符池中的下一个空闲文件描述符分配给文件。当我们关闭文件时,文件描述符被释放并可供进一步分配。
有关更多详细信息,请参见此图像:
当我们想要读取或写入文件时,我们使用open()或create()函数调用返回的文件描述符来识别文件,并将其用作read()或write()的参数。
按照惯例,UNIX 系统 shell 将文件描述符 0 与进程的标准输入相关联,文件描述符 1 与标准输出相关联,文件描述符 2 与标准错误相关联。
文件描述符范围从 0 到 OPEN_MAX。文件描述符最大值可以通过ulimit -n
. 有关更多信息,请参阅 APUE Book 的第 3 章。
其他答案添加了很棒的东西。我只加我的 2 美分。
根据维基百科,我们肯定知道:文件描述符是一个非负整数。我认为缺少的最重要的事情是:
文件描述符绑定到进程 ID。
我们知道最著名的文件描述符是 0、1 和 2。0 对应于STDIN
,1对应于 ,2 对应STDOUT
于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
第 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——字符特殊文件(或字符设备文件)
现在,我们可以识别 的文件描述符STDIN
,STDOUT
并且STDERR
很容易使用lsof -p PID
,或者我们可以看到相同的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 的操作系统定义了七种类型的文件:
- 常规文件
- 目录
- 字符设备文件
- 阻止设备文件
- 本地域套接字
- 命名管道 (FIFO) 和
- 符号链接
文件描述符(FD):
- 在Linux/Unix中,一切都是文件。常规文件、目录,甚至设备都是文件。每个文件都有一个关联的编号,称为文件描述符 (FD)。
- 您的屏幕也有一个文件描述符。执行程序时,输出将发送到屏幕的文件描述符,您可以在监视器上看到程序输出。如果将输出发送到打印机的文件描述符,则程序输出将被打印。
错误重定向:
每当您在终端执行程序/命令时,始终打开 3 个文件- 标准输入
- 标准输出
- 标准错误。
每当运行程序时,这些文件始终存在。如前所述,文件描述符与这些文件中的每一个相关联。
文件 文件描述符
标准输入 STDIN 0
标准输出 STDOUT 1
标准错误 STDERR 2
- 例如,在搜索文件时,通常会出现权限被拒绝错误或其他类型的错误。这些错误可以保存到特定文件中。
示例 1
$ ls mydir 2>errorsfile.txt
标准错误的文件描述符是 2。
如果没有任何名为 mydir 的目录,则命令的输出将保存到文件 errorfile.txt
使用“2>”我们将错误输出重定向到名为“errorfile.txt”的文件。 txt"
因此,程序输出不会杂乱无章地出现错误。
我希望你得到你的答案。
更多关于File Descriptor
:
File Descriptors
(0, 1, 2, ...)
(FD) 是与打开的文件相关联的非负整数。0, 1, 2
是标准的FD,对应于STDIN_FILENO
,STDOUT_FILENO
和STDERR_FILENO
(在 中定义unistd.h
)在程序启动时默认代表 shell 打开。FD 是按顺序分配的,这意味着可能的最低未分配整数值。
特定进程的 FD 可以在
/proc/$pid/fd
(基于 Unix 的系统上)中看到。
作为其他答案的补充,unix 将所有内容都视为文件系统。你的键盘是一个从内核的角度只读的文件。屏幕是只写文件。同样,文件夹、输入输出设备等也被视为文件。每当打开文件时,例如当设备驱动程序 [for device files] 请求 open() 或进程打开用户文件时,内核分配一个文件描述符,一个指定对该文件的访问权限的整数,因此它是只读的,只写等[供参考:https ://en.wikipedia.org/wiki/Everything_is_a_file ]
文件描述符
- 对于内核,所有打开的文件都由文件描述符引用。
- 文件描述符是一个非负整数。
- 当我们打开现有文件或创建新文件时,内核将文件描述符返回给进程。
- 当我们想要读取或写入文件时,我们使用由 open 或 create 重新调整的文件描述符来标识文件,作为 read 或 write 的参数。
- 每个 UNIX 进程有 20 个文件描述符和它处理,编号从 0 到 19,但它被许多系统扩展为 63。
- 进程开始时前三个已经打开 0:标准输入 1:标准输出 2:标准错误输出
- 当父进程fork一个进程时,子进程继承父进程的文件描述符
任何操作系统都有进程(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 结束)。
除了以上所有简化的响应。
如果您在 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
文件描述符只不过是任何开放资源的引用。一旦你打开一个资源,内核就会假设你将对它进行一些操作。通过您的程序和资源进行的所有通信都通过一个接口进行,该接口由文件描述符提供。
由于一个进程可以打开多个资源,因此一个资源可能具有多个文件描述符。
您可以通过简单的运行查看所有链接到该进程的文件描述符,
ls -li /proc/<pid>/fd/
这里 pid 是您的进程的进程 ID
我不知道内核代码,但我会在这里加两分钱,因为我已经考虑了一段时间,我认为它会很有用。
当您打开一个文件时,内核会返回一个文件描述符以与该文件进行交互。
文件描述符是您正在打开的文件的 API 实现。内核创建此文件描述符,将其存储在一个数组中,然后将其提供给您。
例如,此 API 需要一个允许您读取和写入文件的实现。
现在,再想想我说过的话,记住一切都是文件——打印机、监视器、HTTP 连接等。
文件描述符是文件的描述符。他们提供文件的链接。在它们的帮助下,我们可以读取、写入和打开文件。