内核模块编码流程:
代码实现:
<1>、包含头文件:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
……
<2>、模块许可声明:
MODULE_LICENSE("GPL");
<3>、其他函数模块
……
<4>、模块加载函数
static int mqx_init(void)
{
……
return 0;
}
<5>、模块卸载函数
static void mqx_exit(void)
{
……
}
<6>、模块注册
module_init(mqx_init);
module_exit(mqx_exit);
<7>、可选
MODULE_AUTHOR("maqingxiang");
MODULE_DESCRIPTION("This is a ****** module! ");
MODULE_ALIAS("A simplest example");
makefile实现(通用模板):
obj-m += mqx.o
#generate the path
CURRENT_PATH:=$(shell pwd)
#the current kernel version number
LINUX_KERNEL:=$(shell uname -r)
#the absolute path
LINUX_KERNEL_PATH:=/usr/src/linux-headers-$(LINUX_KERNEL)
#complie object
all:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
#clean
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
注:make前面一定是Tab键!
附:Makefile:http://bbs.chinaunix.net/thread-1950588-1-1.html
http://www.cpplive.com/html/1776.html
具体实现:
<1>make 编译模块
<2>insmod mqx.ko 加载模块
<3>dmesg 观察结果(信息在内核日志中)
<4>lsmod 观察内核模块
<5>rmmod mqx 卸载模块
注:用户的切换:su,退出:exit!而用户态执行程序:./test
Gcc编译过程:
对于某个.c文件,我们通常一步到位进行编译,即:gcc *.c -o *
其实gcc编译过程分为4个阶段,即预处理(pre-processing)、编译(compiling)、汇编(assembling)、链接(linking)
详细介绍——>
1、预处理阶段:gcc -E *.c -o *.i
输入.c文件,输出.i文件;在此阶段源文件(*.c)和相应的包含文件(*.h)一起进行预处理,对#define,#ifdef,#include等预编译指令进行解析(宏替换、注释消除、加载库文件)。
2、编译阶段:gcc -S *.i -o *.s(汇编语言文件)
输入.i的中间文件,输出.s的汇编语言文件
3、汇编阶段:gcc -c *.s -o *.o(目标文件)
输入.s的汇编语言文件,输出.o的二进制机器代码文件
4、链接阶段:gcc *.o -o *(可执行文件)
输入.o的二进制代码文件,连同其他的(如果有的话)机器代码文件和库文件一起汇集成一个可执行的二进制代码文件。
其格式如下:
gcc [option] [filename]
-x language 指定使用的语言
-c 对文件进行编译和汇编,但不进行连接
-S 对文件进行编译,但不汇编和连接
-E 仅对文件进行预处理
-o 编译成可执行文件
-wall 显示附加的警告信息
-g 显示错误信息以便调试(调试标记)
-O1/O2/O3 对代码进行优化,数字越大优化等级越高
-v 查看gcc版本
附:其它一些GNU工具
1、怎么查明可执行映像或是中间目标文件的大小呢?
答:size工具可以给出text大小(指令和常量)、data段、bss段。命令为:
$ size test.o
2、希望得到有关映像的更详细的信息,怎么办呢?
答:可以使用objdump工具,加上-syms参数使用objdump工具可以得到映像文件或目标文件中的符号列表,包括它们的类型(text,bss,data)、长度、偏移等信息。命令为:
$ objdump -syms test.o
使用参数-disassemble来进行反汇编映像文件到自然指令集合,此命令将列出在目标文件中找到的函数,以及GCC为这些函数分别生成的指令。命令为:
$ objdump -disassemble test.o
Gdb调试过程:
首先在编译时,必须把调试信息加入到可执行文件中,使用gcc的-g参数可以做到这一点。
最常用的调试命令:
gdb test ----------------------------------------启动gdb,test为执行文件,一般在当前目录下
q----------------------------------------------------退出gdb
查看源码:
l----------------------------------------------------相当于list,从第一行开始列出源码
list -----------------------------------------------显示上次列出源码的前十行,类似于向上翻页
enter---------------------------------------------直接回车,表示重复上一次命令
directory DIR--------------------------------新增一个路径到源码搜索路径
设置中断点:
break num-------------------------------------设置断点,num为行号
break func-------------------------------------设置断点,在函数func()入口处
info break--------------------------------------查看断点信息
delete number-------------------------------删除指定编号的中断点
disable number------------------------------使指定编号的中断点失效
enable number-------------------------------取消disable,使指定编号的中断点生效
程序执行:
r----------------------------------------------------相当于run,运行程序
n----------------------------------------------------相当于next,单条语句执行,step over
c----------------------------------------------------相当于continue,离开中断点,继续执行命令
until-----------------------------------------------离开for,while等循环
finish---------------------------------------------继续执行程式,直到函数返回
k---------------------------------------------------终止程序执行
变量检查:
p i--------------------------------------------------相当于print,打印变量i的取值
whatis--------------------------------------------识别数组或变量的类型
ptype----------------------------------------------提供结构体的定义
bt---------------------------------------------------查看函数堆栈,backtrace
Multi-thread:
info threads------------------------------------显示目前所有的thread
thread thread_num-------------------------切换GDB到指定的thread_num
改变程序执行行为:
return---------------------------------------------直接从程序当前执行位置返回,放弃未执行部分
return expression----------------------------执行return,并返回exprssion的值
set arname=xxx------------------------------更改变量值
多进程应用程序调试:
可以使用follow-fork-mode child/parent 命令告诉GDB返回到哪一个并跟随它,GDB跟随其中一个进程,另一个进程则会不受阻碍地继续运行。还可以告诉GDB在出现fork后询问要跟随哪一个进程,命令为:set follow-fork-mode ask。
多线程应用程序调试:
1、如果把断点设置在源程序的某一行,而这一行又被多个线程调用,那么每一个调用它的线程都会被影响。这明显不是我们想要的结果,那么怎么解决呢?
答:可以通过指定会被影响的线程来限制这种情况,即设置断点时可以指定会被影响的线程:break test.c : 5 thread 5 (if 条件)
可以使用命令info threads列出所以活动的线程和它们当前的状态。某线程前面的*说明它是调试器当前关注的线程;可以命令thread换到任何一个线程,即可让调试器关注的线程改为指定线程。
2、逐步运行多线程应用程序时,会发现调试器关注的线程每一步都可能变化,当我们对当前线程特别感兴趣时往往会很烦人,那么怎么解决呢?
答:可以通过锁定调度器来命令GDB不要自行根据优先权来改变当前线程,命令为:(gdb) set scheduler - locking on
如果希望允许比当前更优先的线程,可以把上面设置的模式关掉,命令为:
(gdb) set scheduler - locking off
我们还可以使用show命令来确定当前模式,命令为:
(gdb) show scheduler – locking
3、如果我们希望对所有的线程应用一个命令,那么如何解决呢?
答:命令thread apply all就用于向所有线程应用同一个命令,例如:
(gdb) thread apply all backtrace (让所有的活动线程发回栈回溯)
命令thread apply还可以对几个指定的线程列表应用命令,例如:
(gdb) thread apply 1 4 9 backtrace(显示第1,4,9号线程的栈回溯)
调试已有的进程:
对于一个正在运行的应用程序,可以把GDB挂接到它的进程进行调试,这里只需要知道这个需要调试的进程的识别符。启动GDB后,使用命令attach挂接到指定进程,将进程挂起,进行控制它。完成调试后,可以使用命令detach与进程脱离,让进程继续运行。
注:这种方法非常适用于处理那些运行一段时间后没有响应的程序,也适用于处理实际运行环境中意外地无法响应的程序。
事后分析调试:
segmentation fault (core dumped)或者总线错误,内核文件已转储
应用程序异常结束并转储内核转储文件时,可以使用GDB来确定到底发生了什么。
注:要激活DNU/Linux生成内核转储,应该执行限制解除命令 ulimit -c 。否则,在有这个限制时,不会生成内核转储文件。
先执行应用程序,得到内核转储文件。
$ ./test
segmentation fault (core dumped)
$ ls
core.12345 test test.c
把应用程序可执行映像和内核转储文件都提供给GDB。
# gdb test core.12345
GDB加载应用程序,然后用内核转储文件来确定在运行错误时发生了什么。(在加载完所有的符号后,可以看到失败发生在哪个函数中;更进一步地,可以看到那个引起错误的调用,这一条语句是为了进行内核转储添加的。)
利用core file排错:
当程序产生segmentation fault时,系统会将当时的所有状态,包括变量值,记忆体资料以及程序中各个函数的呼叫状态dump成一个core file。我没可以用GDB来读取core file,分析当时引起segmentation fault的原因。
@编译gcc -g test.c -o test
@$ gdb ./test core
@设置core file 最大szie:其预设值为0,用命令$ ulimit -c unlimited改成没有限制。
@执行程序:./test
@利用bt找出发生错误的地方
@利用list *ADDRESS列出产生错误的程序代码
@利用print ptr观察变量值
参考网站:
附:GDB调试手册:
http://www.cmlab.csie.ntu.edu.tw/~daniel/linux/gdb.html
http://baike.baidu.com/view/639266.htm
http://www.programlife.net/gdb-manual.html
http://www.cnblogs.com/shipfi/archive/2008/08/04/1260293.html
gdb学习笔记:(在看中...)——强力推荐
http://yaronspace.cn/blog/archives/tag/gdb
调试内核:(在看中...)
http://blog.csdn.net/jiayanhui2877/article/details/4452960
http://www.ibm.com/developerworks/cn/linux/l-kdb/#iratings
http://www.ibm.com/developerworks/cn/linux/l-kdbug/index.html
http://www.ibm.com/developerworks/cn/linux/l-kprobes.html