• 内核编码与调试小结


    内核模块编码流程:

    代码实现:

    <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

    附:Makefilehttp://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工具可以得到映像文件或目标文件中的符号列表,包括它们的类型(textbssdata)、长度、偏移等信息。命令为:

    $ objdump -syms test.o

    使用参数-disassemble来进行反汇编映像文件到自然指令集合,此命令将列出在目标文件中找到的函数,以及GCC为这些函数分别生成的指令。命令为:

    $ objdump -disassemble test.o


    Gdb调试过程:

    首先在编译时,必须把调试信息加入到可执行文件中,使用gcc-g参数可以做到这一点。

    最常用的调试命令:

    gdb test ----------------------------------------启动gdbtest为执行文件,一般在当前目录下

    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-----------------------------------------------离开forwhile等循环

    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不要自行根据优先权来改变当前线程,命令为:gdbset scheduler - locking on

    如果希望允许比当前更优先的线程,可以把上面设置的模式关掉,命令为:

    gdbset scheduler - locking off

    我们还可以使用show命令来确定当前模式,命令为:

    gdbshow scheduler – locking

    3、如果我们希望对所有的线程应用一个命令,那么如何解决呢?

    答:命令thread apply all就用于向所有线程应用同一个命令,例如:

    gdbthread apply all backtrace (让所有的活动线程发回栈回溯)

    命令thread apply还可以对几个指定的线程列表应用命令,例如:

    gdbthread apply 1 4 9 backtrace(显示第149号线程的栈回溯)


     调试已有的进程:

    对于一个正在运行的应用程序,可以把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







  • 相关阅读:
    URAL 2034 : Caravans (二分最短路)
    Qt程序的字符编码方式
    Qt程序国际化
    Qt5 + msvc2015编译器 环境配置 (不安装VS)
    error: undefined reference to `Dialog::on_pushButton_clicked()'在程序代码的后台程序代码出现问题
    Qt的inherits()函数判断qt控件是否为某个类实例
    WPS使用书签跳转到指定的文档位置
    Qt error ------ 出现Error
    环境名词
    source insight 联想出Qt库函数
  • 原文地址:https://www.cnblogs.com/xymqx/p/3435151.html
Copyright © 2020-2023  润新知