文件描述符
文件描述符(File Descriptor)是一个非负整数,用于标识打开的文件、套接字或其他I/O资源。它在Unix和类Unix操作系统中是一个重要的概念,用于管理进程对文件和I/O资源的访问。
文件描述符是操作系统内核与用户进程之间通信的一种方式。当用户进程需要访问文件、网络套接字或其他I/O资源时,它通过系统调用(如open
、read
、write
、close
等)向内核请求操作,内核会返回一个文件描述符作为对该资源的引用。进程使用文件描述符来执行读、写、关闭等操作。
每个Unix进程启动时都会自动打开三个标准文件描述符:
0
:标准输入(stdin
),用于读取输入。1
:标准输出(stdout
),用于输出信息。2
:标准错误(stderr
),用于输出错误信息。
这些文件描述符在进程启动时就会被分配,它们通常被用来与终端进行交互,但也可以通过重定向来改变它们的行为。
在Unix和类Unix操作系统中,每个进程都有一个打开文件描述符的表,这个表是通过files_struct
结构体来表示的。files_struct
结构体中包含了指向打开文件的指针数组,每个元素指向一个打开的文件对象,这些文件对象包含了关于文件的元数据以及指向文件数据的指针等信息。
当进程打开一个文件时,操作系统会在内核中创建一个文件对象(通常是一个file
结构体),用来描述该文件的状态和相关信息。然后,操作系统会将这个文件对象添加到进程的文件描述符表中,并将文件描述符返回给进程。这样,进程就可以通过文件描述符来访问相应的文件对象。
因此,文件描述符确实是进程与文件之间的关联,通过文件描述符,进程可以在文件描述符表中找到对应的文件对象,从而对文件进行读取、写入、关闭等操作。
需要注意的是,文件描述符是一个抽象的概念,它并不直接指向文件对象,而是作为索引或标识符,通过它来查找文件对象。在内核中,文件描述符通常被映射到文件对象的数据结构,以便操作系统能够识别和处理相应的文件请求。
文件描述符分配规则
文件描述符通常是从最小的可用非负整数开始分配的。在大多数系统上,前三个文件描述符(0、1、2)通常用于标准输入、标准输出和标准错误,因此新的文件描述符会从3开始分配。
当一个文件描述符被关闭时,操作系统会尽可能快地重新使用该文件描述符。这意味着如果一个文件描述符被关闭,然后另一个文件被打开,那么新的文件描述符很可能会是之前关闭的那个。
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <fcntl.h>
6
7 int main()
8 {
9 printf("%d\n", stdin->_fileno);
10 printf("%d\n", stdout->_fileno);
11 printf("%d\n", stderr->_fileno);
12
13 close(0);
14 int fd = open("demo.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
15 printf("%d\n", fd);
16 return 0;
17 }
总体而言,文件描述符的分配规则通常是简单而直接的,但在编写涉及大量文件处理的程序时,了解文件描述符的分配规则是非常重要的,可以帮助您更好地管理和利用文件描述符资源。
重定向
重定向是Unix和类Unix操作系统中一个重要的概念,它允许将一个命令的输入和输出从默认位置(通常是终端)改变为其他位置,比如文件或另一个进程。
重定向通过修改文件描述符的行为来实现。具体来说,可以使用以下基本重定向操作符:
<
:用于将文件内容重定向到命令的标准输入(stdin)。>
:用于将命令的标准输出(stdout)重定向到文件中,如果文件不存在,则创建文件;如果文件已存在,则将文件内容截断为零。>>
:用于将命令的标准输出(stdout)追加到文件末尾,如果文件不存在,则创建文件。
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <fcntl.h>
6
7 int main()
8 {
9 printf("%d\n", stdin->_fileno);
10 printf("%d\n", stdout->_fileno);
11 printf("%d\n", stderr->_fileno);
12
13 close(1);
14 int fd = open("demo.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
15 printf("%d\n", fd);
16 return 0;
17 }
代码中,首先打印了标准输入、标准输出和标准错误的文件描述符0,1,2。然后关闭标准输出文件描述符1后继续的打开文件,这时候就将文件描述符1分配给新打开的文件,所以打印的文件描述符就没有打印到显示器上,而是打印进了文件demo.txt中。
在Linux系统中,可以使用dup
、dup2
等系统调用来实现文件描述符重定向。这些系统调用允许将一个文件描述符复制到另一个文件描述符,并可以用于将标准输入、标准输出重定向到文件。
dup2(oldfd, newfd)
:将文件描述符oldfd
复制到文件描述符newfd
上,并关闭newfd
之前打开的任何文件。dup(oldfd)
:复制文件描述符oldfd
,返回新的文件描述符。
软硬链接
真正找到磁盘上文件的并不是文件名,而是inode。 其实在linux中可以让多个文件名对应于同一个inode。软链接(Symbolic Link)和硬链接(Hard Link)就是Linux操作系统中用于创建文件链接的两种不同方式。
软链接(Symbolic Link):
- 定义: 软链接是一个特殊的文件,它包含了目标文件的路径信息。
- 特点:
- 软链接文件有自己的inode和数据块,它们只是指向目标文件的路径,并不包含实际的数据。
- 软链接文件的大小为目标文件名的长度加上额外的一些元数据。
- 软链接可以跨越文件系统边界,可以链接到不同文件系统中的文件。
- 如果源文件被删除或移动,软链接仍然存在,但它将指向一个无效的位置。
- 创建: 使用
ln -s
命令创建软链接,语法为:ln -s [target] [link_name]
。
硬链接(Hard Link):
- 定义: 硬链接是目标文件的另一个名称,它与原始文件共享相同的inode和数据块。
- 特点:
- 硬链接文件和原始文件具有相同的inode号,因此它们共享相同的数据块。
- 硬链接不能跨越文件系统边界,只能在同一个文件系统中创建。
- 如果原始文件被删除,硬链接仍然存在,因为它们共享相同的inode,直到所有链接都被删除。
- 创建: 使用
ln
命令创建硬链接,语法为:ln [target] [link_name]
。
软链接和硬链接是文件系统中两种不同的链接方式,它们有一些重要的区别:
-
指向对象不同:
- 软链接指向的是文件的路径,而不是文件本身。
- 硬链接指向的是文件本身的inode,即文件的实际数据块和元数据。
-
跨文件系统边界:
- 软链接可以跨越文件系统边界,即可以链接到不同文件系统中的文件。
- 硬链接不能跨越文件系统边界,只能在同一个文件系统中创建。
-
目标文件删除或移动的影响:
- 如果软链接指向的目标文件被删除或移动,软链接仍然存在,但是它会变成一个无效的链接。
- 如果硬链接指向的目标文件被删除,硬链接仍然存在,直到所有指向该文件的硬链接都被删除,才会真正释放文件的数据块和inode。
-
文件类型:
- 软链接是一个特殊类型的文件,它有自己的inode和数据块,但是不包含实际的文件内容。
- 硬链接是文件本身的另一个名称,它与原始文件共享相同的inode和数据块。
-
文件大小:
- 软链接文件的大小是目标文件路径的长度加上一些额外的元数据大小。
- 硬链接文件和原始文件共享相同的数据块,因此它们的大小相同。
总的来说,软链接和硬链接各有其自身的特点和应用场景。软链接适用于需要跨越文件系统边界、需要指向目录、或者需要链接到不存在的文件的情况。硬链接适用于需要共享相同数据块的文件,且只能在同一个文件系统中使用。