好久没来这里写博客了。看到浏览量有所增加,十分开心。希望自己的点滴付出对他人能有所帮助。要是有不对的地方,也希望博友能给予点出纠正。
要是能获得博友的转发,也希望能注明下原出处。打字不易,在此谢过啦!
下面介绍下2个学习内核时很有用的工具(方法):
1.函数:dump_stack(); -------------------屠龙刀
内核中,在调试过程中使用printk打印信息当然是最直接的办法。但当我们在刚开始学习内核时,要是一开始就定位到底层的某一个模块(函数),往往想知道它(模块、函数)到底是怎么被顶层一步一步调用下来,并最终调用到的。这时,我们可以利用dump_stack来最根溯源。
以下用例子做使用说明:
static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,size_t *retlen, u_char *buf)
{
......
printk(KERN_DEBUG "==============m25p80_read=======dump_stack s=====================\n");
dump_stack();
printk(KERN_DEBUG "==============m25p80_read=======dump_stack e=====================\n");
......
}
这是在内核代码 ./drivers/mtd/devices/m25p80.c 中的函数m25p80_read(...)内加入的。编译该内核,下载到你的板子上。上电。
看看dump_stack()能打印出什么信息:
==============m25p80_read=======dump_stack s=====================
CPU: 0 PID: 284 Comm: hexdump Not tainted 3.10.31-ltsi #269
Backtrace:
[<c0012f24>] (dump_backtrace+0x0/0x110) from [<c001313c>] (show_stack+0x18/0x1c)
r6:d6057810 r5:00000000 r4:d6057814 r3:00000000
[<c0013124>] (show_stack+0x0/0x1c) from [<c04968a4>] (dump_stack+0x24/0x28)
[<c0496880>] (dump_stack+0x0/0x28) from [<c02b87a8>] (m25p80_read+0x34/0x148)
[<c02b8774>] (m25p80_read+0x0/0x148) from [<c02b927c>] (spi_nor_read+0x60/0x7c)
r9:00001000 r8:d6225ef0 r7:00000000 r6:d6057814 r5:00000000
r4:00700000
[<c02b921c>] (spi_nor_read+0x0/0x7c) from [<c02aa52c>] (part_read+0x70/0xa8)
r7:d632b000 r6:d606c400 r5:00000000 r4:00000000
[<c02aa4bc>] (part_read+0x0/0xa8) from [<c02a7d9c>] (mtd_read+0xc0/0xec)
[<c02a7cdc>] (mtd_read+0x0/0xec) from [<c02abcf4>] (mtdchar_read+0xe0/0x20c)
[<c02abc14>] (mtdchar_read+0x0/0x20c) from [<c00c49e4>] (vfs_read+0xa0/0x14c)
[<c00c4944>] (vfs_read+0x0/0x14c) from [<c00c5070>] (SyS_read+0x44/0x80)
r8:00000000 r7:b6f26000 r6:d6145cc0 r5:00000000 r4:00000000
[<c00c502c>] (SyS_read+0x0/0x80) from [<c000f100>] (ret_fast_syscall+0x0/0x30)
r9:d6224000 r8:c000f2a8 r7:00000003 r6:b6effcb8 r5:0000000a
r4:00000000
==============m25p80_read=======dump_stack e=====================
以上可以看到调用dump_stack的函数是m25p80_read,并且也给出了函数m25p80_read的所在地址,
这样:m25p80_read<--spi_nor_read<--part_read<--mtd_read<--mtdchar_read<--vfs_read(文件系统相关的)
我们就可以看到相关各个层的大体关系了:flash具体《== spi-nor 《== mtd_part 《== mtdcore 《== mtdchar 《== vfs。
对你去理解内核的运作是不是很有帮助 ^_^
以上是自底而上的方法,下面介绍一种自顶而下的方法:
2.运用printk打印函数指针 ------------------倚天剑
当用函数指针调用一个函数时,我们在看代码时,往往不知道它调用的是哪里(哪个函数),举个例子,如下面的ret = master->transfer_one(master, msg->spi, xfer) :
static int spi_transfer_one_message(struct spi_master *master, struct spi_message *msg)
{
.............
printk(KERN_DEBUG "====master->transfer_one=%p=================\n",master->transfer_one); //这里是额外加入的,查看master->transfer_one值
ret = master->transfer_one(master, msg->spi, xfer);
.............
}
这时,我们在它的上面加上printk,把master->transfer_one值打印出来:
编译该内核,下载到你的板子上。上电。log如下:
====master->transfer_one=c02be98c=================
拿着 c02be98c ,进入编译好的内核源码根目录下,打开文件:System.map,查找c02be98c,可以看到:
c02be884 t rspi_common_transfer
c02be98c t qspi_transfer_one (这里)
c02bec30 t rspi_rz_transfer_one
这个函数正是 master->transfer_one 指向的函数。
接下来,不用我说了,你也会拿着这个函数名去内核代码里面查找了吧^_^
要是查找出一堆函数,不知道用的是哪一个,那就接着用printk在各个函数里将该函数名的值(即它的地址)也打印出来,一对比不就清楚孰是孰非了吗。
祝你内核学习愉快^_^