20252820_中断和异常管理
实验内容:Tasklet / Workqueue / 信号捕获
实验平台:openEuler
内核版本:5.10.0+
1. 实验目的
- 理解 Linux 内核中下半部机制(Bottom Half)的基本思想与使用方式。
- 掌握tasklet的创建、调度与释放流程。
- 掌握workqueue(工作队列)的创建、延迟调度、周期执行与退出清理流程。
- 理解 Linux 用户态信号机制,能捕获并处理
Ctrl+C / Ctrl+Z / Ctrl+\对应信号。
2. 实验环境与准备
2.1 软硬件环境
- 操作系统:openEuler
- 内核版本:
5.10.0+ - 编译工具:
gcc / make - 内核构建目录:
/lib/modules/$(uname -r)/build -> /usr/src/kernels/5.10.0+
2.2 实验目录结构
chapter4_interrupt_exception/ ├── task1_tasklet/ │ ├── tasklet_intertupt.c │ └── Makefile ├── task2_workqueue/ │ ├── workqueue_test.c │ └── Makefile └── task3_signal/ ├── catch_signal.c └── Makefile2.3 建立目录
mkdir-p chapter4_interrupt_exception/{task1_tasklet,task2_workqueue,task3_signal}3. 任务一:Tasklet 打印 HelloWorld
3.1 实验内容说明
本任务通过 tasklet 实现一个简单的下半部处理:模块加载时初始化 tasklet 并调度执行,在 tasklet 处理函数中打印Hello World!,模块卸载时使用tasklet_kill()确保 tasklet 安全退出。
3.2 核心代码要点(tasklet_intertupt.c)
文件位置:
chapter4_interrupt_exception/task1_tasklet/tasklet_intertupt.c
tasklet_init(&tasklet, handler, data):初始化 tasklettasklet_schedule(&tasklet):调度 tasklet 执行tasklet_kill(&tasklet):退出时确保 tasklet 不再运行
3.3 Makefile 配置(关键点:KERNELDIR)
文件位置:
chapter4_interrupt_exception/task1_tasklet/Makefile
- 外部模块编译:
make -C $(KERNELDIR) M=$(PWD) modules - openEuler 推荐:
KERNELDIR=/lib/modules/$(shell uname -r)/build
3.4 实验步骤
1)进入目录并编译:
cdchapter4_interrupt_exception/task1_taskletmakeclean&&make2)加载模块并查看输出:
sudoinsmod tasklet_intertupt.kodmesg|tail-n303)卸载模块并查看输出:
sudormmod tasklet_intertuptdmesg|tail-n303.5 运行结果与截图
截图1:
make编译成功(生成.ko)
【截图:任务1-编译成功】截图2:
insmod后dmesg中出现Start...与Hello World...
【截图:任务1-加载与打印】
3.6 结果分析与总结
- tasklet 属于下半部机制之一,适合执行短小、非阻塞的延后处理。
- 退出时必须
tasklet_kill(),避免模块卸载后 tasklet 仍可能访问已释放代码导致异常。
4. 任务二:Workqueue 周期打印 HelloWorld
4.1 实验内容说明
本任务使用工作队列实现“周期打印”效果:创建专用 workqueue,初始化 delayed_work,首次延迟入队;在 work 回调中打印信息并再次queue_delayed_work()自己,从而形成周期执行;卸载模块时取消 delayed_work 并销毁工作队列。
4.2 核心代码要点(workqueue_test.c)
文件位置:
chapter4_interrupt_exception/task2_workqueue/workqueue_test.c
关键点:
create_singlethread_workqueue()创建队列INIT_DELAYED_WORK(&mywork, work_handle)初始化延迟任务queue_delayed_work(queue, &mywork, period*HZ)延迟调度- 在
work_handle()中再次queue_delayed_work()实现周期循环 - 退出时:
cancel_delayed_work_sync()+destroy_workqueue()避免竞态与悬挂任务
4.3 Makefile 配置
文件位置:
chapter4_interrupt_exception/task2_workqueue/Makefile
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
4.4 实验步骤
1)进入目录并编译:
cdchapter4_interrupt_exception/task2_workqueuemakeclean&&make2)加载模块(设置周期参数 5 秒):
sudoinsmod workqueue_test.koperiod=53)观察周期打印:
dmesg-w4)卸载模块:
sudormmod workqueue_test4.5 运行结果与截图
截图1:
make编译成功(生成.ko)
【截图:任务2-编译成功】截图2:
insmod后dmesg连续多次输出(体现周期性)
【截图:任务2-周期打印】
4.6 结果分析与总结
- workqueue 在进程上下文中执行,允许睡眠/阻塞,适合执行相对更复杂的任务。
- 周期执行推荐在 work 回调中自我重新入队,而不是在
module_init()中sleep,避免insmod阻塞。 - 退出必须同步取消 delayed_work,防止模块卸载后工作仍被调度。
5. 任务三:捕获 Ctrl+C / Ctrl+Z / Ctrl+\ 信号
5.1 实验内容说明
本任务在用户态编写程序捕获三类终端按键信号:
Ctrl + C→SIGINTCtrl + Z→SIGTSTPCtrl + \→SIGQUIT
程序打印当前进程 PID,注册信号处理函数,在捕获到信号后输出对应提示并退出。
5.2 核心代码要点(catch_signal.c)
文件位置:
chapter4_interrupt_exception/task3_signal/catch_signal.c
- 使用
signal(SIGxxx, handler)注册处理器 - 使用
pause()阻塞等待信号到来 - 在 handler 中区分不同
sig并输出提示
5.3 编译与运行步骤
1)进入目录并编译:
cdchapter4_interrupt_exception/task3_signalmakeclean&&make2)运行程序:
./catch_signal3)分别测试三种按键(每次测试重新运行一次程序):
Ctrl + C(SIGINT)Ctrl + Z(SIGTSTP)Ctrl + \(SIGQUIT)
5.4 运行结果与截图
截图1:程序启动输出 PID
【截图:任务3-启动输出PID】截图2:按下
Ctrl+C捕获 SIGINT 输出,按下Ctrl+Z捕获 SIGTSTP 输出,按下Ctrl+\捕获 SIGQUIT 输出
5.5 结果分析与总结
- 信号是 Linux 用户态重要的异步事件通知机制,终端按键会向前台进程组发送特定信号。
- 通过注册 handler 可以实现自定义行为(打印、资源释放、优雅退出等)。
- 本实验通过 handler 拦截默认行为,实现“捕获并提示”的效果。
6. 实验中遇到的问题与解决方案
6.1 问题:内核源码目录找不到导致编译失败
现象:make[1]: *** /root/raspberrypi-kernel: No such file or directory
原因:Makefile 中KERNELDIR指向了不存在路径。
解决:openEuler 使用:/lib/modules/$(uname -r)/build(本机链接到/usr/src/kernels/5.10.0+)。
7. 实验结论
完成 tasklet、workqueue 与用户态信号捕获三个任务,理解了 Linux 中断下半部与异步事件处理的基本机制,并掌握了模块编译加载、日志验证、退出清理等关键流程。