20:50-21:55 p99-p120
通常, 当你面对一个 oops, 第一件事是查看发生问题的位置, 常常与调用堆栈分开列出. 在上面展示的第一个 oops, 相关的行是:
EIP is at faulty_write+0x4/0x10 [faulty]
这里我们看到, 我们曾在函数 faulty_write, 它位于 faulty 模块( 在方括号中列出的 ). 16 进制数指示指令指针是函数内 4 字节, 函数看来是 10 ( 16 进制 )字节长. 常常这就足够来知道问题是什么.
通过一些工作,我们可以通过栈清单确定局部变量和函数参数的值。
【Q 哪些工作?】
需要注意的是, 只有在内核编译时,打开了CONFIG_KALLSYMS 选项,我们才能看到符号化的调用栈。
系统挂起
通过在一些关键点上插入schedule调用,防止死循环。
注意:schedule会给驱动程序带来代码重入问题。这可以通过使用合适的锁定来解决。但是一定不要在占有自旋锁的任何时候调用schedule。因为很有可能会导致系统死锁。
有时候系统是假死,观察系统时钟是否还在运行可以判断。
学会使用 SysRq魔法键【p100】
SysRq功能必须在内核配置时启用。
【尝试 SysRq】
调试器和相关工具
使用gdb
要熟练使用gdb需要掌握gdb命令,目标平台的汇编代码,还要具备对源代码和优化后的汇编代码进行匹配的能力。
一个典型的 gdb 调用看来如下:
gdb /usr/src/linux/vmlinux /proc/kcore
注意:为了使gdb使用内核的符号信息,我们必须打开CONFIG_DEBUG_INFO选项。
我们现在该发出一个 gdb 命令来告诉它关于我们的模块. 我们需要的命令是 add-symble-flile;
【尝试 gdb, 和 add-symble-flile 命令】
kdb内核调试器
【尝试p105】
kgdb补丁
p107
用户模式虚拟机
Linux跟踪工具包
动态探测
第五章 并发和 竞态
设备驱动开发者必须在开始设计时就考虑到并发因素,并且还必须对内核提供的并发管理机制有坚实的理解。
scull的缺陷
静态会导致对共享数据的非受控访问
并发及其管理
设计驱动程序,第一个规则:只要可能,就应该避免资源共享。如尽量减少使用全局变量。
第二点:要访问共享资源必须显示地使用访问控制技术。即锁定或互斥。
当内核代码创建了一个可能和其他线程共享的对象时,该对象必须在还有其他组件引用自己的情况下保持存在。
【q 怎样理解?】
信号量和互斥体
信号量 P和V操作。P 操作对信号量减一, V操作加一
Linux信号量的实现
void down(struct semaphore *sem);
int down_interruptible(struct semaphore *sem);
int down_trylock(struct semaphore *sem);
down 减少信号量的值并在必要时一直等待。
down_interruptible 同样, 但是操作是可中断的. 如果操作被中断, 函数返回一个非零值。 正确的使用 down_interruptible 需要一直检查返回值并且针对性地响应.
【什么是可中断?不是原子操作?】
最后的一个 down_trylock, 从不休眠; 如果信号量在调用时不可用, down_trylock 立刻返回一个非零值.
completion
内核编程的一个常见模式是,在当前线程之外初始化某个动作, 接着等待这个动作结束.
自旋锁 spinlock
和信号量不同,自旋锁可在不能休眠的代码中使用,如中断处理程序。
【Q 信号量为什么不可以?】
如果这个锁已经被别人获得, 代码进入一个紧凑的循环中反复检查这个锁, 直到它变为可用. 这个循环就是自旋锁的"自旋"部分.
【所以此时这个线程就不会释放cpu资源? 编程测试】
自旋锁 API 简介
注意所有的自旋锁等待在本质上是不可中断的. 一旦你调用 spin_lock, 在获得锁之前,你将一直自旋.
适用于自旋锁的核心规则是:任何代码必须, 在持有自旋锁时, 是原子性的. 它不能休眠。
在持有自旋锁时禁止中断( 只在本地 CPU ).
最后一个重要规则是自旋锁必须一直是尽可能短时间的持有
LDD3 D08 01.08 星一
最新推荐文章于 2023-03-17 12:05:41 发布