歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux內核

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;
  .........................
}

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

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

 

在用戶空間中使用 cat /proc/kmsg的時候,會調用kmsg_open,在調用kmsg_read函數,讀取log_buf中的數據,拷貝到用戶空間顯示。

3、在寫之前,我們需要來學習一下循環隊列

  信息來源:(http://blog.sina.com.cn/s/blog_8b200d440100xsug.html)

   環形隊列是在實際編程極為有用的數據結構,它有如下特點。

      它是一個首尾相連的FIFO的數據結構,采用數組的線性空間,數據組織簡單,能很快知道隊列是否滿為空。能以很快速度的來存取數據。       因為有簡單高效的原因,甚至在硬件都實現了環形隊列。        環形隊列廣泛用於網絡數據收發,和不同程序間數據交換(比如內核與應用程序大量交換數據,從硬件接收大量數據)均使用了環形隊列。

3.1.環形隊列實現原理

    內存上沒有環形的結構,因此環形隊列實上是數組的線性空間來實現。那當數據到了尾部如何處理呢?它將轉回到0位置來處理。這個的轉回是通過取模操作來執行的。        因此環列隊列的是邏輯上將數組元素q[0]與q[MAXN-1]連接,形成一個存放隊列的環形空間。        為了方便讀寫,還要用數組下標來指明隊列的讀寫位置。head/tail.其中head指向可以讀的位置,tail指向可以寫的位置。

 

 環形隊列的關鍵是判斷隊列為空,還是為滿。當tail追上head時,隊列為滿時,當head追上tail時,隊列為空。但如何知道誰追上誰。還需要一些輔助的手段來判斷.

    如何判斷環形隊列為空,為滿有兩種判斷方法。   一.是附加一個標志位tag       當head趕上tail,隊列空,則令tag=0,
      當tail趕上head,隊列滿,則令tag=1,     二.限制tail趕上head,即隊尾結點與隊首結點之間至少留有一個元素的空間。       隊列空:   head==tail
      隊列滿:   (tail+1)% MAXN ==head

 

 

 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");

 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"); View Code

6、在tty中測試效果

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

成功!!!

感謝韋東山老師!!!!

Copyright © Linux教程網 All Rights Reserved