使用proc文件系统和内核交互

时间:2022-09-26 23:35:59
《使用proc文件系统和内核交互》
本文档的Copyleft归rosetta所有,使用GPL发布,可以*拷贝、转载,转载时请保持文档的完整性。
参考资料:IBM developerWorks 《使用/proc文件系统来访问Linux内核的内容》、Linux-2.6.10内核
来源:http://blog.csdn.net/rosetta/article/details/7563610

    此篇是在《内核模块编写》和《字符设备驱动程序编写基础》基础上写的,但这篇也是基础文章。使用proc文件系统与内核数据交互和之前写的《字符设备驱动程序编写基础》原理非常相似,只不过前者使用的是内核提供给/proc文件系统的一组专用函数,所以本节主要介绍如何使用这组函数,具体原理学习可参考给出的链接。
    可能大家都使用过cat /proc/cpuinfo(meminfo)来查看系统cpu或内存信息,或者也注意到在/proc目录中存在所有以ps -ef进程号为命令的目录,在所有对应的进程号目录中都有许多和该进程相关的文件,具体是些什么文件我不能用最确切的语言来描述或者定义,但我可以肯定的一点是这些文件应该都是由内核里反馈出来的,比如进入某个文件夹,可以cat 对应的status、maps、limits来看看到底包含什么。
    下面首先例出这组函数,再给出一个例子。
一、相关函数和结构体
  1,struct proc_dir_entry *proc_mkdir(const char *name,
        struct proc_dir_entry *parent)//在parent目录创建一个名为name的目录。
   比如:struct proc_dir_entry *proc_net_ipsec_dir = proc_mkdir("ipsec", proc_net);//在proc_net目录创建名为ipsec的目录
  其中proc_net相当于宏,指/proc/net目录,如果第二个参数传NULL,即指默认/proc目录。以下是几个类似的宏。
    proc_root_fs /proc
    proc_net /proc/net
    proc_bus /proc/bus
    proc_root_driver /proc/driver
  2,再继续第二个函数之前有必要先说下porc_mkdir的返回值,它是一个结构体struct pro_dir_entry
  struct proc_dir_entry {
    unsigned int low_ino;
    unsigned short namelen;
    const char *name;
    mode_t mode;
    nlink_t nlink;
    uid_t uid;
    gid_t gid;       
    unsigned long size;
    struct inode_operations * proc_iops;
    struct file_operations * proc_fops;
    get_info_t *get_info;
    struct module *owner;
    struct proc_dir_entry *next, *parent, *subdir;
    void *data;
    read_proc_t *read_proc;
    write_proc_t *write_proc;
    atomic_t count;     /* use count */
    int deleted;        /* delete flag */
  };
  里面包含了一个文件(Linux把所有对象都当文件看代,目录当然也是一个文件)所有属性,比如:文件名、权限、uid、gid、前一级目录、包含的子目录等等,我记得在APUE2e在讲解文件系统时讲到过,所以可以结合上面内容进行理解。
  这次我们不对这些属性感兴趣,主要是关注下目录的读写函数,即read_proc和write_proc,一会例子中会提到。
  3,struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
                     struct proc_dir_entry *parent)//在parent目录创建一个名为name,权限为mode的文件。
    比如:struct proc_dir_entry * item = create_proc_entry("klipsdebug", 0400, "/proc/ipsec")//在/proc/ipsec目录创建一个权限为0400(只读)的文件klipsdebug
  4,create_proc_entry执行后返回的proc_dir_entry可以自己指定read、write等函数,如果只需要read函数,可以使用
    static inline struct proc_dir_entry *create_proc_read_entry(const char *name,
    mode_t mode, struct proc_dir_entry *base,
    read_proc_t *read_proc, void * data)
    它其实是对create_proc_entry进行了封装,把create_proc_read_entry的传入值read_proc赋给了create_proc_entry返回值的read_proc成员,具体可看内核实现。
  5,struct proc_dir_entry *proc_symlink( const char *name,
                                       struct proc_dir_entry *parent,
                                       const char *dest );//在parent目录创建指定dest目录的名为name的符号链接。
  比如: proc_symlink("ipsec_eroute", proc_net, "ipsec/eroute/all");//在/proc/net创建指向/proc/net/ipsec/eroute/all的符号链接ipsec_eroute。
  6,void remove_proc_entry( const char *name, struct proc_dir_entry *parent );//删除parent目录中的名为name的文件
  7,还有两上函数比较重要,就是之前提到的read、write函数中需要调用的函数,这两个函数即完成了用户空间和内核空间的数据交互。
  unsigned long copy_to_user( void __user *to,
                              const void *from,
                              unsigned long n );//

  unsigned long copy_from_user( void *to,
                                const void __user *from,
                                unsigned long n );

二、给出一个具体的例子以便理解,偷懒起见,直接copy链接处的例子,为保证代码完整性,作者及说明在代码中有所体现。

  //proc_study.c
  #include <linux/module.h>
  #include <linux/kernel.h>
  #include <linux/proc_fs.h>
  #include <linux/string.h>
  #include <linux/vmalloc.h>
  #include <asm/uaccess.h>
  MODULE_LICENSE("GPL");
  MODULE_DESCRIPTION("Fortune Cookie Kernel Module");
  MODULE_AUTHOR("M. Tim Jones");
  #define MAX_COOKIE_LENGTH       PAGE_SIZE
  static struct proc_dir_entry *proc_entry;
  static char *cookie_pot;  // Space for fortune strings
  static int cookie_index;  // Index to write next fortune
  static int next_fortune;  // Index to read next fortune
 
  int fortune_read( char *page, char **start, off_t off,
                             int count, int *eof, void *data )
  {
      int len;
      if (off > 0) {
          *eof = 1;
          return 0;
      }
 
      /* Wrap-around */
      if (next_fortune >= cookie_index)
          next_fortune = 0;
 
      len = sprintf(page, "%s\n", &cookie_pot[next_fortune]);
      next_fortune += len;
 
      return len;
  }
 
  ssize_t fortune_write( struct file *filp, const char __user *buff,
                                  unsigned long len, void *data )
  {
      int space_available = (MAX_COOKIE_LENGTH-cookie_index)+1;
      if (len > space_available) {
          printk(KERN_INFO "fortune: cookie pot is full!\n");
          return -ENOSPC;
      }
 
      if (copy_from_user( &cookie_pot[cookie_index], buff, len )) {
          return -EFAULT;
      }
 
      cookie_index += len;
      cookie_pot[cookie_index-1] = 0;
 
      return len;
  }
 
  int init_fortune_module( void )
  {
      int ret = 0;
      cookie_pot = (char *)vmalloc( MAX_COOKIE_LENGTH );
      if (!cookie_pot) {
          ret = -ENOMEM;
      } else {
          memset( cookie_pot, 0, MAX_COOKIE_LENGTH );
          proc_entry = create_proc_entry( "fortune", 0644, NULL );
          if (proc_entry == NULL) {
              ret = -ENOMEM;
              vfree(cookie_pot);
              printk(KERN_INFO "fortune: Couldn't create proc entry\n");
          } else {
              cookie_index = 0;
              next_fortune = 0;
              proc_entry->read_proc = fortune_read;
              proc_entry->write_proc = fortune_write;
              proc_entry->owner = THIS_MODULE;
              printk(KERN_INFO "fortune: Module loaded.\n");
          }
      }
 
      return ret;
  }
 
  void cleanup_fortune_module( void )
  {
      remove_proc_entry("fortune", &proc_root);
      vfree(cookie_pot);
      printk(KERN_INFO "fortune: Module unloaded.\n");
  }
 
  module_init( init_fortune_module );
  module_exit( cleanup_fortune_module );

  //Makefile 自己改的一个Makefile
  #ifneq ($(KERNELRELEASE),)
    obj-m := my_proc.o
    my_proc-objs := proc_study.o
  #else
  KERNELDIR ?= /lib/modules/$(shell uname -r)/build
  PWD := $(shell pwd)
 
  default:
      $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
 
  clean:
      rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
      rm -rf Module.* modules.*
 
  .PHONY: default clean
  #endif

  编译生成my_proc.ko
  下面来测试下:
  [root@xxx proc]# ls /proc/fortune -al
  -rw-r--r-- 1 root root 0 05-13 16:23 /proc/fortune

  [root@xxx proc_study]# insmod my_proc.ko
  [root@xxx proc_study]# dmesg
  fortune: Module loaded.
  [root@xxx proc]# echo "Hello, Just a test" > /proc/fortune
  [root@xxx proc]# echo "Secondary test" > /proc/fortune  
  [root@xxx proc]# ls /proc/fortune  -al                     
  -rw-r--r-- 1 root root 0 05-01 07:04 /proc/fortune
  [root@xxx proc]# cat /proc/fortune
  Secondary test
  [root@xxx proc]# cat /proc/fortune

  [root@xxx proc_study]# rmmod my_proc
  [root@xxx proc_study]# dmesg
  fortune: Module loaded.
  fortune: Module unloaded.