![当我 close() 文件描述符时会发生什么?](https://linux22.com/image/19469/%E5%BD%93%E6%88%91%20close()%20%E6%96%87%E4%BB%B6%E6%8F%8F%E8%BF%B0%E7%AC%A6%E6%97%B6%E4%BC%9A%E5%8F%91%E7%94%9F%E4%BB%80%E4%B9%88%EF%BC%9F.png)
我正在尝试使用文件描述符来了解整个情况。假设我有 process1,它最初有这些文件描述符:
_process1_
| |
| 0 stdin |
| 1 stdout |
| 2 stderr |
|__________|
然后我关闭文件描述符 1:
close(1);
文件描述符 1 转换(指向)到标准输出文件结构在内核的打开文件表。
使用上面的代码,文件描述符 1 从进程表中删除,变为:
_process1_
| |
| 0 stdin |
| 2 stderr |
|__________|
但是内核中发生了什么? FILE 结构是否stdout
被释放?如果 stdout 是一个特殊文件(监视器)并且可能被其他进程使用,这怎么可能?如果 FILE 结构只是普通文件(例如 .txt)呢?如果其他进程正在使用这样的文件怎么办?
答案1
文件描述符 1 转换为内核打开文件表中的 stdout FILE 结构。
这是一个误解。内核的文件表与用户空间文件结构没有任何关系。
无论如何,内核有两个间接级别。有代表文件本身的内部结构,它是引用计数的。有一个“打开文件描述”是引用计数的。然后是文件句柄,它不被引用计数。文件结构指向索引节点本身。打开文件描述包含打开模式和文件指针等内容。
当您调用 close 时,您总是关闭文件句柄。当文件句柄关闭时,其打开的文件描述的引用计数将减少。如果它变为零,则打开的文件描述也会被释放,并且文件本身的引用计数会减少。只有当该值变为零时,内核的文件结构才会被释放。
一个进程没有机会释放另一进程正在使用的资源,因为共享资源是引用计数的。
答案2
在这种情况下,不会发生太多事情。 stdin、stdout 和 stderr 都倾向于是同一文件描述符的克隆。文件描述符的引用计数器将减一。运行程序的 shell 通常保存相同的文件描述符,因此需要保留文件描述符。
内核保留所有打开的文件(索引节点)的引用计数。只要引用计数大于零,文件就会被保留。我希望为打开的文件句柄保留一个单独的计数器。一旦达到零,内核就可以释放文件句柄使用的内存。
当对文件的所有引用(目录条目和文件句柄)都被删除时,文件系统代码将标记该索引节点以供重用。文件具有的任何块都可供分配。许多文件系统在释放inode时都会清除inode中的块指针。这使得恢复已删除的文件变得困难。对磁盘的更新可能会被缓冲并稍后完成。
答案3
您的描述直到最后一段都是正确的。
但是内核中发生了什么? stdout FILE 结构是否被释放?如果 stdout 是一个特殊文件(监视器)并且可能被其他进程使用,这怎么可能?如果 FILE 结构只是普通文件(
afile.txt
例如)呢?如果其他进程正在使用这样的文件怎么办?
除了内核对 stdin/stdout/stderr 一无所知。这些只是文件描述符 0、1 和 2。这是关于它们用途的约定。
不是最后一段。终端窗口已保存。删除文件描述符并不是删除文件。即使rm
只删除目录条目,它也不会删除文件(请参阅其他答案中的引用计数:当没有任何内容引用它时,文件将被删除)。因此,当文件没有目录条目并且不被任何进程打开时,该文件就会被删除。