信号量驱动实验核心要点:
一、实验目标
用linux内核信号量实现 LED 设备互斥访问——同一时间只允许一个应用程序操作 /dev/gpioled,解决多应用竞争访问的问题,相比自旋锁更适合需要长时间占用设备的场景。
二、核心逻辑:信号量的优势
信号量和自旋锁本质都是互斥工具,但特性截然不同:
自旋锁:不能休眠,获取不到锁就“死等”,只适合中断、短时间临界区,否则会卡死系统;
信号量:允许进程休眠,获取不到锁的进程会主动等待,不占用CPU,适配进程上下文、长时间占用设备的场景,完全不能用在中断里。
本次实验就是用信号量替换之前的自旋锁,让访问逻辑更安全。
三、驱动代码关键修改(semaphore.c)
整个驱动基于之前自旋锁版本优化,核心改动围绕信号量展开:
添加头文件与结构体
必须先引入 <linux/semaphore.h>,才能使用信号量功能;
在设备结构体 gpioled_dev里新增信号量成员 sem,作为互斥的核心凭证。
初始化信号量在驱动入口函数 led_init中,用 sema_init(&gpioled.sem, 1)初始化信号量,初始值设为 1,代表设备默认空闲,其他进程可以直接申请使用。
open 函数:申请信号量打开设备时,用 down_interruptible()尝试获取信号量:
如果信号量值为 1(设备空闲),获取成功,信号量减为 0,该进程开始占用设备;
如果信号量值为 0(设备被占用),进程自动进入休眠状态,等待设备释放;
该方法支持被信号打断,比如用户按 Ctrl+C 可终止等待,避免死等,安全性更高。
release 函数:释放信号量关闭设备时,用 up()释放信号量,信号量值从 0 变回 1,唤醒所有因等待设备而休眠的进程,让它们重新竞争访问权。
四、互斥运行流程
假设应用 A 和应用 B 同时要操作 LED,运行逻辑是这样的:
初始时信号量值为 1,代表 LED 空闲;
应用 A 打开设备,成功获取信号量,信号量变为 0,A 正常操作 LED,其他进程无法再获取;
此时应用 B 打开设备,发现信号量为 0,直接进入休眠等待;
应用 A 操作完成后关闭设备,调用 up()释放信号量,信号量回到 1;
休眠中的 B 被唤醒,成功获取信号量(信号量再次变 0),开始操作设备,形成严格的互斥访问。
五、编译与测试步骤
编译驱动编写 Makefile,把模块名设为 semaphore.o,执行 make -j32,生成 semaphore.ko模块。
编译测试程序复用之前的 atomicApp.c,重命名为 semaApp.c,用交叉编译器编译成 semaApp应用程序。
加载驱动与测试
先把 semaphore.ko和 semaApp拷贝到开发板对应目录,重启开发板后,依次执行 depmod和 modprobe semaphore.ko加载驱动;
并发测试:先后台运行 ./semaApp /dev/gpioled 1 &(开灯并占用 25 秒),再后台运行 ./semaApp /dev/gpioled 0 &(关灯);
现象:第一个应用先执行,第二个应用休眠等待,等第一个完成后才启动,验证互斥效果。
卸载驱动测试结束后,执行 rmmod semaphore.ko卸载模块,注意必须确保没有进程正在占用设备,否则卸载会失败。
六、关键注意事项
信号量绝对不能用在中断里,因为中断上下文不允许休眠,会导致系统崩溃;
申请信号量用 down_interruptible()时,必须判断返回值,若被信号打断,要返回 -ERESTARTSYS,避免异常;
测试程序必须有明显延时,模拟长时间占用设备的场景,才能验证信号量的休眠唤醒机制。