文件描述符存储在进程内存中的什么位置?

where is file descriptor stored in process memory?

从执行点调用函数A时,内部是JMP语句到指向函数A的地址。调用函数 A 并继续。

为了在函数调用后回到执行点,函数块应该有相等的入栈和出栈。通常在 C 中退出函数时,定义的堆栈变量被销毁(我认为这意味着从堆栈中弹出),但我决定在我的函数中定义一个文件描述符变量。代码如下:

void main() {
    printf("In the beginning there was main()\n");
    func_call();
    printf("func_call complete\n");
    while(1);
}

void func_call() {
    int fp;
    //Opening a file to get handle to it.
    fp = open("stack_flush.c", O_RDONLY);
    if (fp < 0 ) {
        perror("fp could not open stack_flush.c");
        return;
    }
}

在 运行 这个程序上检查 lsof,我可以看到 fd 在退出函数 func_call() 时仍然打开。

stack_flu 3791 vvdnlt260    0u   CHR  136,1      0t0        4 /dev/pts/1
stack_flu 3791 vvdnlt260    1u   CHR  136,1      0t0        4 /dev/pts/1
stack_flu 3791 vvdnlt260    2u   CHR  136,1      0t0        4 /dev/pts/1
stack_flu 3791 vvdnlt260    3r   REG    8,3      526 24660187 /home/vvdnlt260/Nishanth/test_space/stack_flush.c

我检查了文件描述符的维基百科条目,我发现了这个:

To perform input or output, the process passes the file descriptor to the kernel through a system call, and the kernel will access the file on behalf of the process. The process does not have direct access to the file or inode tables.

从上面的语句可以明显看出文件描述符整数值存储在进程内存中,但尽管它是在函数中定义的,文件描述符不是函数的本地文件描述符,因为它在函数退出时没有被删除.

所以我的问题是 2 折:

1) 如果文件描述符是func_call()栈的一部分,那么代码return虽然还没有被pop掉,但是如何到它的pre function call执行点呢?同样在这种情况下,为什么它在函数调用存在后仍然存在?

2) 如果不是 func_call() 堆栈的一部分,文件描述符驻留在进程内存中的哪个位置?

当你打开一个文件时,内核中有一个 table 存储文件描述符的地方。因此,当您打开文件时,您在 table 中创建了一个条目。如果您不关闭文件(及其描述符),则该条目永远不会被删除(这并不意味着您不能再次打开该文件)。

If the file descriptor is part of the func_call() stack, then how does the code return to its pre function call execution point although it has not been popped off? Also in this case why does it persist after the function call exists?

据我所知,每个进程只有一个堆栈,而不是每个函数。所以 fp 变量存储在进程的堆栈中,并在函数结束时从那里删除。

变量int fd;仅在函数func_call()中可见,并且在该函数执行完毕后,它将被弹出堆栈,当输入新函数时,内存可能会被覆盖。事实上,你销毁了一些指向该文件的 int 值并不意味着你关闭了该文件。如果你做了类似的事情会怎样:

int global_fd;
void foo() {
    int local_fd = open("bar.txt", O_RDONLY);
    global_fd = local_fd;
}

并叫 foo()?您是否希望在 foo 退出后无法再使用 global_fd

在这种情况下,将文件描述符视为指针是有帮助的,您要求内核为您提供文件,它为您提供一个值,您可以将其用作该特定文件的标记,这个标记是用来让内核知道 readlseek 之类的函数应该作用于哪个文件。当令牌被传递或销毁时,文件保持打开状态,就像销毁指针不会释放分配的内存一样。

文件描述符很特殊。如您所知,它们只是整数。但是它们 "contain" 大量关于正在读取的文件的信息(文件在磁盘上的位置、read/write 指针在文件中的位置等),那么这些信息存储在哪里?答案是它存储在 OS 内核的某个地方。它存储在 OS 内核中,因为内核的工作是为您管理文件 I/O。当我们说指向打开文件的 int 是 "file descriptor" 时,我们的意思是 int 指的是存储在其他地方的信息,有点像指针。 "descriptor" 这个词很重要。有时用于此类情况的另一个词是 "handle"。

大家知道,局部变量的内存一般都存放在栈中。当您从一个函数中 return 时,释放函数局部变量的内存非常简单——它们基本上与函数的堆栈框架一起消失。当它们消失时,它们就真的消失了:(在 C 中)没有办法让一些动作与它们的消失相关联。特别是,对于恰好是文件描述符的变量,无法调用 close() 的效果。

(如果你想在变量消失时进行清理操作,一种方法是使用 C++,并使用 class 变量,并定义一个显式 析构函数 .)

调用 malloc 时会出现类似情况。在这个函数中:

void f()
{
    char *p = malloc(10);
}

我们调用malloc分配10个字节的内存并将returned指针存储在局部指针变量p中,当函数freturn秒。所以我们丢失了指向已分配内存的指针,但是没有调用 free(),所以内存仍然是已分配的。 (这是 内存泄漏 的示例。)