Linux内核调试技术之自构proc

在内核中使用printk可以讲调试信息保存在log_buf缓冲区中,可以使用命令 #cat /proc/kmsg 将缓冲区的数区的数数据打印出来,今天我们就来研究一下,自己写kmsg这个文件,我们取名叫做 mymsg。
首页 新闻资讯 行业资讯 Linux内核调试技术之自构proc

1、简介

在内核中使用printk可以讲调试信息保存在log_buf缓冲区中,可以使用命令 #cat /proc/kmsg  将缓冲区的数区的数数据打印出来,今天我们就来研究一下,自己写kmsg这个文件,我们取名叫做 mymsg。

2、查看内核中 /proc/kmsg怎么写的!

在Proc_misc.c (fs\proc) 文件中:

复制

void __init proc_misc_init(void){     .........................         struct proc_dir_entry *entry;         //这里创建了一个proc入口kmsg         entry = create_proc_entry("kmsg", S_IRUSR, &proc_root);         if (entry)       /*构造一个proc_fops结构*/        entry->proc_fops = &proc_kmsg_operations;  ......................... }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

在Kmsg.c (fs\proc) 文件中:

复制

const struct file_operations proc_kmsg_operations = {     .read        = kmsg_read,     .poll        = kmsg_poll,     .open        = kmsg_open,     .release    = kmsg_release,};
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

在用户空间中使用 cat  /proc/kmsg的时候,会调用kmsg_open,在调用kmsg_read函数,读取log_buf中的数据,拷贝到用户空间显示。

3、在写之前,我们需要来学习一下循环队列

环形队列是在实际编程极为有用的数据结构,它有如下特点:

  • 它是一个首尾相连的FIFO的数据结构,采用数组的线性空间,数据组织简单,能很快知道队列是否满为空。能以很快速度的来存取数据。

  • 因为有简单高效的原因,甚至在硬件都实现了环形队列。

  • 环形队列广泛用于网络数据收发,和不同程序间数据交换(比如内核与应用程序大量交换数据,从硬件接收大量数据)均使用了环形队列。

3.1.环形队列实现原理

内存上没有环形的结构,因此环形队列实上是数组的线性空间来实现。那当数据到了尾部如何处理呢?它将转回到0位置来处理。这个的转回是通过取模操作来执行的。

因此环列队列的是逻辑上将数组元素q[0]与q[MAXN-1]连接,形成一个存放队列的环形空间。

为了方便读写,还要用数组下标来指明队列的读写位置。head/tail.其中head指向可以读的位置,tail指向可以写的位置。 

 


 

[[181482]] 

环形队列的关键是判断队列为空,还是为满。当tail追上head时,队列为满时,当head追上tail时,队列为空。但如何知道谁追上谁。还需要一些辅助的手段来判断.

如何判断环形队列为空,为满有两种判断方法。

(1)是附加一个标志位tag

当head赶上tail,队列空,则令tag=0,

当tail赶上head,队列满,则令tag=1,

(2)限制tail赶上head,即队尾结点与队首结点之间至少留有一个元素的空间。

队列空: head==tail

队列满: (tail+1)% MAXN ==head 

[[181483]]

 

4、程序编写

复制

#include <linux/module.h> #include<linux/kernel.h> #include<linux/fs.h> #include<linux/init.h> #include<linux/delay.h> #include<asm/uaccess.h> #include<asm/irq.h> #include<asm/io.h> #include<asm/arch/regs-gpio.h> #include<asm/hardware.h> #include<linux/proc_fs.h>  #define MYLOG_BUF_LEN 1024 static char mylog_buf[MYLOG_BUF_LEN]; static char tmp_buf[MYLOG_BUF_LEN]; static int mylog_r = 0; static int mylog_w = 0; static int mylog_r_tmp = 0;  /*休眠队列初始化*/ static DECLARE_WAIT_QUEUE_HEAD(mymsg_waitq);  /* *判断环形队列是否为空 *返回0:表示不空  返回1:表示空 */ static int is_mylog_empty(void) {    return (mylog_r == mylog_w); }  /* *判断环形队列是否满 *返回0:表示不满  返回1:表示满 */ static int is_mylog_full(void) {    return((mylog_w + 1)% MYLOG_BUF_LEN == mylog_r); }  /* *在读取的时候,判断环形队列中数据是否为空 *返回0:表示不空  返回1:表示空 */ static int is_mylog_empty_for_read(void) {    return (mylog_r_tmp == mylog_w); }  /* *往循环队列中存字符 *输入:c字符 单位:1byte *输出:无 */ static void mylog_putc(char c) {     if(is_mylog_full())     {        /*如果检测到队列已经满了,则丢弃该数据*/         mylog_r= (mylog_r + 1) % MYLOG_BUF_LEN;                 /*mylog_r_tmp不能大于mylog_r*/         if((mylog_r_tmp + 1)% MYLOG_BUF_LEN == mylog_r)             mylog_r_tmp= mylog_r;              }     mylog_buf[mylog_w]= c;    /*当mylog_w=1023的时候 (mylog_w+1) % MYLOG_BUF_LEN =0,回到队列头,实现循环*/     mylog_w= (mylog_w + 1) % MYLOG_BUF_LEN;    /* 唤醒等待数据的进程*/         wake_up_interruptible(&mymsg_waitq);   }  /* *从循环队列中读字符 *输入:*p 单位:1byte *输出:1表示成功 */ static int mylog_getc(char *p) {    /*判断数据是否为空*/     if (is_mylog_empty_for_read())     {        return 0;     }    *p = mylog_buf[mylog_r_tmp ];     mylog_r_tmp = (mylog_r_tmp  + 1) % MYLOG_BUF_LEN;    return 1; }  /* *调用myprintk,和printf用法相同 */ int myprintk(const char *fmt, ...) {     va_list args;    int i;    int j;      va_start(args, fmt);     i= vsnprintf(tmp_buf, INT_MAX, fmt, args);     va_end(args);         for (j = 0; j < i; j++)         mylog_putc(tmp_buf[j]);             return i; }   static ssize_t mymsg_read(struct file *file, char __user *buf,             size_t count, loff_t*ppos) {    int error=0;     size_t i=0;    char c;    /* 把mylog_buf的数据copy_to_user, return*/      /*非阻塞 和 缓冲区为空的时候返回*/     if ((file->f_flags & O_NONBLOCK) && is_mylog_empty())        return -EAGAIN;         /*休眠队列wait_event_interruptible(xxx,0)-->休眠*/     error= wait_event_interruptible(mymsg_waitq, !is_mylog_empty_for_read());         /* copy_to_user*/     while (!error && (mylog_getc(&c)) && i < count) {         error= __put_user(c, buf);         buf++;         i++;     }    if (!error)         error= i;    /*返回实际读到的个数*/     return error; }  static int mymsg_open(struct inode * inode, struct file * file) {     mylog_r_tmp= mylog_r;    return 0; }   const struct file_operations proc_mymsg_operations = {     .read= mymsg_read,     .open= mymsg_open,     }; static int mymsg_init(void) {    struct proc_dir_entry *myentry; kmsg     myentry= create_proc_entry("mymsg", S_IRUSR, &proc_root);    if (myentry)         myentry->proc_fops = &proc_mymsg_operations;    return 0; }  static void mymsg_exit(void) {     remove_proc_entry("mymsg", &proc_root); }  module_init(mymsg_init); module_exit(mymsg_exit);  /*声名到内核空间*/ EXPORT_SYMBOL(myprintk);  MODULE_LICENSE("GPL");
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

  • 25.

  • 26.

  • 27.

  • 28.

  • 29.

  • 30.

  • 31.

  • 32.

  • 33.

  • 34.

  • 35.

  • 36.

  • 37.

  • 38.

  • 39.

  • 40.

  • 41.

  • 42.

  • 43.

  • 44.

  • 45.

  • 46.

  • 47.

  • 48.

  • 49.

  • 50.

  • 51.

  • 52.

  • 53.

  • 54.

  • 55.

  • 56.

  • 57.

  • 58.

  • 59.

  • 60.

  • 61.

  • 62.

  • 63.

  • 64.

  • 65.

  • 66.

  • 67.

  • 68.

  • 69.

  • 70.

  • 71.

  • 72.

  • 73.

  • 74.

  • 75.

  • 76.

  • 77.

  • 78.

  • 79.

  • 80.

  • 81.

  • 82.

  • 83.

  • 84.

  • 85.

  • 86.

  • 87.

  • 88.

  • 89.

  • 90.

  • 91.

  • 92.

  • 93.

  • 94.

  • 95.

  • 96.

  • 97.

  • 98.

  • 99.

  • 100.

  • 101.

  • 102.

  • 103.

  • 104.

  • 105.

  • 106.

  • 107.

  • 108.

  • 109.

  • 110.

  • 111.

  • 112.

  • 113.

  • 114.

  • 115.

  • 116.

  • 117.

  • 118.

  • 119.

  • 120.

  • 121.

  • 122.

  • 123.

  • 124.

  • 125.

  • 126.

  • 127.

  • 128.

  • 129.

  • 130.

  • 131.

  • 132.

  • 133.

  • 134.

  • 135.

  • 136.

  • 137.

  • 138.

  • 139.

  • 140.

  • 141.

  • 142.

  • 143.

  • 144.

  • 145.

  • 146.

  • 147.

  • 148.

  • 149.

  • 150.

  • 151.

  • 152.

  • 153.

  • 154.

  • 155.

  • 156.

  • 157.

  • 158.

  • 159.

  • 160.

  • 161.

  • 162.

  • 163.

  • 164.

  • 165.

  • 166.

  • 167.

  • 168.

  • 169.

  • 170.

5、测试程序

注意:在上面程序中 使用了 EXPORT_SYMBOL(myprintk);意思是把myprintk可以在整个内核空间使用。

使用方法:

①extern int myprintk(const char *fmt, ...);声明

② myprintk("first_drv_open : %d\n", ++cnt);使用

复制

#include <linux/module.h> #include<linux/kernel.h> #include<linux/fs.h> #include<linux/init.h> #include<linux/delay.h> #include<asm/uaccess.h> #include<asm/irq.h> #include<asm/io.h> #include<asm/arch/regs-gpio.h> #include<asm/hardware.h>  static struct class *firstdrv_class; static struct class_device    *firstdrv_class_dev;  volatile unsigned long *gpfcon = NULL; volatile unsigned long *gpfdat = NULL;  extern int myprintk(const char *fmt, ...);  static int first_drv_open(struct inode *inode, struct file *file) {    static int cnt = 0;     myprintk("first_drv_open : %d\n", ++cnt);    /* 配置GPF4,5,6为输出*/     *gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)));    *gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));    return 0; }  static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) {    int val;    static int cnt = 0;      myprintk("first_drv_write : %d\n", ++cnt);      copy_from_user(&val, buf, count); //    copy_to_user();      if (val == 1)     {        // 点灯         *gpfdat &= ~((1<<4) | (1<<5) | (1<<6));     }    else     {        // 灭灯         *gpfdat |= (1<<4) | (1<<5) | (1<<6);     }         return 0; }  static struct file_operations first_drv_fops = {     .owner =  THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量*/     .open  =  first_drv_open,         .write   =    first_drv_write,       };   int major; static int first_drv_init(void) {     myprintk("first_drv_init\n");     major= register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核      firstdrv_class= class_create(THIS_MODULE, "firstdrv");      firstdrv_class_dev= class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz*/      gpfcon= (volatile unsigned long *)ioremap(0x56000050, 16);     gpfdat= gpfcon + 1;     return 0; }  static void first_drv_exit(void) {     unregister_chrdev(major,"first_drv"); // 卸载      class_device_unregister(firstdrv_class_dev);     class_destroy(firstdrv_class);     iounmap(gpfcon); }  module_init(first_drv_init); module_exit(first_drv_exit);   MODULE_LICENSE("GPL");
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

  • 25.

  • 26.

  • 27.

  • 28.

  • 29.

  • 30.

  • 31.

  • 32.

  • 33.

  • 34.

  • 35.

  • 36.

  • 37.

  • 38.

  • 39.

  • 40.

  • 41.

  • 42.

  • 43.

  • 44.

  • 45.

  • 46.

  • 47.

  • 48.

  • 49.

  • 50.

  • 51.

  • 52.

  • 53.

  • 54.

  • 55.

  • 56.

  • 57.

  • 58.

  • 59.

  • 60.

  • 61.

  • 62.

  • 63.

  • 64.

  • 65.

  • 66.

  • 67.

  • 68.

  • 69.

  • 70.

  • 71.

  • 72.

  • 73.

  • 74.

  • 75.

  • 76.

  • 77.

  • 78.

  • 79.

  • 80.

  • 81.

  • 82.

  • 83.

  • 84.

  • 85.

  • 86.

  • 87.

  • 88.

  • 89.

6、在tty中测试效果

复制

# insmod my_msg.ko # insmod first_drv.ko # cat /proc/mymsg mymsg_open mylog_r_tmp=0 first_drv_init
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.