Chinaunix首页 | 论坛 | 博客
  • 博客访问: 152371
  • 博文数量: 115
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2016-11-28 14:16
文章分类

全部博文(115)

文章存档

2017年(36)

2016年(79)

我的朋友

分类: LINUX

2017-03-12 17:09:09

linux 内核调试技术之proc文件系统的应用


1, proc文件系统初步
1.1, /proc文件系统
   /proc文件系统是一种特殊的,由软件创建的文件系统,内核使用它向外界导出信息。 /proc下面的每个文件都绑定一个内核函数,用户读取其中的文件时,该函数动态的生成文件的“内容”。
   由于/proc文件系统已经被添加了大量的信息。因此,最好的办法是用sysfs而不是/proc文件系统来向外导出信息。
   /proc文件不仅可以用于读数据,也可以用于写数据,不过写数据比较麻烦一些,这里只描述读数据的用法.写数据的用法可以在看完读数据的过程后参考kernel源码.

1.2,创建/proc文件的函数
   前面介绍过/proc下的文件都是在访问时实时生成文件内容的,那么为了创建/proc下的一个只读的文件,我们必须实现一个函数用于在读取文件时生成数据,幸运的是,该函数接口已经设计好了,我们只需要按照函数接口实现自己需要的功能就可以了.该函数的原型如下:
   *向用户层导出数据的读取函数
   int (*read_proc)(char *page, char **start, off_t offset, int count,
                    int *eof, void *data);
   参数说明:
   page : 用来写入数据的缓冲区;也就是说从/proc文件中读到的数据都写入到page指向的缓冲区中.
   start: 用于指定实际的数据写入到page指向的内存页的具体的那个位置.
   offset : 和read参数中的相同
   count  : 和read函数中的参数意义相同
   eof  : 当没有数据返回时,必须设置该参数为一个整数,例如: *eof = 1;
   data : 该参数是内核提供给驱动程序的专用指针,可以用于内部记录

   *创建只读/proc文件的函数
   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)
   参数说明:
   name : 要创建的/proc下的文件名
   mode : 创建的文件权限的掩码,若为0,则使用系统默认的权限
   base : 该文件所在的父目录,若该参数为null,则该文件将会被创建在/proc的根目录下
   read_proc : 读取/proc下的文件时调用的函数,也就是前面讲解的那个函数
   data : 内核会忽略data,但会把该参数传递给read_proc函数

   删除/proc系统文件的函数
   void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
   参数说明:
   name : 在/proc文件系统中创建的文件名
   parent : 父目录名

1.3, 使用/proc文件系统的缺点
   (1) 删除调用可能在/proc文件系统的文件正在被使用时发生
   (2) 同一个文件名可能注册两次,这将会发生错误

 
2, 创建简单的proc文件(一)


/*
 * simple /proc file
 */


#include <linux/module.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/errno.h>

#include <linux/proc_fs.h>
#include <linux/fs.h>

static char dev_name[] = "helloproc";

/*
 * do proc file
 */

static int do_proc_mem(char *buf, char **start, off_t offset,
                        int count, int *eof, void *data)
{
    int len = 0;
    
    len += sprintf(buf+len, "hello world!\n");
    *eof = 1;
    
    return len;
}


static int __init do_proc_init(void)
{
    create_proc_read_entry(dev_name, 0, NULL, do_proc_mem, NULL);

    printk("%s proc file created successfully!\n", dev_name);
    return 0;
}

static void __exit do_proc_exit(void)
{
    remove_proc_entry(dev_name, NULL);

    printk("goodbye %s!\n", dev_name);
}

module_init(do_proc_init);
module_exit(do_proc_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("hover");



2.1, 创建简单的proc文件(二)

/*
 * simple_proc-2.c - create a "file" in /proc
 */


#include <linux/module.h>    /* Specifically, a module */
#include <linux/kernel.h>    /* We're doing kernel work */
#include <linux/proc_fs.h>    /* Necessary because we use the proc fs */

#define procfs_name "helloworld"

/*
 * This structure hold information about the /proc file
 */

struct proc_dir_entry *Our_Proc_File;

/* Put data into the proc fs file.
 *
 * Usage and Return Value
 * ======================
 * 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.
 */

int
procfile_read(char *buffer,
     char **buffer_location,
     off_t offset, int buffer_length, int *eof, void *data)
{
    int ret;
    
    printk(KERN_INFO "procfile_read (/proc/%s) called\n", procfs_name);
    
    if (offset > 0) {
        /* we have finished to read, return 0 */
        ret = 0;
    } else {
        /* fill the buffer, return the buffer size */
        ret = sprintf(buffer, "HelloWorld!\n");
    }

    return ret;
}

static __init int proc_init(void)
{
    Our_Proc_File = create_proc_entry(procfs_name, 0644, NULL);
    
    if (Our_Proc_File == NULL) {
        remove_proc_entry(procfs_name, NULL);
        printk(KERN_ALERT "Error: Could not initialize /proc/%s\n",
         procfs_name);
        return -ENOMEM;
    }

    Our_Proc_File->read_proc = procfile_read;
    Our_Proc_File->owner      = THIS_MODULE;
    Our_Proc_File->mode      = S_IFREG | S_IRUGO;
    Our_Proc_File->uid       = 0;
    Our_Proc_File->gid      = 0;
    Our_Proc_File->size      = 37;

    printk(KERN_ALERT "/proc/%s created\n", procfs_name);    
    return 0;    /* everything is ok */
}

static __exit void proc_cleanup(void)
{
    remove_proc_entry(procfs_name, NULL);
    printk(KERN_ALERT "/proc/%s removed\n", procfs_name);
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("tree");

module_init(proc_init);
module_exit(proc_cleanup);


2.3 利用seq_file管理大容量的/proc文件

   seq_file提供了一套接口函数和方案可以用来安全的访问/proc文件系统。因为它可以容易的管理输出的缓冲区溢出和大于一页大小的文件。
   seq_file接口建设我们正在创建的虚拟文件要遍历一个项目序列,而这些项目正是必须返回给用户空间的。为使用seq_file我们必须创建一个简单的“迭代器iterator”对象,该对象用来表示项目序列中的位置,每前进一步,该对象输出序列中的一个项目。

所以第一个就是建立4个迭代对象,分别为start,next,stop和show
void *start(struct seq_file *sfile, loff_t *pos);
这里pos是一个整数的位置值。
void *next(struct seq_file *sfile, void *v, loff_t *pos);
这里v是先前对start或next的调用所返回的迭代器,pos是文件的当前位置。next方法应增加pos指向的值。这当然依赖于迭代器的工作方式。
void *stop(struct seq_file *sfile, void *v);

int  show(struct seq_file *sfile, void *v);


/*
 * seqproc.c - create a "file" in /proc
 *     This program uses the seq_file library to manage the /proc file.
 */


#include <linux/kernel.h>    /* We're doing kernel work */
#include <linux/module.h>    /* Specifically, a module */
#include <linux/proc_fs.h>    /* Necessary because we use proc fs */
#include <linux/seq_file.h>    /* for seq_file */

#define PROC_NAME    "hover_iter"

MODULE_AUTHOR("PR");
MODULE_LICENSE("GPL");

/*
 * This function is called at the beginning of a sequence.
 * ie, when:
 *    - the /proc file is read (first time)
 *    - after the function stop (end of sequence)
 */

static void *my_seq_start(struct seq_file *s, loff_t *pos)
{
    static unsigned long counter = 0;

    /* beginning a new sequence ? */    
    if (*pos == 0) {    
        /* yes => return a non null value to begin the sequence */
        return &counter;
    } else {
        /* no => it's the end of the sequence, return end to stop reading */
        *pos = 0;
        return NULL;
    }
}

/*
 * This function is called after the beginning of a sequence.
 * It's called untill the return is NULL (this ends the sequence).
 */

static void *my_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
    unsigned long *tmp_v = (unsigned long *)v;
    (*tmp_v)++;
    (*pos)++;
    return NULL;
}

/*
 * This function is called at the end of a sequence
 */

static void my_seq_stop(struct seq_file *s, void *v)
{
    /* nothing to do, we use a static value in start() */
}

/*
 * This function is called for each "step" of a sequence
 */

static int my_seq_show(struct seq_file *s, void *v)
{
    loff_t *spos = (loff_t *) v;
    
    seq_printf(s, "%Ld\n", *spos);
    return 0;
}

/*
 * This structure gather "function" to manage the sequence
 */

static struct seq_operations my_seq_ops = {
    .start = my_seq_start,
    .next = my_seq_next,
    .stop = my_seq_stop,
    .show = my_seq_show
};

/*
 * This function is called when the /proc file is open.
 */

static int my_open(struct inode *inode, struct file *file)
{
    return seq_open(file, &my_seq_ops);
};

/*
 * This structure gather "function" that manage the /proc file
 */

static struct file_operations my_file_ops = {
    .owner = THIS_MODULE,
    .open = my_open,
    .read = seq_read,
    .llseek = seq_lseek,
    .release = seq_release
};
    
    
/*
 * This function is called when the module is loaded
 */

static __init int seq_proc_init(void)
{
    struct proc_dir_entry *entry;

    entry = create_proc_entry(PROC_NAME, 0, NULL);
    if (entry) {
        entry->proc_fops = &my_file_ops;
    }
    
    return 0







阅读(1426) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~