結合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,隻是需要兩台機器通過序列槽來進行調試。