5.1.关于 /proc 文件系统

在Linux中有另一种内核和内核模块向进程传递信息的方法,那就是通过 /proc 文件系统。它原先设计的目的是为查看进程信息提供一个方便的途径,现在它被用来向用户提供各种内核中被感兴趣的内容。像文件 /proc/modules 里是已加载模块的列表,文件 /proc/meminfo 里是关于内存使用的信息。

使用 proc 文件系统的方法同使用设备文件很相似。——你建立一个包含 /proc 文件需要的所有信息的结构体,这其中包括处理各种事务的函数的指针(在我们的例子中,只用到从 /proc 文件读取信息的函数)。然后在 init_module 时向内核注册这个结构体,在 cleanup_module 时注销这个结构体。

我们使用 proc_register_dynamic[1] 的原因是我们并没有提前设置 inode 数值而由系统内核去动态分配从而避免冲突。普通的文件是建立在磁盘上的,而 /proc 的文件仅仅是建立在内存中的。在前种情况中,inode的数值是一个指向存储在磁盘某个位置的文件的索引节点(inode就是index-node的缩写)。该索引节点储存着文件的信息,像文件的权限;同时还有在哪儿能找到文件中的数据。

因为我们无法得知该文件是被打开的或关闭的,我们也无法去使用宏 MOD_INC_USE_COUNTMOD_DEC_USE_COUNT 在下面的模块中,我们无法避免该文件被打开而同时模块又被卸载。在下章中我将介绍一个较难实现,却更灵活,更安全的处理 /proc 文件的方法。

Example 5-1. procfs.c

/*  procfs.c -  create a "file" in /proc 
 */

#include <linux/kernel.h>   /* We're doing kernel work */
#include <linux/module.h>   /* Specifically, a module */

/* Deal with CONFIG_MODVERSIONS */
#if CONFIG_MODVERSIONS==1
#define MODVERSIONS
#include <linux/modversions.h>
#endif        


/* Necessary because we use the proc fs */
#include <linux/proc_fs.h>



/* In 2.2.3 /usr/include/linux/version.h includes a 
 * macro for this, but 2.0.35 doesn't - so I add it 
 * here if necessary. */
#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c))
#endif



/* Put data into the proc fs file.

   Arguments
   =========
   1. The buffer where the data is to be inserted, if 
      you decide to use it.
   2. A pointer to a pointer to characters. This is 
      useful if you don't want to use the buffer 
      allocated by the kernel.
   3. The current position in the file. 
   4. The size of the buffer in the first argument.  
   5. Zero (for future use?).


   Usage and Return Value
   ======================
   If you use your own buffer, like I do, put its 
   location in the second argument and return the 
   number of bytes used in the buffer.

   A return value of zero means you have no further 
   information at this time (end of file). A negative 
   return value is an error condition.
   

   For More Information
   ==================== 
   The way I discovered what to do with this function 
   wasn't by reading documentation, but by reading the 
   code which used it. I just looked to see what uses 
   the get_info field of proc_dir_entry struct (I used a 
   combination of find and grep, if you're interested), 
   and I saw that  it is used in <kernel source 
   directory>/fs/proc/array.c.

   If something is unknown about the kernel, this is 
   usually the way to go. In Linux we have the great 
   advantage of having the kernel source code for 
   free - use it.
 */
int procfile_read(char *buffer, 
		  char **buffer_location, 
		  off_t offset, 
		  int buffer_length, 
		  int zero)
{
  int len;  /* The number of bytes actually used */

  /* This is static so it will still be in memory 
   * when we leave this function */
  static char my_buffer[80];  

  static int count = 1;

  /* We give all of our information in one go, so if the 
   * user asks us if we have more information the 
   * answer should always be no. 
   *
   * This is important because the standard read 
   * function from the library would continue to issue 
   * the read system call until the kernel replies
   * that it has no more information, or until its 
   * buffer is filled.
   */
  if (offset > 0)
    return 0;

  /* Fill the buffer and get its length */
  len = sprintf(my_buffer, 
    "For the %d%s time, go away!\n", count,
    (count % 100 > 10 && count % 100 < 14) ? "th" : 
      (count % 10 == 1) ? "st" :
        (count % 10 == 2) ? "nd" :
          (count % 10 == 3) ? "rd" : "th" );
  count++;

  /* Tell the function which called us where the 
   * buffer is */
  *buffer_location = my_buffer;

  /* Return the length */
  return len;
}


struct proc_dir_entry Our_Proc_File = 
  {
    0, /* Inode number - ignore, it will be filled by 
        * proc_register[_dynamic] */
    4, /* Length of the file name */
    "test", /* The file name */
    S_IFREG | S_IRUGO, /* File mode - this is a regular 
                        * file which can be read by its 
                        * owner, its group, and everybody
                        * else */
    1,	/* Number of links (directories where the 
         * file is referenced) */
    0, 0,  /* The uid and gid for the file - we give it 
            * to root */
    80, /* The size of the file reported by ls. */
    NULL, /* functions which can be done on the inode 
           * (linking, removing, etc.) - we don't 
           * support any. */
    procfile_read, /* The read function for this file, 
                    * the function called when somebody 
                    * tries to read something from it. */
    NULL /* We could have here a function to fill the 
          * file's inode, to enable us to play with 
          * permissions, ownership, etc. */
  }; 





/* Initialize the module - register the proc file */
int init_module()
{
  /* Success if proc_register[_dynamic] is a success, 
   * failure otherwise. */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,0)
  /* In version 2.2, proc_register assign a dynamic 
   * inode number automatically if it is zero in the 
   * structure , so there's no more need for 
   * proc_register_dynamic
   */
  return proc_register(&proc_root, &Our_Proc_File);
#else
  return proc_register_dynamic(&proc_root, &Our_Proc_File);
#endif
 
  /* proc_root is the root directory for the proc 
   * fs (/proc). This is where we want our file to be 
   * located. 
   */
}


/* Cleanup - unregister our file from /proc */
void cleanup_module()
{
  proc_unregister(&proc_root, Our_Proc_File.low_ino);
}  

Notes

[1]

这是在2.0版本中的做法,在版本2.2中,当我们把inode设为0时,就已经这样自动处理了。