天天看點

linux驅動常用輸出和調試手段 1.   建立/proc檔案調試 2.   seq_file 3.   ioctl 4.   strace 5.   gdb調試核心

結合scull驅動代碼,來觀察其實作使用。

1.  

建立/proc檔案調試

在/proc 下的每個檔案都綁到一個核心函數上, 當檔案被讀的時候即時産生檔案内容.

使用 /proc 的子產品需要包含

<linux/proc_fs.h>

當一個程序讀子產品的 /proc 檔案, 核心配置設定了一頁記憶體(就是說,

PAGE_SIZE 位元組), 驅動可以寫入資料來傳回給使用者空間.

那個緩存區傳遞給你的函數, 是一個file_operations的函數集方法:

struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,

                                        struct proc_dir_entry *parent,

                                        const struct file_operations *proc_fops,

                                        void *data)

在驅動中通過如下調用:

proc_create_data("scullmem", 0 /* default mode */,

                        NULL /* parent dir */, &scullmem_proc_ops,

                        NULL /* client data */);

在/proc檔案夾下建立scullmem檔案,其對應的操作函數集合是

scullmem_proc_ops,如下:

static struct file_operations scullmem_proc_ops = {

        .owner  

= THIS_MODULE,

.open    = scullmem_proc_open,

.read    = seq_read,

.llseek  = seq_lseek,

.release = single_release

};

主要是scullmem_proc_open函數,就一條語句,調用scull_read_procmem.

static int scullmem_proc_open(struct inode *inode, struct file *file)

{

return single_open(file, scull_read_procmem, NULL);

}

其中single_open在核心中定義如下:

int single_open(struct file *file, int (*show)(struct seq_file *, void *), void *data) 

而scull_read_procmem函數實作真正的輸出操作。

移除/proc下目錄:

void remove_proc_entry(const char *name, struct proc_dir_entry *parent)

/proc處理大檔案時候容易出錯。核心提供了seq_file接口。

2.  

seq_file

需要包含

<linux/seq_file.h> ,必須建立 4 個 iterator 方 法, 稱為

start, next, stop, 和 show.

建立的proc下目錄如下,并關聯proc檔案相關的操作:

proc_create("scullseq", 0, NULL, &scullseq_proc_ops);

其中scullseq_proc_ops如下:

static struct file_operations scullseq_proc_ops = {

.owner   = THIS_MODULE,

.open    = scullseq_proc_open,

.release = seq_release

在scullseq_proc_open中會調用seq_open函數,如下,并關聯打開相關的函數集scull_seq_ops。

static int scullseq_proc_open(struct inode *inode, struct file *file)

return seq_open(file,

&scull_seq_ops);

static struct seq_operations scull_seq_ops = {

.start = scull_seq_start,

.next  = scull_seq_next,

.stop  = scull_seq_stop,

.show  = scull_seq_show

然後seq_open在核心中如下:

/**

 *      seq_open -      initialize sequential file

 *     

@file: file we initialize

@op: method table describing the sequence

 *

 *      seq_open() sets @file, associating it

with a sequence described

by @op.  @op->start() sets the

iterator up and returns the first

element of sequence. @op->stop() shuts it down.  @op->next()

returns the next element of sequence. 

@op->show() prints element

into the buffer.  In case of error

->start() and ->next() return

ERR_PTR(error).  In the end of

sequence they return %NULL. ->show()

returns 0 in case of success and negative number in case of error.

Returning SEQ_SKIP means "discard this element and move on".

Note: seq_open() will allocate a struct

seq_file and store its

pointer in @file->private_data. This pointer should not be modified.

 */

int seq_open(struct file *file, const struct seq_operations *op)

3.  

ioctl

ioctl是一個系統調用, 作用于一個檔案描述符;接收一個确定要進行的指令的數字和(可選地)另一個參數,常常是一個指針。可以作為使用 /proc 檔案系統的替代,可以實作幾個用來調試用的 ioctl 指令. 這些指令可以從 驅動拷貝相關的資料結構到使用者空間, 這裡你可以檢查它們.

使用 ioctl 來擷取資訊比使用/proc 困難,運作比讀取/proc快,不要求劃分資料為小于一頁的片段。

另外,ioctl 指令可能不記錄文檔并不為人知。如果驅動發生了怪異的事情, 仍将在那裡.缺點是子產品可能會稍微大些.

4.  

strace

strace其中最有用 的是 -t 來顯示每個調用執行的時間, -T 來顯示調用中花費的時間, -e 來限制被跟蹤調 用的類型, 以及-o 來重定向輸出到一個檔案。

strace 從核心自身擷取資訊,是以不管它是否帶有調試支援編譯(對 gcc 是 -g 選項)以及不管它是否 strip 過,都可以跟蹤。

5.  

gdb調試核心

使用gdb方式,gdb 不能修改核心資料,也不可能設定斷點或觀察點, 或單步核心函數。

#gdb

/usr/src/linux/vmlinux /proc/kcore

(gdb)p jiffies

也可添加子產品的符号:

(gdb)add-symbol-file

scull.ko 0xffffffffc0184000 -s .bss 0xffffffffc0188d40 -s .data 0xffffffffc0188000

然後可以列印驅動中相關參數:

(gdb) p scull_devices[0]

$1 = {data = 0x0 <irq_stack_union>, quantum = 4000,

qset = 1000, size = 0, access_key = 0, sem = {lock = {raw_lock = {val = {

          counter =

0}}}, count = 1, wait_list = {next = 0xffff89b335861028, prev =

0xffff89b335861028}}, cdev = {kobj = {

      name = 0x0

<irq_stack_union>, entry = {next = 0xffff89b335861040, prev =

0xffff89b335861040}, parent = 0x0 <irq_stack_union>,

      kset = 0x0

<irq_stack_union>, ktype = 0xffffffffba661120, sd = 0x0

<irq_stack_union>, kref = {refcount = {refs = {counter = 1}}},

state_initialized = 1, state_in_sysfs = 0, state_add_uevent_sent = 0,

state_remove_uevent_sent = 0, uevent_suppress = 0},

    owner =

0xffffffffc0188a00, ops = 0xffffffffc0188000 <scull_fops>, list = {next =

0xffff89b335861088, prev = 0xffff89b335861088},

dev = 260046848, count = 1}}

其中位址符号來自/sys/module/scull/sections/.data

/sys/module/scull/sections/.text

/sys/module/scull/sections/.bss

要設定斷點可以使用kdb,不過是彙編級别的。要代碼級别的可以使用kgdb,隻是需要兩台機器通過序列槽來進行調試。

繼續閱讀