文件控制的内核表达
2023-12-13 12:30:51
打开文件的内核表达
内核用3个相关的数据结构来表达打开的文件
-
描述符表
- 每个进程都有一个独立的文件描述符表
- 文件描述符是一个非负整数,它是对文件或其他I/O资源的引用
- 文件描述符表中的每个元素是一个索引,用于引用文件表中的相应文件
- 通过文件描述符,进程可以访问其打开的文件
-
文件表
- 所有进程共享。
- 是内核中的一个数据结构,它记录了系统中所有打开文件的信息
- 每个打开的文件都在文件表中有一个唯一相应的表项
- 每项包含以下信息:文件位置、引用数、…、指 向v-node表的一项的指针等
-
v-node表
- 所有进程共享
- 是文件系统级别的数据结构,它表示文件系统中的一个文件或目录
- 每个文件表项通常包含一个指向相应v节点的指针。v节点包含可被stat系统调用读取的信,如文件类型、文件所有者、文件大小等。
内核跟踪用户进程打开的文件
在文件打开过程中,文件表、文件描述符表和v节点之间的关系大致如下:
- 进程通过系统调用(如
open
)请求打开文件。 - 内核在文件表中创建一个文件表项,同时创建或更新相应的v节点。
- 内核分配一个文件描述符,将文件表项的索引添加到进程的文件描述符表中。
- 进程通过文件描述符访问文件,而文件描述符通过文件描述符表中的索引引用文件表项,后者包含了对v节点的引用。
用同一文件名两次调用open
调用fork()之后(与图1对比)
-
子进程的描述符表与父进程的一样
-
打开文件表的引用数+1
文件描述符
-fork
如果文件仅包含串”abcde”,程序将打印什么?
#include <stdio.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd1;
char c1, c2;
char *fname = argv[1];
fd1 = open(fname, O_RDONLY, 0);
read(fd1, &c1, 1);
if (fork())
{ /*Parent*/
read(fd1, &c2, 1);
printf("Parent:c1 = %c, c2 = %c\n", c1, c2);
}
else
{ /*Child*/
sleep(5);
read(fd1, &c2, 1);
printf("Child:c1 = %c, c2 = %c\n", c1, c2);
}
}
-
打开文件并读取第一个字符 ‘a’ 存储在变量
c1
中:fd1 = open(fname, O_RDONLY, 0); read(fd1, &c1, 1);
-
进行 fork 操作,创建子进程:
if (fork()) { // Parent process } else { // Child process }
在这一步之后,父子进程拥有相同的文件描述符
fd1
,但是它们有各自的地址空间。 -
在父进程中:
read(fd1, &c2, 1); printf("Parent: c1 = %c, c2 = %c\n", c1, c2);
- 父进程继续从文件中读取下一个字符 ‘b’,并将其存储在变量
c2
中。 - 父进程打印 “Parent: c1 = a, c2 = b”。
- 父进程继续从文件中读取下一个字符 ‘b’,并将其存储在变量
-
在子进程中:
sleep(5); read(fd1, &c2, 1); printf("Child: c1 = %c, c2 = %c\n", c1, c2);
- 子进程调用
sleep(5)
暂停执行 5 秒。 - 子进程在暂停之后继续执行,从文件中读取下一个字符 ‘c’,并将其存储在变量
c2
中。 - 子进程打印 “Child: c1 = a, c2 = c”。
- 子进程调用
总结:
- 父子进程共享相同的文件描述符,但它们各自有独立的地址空间。
- 父进程先读取一个字符 ‘a’,然后从文件中读取下一个字符 ‘b’。
- 子进程在调用
sleep(5)
暂停 5 秒后,继续从文件中读取下一个字符 ‘c’。
因此,输出是:
c1 = a, c2 = b
Child: c1 = a, c2 = c
-dup2
如果文件仅包含串”abcde”,程序将打印什么?
#include <stdio.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd1, fd2, fd3;
char c1, c2, c3;
char *fname = argv[1];
fd1 = open(fname, O_RDONLY, 0);
fd2 = open(fname, O_RDONLY, 0);
fd3 = open(fname, O_RDONLY, 0);
printf("fd1 = %d, fd2 = %d, fd3 = %d\n", fd1, fd2, fd3);
dup2(fd2, fd3);
printf("fd1 = %d, fd2 = %d, fd3 = %d\n", fd1, fd2, fd3);
read(fd1, &c1, 1);
read(fd2, &c2, 1);
read(fd3, &c3, 1);
printf("c1 = %c, c2 = %c, c3 = %c\n", c1, c2, c3);
return 0;
}
fd1
读取文件中的第一个字符 ‘a’,存储在变量c1
中。fd2
和fd3
与fd1
共享相同的文件描述符表项,包括读取位置。所以,fd2
实际上也从文件的开头读取字符,即读取文件中的第一个字符 ‘a’,存储在变量c2
中。同时,fd3
从fd2
读取文件后的当前位置,即第二个字符读取字符,因此fd3
读取文件中的第二个字符 ‘b’,存储在变量c3
中。
实际的输出是:
c1 = a, c2 = a, c3 = b
总结
? 在同一个进程中,如果多次使用 open
打开同一个文件,每次调用 open
都会返回一个新的文件描述符。这些文件描述符是相互独立的,每个都有自己的文件读写位置等状态。修改其中一个文件描述符的状态不会影响其他文件描述符。
? 在父子进程之间,如果调用 fork
创建子进程,子进程会继承父进程的文件描述符表。这意味着父子进程共享相同的文件描述符,包括相同的文件读写位置。如果一个进程修改了文件的读写位置,另一个进程也会受到影响。
文章来源:https://blog.csdn.net/Liyolo007/article/details/134951300
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!