快捷搜索:   nginx

使用/proc文件系统来访问Linux内核

    /proc 文件系统是一个虚拟文件系统,通过它可以使用一种新的方法在 Linux? 内核空间和用户空间之间进行通信。在 /proc 文件系统中,我们可以将对虚拟文件的读写作为与内核中实体进行通信的一种手段,但是与普通文件不同的是,这些虚拟文件的内容都是动态创建的。本文对 /proc 虚拟文件系统进行了介绍,并展示了它的用法。

    最初开发 /proc 文件系统是为了提供有关系统中进程的信息。但是由于这个文件系统非常有用,因此内核中的很多元素也开始使用它来报告信息,或启用动态运行时配置。

    /proc 文件系统包含了一些目录(用作组织信息的方式)和虚拟文件。虚拟文件可以向用户呈现内核中的一些信息,也可以用作一种从用户空间向内核发送信息的手段。实际上我们并不会同时需要实现这两点,但是本文将向您展示如何配置这个文件系统进行输入和输出。

    尽管像本文这样短小的一篇文章无法详细介绍 /proc 的所有用法,但是它依然对这两种用法进行了展示,从而可以让我们体会一下 /proc 是多么强大。清单 1 是对 /proc 中部分元素进行一次交互查询的结果。它显示的是 /proc 文件系统的根目录中的内容。注意,在左边是一系列数字编号的文件。每个实际上都是一个目录,表示系统中的一个进程。由于在 GNU/Linux 中创建的第一个进程是 init 进程,因此它的 process-id 为 1.然后对这个目录执行一个 ls 命令,这会显示很多文件。每个文件都提供了有关这个特殊进程的详细信息。例如,要查看 init 的 command-line 项的内容,只需对 cmdline 文件执行 cat 命令。

    /proc 中另外一些有趣的文件有:cpuinfo,它标识了处理器的类型和速度;pci,显示在 PCI 总线上找到的设备;modules,标识了当前加载到内核中的模块。

    清单 1. 对 /proc 的交互过程

[root@plato]# ls /proc
1     2040  2347  2874  474          fb           mdstat      sys
104   2061  2356  2930  9            filesystems  meminfo     sysrq-trigger
113   2073  2375  2933  acpi         fs           misc        sysvipc
1375  21    2409  2934  buddyinfo    ide          modules     tty
1395  2189  2445  2935  bus          interrupts   mounts      uptime
1706  2201  2514  2938  cmdline      iomem        mtrr        version
179   2211  2515  2947  cpuinfo      ioports      net         vmstat
180   2223  2607  3     crypto       irq          partitions
181   2278  2608  3004  devices      kallsyms     pci
182   2291  2609  3008  diskstats    kcore        self
2     2301  263   3056  dma          kmsg         slabinfo
2015  2311  2805  394   driver       loadavg      stat
2019  2337  2821  4     execdomains  locks        swaps
[root@plato 1]# ls /proc/1
auxv     cwd      exe  loginuid  mem     oom_adj    root  statm   task
cmdline  environ  fd   maps      mounts  oom_score  stat  status  wchan
[root@plato]# cat /proc/1/cmdline
init [5]
[root@plato]#

  清单 2 展示了对 /proc 中的一个虚拟文件进行读写的过程。这个例子首先检查内核的 TCP/IP 栈中的 IP 转发的目前设置,然后再启用这种功能。


  清单 2. 对 /proc 进行读写(配置内核)
[root@plato]# cat /proc/sys/net/ipv4/ip_forward 0 [root@plato]# echo "1" > /proc/sys/net/ipv4/ip_forward [root@plato]# cat /proc/sys/net/ipv4/ip_forward 1 [root@plato]#   另外,我们还可以使用 sysctl 来配置这些内核条目。有关这个问题的更多信息,请参阅 参考资料 一节的内容。

    顺便说一下,/proc 文件系统并不是 GNU/Linux 系统中的惟一一个虚拟文件系统。在这种系统上,sysfs 是一个与 /proc 类似的文件系统,但是它的组织更好(从 /proc 中学习了很多教训)。不过 /proc 已经确立了自己的地位,因此即使 sysfs 与 /proc 相比有一些优点,/proc 也依然会存在。还有一个 debugfs 文件系统,不过(顾名思义)它提供的更多是调试接口。debugfs 的一个优点是它将一个值导出给用户空间非常简单(实际上这不过是一个调用而已)。

    内核模块简介

    可加载内核模块(LKM)是用来展示 /proc 文件系统的一种简单方法,这是因为这是一种用来动态地向 Linux 内核添加或删除代码的新方法。LKM 也是 Linux 内核中为设备驱动程序和文件系统使用的一种流行机制。

    如果您曾经重新编译过 Linux 内核,就可能会发现在内核的配置过程中,有很多设备驱动程序和其他内核元素都被编译成了模块。如果一个驱动程序被直接编译到了内核中,那么即使这个驱动程序没有运行,它的代码和静态数据也会占据一部分空间。但是如果这个驱动程序被编译成一个模块,就只有在需要内存并将其加载到内核时才会真正占用内存空间。有趣的是,对于 LKM 来说,我们不会注意到有什么性能方面的差异,因此这对于创建一个适应于自己环境的内核来说是一种功能强大的手段,这样可以根据可用硬件和连接的设备来加载对应的模块。

    下面是一个简单的 LKM,可以帮助您理解它与在 Linux 内核中看到的标准(非动态可加载的)代码之间的区别。清单 3 给出了一个最简单的 LKM.(可以从本文的 下载 一节中下载这个代码)。

    清单 3 包括了必须的模块头(它定义了模块的 API、类型和宏)。然后使用 MODULE_LICENSE 定义了这个模块使用的许可证。此处,我们定义的是 GPL,从而防止会污染到内核。

    清单 3 然后又定义了这个模块的 init 和 cleanup 函数。my_module_init 函数是在加载这个模块时被调用的,它用来进行一些初始化方面的工作。my_module_cleanup 函数是在卸载这个模块时被调用的,它用来释放内存并清除这个模块的踪迹。注意此处 printk 的用法:这是内核的 printf 函数。KERN_INFO 符号是一个字符串,可以用来对进入内核回环缓冲区的信息进行过滤(非常类似于 syslog)。

    最后,清单 3 使用 module_init 和 module_exit 宏声明了入口函数和出口函数。这样我们就可以按照自己的意愿来对这个模块的 init 和 cleanup 函数进行命名了,不过我们最终要告诉内核维护函数就是这些函数。

    清单 3. 一个简单的但可以正常工作的 LKM(simple-lkm.c)

#include <linux/module.h>

/* Defines the license for this LKM */
MODULE_LICENSE("GPL");

/* Init function called on module entry */
int my_module_init( void )
{
  printk(KERN_INFO "my_module_init called.  Module is now loaded.\n");

  return 0;
}

/* Cleanup function called on module exit */
void my_module_cleanup( void )
{
  printk(KERN_INFO "my_module_cleanup called.  Module is now unloaded.\n");

  return;
}

/* Declare entry and exit functions */
module_init( my_module_init );
module_exit( my_module_cleanup );

  清单 3 尽管非常简单,但它却是一个真正的 LKM。现在让我们对其进行编译并在一个 2.6 版本的内核上进行测试。2.6 版本的内核为内核模块的编译引入了一种新方法,我发现这种方法比原来的方法简单了很多。对于文件 simple-lkm.c,我们可以创建一个 makefile,其惟一内容如下:

obj-m += simple-lkm.o
  要编译 LKM,请使用 make 命令,如清单 4 所示。

 [2] [3] 下一页

顶(0)
踩(0)

您可能还会对下面的文章感兴趣:

最新评论