第10章 嵌入式linux的调试技术

一、      防止函数printk降低linux性能:

利用C语言中的编译指令(#if、#else、#endif等)。

现在修改printk_demo驱动代码,通过编译指令定义了一个pr_debug宏,并通过修改编译指令的条件值来控制是否调用printk函数。如下:

# if 1//此处为1,使用printk函数,为0,忽略printk函数

#define pr_debug(x,…)  do { } while(0)

#endif

除此之外,我们还需要了解两个知识点:1.可变参数的宏:可变参数的宏与固定参数的宏之间的区别:可变参数的宏需要通过_VA_ARGS_宏【不支持可变参数个数为0的情况】获取可变参数的宏的可变参数。定义可变参数宏与定义可变参数函数的方法相同,都使用3个点(…)来表示可变参数,可变参数必须是宏和函数最后的参数。

二、      通过虚拟文件系统(/proc)进行数据交互:

必要性:在linux文件系统中,/proc经常被用来作为内核空间进行数据交互的工具。/proc是虚拟文件系统,也就是说,/proc并不是真正的文件系统,而是内存映射。所有读写/proc的操作军事对内存读写的操作。所以读写/proc文件系统的速度远比读写/dev要快。因此,/proc文件系统也可作为linux驱动与用户空间程序交互的工具。

很多信息就是通过/proc文件系统由内核空间的程序向外届提供的。例如:当前系统内存资源就是通过/proc/meminfo文件读取的,读者可以使用如下命令:查看/proc/meminfo文件的内容:Cat /proc/meminfo.我们可以通过执行free命令看看显示的信息是否和meminfo文件中的部分内容相匹配。

在linux驱动程序中可以使用内核函数在/proc目录中创建和删除虚拟文件,也可以建立和删除虚拟目录。

  1. 1. create_proc_entry 创建proc 文件 
    struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,struct
    proc_dir_entry *parent);

name: 要创建的文件名称;

mode: 该文件的保护掩码;

parent: 确定文件所在目录,如果置NULL ,则位置为/proc 下。

  1. 2. proc_mkdir 创建目录

/* 该函数在父目录parent 下创建一个目录name* */

struct proc_dir_entry * proc_mkdir (const char *name,struct
proc_dir_entry *parent);

@name : 要创建的目录名

@parent : 这个目录的父目录

3.remove_proc_entry 删除文件或目录

/* 这个函数从proc 文件系统中删除一个文件或目录。

* 注意:1 .是通过参数name ,而不是通过创建时返回的指针来删除的。

* 2 .该函数不会递归删除目录下的文件。

* 3 .data 变量保存了分配的内存,要先释放对应内存,再删除该文件。

* */

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

@name : 要删除的文件或目录名

@parent : 所在的父目录

 4.create_proc_read_entry 创建只读proc 文件

struct proc_dir_entry * create_proc_read_entry (const char

*name,mode_t mode,struct proc_dir_entry *parent,read_proc_t*

read_proc,void *data);

@name : 要创建的文件名

@mode : 要创建的文件的属性
默认0755

@parent : 这个文件的父目录

@read_proc : 当用户读这个文件时,内核调用的函数

@data : 传给read_proc 的参数

注意:删除虚拟文件目录之前,要先删除目录中的虚拟文件。

执行build.sh脚本文件,会将proc_demo驱动安装在Ubuntu Linux、开发板或Android虚拟机上。然后执行下面的命令查看/proc/proc_demo目录中的内容。

Ls –al
/proc/proc_demo/bin2dec

Cat /proc/proc_demo/bin2dec

Cat /proc_demo/readonly

三、     
调试工具:

  1. 用gdb调试用户空间程序:

Gdb可以跟踪调试用户空间的程序。

对于一个用于测试的可执行程序(gdb_debug.c),我们可以直接运行build.sh脚本文件,但注意加上命令参数-g,完整的编译命令如下:

#gcc –static –g –o gdb_debug
/root/drivers/debug/gdb_debug.c

现在使用下面的命令来调试:

Gdb gdb_debug

Gdb包含的命令:

1>        
quit:用于退出gdb调试界面

2>        
 list:用于列出程序中的代码。有三种调用格式:list:显示上一次调用list命令输出的最后一行后面的10行。 list-: 显示上一次调用list命令输出的第一行前面的10行,第一次调用list命令什么都不会显示。list n:显示第n行附近的10行,一般会显示第n行前面5 行和后面4行,加上第n行,正好是10行。

3>        
Break
n:将指定行设置为断点,n表示行号

4>        
Clear
n:清除指定行的断点。

5>        
Tbreak
n:将指定行设置为断点,断点只能使用一次,使用完成后自动清零。

6>        
Cont/continue:跳过当前断点继续执行。[cont:跳过当前断点继续执行;cont n:跳过n次断点继续前行]

7>        
Next:继续执行下面的语句,但跳过这程序。等价于step over。[同上有两种格式next 及next
n]

8>        
Nexti:单步执行语句,和next的区别:它会跟踪到子程序的内部,但不打印出子程序内部的语句。

9>        
Print
var_name:查看变量值.

  1. 用gdbserver远程调试用户空间程序:

1.与GDB的区别:gdb用于PC上进行测试,而gdbserver测试运行于开发板、手机、Android模拟器上。

2.在开发板上使用gdbserver打开测试程序,然后通过串口、有线、无线网络可以在PC上进行测试。

3.第一步:进入Android模拟器的终端,然后进入data/local目录,并执行如下命令:gdbserver :4321
./gdb_debug启动gdbserver监听程序。其中4321表示使用本机的4321端口号进行监听。

第二步:开启另一个Linux终端,将外部访问模拟器的4321端口的数据包转发到Android模拟器内部的4321端口:adb
–s emulator-5544 forword tcp:4321[有多个Android设备时要加-s命令行参数指定具体的Android设备]。

第二个方法[映射端口]:1.进入telent:telent
localhost 5554 2.映射端口:redir add tcp:4321:4321

第三步:进入gdb控制台:arm-none-linux-guneabi-gdb gdb_debug

第四步:连接Android模拟器:(gdb) target remote
localhost:4321

最后:输入gdb命令进行调试。

通过IP方式连接开发板上的gdbserver:1.gdbserver localhost:4321
./gdb_debug  2.在linux终端的gdb控制台链接开发板的gdbsrever:(gdb) target remote
192.168.17.103 ./gdb_debug.

通过串口方式:对应的应该是:1.gdbserver
/dev/s3c2410_serial0 ./gdb_debug  2.(gdb)
target remote /dev/ttyUSB0

  1. 用kgdb远程调试内核程序:

Kgdb除了提供类似printk函数的日志输出功能,还允许开发人员直接在PC上通过GDB链接目标设备。

Kgdb包含两个部分:kgdb内核和一套链接接口【目前支持串口tty设备链接和以太网连接】。其中串口连接需要通过内核参数kgdboc指定要链接的串口tty设备:以太网连接通过内核参数kgdboc指定IP和端口号。

要想用kgdb调试内核,首先需要配置linux内核。使用make menuconfig命令进入Linux内核的配置菜单。【Kernel hacking--àKGDB:kernel debugger】

配置内核参数时,这些参数通知Linux内核要如何进行调试。假设要通过USB转COM口数据线进行调试,需要将kgdboc参数值折为ttyUSB0,传输效率为115200,一般会指定kdbwait。这些参数需要在S3C开发板过程中按回车进入Uboot模式,然后使用setenv命令设置Linux内核的启动参数,然后使用saveenv和rest命令保存和重新启动Linux内核。

在设置完成后,主机就可以使用gdb命令像调试普通嵌入式应用程序一样调试Linux内核,执行的命令如下:

#gdb  ./vmlinux

完成后,使用如下的命令设置传输速率和连接也要调试的Linux内核。

(gdb) set remoteband 115200

(gdb) target remote /dev/ttyUSB0

最后使用各种gdb命令进行Linux内核调试。

时间: 2024-10-25 09:26:44

第10章 嵌入式linux的调试技术的相关文章

第10章 嵌入式Linux的调试技术 学习心得

第10章嵌入式Linux的调试技术 心得体会 这一章主要概括的介绍了嵌入式Linux的调试技术的主要内容.通过对第10章的学习,使我对嵌入式Linux的调试技术的方法以及操作步骤有了一个基本的认识. 一 调试前的准备 在调试一个bug之前,我们所要做的准备工作有: 有一个被确认的bug. 包含这个bug的内核版本号,需要分析出这个bug在哪一个版本被引入,这个对于解决问题有极大的帮助.可以采用二分查找法来逐步锁定bug引入版本号. 对内核代码理解越深刻越好,同时还需要一点点运气. 该bug可以复

第十章 嵌入式Linux的调试技术

本章的主要内容是嵌入式Linux的调试技术.对于复杂的Linux驱动及HAL等程序库,需要使用各种方法对其进行调试. 一.打印内核调试信息:printk 该函数的用法与printf函数类似,只不过printk函数运行在内核空间,printf函数运行在用户空间.像Linux驱动这样的Linux内核程序只能使用printk函数输出调试信息.Printk函数在printk.c文件中实现,该文件的路径:/root/kernel/linux_kernel_2.6.36/kernel/printk.c. 二

嵌入式Linux的调试技术

嵌入式Linux的调试技术对于复杂的Linux驱动以及HAL等程序库,需要使用各种方法对其进行调试.例如,设置断点.逐步跟踪代码.输出调试信息等. Printk函数的用法与printf函数类似,只不过printk函数运行在内核空间,printf函数运行在用户空间.也就是说,像Linux驱动这样的Linux内核程序只能使用printk函数输出调试信息.Printk函数在printk.c文件中实现. 虽然使用printk函数可以很方便的将消息写入日志文件或控制台.但大量使用printk函数频繁操作日

第10章:嵌入式Linux的调试技术

对于复杂的Linux驱动及HAL等程序库, 需要使用各种方法对其进行调试.例如, 设置断点.逐步跟踪代码.输出调试信息等. 一.打印内核调试信息:printk printk函数在前面的章节己多次使用过.该函数的用法与printf函数类似,只不过printk函数运行在内核空间,printf函数运行在用户空间.也就是说,像Linux驱动这样的Linux内核程序只能使用printk函数输出凋试信息. 二.防止printk函数降低Linux驱动性能 虽然使用printk函数可以很方便地将消息写入日志文件

嵌入式 Linux 的调试技术读书笔记

对于复杂的 Linux 驱动及 HAL等程序库,需要使用各种方法对其进行调试. 例如,设置断点. 逐步跟踪代码.-输出调试值息等.虽然撒入式Linux在调试方面并没有 Visual Studio. Delphi. Eclipse 这些可视偌工其荒便,但学习一些这方面的技术无疑更奇脚手亮发强款而稳定的 Linux 内核模块对于嵌入式 Linux 俞核模块.可fA,衍程庄. 央事库的调试可能要更复杂一些.因为在失多赞时候需要至 少两台机器开发机和目标机. 在 Linux 文件系统中, /proc 经

第十章、嵌入式Linux的调试技术

通过对本章节的学习我学到了如下知识点: 嵌入式Linux调用技术 (1)调试方法:设置断点.逐步跟踪代码.输出调试信息. (2)打印内核调试信息:printk,printk函数运行在内核空间,printk函数运行在用户空间,Linux驱动这样的Linux内核程序只能使用printk函数输出调试信息. Asmlinkage int printk(const char *fmt, …)第一个参数表示格式字符串,后面是可变参数. 虽然使用printk函数可以很方便的将纤细写入日志文件或控制台,但是大量

Android深度探索(卷1)HAL与驱动开发 第十章 嵌入式Linux的调试技术 读书笔记

对于复杂的Linux驱动以及HAL等程序库,需要使用各种方法对其进行测试.如设置断点,逐步跟踪代码,输出调试信息等.本章主要讲述了如何利用开发板.Android模拟器以及一些函数.工具调试嵌入式Linux内核模块.可执行程序和共享库. 1.打印内核调试信息:printk 该函数的用法和printf函数类似,只不过printk函数运行在内核空间,printf函数运行在用户空间.函数原型: asmlinkage printk(const char *fmt, ...) 第一个参数表示格式字符串.后面

第十章嵌入式Linux的调试技术

1.对于复杂的Linux驱动及HAL等程序库,需要使用各种方法对其进行调试,例如,设置断点.逐步跟踪代码.输出调试信息等. 2.Printk函数运行在内核空间,printf函数运行在用户空间. 3.初始化Linux驱动 Static int _init printk_demo_init(void) 4.卸载Linux驱动 Static void _exit printk_demo_exit(void) 5.使用printk函数可以很方便地将消息写入日志文件或控制台,但大量使用printk函数操作

第十章 嵌入式linux的调试技术 心得笔记

1.主要讲了对于复杂的Linux驱动以及HAL等程序库,需要使用各种方法对其进行调试.例如,设置断点.逐步跟踪代码.输出调试信息等. 2.打印内核调试信息:printk printk 函数的用法与printf 函数类似,只不过printk函数运行在内核空间, printf函数运行在用户空间.也就是说,像Linux 驱动这样的Linux内核程序只能使用printk函数输出调试信息. 3.防止printk函数降低Linux驱动性能 虽然使用printk 函数可以很方便地将消息写入日志文件或控制台.但