Linux內(nèi)核調(diào)試技術之自構proc
1、簡介
在內(nèi)核中使用printk可以講調(diào)試信息保存在log_buf緩沖區(qū)中,可以使用命令 #cat /proc/kmsg 將緩沖區(qū)的數(shù)區(qū)的數(shù)數(shù)據(jù)打印出來,今天我們就來研究一下,自己寫kmsg這個文件,我們?nèi)∶凶?mymsg。
2、查看內(nèi)核中 /proc/kmsg怎么寫的!
在Proc_misc.c (fs\proc) 文件中:
- void __init proc_misc_init(void){
- .........................
- struct proc_dir_entry *entry;
- //這里創(chuàng)建了一個proc入口kmsg
- entry = create_proc_entry("kmsg", S_IRUSR, &proc_root);
- if (entry) /*構造一個proc_fops結(jié)構*/
- 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的時候,會調(diào)用kmsg_open,在調(diào)用kmsg_read函數(shù),讀取log_buf中的數(shù)據(jù),拷貝到用戶空間顯示。
3、在寫之前,我們需要來學習一下循環(huán)隊列
環(huán)形隊列是在實際編程極為有用的數(shù)據(jù)結(jié)構,它有如下特點:
- 它是一個首尾相連的FIFO的數(shù)據(jù)結(jié)構,采用數(shù)組的線性空間,數(shù)據(jù)組織簡單,能很快知道隊列是否滿為空。能以很快速度的來存取數(shù)據(jù)。
- 因為有簡單高效的原因,甚至在硬件都實現(xiàn)了環(huán)形隊列。
- 環(huán)形隊列廣泛用于網(wǎng)絡數(shù)據(jù)收發(fā),和不同程序間數(shù)據(jù)交換(比如內(nèi)核與應用程序大量交換數(shù)據(jù),從硬件接收大量數(shù)據(jù))均使用了環(huán)形隊列。
3.1.環(huán)形隊列實現(xiàn)原理
內(nèi)存上沒有環(huán)形的結(jié)構,因此環(huán)形隊列實上是數(shù)組的線性空間來實現(xiàn)。那當數(shù)據(jù)到了尾部如何處理呢?它將轉(zhuǎn)回到0位置來處理。這個的轉(zhuǎn)回是通過取模操作來執(zhí)行的。
因此環(huán)列隊列的是邏輯上將數(shù)組元素q[0]與q[MAXN-1]連接,形成一個存放隊列的環(huán)形空間。
為了方便讀寫,還要用數(shù)組下標來指明隊列的讀寫位置。head/tail.其中head指向可以讀的位置,tail指向可以寫的位置。
環(huán)形隊列的關鍵是判斷隊列為空,還是為滿。當tail追上head時,隊列為滿時,當head追上tail時,隊列為空。但如何知道誰追上誰。還需要一些輔助的手段來判斷.
如何判斷環(huán)形隊列為空,為滿有兩種判斷方法。
(1)是附加一個標志位tag
當head趕上tail,隊列空,則令tag=0,
當tail趕上head,隊列滿,則令tag=1,
(2)限制tail趕上head,即隊尾結(jié)點與隊首結(jié)點之間至少留有一個元素的空間。
隊列空: 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);
- /*
- *判斷環(huán)形隊列是否為空
- *返回0:表示不空 返回1:表示空
- */
- static int is_mylog_empty(void)
- {
- return (mylog_r == mylog_w);
- }
- /*
- *判斷環(huán)形隊列是否滿
- *返回0:表示不滿 返回1:表示滿
- */
- static int is_mylog_full(void)
- {
- return((mylog_w + 1)% MYLOG_BUF_LEN == mylog_r);
- }
- /*
- *在讀取的時候,判斷環(huán)形隊列中數(shù)據(jù)是否為空
- *返回0:表示不空 返回1:表示空
- */
- static int is_mylog_empty_for_read(void)
- {
- return (mylog_r_tmp == mylog_w);
- }
- /*
- *往循環(huán)隊列中存字符
- *輸入:c字符 單位:1byte
- *輸出:無
- */
- static void mylog_putc(char c)
- {
- if(is_mylog_full())
- {
- /*如果檢測到隊列已經(jīng)滿了,則丟棄該數(shù)據(jù)*/
- 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,回到隊列頭,實現(xiàn)循環(huán)*/
- mylog_w= (mylog_w + 1) % MYLOG_BUF_LEN;
- /* 喚醒等待數(shù)據(jù)的進程*/
- wake_up_interruptible(&mymsg_waitq);
- }
- /*
- *從循環(huán)隊列中讀字符
- *輸入:*p 單位:1byte
- *輸出:1表示成功
- */
- static int mylog_getc(char *p)
- {
- /*判斷數(shù)據(jù)是否為空*/
- 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;
- }
- /*
- *調(diào)用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的數(shù)據(jù)copy_to_user, return*/
- /*非阻塞 和 緩沖區(qū)為空的時候返回*/
- 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;
- /*返回實際讀到的個數(shù)*/
- 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);
- /*聲名到內(nèi)核空間*/
- EXPORT_SYMBOL(myprintk);
- MODULE_LICENSE("GPL");
5、測試程序
注意:在上面程序中 使用了 EXPORT_SYMBOL(myprintk);意思是把myprintk可以在整個內(nèi)核空間使用。
使用方法:
①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, /* 這是一個宏,推向編譯模塊時自動創(chuàng)建的__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); // 注冊, 告訴內(nèi)核
- 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");
6、在tty中測試效果
- # insmod my_msg.ko
- # insmod first_drv.ko
- # cat /proc/mymsg
- mymsg_open mylog_r_tmp=0
- first_drv_init