<menu id="w8yyk"><menu id="w8yyk"></menu></menu>
  • <dd id="w8yyk"><nav id="w8yyk"></nav></dd>
    <menu id="w8yyk"></menu>
    <menu id="w8yyk"><code id="w8yyk"></code></menu>
    <menu id="w8yyk"></menu>
    <xmp id="w8yyk">
    <xmp id="w8yyk"><nav id="w8yyk"></nav>
  • 網站首頁 > 物聯資訊 > 技術分享

    驅動程序調試方法之printk――自制proc文件(一)

    2016-09-28 00:00:00 廣州睿豐德信息科技有限公司 閱讀
    睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接

    首先我們需要弄清楚proc機制,來看看fs/proc/proc_misc.c這個文件,從入口函數開始看:

    proc_misc_init(void)         #ifdef CONFIG_PRINTK { struct proc_dir_entry *entry; entry = create_proc_entry("kmsg", S_IRUSR, &proc_root);//這里創建了一個proc入口kmsg if (entry) entry->proc_fops = &proc_kmsg_operations;//設置操作函數,見注釋1 }   注釋1: const struct file_operations proc_kmsg_operations = { .read = kmsg_read, .poll = kmsg_poll, .open = kmsg_open, .release = kmsg_release, }; 這個操作函數就用于多kmsg來進行操作   于是我們可以仿照來設計一下! #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>   struct proc_dir_entry *myentry;   const struct file_operations proc_mymsg_operations = { };   static int mymsg_init(void) { myentry = create_proc_entry("mymsg", S_IRUSR, &proc_root); if (myentry)     myentry->proc_fops = &proc_mymsg_operations ;       return 0; }   void  mymsg_eixt(void) {     remove_proc_entry("mymsg", &proc_root);   module_init(mymsg_init); module_exit(mymsg_eixt);   這個函數只是在proc目錄下面創建了一個入口而已!我們加載后: ls /proc/mymsg -l 打印出如下信息: -r--------    1 0        0               0 Feb  4 13:37 /proc/mymsg   如果我們想查看/proc/mymsg內容的話: # cat /proc/mymsg 打印出來 cat: read error: Invalid argument 這也是理所當然的,因為我們根本沒有讀函數嘛!   所以我們接下來要做的就是來完成這個讀函數,而這這個讀函數里面我們要做的就是將mylog_buf中的中的數據拷貝到用戶空間。有一個很關鍵的地方就是,我們的 mylog_buf 應該是一個環形隊列,關于環形隊列的概念我們先來說一下: front指向隊列首部,rear指向隊列尾部,size表示隊列長度。讀只能從首部讀,寫的話只能從尾部寫!寫到尾部的話,就回去重頭開始寫! 當front==rear的時候,可以判斷隊列是空的。 當(rear+1)/size==front的時候,可以判斷隊列是滿的。程序如下: #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   struct proc_dir_entry *myentry;   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 DECLARE_WAIT_QUEUE_HEAD(mymsg_waitq);   //判斷環形緩沖區是否為空 static int is_mylog_empty(void) { return (mylog_r == mylog_w); } //判斷環形緩沖區是否已滿 static int is_mylog_full(void) { return ((mylog_w + 1)% MYLOG_BUF_LEN == mylog_r); }   /*寫緩沖區:如果緩沖區已滿的話,就讓覆蓋掉下一個要讀的數據 *否則就直接寫入 此外在寫緩沖區函數里面還需要做的一件事情就是喚醒等待隊列, 這是因為當緩沖區為空的時候,如果調用讀函數的話,就會使進程 進入等待隊列,理當在寫入數據的時候喚醒進程 */ static void mylog_putc(char c) { if (is_mylog_full()) { /* 丟棄一個數據 */ mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN; }   mylog_buf[mylog_w] = c; mylog_w = (mylog_w + 1) % MYLOG_BUF_LEN;   /* 喚醒等待數據的進程 */     wake_up_interruptible(&mymsg_waitq);   /* 喚醒休眠的進程 */ }   /*讀緩沖區:如果緩沖區為空的話,就返回0 否則從首部讀出一個數據,返回1 */ static int mylog_getc(char *p) { if (is_mylog_empty()) { return 0; } *p = mylog_buf[mylog_r]; mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN; return 1; }   /*打印函數:這個函數是參考sprintf函數得編寫的 *它將傳遞進來的參數轉換為固定的格式之后,放入到一個臨時緩沖區里面 *然后將環形緩沖區的值寫入到mylog_buf緩沖區里面,詳見注釋2 */ int myprintk(const char *fmt, ...) { va_list args; int i; int j;   va_start(args, fmt); i = vsnprintf(tmp_buf, INT_MAX, fmt, args);//將傳進來的參數轉換后放入tmp_buf va_end(args);   for (j = 0; j < i; j++) mylog_putc(tmp_buf[j]);//將tmp_buf里面的東東放入mylog_buf緩沖區里面   return i; }   /*讀函數:當在應用空間調用命令:cat /proc/mymsg的時候,會調用這個函數 * */ static ssize_t mymsg_read(struct file *file, char __user *buf,  size_t count, loff_t *ppos) { int error = 0; int i = 0; char c;   /* 把mylog_buf的數據copy_to_user, return */           //如果為非阻塞且mylog_buf為空,那么就出錯返回 if ((file->f_flags & O_NONBLOCK) && is_mylog_empty()) return -EAGAIN;           //如果mylog_buf為空的話進程進入等待隊列,還記得我們在寫緩沖區        //函數里面會喚醒進程這件事情吧! error = wait_event_interruptible(mymsg_waitq, !is_mylog_empty());   /* copy_to_user */         //首先從緩沖區里面獲得一個字符,然后拷貝到用戶空間         //如果緩沖區還有信息的話,就再次獲得字符,拷貝到用戶        //空間,直到緩沖區為空 while (!error && (mylog_getc(&c)) && i < count) { error = __put_user(c, buf);//將c的內容拷貝到用戶空間 buf++; i++; }   if (!error) error = i;   return error; }   const struct file_operations proc_mymsg_operations = { .read = mymsg_read, };   static int mymsg_init(void) { 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);   /*因為myprintk是我們自己寫的打印語句 *所以需要導出才能被使用,使用的時候還需要聲明一下: extern int myprintk(const char *fmt,...); */ EXPORT_SYMBOL(myprintk);   MODULE_LICENSE("GPL");   我們在來總結一下:在本文件里面我們做了兩件事情,一件事情是定義了一個寫函數,當我們在用戶空間使用命令:cat /proc/mymsg的時候,就會調用到這個讀函數,這個讀函數會將mylog_buf中的數據拷貝到用戶空間,那么mylog_buf里面的數據哪里來的呢?這就是我們做的另外一件事情,我們定義了一個打印函數,這個打印函數會將要打印的數據寫入一個臨時緩沖區,然后又從臨時緩沖區里面取出數據放入mylog_buf中。cat /proc/mymsg的候就會將mylog_buf中的數據拷貝到用戶空間,就可以顯示出來了!   注釋2: int sprintf(char * buf, const char *fmt, ...) { va_list args; int i;   va_start(args, fmt); i=vsnprintf(buf, INT_MAX, fmt, args); va_end(args); return i; } 這個就是sprintf函數,它將傳遞進來的參數放入buf緩沖區,我們做的就是將這個緩沖區里面的值在放入mylog_buf緩沖區里面!相當的巧妙啊!   接下來我們不妨測試一下,測試函數如下:   #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>   xtern int myprintk(const char *fmt, ...);   static int first_drv_init(void) { myprintk("first_drv_init\n");   return 0; }   static void first_drv_exit(void) {     myprintk("abcdefhg\n"); }   module_init(first_drv_init); module_exit(first_drv_exit);     MODULE_LICENSE("GPL");   先加載proc.ko 在加載test.ko cat /proc/mymsg,打印如下信息: first_drv_init 這正是我們打印的信息,成功了! RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成
    最近免费观看高清韩国日本大全