文件控制的内核表达

2023-12-13 12:30:51

打开文件的内核表达

内核用3个相关的数据结构来表达打开的文件

  • 描述符表

    • 每个进程都有一个独立的文件描述符表
    • 文件描述符是一个非负整数,它是对文件或其他I/O资源的引用
    • 文件描述符表中的每个元素是一个索引,用于引用文件表中的相应文件
    • 通过文件描述符,进程可以访问其打开的文件
  • 文件表

    • 所有进程共享。
    • 是内核中的一个数据结构,它记录了系统中所有打开文件的信息
    • 每个打开的文件都在文件表中有一个唯一相应的表项
    • 每项包含以下信息:文件位置、引用数、…、指 向v-node表的一项的指针等
  • v-node表

    • 所有进程共享
    • 是文件系统级别的数据结构,它表示文件系统中的一个文件或目录
    • 每个文件表项通常包含一个指向相应v节点的指针。v节点包含可被stat系统调用读取的信,如文件类型、文件所有者、文件大小等。

内核跟踪用户进程打开的文件

在文件打开过程中,文件表、文件描述符表和v节点之间的关系大致如下:

  1. 进程通过系统调用(如 open)请求打开文件。
  2. 内核在文件表中创建一个文件表项,同时创建或更新相应的v节点。
  3. 内核分配一个文件描述符,将文件表项的索引添加到进程的文件描述符表中。
  4. 进程通过文件描述符访问文件,而文件描述符通过文件描述符表中的索引引用文件表项,后者包含了对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);
    }
}
  1. 打开文件并读取第一个字符 ‘a’ 存储在变量 c1 中:

    fd1 = open(fname, O_RDONLY, 0);
    read(fd1, &c1, 1);
    
  2. 进行 fork 操作,创建子进程:

    if (fork()) {
        // Parent process
    } else {
        // Child process
    }
    

    在这一步之后,父子进程拥有相同的文件描述符 fd1,但是它们有各自的地址空间。

  3. 在父进程中:

    read(fd1, &c2, 1);
    printf("Parent: c1 = %c, c2 = %c\n", c1, c2);
    
    • 父进程继续从文件中读取下一个字符 ‘b’,并将其存储在变量 c2 中。
    • 父进程打印 “Parent: c1 = a, c2 = b”。
  4. 在子进程中:

    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;
}
  1. fd1 读取文件中的第一个字符 ‘a’,存储在变量 c1 中。
  2. fd2fd3fd1 共享相同的文件描述符表项,包括读取位置。所以,fd2 实际上也从文件的开头读取字符,即读取文件中的第一个字符 ‘a’,存储在变量 c2 中。同时,fd3fd2读取文件后的当前位置,即第二个字符读取字符,因此 fd3 读取文件中的第二个字符 ‘b’,存储在变量 c3 中。

实际的输出是:

c1 = a, c2 = a, c3 = b

总结

? 在同一个进程中,如果多次使用 open 打开同一个文件,每次调用 open 都会返回一个新的文件描述符。这些文件描述符是相互独立的,每个都有自己的文件读写位置等状态。修改其中一个文件描述符的状态不会影响其他文件描述符。

? 在父子进程之间,如果调用 fork 创建子进程,子进程会继承父进程的文件描述符表。这意味着父子进程共享相同的文件描述符,包括相同的文件读写位置。如果一个进程修改了文件的读写位置,另一个进程也会受到影响。

文章来源:https://blog.csdn.net/Liyolo007/article/details/134951300
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。