驱动调试之proc文件系统

一、思维导读
    我们知道在/proc目录下存在一些文件,我们可以使用cat、echo命令来查询和设置一些系统的信息(比如查看内存的信息和cpu的信息等),可见/proc目录下的文件提供给我们和内核交互的功能。那么如果我们的驱动也有对应的proc文件,我们就可以和驱动进行交互,实时读取和修改驱动中变量的值,这样一来驱动调试就非常方便了!本文会给出proc的一些基本概念来加深对proc文件系统的理解,但是主要在于应用,下面会贴出一个简单的例子,若是要用直接复制然后自己进行相应的修改即可!

二、proc文件系统 
   
    proc文件系统是一种无存储的文件系统,当读其中的文件时,其内容由文件关联的读函数动态生成,当写文件时,文件所关联的写函数被调用。每个proc文件都关联特定的读写函数,因而它提供了另外的一种和内核通信的机制:内核部件可以通过该文件系统向用户空间提供接口来提供查询信息、修改软件行为,因而它是一种比较重要的特殊文件系统。
    
    由于proc文件系统以文件的形式向用户空间提供了访问接口,这些接口可以用于在运行时获取相关部件的信息或者修改部件的行为,因而它是非常方便的一个接口。内核中大量使用了该文件系统。proc文件系统就是一个文件系统,它可以挂载在目录树的任意位置,不过通常挂载在/proc下。
    
    
三、proc数据结构
    a、proc文件及目录在内核中用proc_dir_entry来表示。它在proc文件系统内部包含了proc文件的所有信息。其数据结构如下所示
        struct proc_dir_entry {  
            unsigned int low_ino;  
            umode_t mode;  
            nlink_t nlink;  
            kuid_t uid;  
            kgid_t gid;  
            loff_t size;  
            const struct inode_operations *proc_iops;//inode操作  
            const struct file_operations *proc_fops;//文件操作  
            struct proc_dir_entry *next, *parent, *subdir;  
            void *data;  
            read_proc_t *read_proc;  
            write_proc_t *write_proc;  
            atomic_t count;       
            int pde_users;    
            struct completion *pde_unload_completion;  
            struct list_head pde_openers;     
            spinlock_t pde_unload_lock;   
            u8 namelen;  
            char name[];  
        };  
    
    b、内核还提供了一个数据结构proc_inode用于将特定于proc的数据与文件所对应的inode关联起来,其定义如下。借助该数据结构,内核可以方便的在inode和与该inode相关的proc数据之间进行转换。    
        struct proc_inode {  
            struct pid *pid;  
            int fd;  
            union proc_op op;  
            struct proc_dir_entry *pde;  
            struct ctl_table_header *sysctl;  
            struct ctl_table *sysctl_entry;  
            void *ns;  
            const struct proc_ns_operations *ns_ops;  
            struct inode vfs_inode;  
        };  
        
四、操作proc文件系统的API    
    相关的函数在内核代码的头文件<linux/proc_fs.h>声明,主要用到以下三个函数。

    1、这个函数用于在proc文件系统中创建一个proc文件。
    struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,  
        struct proc_dir_entry *parent);  
      
    2、这个函数用于在proc文件系统中创建一个目录项,大多数时候,当我们期望实现自己的proc文件时,都要先创建一个自己的目录,然后在该目录里创建自己的文件,当然我们也可以直接在已经存在的proc文件系统目录里创建自己的文件
    struct proc_dir_entry *proc_mkdir(const char *name,
        struct proc_dir_entry *parent);  
    
    3、这个函数用于从proc文件系统的指定目录删除指定的proc文件。实际上也可以用来删除目录的。
    void remove_proc_entry(const char *name, struct proc_dir_entry *parent);


五、例子   

    驱动源代码:

#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/of_address.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/i2c.h>
#include <linux/of_gpio.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>

#define PROCFS_MAX_SIZE		     128
#define PROC_DIR                 "dspinfo"

/*类型定义*/
struct plat_info {
    unsigned char cputype[10];
    unsigned char cpufreq[10];
    unsigned char ramsize[10];
    unsigned char attr[10];
};    

/*全局变量*/
static char S_procfs_buffer[PROCFS_MAX_SIZE];
static char S_debug = 0;

struct plat_info dsp_data[] = {
    {
        .cpufreq = "456",
        .cputype = "C6746",
        .ramsize = "64M",
        .attr    = "standard",
    },
    {
        .cpufreq = "456",
        .cputype = "C6746",
        .ramsize = "64M",
        .attr    = "standard",
    },
    {
        .cpufreq = "456",
        .cputype = "C6746",
        .ramsize = "64M",
        .attr    = "extended",
    },
    {
        .cpufreq = "456",
        .cputype = "C6746",
        .ramsize = "64M",
        .attr    = "extended",
    },
    {
        .cpufreq = "456",
        .cputype = "C6746",
        .ramsize = "64M",
        .attr    = "extended",
    },
    {
        .cpufreq = "456",
        .cputype = "C6746",
        .ramsize = "64M",
        .attr    = "extended",
    },       
    {
        .cpufreq = "456",
        .cputype = "C6746",
        .ramsize = "64M",
        .attr    = "extended",
    }
};

/*函数声明*/
static int dsp_proc_read(struct seq_file *m, void *p);


static void *c_start(struct seq_file *m, loff_t *pos)
{
	return *pos < 1 ? (void *)1 : NULL;
}

static void *c_next(struct seq_file *m, void *v, loff_t *pos)
{
	++*pos;
	return NULL;
}

static void c_stop(struct seq_file *m, void *v)
{
    /*nothing to do*/
}

static int c_show(struct seq_file *m, void *p)
{
    /*调用自己实现的proc读函数*/
    dsp_proc_read(m, p);  
    
    return 0;    
}

static struct seq_operations proc_seq_ops = {
    .show  = c_show,
    .start = c_start,
    .next  = c_next,
    .stop  = c_stop
};

static int dsp_proc_open(struct inode *inode, struct file *file)
{
    int ret = 0;    
    struct seq_file *m;    
    
    ret = seq_open(file, &proc_seq_ops);    
    m = file->private_data;
    m->private = file->f_dentry->d_iname;
    
    return ret; 
}

static int dsp_proc_read(struct seq_file *m, void *p)
{
	int num = 0;
    u8 *name, *cputype, *cpufreq, *ramsize, *attr;

         
	name = m->private;	
	num = name[3] - '0';

    cpufreq = dsp_data[num-1].cpufreq;
    cputype = dsp_data[num-1].cputype;
    ramsize = dsp_data[num-1].ramsize;
    attr    = dsp_data[num-1].attr; 
	
	seq_printf(m, "attr              : %s\n", attr);
	seq_printf(m, "cputype           : %s\n", cputype);
	seq_printf(m, "cpufreq           : %s\n", cpufreq);
	seq_printf(m, "ramsize           : %s\n", ramsize);
	seq_printf(m, "\n");	
    
    return 0;
}

static int dsp_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *offp)
{
    int value;
    int procfs_buffer_size;
    
	if(count > PROCFS_MAX_SIZE) {
	    procfs_buffer_size = PROCFS_MAX_SIZE;
	} else {
	    procfs_buffer_size = count;
	}
	
	if (copy_from_user(S_procfs_buffer, buffer, procfs_buffer_size)) {
		return -EFAULT;
	}
	*(S_procfs_buffer+procfs_buffer_size) = 0;
	
    if(sscanf(S_procfs_buffer , "set debug %d" , &value)==1) {
        S_debug = value;
    } else {
        printk("=================\n");
        printk("usage: \n");
        printk("set debug <value>\n");
        printk("=================\n");
    }
    
    return count;
}

static struct file_operations proc_fops = {
    .open  = dsp_proc_open,
    .read  = seq_read,
    .write = dsp_proc_write,      
};
 
int dsp_create_proc(char *name)
{
    struct proc_dir_entry *dsp_proc_entries;
    u8 proc_name[50]={0};
    
    if(PROC_DIR == NULL) {
        sprintf(proc_name, "%s", name);
    } else {
        sprintf(proc_name, "%s/%s", PROC_DIR, name);
    }
    
    dsp_proc_entries = proc_create(proc_name, 0644, NULL, &proc_fops);
    if (NULL == dsp_proc_entries) {
	    remove_proc_entry(proc_name, NULL);
	    printk("Error: Could not initialize /proc/%s\n", proc_name);
	    return -ENOMEM;
    }

	return 0;
}

int dsp_create_proc_parentdir(void)
{
    struct proc_dir_entry *mydir = NULL;
    
    mydir = proc_mkdir(PROC_DIR, NULL);
    if (!mydir) {
		printk(KERN_ERR "Can't create /proc/%s\n", PROC_DIR);
		return -1;
	}
	return 0;
}

void dsp_remove_proc(char *name)
{
    u8 proc_name[50]={0};
    
    if(PROC_DIR == NULL) {
        sprintf(proc_name, "%s", name);
    } else {
        sprintf(proc_name, "%s/%s", PROC_DIR, name);
    }
    
	remove_proc_entry(proc_name, NULL);
	
	return;
}

void dsp_remove_proc_parentdir(void)
{
    if(PROC_DIR != NULL) {
        remove_proc_entry(PROC_DIR, NULL);
    }
    
    return;
}

static int __init driver_proc_init(void)
{
    printk("%s...\n", __func__);
    
    dsp_create_proc_parentdir();
    dsp_create_proc("dsp1");
    dsp_create_proc("dsp2");
    dsp_create_proc("dsp3");
    
    return 0;
}

static void __exit driver_proc_exit(void)
{
    printk("%s...\n", __func__);
    
    dsp_remove_proc("dsp1");
    dsp_remove_proc("dsp2");
    dsp_remove_proc("dsp3");
    dsp_remove_proc_parentdir();
}

module_init(driver_proc_init);
module_exit(driver_proc_exit);

MODULE_AUTHOR("Jimmy");
MODULE_DESCRIPTION("driver for proc test");
MODULE_LICENSE("GPL");

    Makefile文件:

ifneq ($(KERNELRELEASE),)
obj-m := driver_proc.o
else
KERNELDIR ?= /ljm/git_imx6/linux-fsl/src/linux-3-14-28-r0
TARGET_CROSS = arm-none-linux-gnueabi-

PWD := $(shell pwd)

default:
	$(MAKE) ARCH=arm CROSS_COMPILE=$(TARGET_CROSS) -C $(KERNELDIR) M=$(PWD) modules

endif

install:
	$(MAKE) ARCH=arm CROSS_COMPILE=$(TARGET_CROSS) -C $(KERNELDIR) M=$(PWD) modules_install

clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.symvers *.order


  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值