/proc/* 如何工作?

use*_*000 69 linux proc

有许多文件/proc,如/proc/cpuinfo/proc/meminfo/proc/devices等等,其中,打开时,返回系统信息。

这些文件在现实中似乎不存在,因为file在它们上运行只会表明它们是空的。

$ file /proc/cpuinfo
/proc/cpuinfo: empty
Run Code Online (Sandbox Code Playgroud)

这些文件究竟是如何工作的?

use*_*ser 77

它实际上非常简单,至少如果您不需要实现细节。

首先,在 Linux 上,所有文件系统(ext2、ext3、btrfs、reiserfs、tmpfs、zfs...)都在内核中实现。有些可能通过 FUSE 将工作卸载到用户态代码,有些仅以内核模块的形式出现(由于许可限制,原生 ZFS是后者的一个显着例子),但无论哪种方式,都保留了内核组件。这是一个重要的基础。

当程序想要从文件中读取时,它会发出各种系统库调用,这些调用最终以open(), read(),close()序列的形式出现在内核中(seek()为了更好的测量,可能会抛出)。内核采用提供的路径和文件名,并通过文件系统和设备 I/O 层将这些转换为物理读取请求(在许多情况下还包括写入请求——例如 atime 更新)到一些底层存储。

但是,它不必专门将这些请求转换为物理持久存储。内核的约定是发出一组特定的系统调用将提供相关文件的内容。在我们的物理领域中,“文件”存在的确切位置是次要的。

/proc通常安装所谓的procfs。这是一种特殊的文件系统类型,但由于它是一个文件系统,它确实与例如ext3安装在某处的文件系统没有什么不同。所以请求被传递到 procfs 文件系统驱动程序代码,它知道所有这些文件和目录,并从内核数据结构返回特定的信息片段

在这种情况下,“存储层”是内核数据结构,它procfs提供了一个干净、方便的接口来访问这些数据结构。请记住,将 procfs 挂载/proc为简单的约定;您可以轻松地将其安装在其他地方。事实上,有时会这样做,例如在 chroot jails 中,当运行在那里的进程出于某种原因需要访问 /proc 时。

如果您将值写入某个文件,它的工作方式相同;在内核级别,这转化为一系列open(), seek(), write(),close()调用,这些调用再次传递给文件系统驱动程序;再次,在这种特殊情况下,procfs 代码。

您看到file返回的特定原因empty是 procfs 公开的许多文件都以0 字节的大小公开0 字节大小可能是内核方面的优化(/proc 中的许多文件是动态的,长度很容易变化,甚至可能从一次读取到下一次读取,并且计算每个目录读取的每个文件的长度会可能非常昂贵)。根据对此答案的评论,您可以通过运行 strace 或类似工具在您自己的系统上进行验证,file首先发出stat()调用以检测任何特殊文件,然后如果文件大小报告为 0,则借此机会, 中止并报告文件为空。

这种行为实际上是记录,并可以通过指定覆盖-s--special-filesfile调用,尽管在可能有副作用的手册页说明。以下引用来自 BSD 文件 5.11 手册页,日期为 2011 年 10 月 17 日。

通常,file 仅尝试读取和确定 stat(2) 报告为普通文件的参数文件的类型。这可以防止出现问题,因为读取特殊文件可能会产生特殊的后果。指定该-s选项会导致 file 还读取作为块或字符特殊文件的参数文件。这对于确定原始磁盘分区中数据的文件系统类型非常有用,这些数据是块特殊文件。 此选项还会导致 file 忽略 stat(2) 报告的文件大小,因为在某些系统上它报告原始磁盘分区的大小为零。

  • 当你用 `strace file /proc/version` 或 `ltrace -S /proc/version` 来查看它时,优化是相当小的。它首先执行 `stat()` 调用并发现大小为 0,因此跳过了 `open()` - 但在此之前它加载了几个魔法文件。 (5认同)
  • @Gilles `-s` 应该用于块/字符特殊设备。最后我查看了 `file` 源,在 fsmagic.c 的末尾我看到了这个解释为什么它返回 `ASCII text` 而不是 `empty`:`如果 stat() 告诉我们文件长度为零,请在此处报告文件是空的,所以我们可以跳过打开和读取文件的所有工作。但是如果给出了 -s 选项,我们会跳过这个优化,因为在某些系统上,stat() 报告原始磁盘分区的大小为零。` (4认同)
  • @ott--这确实是一个奇怪的事件序列,但**可能**与您可以将多个文件名传递给`file`的事实有关。这样,file 预加载魔法文件,然后逐个参数处理命令行参数;而不是将魔法文件加载移动到代码的“在尝试确定*这个*特定文件类型是什么类型的文件之前执行此操作”部分,这会增加复杂性。调用 `stat()` 并对其返回值进行操作基本上是无害的;增加跟踪引入错误的额外内部状态风险的复杂性。 (2认同)

std*_*err 17

在此目录中,您可以控制内核如何查看设备、调整内核设置、向内核添加设备以及再次删除它们。在这个目录下可以直接查看内存使用情况和I/O统计。

您可以查看挂载了哪些磁盘以及使用了哪些文件系统。简而言之,如果您知道要查找的内容,则可以从该目录中检查 Linux 系统的每个方面。

/proc目录不是普通目录。如果您要从引导 CD 引导并查看硬盘驱动器上的该目录,您会看到它是空的。当您在正常运行的系统下查看它时,它可能非常大。但是,它似乎没有使用任何硬盘空间。这是因为它是一个虚拟文件系统。

由于/proc文件系统是一个虚拟文件系统并驻留在内存中,/proc因此每次 Linux 机器重新启动时都会创建一个新的文件系统。

换句话说,它只是一种通过文件和目录类型界面轻松窥视和窥探 Linux 系统内部结构的手段。当您查看/proc目录中的文件时,您正在直接查看 Linux 内核中的内存范围,并查看它可以看到的内容。

文件系统中的层

在此处输入图片说明

例子:

  • 在 里面/proc,每个正在运行的进程都有一个目录,以其进程 ID 命名。这些目录包含具有有关进程的有用信息的文件,例如:
    • exe:这是启动进程的磁盘上文件的符号链接。
    • cwd: 这是进程工作目录的符号链接。
    • wchan: 读取时,返回进程所在的等待通道。
    • maps:读取时返回进程的内存映射。
  • /proc/uptime 将正常运行时间返回为以秒为单位的两个十进制值,用空格分隔:
    • 自内核启动以来的时间。
    • 内核空闲的时间。
  • /proc/interrupts: 有关中断的信息。
  • /proc/modules: 模块列表。

有关更多详细信息,请参阅man prockernel.org


Law*_*ceC 6

你是对的,它们不是真正的文件。

简单来说,它是一种使用读写文件的正常方法与内核对话的方式,而不是直接调用内核。它符合 Unix 的“一切都是文件”的理念。

中的文件/proc实际上并不存在于任何地方,但内核会对您在其中读取和写入的文件做出反应,而不是写入存储,而是报告信息或执行某些操作。

类似地,其中的文件/dev并不是传统意义上的真正文件(尽管在某些系统上,其中的文件/dev实际上可能存在于磁盘上,但除了它们所指的设备外,它们对它们没有任何意义)-它们使您能够交谈到使用普通 Unix 文件 I/O API 的设备 - 或任何使用它的设备,如 shell


Cir*_*郝海东 5

创建新 procfs 条目的最小可运行示例

在我看来,理解这些事情的最好方法是实际使用它们,所以这里是一个创建 procfs 条目的内核模块:

myprocfs.c

#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h> /* seq_read, seq_lseek, single_open, single_release */
#include <uapi/linux/stat.h> /* S_IRUSR */

static const char *filename = "lkmc_procfs";

static int show(struct seq_file *m, void *v)
{
    seq_printf(m, "abcd\n");
    return 0;
}

static int open(struct inode *inode, struct  file *file)
{
    return single_open(file, show, NULL);
}

static const struct file_operations fops = {
    .llseek = seq_lseek,
    .open = open,
    .owner = THIS_MODULE,
    .read = seq_read,
    .release = single_release,
};

static int myinit(void)
{
    proc_create(filename, 0, NULL, &fops);
    return 0;
}

static void myexit(void)
{
    remove_proc_entry(filename, NULL);
}

module_init(myinit)
module_exit(myexit)
MODULE_LICENSE("GPL");
Run Code Online (Sandbox Code Playgroud)

然后我们与它交互如下:

insmod procfs.ko
cat /proc/lkmc_procfs
Run Code Online (Sandbox Code Playgroud)

并产生输出:

abcd
Run Code Online (Sandbox Code Playgroud)

从这个例子中,我们清楚地看到proc文件允许我们实现任意“文件相关的系统调用”,例如openreadllseek

然后,这些系统调用可用于与内核进行任意通信。

因此,这些文件不需要与文件系统中的实际文件有任何关系,几乎所有文件都是如此。

例如,在我们的小示例中,我们只是创建一个read始终返回的无用文件abcd\n

这是我的全自动 QEMU + Buildroot 设置,可以轻松安全地构建和使用此内核模块:

其他一些类似的接口包括: