proc 编程

proc编程

   与sysfs虚拟文件系统相比,proc的使用没有sysfs那么组织严谨,更加随意,因此在内核模块中用proc文件系统与用户空间进行信息交互还是很方便的。

     在内核模块中使用proc,需要包含头文件<linux/proc_fs.h>

     其中常用的函数包括:

  1. 创建一个文件:

     struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,   struct proc_dir_entry *parent); 

      name: 文件名称

      mode:文件属性,常用S_IWUSR | S_IRUGO

      parent:父目录,即创建的文件在那个目录下,若为NULL,则在/proc目录下创建文件

   2.   创建一个只读的文件:

      struct proc_dir_entry* create_proc_read_entry(const char* name, mode_t mode, struct proc_dir_entry* parent, read_proc_t*  read_proc, void* data);

     read_proc是读操作回调函数,data是多个文件共享一个操作函数时区分文件的私有数据

   3.   创建一个目录:

      struct proc_dir_entry *proc_mkdir(const char *name,  struct proc_dir_entry *parent); 

      参数意义同上,只是这个函数在proc文件系统中创建的是一个目录

   4.    创建一个符号链接:

      struct proc_dir_entry* proc_symlink(const chat *name, struct proc_dir_entry *parent, const char *dest)

      在parent目录下创建符号链接,效果如同:ln –s dest name

   5.   创建一个设备节点

      struct proc_dir_entry* proc_mknod(const char *name, mode_t mode, struct proc_dir_entry *parent, kdev_t rdev)

      在parent目录下创建一个设备节点,其中rdev可以由MKDEV宏生成,mode参数必须包含S_IFBLK或S_IFCHR表示创建的是块设备还是字符设备。

      效果如同:mknod --mode=mode name rdev

   6.   移除proc文件系统中的入口

      void remove_proc_entry(const char *name, struct proc_dir_entry *parent);

      上面创建的所有对象,都可以通过这个函数移除

   

      为了对proc文件系统中的文件进行读写操作,需要设置相应的回调函数,在返回的对应proc文件的struct proc_dir_entry结构中,设置其中的:

      entry->read_proc和entry->write_proc参数。

      其中entry->read_proc的函数原型为:

      int read_func(char* page, char** start, off_t off, int count,  int* eof, void* data); 从内核读取数据

      内核返回的数据需要写入page,其中page为内核地址,off和count分别是写入在page中的偏移地址和可以写入的最大字节,则两个参数主要是用于more和less命令,一般若内容较少可以忽略这两个参数。若这两个参数被使用,则需要设置eof为1来表示已到达文件尾部。

     data参数用于同一个read_func函数为多个proc文件服务时,进行区分所操作的proc文件,并可以存放私有数据。

     这个函数的返回值是复制到page内存中的字节数。

      entry->write_proc的函数原型为:

      int write_func(struct file* file, const char* buffer, unsigned long count, void* data); 写入数据到内核

      参数count表示可以从buffer中访问的最大字节数,由于buffer是用户空间内存,需要使用copy_from_user复制buffer中的数据到内核空间。file参数一般被忽略,而data同上也是用于区分多个文件时的操作对象。

      参数data的使用实例一般为:

      struct proc_dir_entry* entry; 
      struct my_file_data *file_data; //文件数据 

      file_data = kmalloc(sizeof(struct my_file_data), GFP_KERNEL); //分配空间 
      entry->data = file_data; //这里是重点,需要将其赋值给对应proc文件的proc_dir_entry结构,在read和write是才能区分

      int foo_read_func(char *page, char **start, off_t off, int count, int *eof, void *data) 
      { 
               int len;


              if(data == file_data) { 
                  /* 操作对应的文件*/ 
               }

              else { 
                   /* 操作其他文件*/ 
                 } 

                return len; 
         }

         需要注意的是,若entry->data的参数是动态分配的,在调用remove_proc_entry时,需要先将对应的data空间释放。

#include <linux/module.h> 
#include <linux/kernel.h> 
#include <linux/init.h> 
#include <linux/proc_fs.h> 
#include <linux/sched.h> 
#include <asm/uaccess.h>

#define MODULE_VERSION "1.0" 
#define MODULE_NAME "procfs_example" 
#define FOOBAR_LEN 8

/*文件私有数据结构*/ 
struct fb_data_t { 
    char name[FOOBAR_LEN + 1]; 
    char value[FOOBAR_LEN + 1]; 
};

static struct proc_dir_entry *example_dir, *foo_file, *bar_file, *jiffies_file, *tty_device, *symlink; 
struct fb_data_t foo_data, bar_data;

/*读取内核jiffies*/ 
static int proc_read_jiffies(char *page, char **start, off_t off, int count, int *eof, void *data) 

    int len;

    len = sprintf(page, "jiffies = %ld\n", jiffies);

    return len; 
}

/*读取foo和bar文件*/ 
static int proc_read_foobar(char *page, char **start, off_t off, int count, int *eof, void *data) 

    int len; 
    struct fb_data_t *fb_data = (struct fb_data_t *)data;

    len = sprintf(page, "%s = ’%s’\n", fb_data->name, fb_data->value);

    return len; 
}

/*写入foo和bar文件*/ 
static int proc_write_foobar(struct file *file, const char *buffer, unsigned long count, void *data) 

    int len; 
    struct fb_data_t *fb_data = (struct fb_data_t *)data;

    if(count > FOOBAR_LEN) 
        len = FOOBAR_LEN; 
    else 
        len = count; 
    if(copy_from_user(fb_data->value, buffer, len)) 
        return -EFAULT;

    fb_data->value[len] = ’\0’;

    return len; 
}

/*模块初始化函数*/ 
static int __init init_procfs_example(void) 

    int rv = 0; 
    /* 在/proc下创建一个父目录 */ 
    example_dir = proc_mkdir(MODULE_NAME, NULL); 
    if(example_dir == NULL) { 
        rv = -ENOMEM; 
        goto out; 
    }

    /* 创建读取jiffies的proc文件 */ 
    jiffies_file = create_proc_read_entry("jiffies", 0444, example_dir, proc_read_jiffies, NULL); 
    if(jiffies_file == NULL) { 
        rv = -ENOMEM; 
        goto no_jiffies; 
    }

    /* 创建foo和bar文件,使用同一读写回调函数 */ 
    foo_file = create_proc_entry("foo", 0644, example_dir); 
    if(foo_file == NULL) { 
        rv = -ENOMEM; 
        goto no_foo; 
    } 
    /*设置foo文件的私有数据和读写回调函数*/ 
    strcpy(foo_data.name, "foo"); 
    strcpy(foo_data.value, "foo"); 
    foo_file->data = &foo_data; 
    foo_file->read_proc = proc_read_foobar; 
    foo_file->write_proc = proc_write_foobar; 
    /* 创建bar文件 */ 
    bar_file = create_proc_entry("bar", 0644, example_dir); 
    if(bar_file == NULL) { 
        rv = -ENOMEM; 
        goto no_bar; 
    }

    /*设置bar私有数据和读写回调函数*/ 
    strcpy(bar_data.name, "bar"); 
    strcpy(bar_data.value, "bar"); 
    bar_file->data = &bar_data; 
    bar_file->read_proc = proc_read_foobar; 
    bar_file->write_proc = proc_write_foobar;

    /* 创建设备文件 */ 
    tty_device = proc_mknod("tty", S_IFCHR | 0666, example_dir, MKDEV(5, 0)); 
    if(tty_device == NULL) { 
        rv = -ENOMEM; 
        goto no_tty; 
    }

    /* 创建符号链接 */ 
    symlink = proc_symlink("jiffies_too", example_dir, "jiffies"); 
    if(symlink == NULL) { 
        rv = -ENOMEM; 
        goto no_symlink; 
    }

    printk(KERN_INFO "%s %s initialised\n", MODULE_NAME, MODULE_VERSION); 
    return 0;

no_symlink: 
    remove_proc_entry("tty", example_dir); 
no_tty: 
    remove_proc_entry("bar", example_dir); 
no_bar: 
    remove_proc_entry("foo", example_dir); 
no_foo: 
    remove_proc_entry("jiffies", example_dir); 
no_jiffies: 
    remove_proc_entry(MODULE_NAME, NULL); 
out: 
    return rv; 
}

/*模块卸载函数*/ 
static void __exit cleanup_procfs_example(void) 

    remove_proc_entry("jiffies_too", example_dir); 
    remove_proc_entry("tty", example_dir); 
    remove_proc_entry("bar", example_dir); 
    remove_proc_entry("foo", example_dir); 
    remove_proc_entry("jiffies", example_dir); 
    remove_proc_entry(MODULE_NAME, NULL); 
    printk(KERN_INFO "%s %s removed\n", 
    MODULE_NAME, MODULE_VERSION); 
}

module_init(init_procfs_example); 
module_exit(cleanup_procfs_example); 
MODULE_AUTHOR("Erik Mouw"); 
MODULE_DESCRIPTION("procfs examples");


转自:http://blog.chinaunix.net/uid-14518381-id-3768946.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值