驱动代码可以在write接口中对这个等待队列进行唤醒操作, 从而实现唤醒进程. 具体的数据结构细节在后面的调试过程中展开.
问题3
怎样调试从进程发起poll调用到返回的过程?
这里构造的场景如下:
- 实现一个字符设备驱动, 驱动中实现了poll, write, read接口;
- 在内核中插入该模块, 在/dev下生成设备文件节点;
- 启动一个用户态进程, 并让它后台运行, 在进程中打开设备文件, 对该文件进行poll操作, 开始时设备数据为空, 进程将睡眠
- 使用echo命令向设备文件写入数据, 驱动的write接口被调用, 睡眠的进程被唤醒, 并读取设备数据;
在对poll的实现有了一个基本了解之后, 调试面临的第一个问题就是找到这个系统调用的入口, 这里提供两个调试技巧:
- 你知道在linux内核中系统调用使用SYSCALL_DEFINEx宏定义, 可以直接在代码中用正则表达式SYSCALL_DEFINE.*poll去搜索poll系统调用的位置, 然后在入口打断点, 开始调试即可.
- 你不知道poll的入口在那, 但是在你的字符设备驱动中实现了poll接口, 这个接口一定会出现在poll系统调用的调用链上, 可以在你的驱动模块上打断点, 断点命中之后, 看调用栈找到syscall的入口, 再进一步调试. 这种方法需要借助内核提供的gdb脚本加载驱动模块的调试信息, 否则gdb无法获得指令和源文件中各行的对应信息以及其他的符号信息.
关于调试的环境问题, 可以参考之前的文章, 以下是调试过程的视频记录:
<iframe src="//player.bilibili.com/player.html?aid=405173438&bvid=BV1NG411Z7qu&cid=1245867597&page=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true" width="100%" height="600">
poll系统调用涉及到的重要数据结构, 以及它们之间的关系总结如下:
在逻辑上可以分成如图所示的两个部分, 分别和poll系统调用的上层实现以及驱动模块的poll接口实现相关. 各数据结构的作用如下:
-
进程进入poll系统调用时, 内核对poll_wqueues的各个成员进行初始化, 包括:
- 用一个默认的函数初始化pt的_qproc函数指针;
- 用current初始化polling_task, 记录发起poll系统调用的进程;
- 虚线框中的成员嵌套着wait_queue_entry, 这个被嵌套的数据类型, 是将来真正插入到驱动模块提供的等待队列wait_queue_head的节点;
-
当驱动模块的poll接口被上层调用时:
- 驱动代码需要调用poll_wait函数, 以自己维护的等待队列wait_queue_head作为参数, 并透传poll_table指针和file指针;
- 在poll_wait的实现中, 会检查poll_table的_qproc是否为空, 不为空则继续透传参数, 调用_qproc;
- 在_qproc中, 会从poll_wqueues中获取一个空闲的poll_table_entry, 初始化图中的三个成员, 其中的wait_queue_entry:
- private指针被设为poll_wqueues的地址, 这样将来被唤醒时就可以找到之前睡眠的进程, 也就是polling_task;
- func被设为一个默认的函数,将来这个节点所属的等待队列被唤醒时, func被调用, 根据private指针找到要唤醒的进程;
- 通过链表操作, 将节点插入到等待队列中;
-
当有数据写入设备时: 驱动模块检测到设备有数据可读了, 需要唤醒传递给poll_wait的等待队列, 这时队列上每个节点的func都会被调用, 最终之前睡眠的进程被唤醒;
-
当设备可写时, 唤醒过程类似, 只是使用的队列不同.
概括下来:
- 驱动模块只要维护自己的等待队列, 在poll接口的实现中, 调用上层提供的poll_wait向队列中插入元素, 并返回当前的设备状态;
- 驱动的其他部分在合适的时机对等待队列执行唤醒操作;
- poll系统调用的上层实现代码, 负责维护一套数据结构, 记录插入到等待队列中的节点, 给节点进行必要的设置, 使得通过节点能够唤醒正确的进程;
总结
设备驱动的开发是在内核提供的框架下进行的, 为了降低驱动的开发难度, 快速支持各种新设备, 这套框架的设计必然要经得住考验, 这也导致驱动的开发存在很多模板一样的套路, 有人戏称为"完形填空". 但是以驱动开发为出发点, 深入了解内核的各个模块, 个人感觉是学习linux的一个很好的方式. 欢迎加入技术讨论qq群: 838923389 一起研究linux相关的底层技术.
|