- 浏览: 143747 次
-
最新评论
【Linux 驱动】第四章 调试技术

1)cd linux-source-3.0.0
2)lsusb /*查看所有连接到系统的USB设备*/
3)拔掉USB设备,然后再运行一遍lsusb命令,这样就可以确定以上哪条信息是针对你的新硬件的了。
Bus 002 Device 004: ID 1e3d:2093 /*我的硬件识别信息*/
其中ID 1e3d:2093这个信息对我们很有用处,我们需要用它来查找内核中与硬件匹配的信息。1e3d代表的是厂商ID,就是哪家厂商。2093是硬件ID。
下面开始用1e3d搜索内核源码树:
4)grep -i -R-l 0403 drivers
drivers/usb/serial/ftdi_sio.mod.o
drivers/usb/serial/ftdi_sio.ko
drivers/usb/serial/ftdi_sio.mod.c
drivers/watchdog/pcwd_pci.ko
drivers/watchdog/pcwd_pci.mod.c
drivers/watchdog/pcwd_pci.mod.o
drivers/bluetooth/btusb.ko
drivers/bluetooth/btusb.mod.c
drivers/bluetooth/btusb.mod.o
drivers/media/rc/keymaps/rc-hauppauge.c
drivers/media/rc/keymaps/rc-dib0700-rc5.c
drivers/media/dvb/dvb-usb/dvb-usb-vp702x.mod.c
drivers/media/dvb/dvb-usb/nova-t-usb2.c
drivers/media/dvb/dvb-usb/dvb-usb-vp702x.ko
drivers/media/dvb/dvb-usb/dvb-usb-vp702x.mod.o
该命令执行完后,会在屏幕上显示若干条以.data .c .h等为结尾的文件,比如drivers/usb/serial/ftdi_sio.ko不用看最后一部分,前三个目录名就可以确定这是个USB串口设备。同样的判断方法,我们就可以确定我们需要的内核文件了。以防万一,我们进入这个文件中,USB驱动告诉内核它们支持哪些谁被,以便内核可以把驱动绑定到设备上。一般在一个结构体变量中列出制造商ID和设备ID。如果我们设备的制造商ID和设备ID在里面的话,说明这个驱动支持我们的硬件设备。
cd linux-3.0.0 /*进入内核文件中*/
find –type f –name Makefile | xargs grep XXXXX/*会显示一个以CONFIG_为前缀的字段*/
找到这个字段后,返回内核Makefile文件中,使用内核配置工具menuconfig,搜索这个字段。最后在该程序菜单中相应位置启动这个驱动。
- struct proc_dir_entry*create_proc_read_entry(constchar*name,mode_t mode,
- struct proc_dir_entry*base,
- read_proc_t*read_proc,void*data);
- /*创建proc文件接口
- **name:要创建的文件名称
- **mode:该文件的保护码,0表示系统默认值
- **base:指定文件所在目录,base如果为NULL,表示在proc根目录下创建该文件
- **read_proc:该文件的read_proc函数,读取该文件时调用
- **data:传递给read_proc的参数
- */
- int(*read_proc)(char*page,char**start,off_t offset,intcount,int*eof,void*data);
- /*
- **page:指针指向用来写入数据的缓冲区
- **start:返回实际的数据写到内存页得哪个位置
- **offset,count:和read方法一样
- **eof:指向一个整形数,当没有数据可返回时,驱动程序必须设置这个参数
- **data:提供给驱动程序的专用数据指针,可用于内部记录
- */
- 返回值:必须返回存放到内存页缓冲区的字节数,*eof和*start也属于返回值
- void remove_proc_entry(constchar*name,struct proc_dir_entry*base)//如果删除已卸载模块,内核会崩溃
- /*移除proc文件
- **name:文件名
- **base:目录,和创建前面一样
- */
Seq_file的实现基于proc文件。使用Seq_file,用户必须抽象出一个链接对象,然后可以依次遍历这个链接对象。这个链接对象可以是链表,数组,哈希表等等。
编程接口
Seq_file必须实现四个操作函数:start(), next(), show(), stop()。
structseq_operations{
void*(*start)(structseq_file*m,loff_t*pos);
void(*stop)(structseq_file*m,void*v);
void*(*next)(structseq_file*m,void*v,loff_t*pos);
int (*show)(structseq_file*m,void*v);
};
start():
stop():
next():
show():
对遍历对象进行操作的函数。主要是调用seq_printf(), seq_puts()之类的函数,打印出这个对象节点的信息。
下图描述了seq_file函数对一个链表的遍历。
2、重要的数据结构
除了struct seq_operations以外,另一个最重要的数据结构是structseq_file:
structseq_file{
char *buf;
size_t size;
size_t from;
size_t count;
loff_t index;
u64 version;
struct mutex lock;
const struct seq_operations *op;
void *private;
};
该结构会在seq_open函数调用中分配,然后作为参数传递给每个seq_file的操作函数。Privat变量可以用来在各个操作函数之间传递参数。
3、Seq_file使用示例:
#include/* for use of init_net*/
#include/* We're doing kernel work */
#include/* Specifically, a module */
#include/* Necessary because we use proc fs */
#include/* forseq_file*/
#definePROC_NAME"my_seq_proc"
MODULE_LICENSE("GPL");
staticvoid*my_seq_start(structseq_file*s,loff_t*pos)
{
staticunsignedlongcounter=0;
printk(KERN_INFO"Invoke start\n");
/* beginning a new sequence ? */
if(*pos==0)
{
/* yes => return a non null value to begin the sequence */
printk(KERN_INFO"pos == 0\n");
return&counter;
}
else
{
/* no => it's the end of the sequence, return end to stop reading */
*pos=0;
printk(KERN_INFO"pos != 0\n");
returnNULL;
}
}
staticvoid*my_seq_next(structseq_file*s,void*v,loff_t*pos)
{
unsignedlong*tmp_v=(unsignedlong*)v;
printk(KERN_INFO"Invoke next\n");
(*tmp_v)++;
(*pos)++;
returnNULL;
}
staticvoidmy_seq_stop(structseq_file*s,void*v)
{
printk(KERN_INFO"Invoke stop\n");
/* nothing to do, we use a static value in start() */
}
staticintmy_seq_show(structseq_file*s,void*v)
{
printk(KERN_INFO"Invoke show\n");
loff_t*spos=(loff_t*)v;
seq_printf(s,"%Ld\n",*spos);
return0;
}
staticstructseq_operations my_seq_ops={
.start=my_seq_start,
.next=my_seq_next,
.stop=my_seq_stop,
.show=my_seq_show
};
staticintmy_open(structinode*inode,structfile*file)
{
returnseq_open(file,&my_seq_ops);
};
staticstructfile_operations my_file_ops={
.owner=THIS_MODULE,
.open=my_open,
.read=seq_read,
.llseek=seq_lseek,
.release=seq_release
};
intinit_module(void)
{
structproc_dir_entry*entry;
entry=create_proc_entry(PROC_NAME,0,init_net.proc_net);
if(entry){
entry->proc_fops=&my_file_ops;
}
printk(KERN_INFO"Initialze my_seq_proc success!\n");
return0;
}
/**
* This function is called when the module is unloaded.
*
*/
voidcleanup_module(void)
{
remove_proc_entry(PROC_NAME,init_net.proc_net);
printk(KERN_INFO"Remove my_seq_proc success!\n");
}
该程序在/proc/net下注册一个my_seq_proc文件。
1. 引用空指针
Unable to handle kernel NULL pointer dereference at virtual address 00000000
printing eip:
d083a064
Oops: 0002 [#1]
SMP
CPU: 0
EIP: 0060:[<d083a064>] Not tainted
EFLAGS: 00010246 (2.6.6)
EIP is at faulty_write+0x4/0x10 [faulty]
eax: 00000000 ebx: 00000000 ecx: 00000000 edx: 00000000
esi: cf8b2460 edi: cf8b2480 ebp: 00000005 esp: c31c5f74
ds: 007b es: 007b ss: 0068
Process bash (pid: 2086, threadinfo=c31c4000 task=cfa0a6c0)
Stack: c0150558 cf8b2460 080e9408 00000005 cf8b2480 00000000 cf8b2460 cf8b2460
fffffff7 080e9408 c31c4000 c0150682 cf8b2460 080e9408 00000005 cf8b2480
00000000 00000001 00000005 c0103f8f 00000001 080e9408 00000005 00000005
Call Trace:
[<c0150558>] vfs_write+0xb8/0x130
[<c0150682>] sys_write+0x42/0x70
[<c0103f8f>] syscall_call+0x7/0xb
Code: 89 15 00 00 00 00 c3 90 8d 74 26 00 83 ec 0c b8 00 a6 83 d0
这个错误消息比较明显的,指到了空指针,位置在faulty_write 后 四个字节。
2. 堆栈被破坏
EIP: 0010:[<00000000>]
Unable to handle kernel paging request at virtual address ffffffff
printing eip:
ffffffff
Oops: 0000 [#5]
SMP
CPU: 0
EIP: 0060:[<ffffffff>] Not tainted
EFLAGS: 00010296 (2.6.6)
EIP is at 0xffffffff
eax: 0000000c ebx: ffffffff ecx: 00000000 edx: bfffda7c
esi: cf434f00 edi: ffffffff ebp: 00002000 esp: c27fff78
ds: 007b es: 007b ss: 0068
Process head (pid: 2331, threadinfo=c27fe000 task=c3226150)
Stack: ffffffff bfffda70 00002000 cf434f20 00000001 00000286 cf434f00 fffffff7
bfffda70 c27fe000 c0150612 cf434f00 bfffda70 00002000 cf434f20 00000000
00000003 00002000 c0103f8f 00000003 bfffda70 00002000 00002000 bfffda70
Call Trace:
[<c0150612>] sys_read+0x42/0x70
[<c0103f8f>] syscall_call+0x7/0xb
Code:Bad EIP value.
这个错误信息比较隐晦的。 说的是,找不到一个虚拟地址。 EIP一看就是个乱七八糟的值。
call trace不完整,只指示到了 sys_read。
造成错误的源代码是:
ssize_t faulty_read(struct file *filp, char __user *buf,size_t count, loff_t *pos)
{
int ret;
char stack_buf[4];
/* Let's try a buffer overflow */
memset(stack_buf, 0xff, 20);
if (count > 4)
count = 4; /* copy 4 bytes to the user */
ret = copy_to_user(buf, stack_buf, count);
if (!ret)
return count;
return ret;
}
相关推荐
Linux设备驱动详解【第二版】,作者宋宝华,此版PDF是经过本人整理的文字版PDF,带目录、高清无水印版。...第4篇 Linux设备驱动调试、移植 第22章 Linux设备驱动的调试 564 第23章 Linux设备驱动的移植 602
第4章 调试技术 第5章 字符设备驱动程序的扩展操作 第6章 时间流 第7章 获取内存 第8章 硬件管理 第9章 中断处理 第10章 合理使用数据类型 第11章 kerneld和高级模块化 第12章 加载快设备驱动程序 第13章 MMAP和DMA ...
第4章 基本概念 第5章 字符设备驱动程序 第6章 串行设备驱动程序 第7章 输入设备驱动程序 第8章 I2C协议 第9章 PCMCIA和CF 第10章 PCI 第11章 USB 第12章 视频驱动程序 ...
第4章 基本概念 第5章 字符设备驱动程序 第6章 串行设备驱动程序 第7章 输入设备驱动程序 第8章 I2C协议 第9章 PCMCIA和CF 第10章 PCI 第11章 USB 第12章 视频驱动程序 第13章 音频驱动程序 第14章 块...
第4章 调试技术 第5章 字符设备驱动程序的扩展操作 第6章 时间流 第7章 获取内存 第8章 硬件管理 第9章 中断处理 第10章 合理使用数据类型 第11章 kerneld和高级模块化 第十二章 加载快设备驱动程序 第十三章 MMAP...
第4章介绍了调试技术。内核开发中难免会遇到各种问题,因此掌握有效的调试技术至关重要。本章提供了一些内核调试支持的细节,包括打印调试、查询调试、使用观察(watchpoint)来调试,以及系统故障的调试方法。调试...
《Linux驱动程序开发第三版》是一本专注于Linux操作系统下驱动程序开发的专业书籍,适用于那些希望深入理解Linux内核机制和想要提升驱动程序编写能力的开发者。本书详细阐述了如何在Linux环境中设计、实现和调试设备...
第一天 1.Linux驱动简介 2.字符设备驱动程序设计 3.驱动调试技术 4. 并发与竞态 第二天 1.Ioctl型驱动 2.内核等待队列 3. 阻塞型驱动程序设计 4.Poll设备操作 第三天 1.Mmap设备操作 2. 硬件访问 3. 混杂...
通过阅读《Linux驱动开发第三版》,读者不仅可以掌握Linux驱动程序开发的基本技术,还能了解到最新的Linux内核特性。书中丰富的实例和详尽的解释,对于提升读者的实践能力非常有帮助,无论你是初学者还是经验丰富的...
学习和参考华清远见的Linux设备驱动开发IDE驱动代码,可以帮助你深入理解这些概念和技术,从而能够独立编写和调试IDE驱动。这不仅有助于你提升Linux内核编程能力,也有助于解决实际项目中遇到的硬件兼容性和性能问题...
《LINUX驱动程序开发实例第2版》是冯国进先生编著的一本关于Linux内核驱动程序开发的专业书籍,该书深入浅出地讲解了Linux系统下的驱动编写技术,旨在帮助读者掌握如何为Linux系统编写高效、稳定的硬件驱动程序。...
4. 设备驱动程序的调试:在调试设备驱动程序时,需要使用各种调试工具和方法,例如使用gdb调试器。 四、嵌入式Linux设备驱动程序的关键代码 嵌入式Linux设备驱动程序的关键代码主要包括设备驱动接口的实现、文件...
这本书是Linux驱动程序开发的经典之作,全面覆盖了从基础到高级的Linux驱动编程技术。书中主要讲解了以下内容: - Linux内核架构:介绍Linux内核的基本组织结构和工作原理。 - 驱动程序基础:讲解设备驱动的分类...
- 访问官方网站或第三方网站获取 Marvell Yukon 88E8056 网卡驱动,文件名为 `Marvell_LAN_CAP_Linux_105013.zip`。 - 注意:官方网站可能不直接提供适用于 RHEL5 的驱动,但通常情况下旧版驱动能够兼容新版系统。...
### 正点原子STM32P1开发板Linux驱动教程知识点概述 #### 一、STM32MP1嵌入式Linux驱动开发基础 **STM32MP1**是一款基于ARM Cortex-A7双核处理器和Cortex-M4微控制器内核的高度集成的微处理器。它融合了高性能应用...
通过学习这四章内容,你将能够掌握Linux驱动开发的基本概念和技术,了解如何编写和调试驱动程序,从而能够有效地控制和优化硬件设备。在实际项目中,你可能需要结合具体的硬件手册和Linux内核源码,进行更深入的学习...
第18章 Linux内核调试技术 第4篇 嵌入式Linux设备驱动开发篇 第19章 字符设备驱动程序 第20章 Linux异常处理体系结构 第21章 扩展串口驱动程序移植 第22章 网卡驱动程序移植 第23章 IDE...
第四章 调试技术.doc 第五章 字符设备驱动程序的扩展操作.doc 第六章 时间流.doc 第七章 获取内存.doc 第八章 硬件管理.doc ... ... ... 第十六章 核心源码的物理布局.doc 第十七章 最新进展.doc
SR9700 Linux驱动的开发和调试是一个系统性的工程,需要对Linux内核、硬件接口和编程有深入理解。成功的驱动编译意味着SR9700芯片能够在Linux环境中正常运作,为开发者提供了一个强大的平台,可以在此基础上构建各种...