技术知识库
使用 SIGIO 实现字符设备异步通知
使用 SIGIO 实现字符设备异步通知
摘要
本实验使用实验手册 lab9_mydemodrv_fasync 驱动创建 8 个字符设备状态,并通过 .fasync、fasync_helper() 和 kill_fasync() 向用户程序发送 SIGIO。ARM64 目标系统已经验证:写者写入 13 字节后触发异步通知,监听进程随即读取相同的 13 字节,实验 6-9 的核心目标完成。
原理见 FASYNC 与 SIGIO 如何实现字符设备异步通知。
环境与工作目录
内核:Running Linux Kernel 5.0,ARM64,5.0.0+
用户空间:Debian 12 bookworm
QEMU:virt
主机源码目录:/home/martin-zou/workspace/runninglinuxkernel_5.0/kmodules/rlk_lab/rlk_basic/chapter_6_device_driver/lab9_mydemodrv_fasync
完成标准
- 模块和用户测试程序均交叉编译为 ARM64。
- 驱动注册
.fasync回调。 - 用户程序设置
F_SETOWN、F_SETSIG和FASYNC。 - 写入后驱动调用
kill_fasync(SIGIO, POLL_IN)。 - 监听进程收到信号并读取数据。
- 实际写入长度和读取长度一致。
Makefile
当前实验目录的 Makefile 同时构建模块和静态用户程序:
KERNEL_DIR ?= /home/martin-zou/workspace/runninglinuxkernel_5.0/build/arm64
ARCH ?= arm64
CROSS_COMPILE ?= aarch64-linux-gnu-
USER_CC ?= $(CROSS_COMPILE)gcc
USER_CFLAGS ?= -Wall -Wextra -O2
USER_LDFLAGS ?= -static
USER_TARGET := test
mydemo_fasync-objs := mydemodrv_fasync.o
obj-m += mydemo_fasync.o
.PHONY: all modules clean
all: modules $(USER_TARGET)
modules:
$(MAKE) -C $(KERNEL_DIR) \
M=$(CURDIR) \
ARCH=$(ARCH) \
CROSS_COMPILE=$(CROSS_COMPILE) \
modules
$(USER_TARGET): test.c
$(USER_CC) $(USER_CFLAGS) $< -o $@ $(USER_LDFLAGS)
clean:
$(MAKE) -C $(KERNEL_DIR) \
M=$(CURDIR) \
ARCH=$(ARCH) \
CROSS_COMPILE=$(CROSS_COMPILE) \
clean
$(RM) $(USER_TARGET)
手动构建与复制
cd /home/martin-zou/workspace/runninglinuxkernel_5.0/kmodules/rlk_lab/rlk_basic/chapter_6_device_driver/lab9_mydemodrv_fasync
make clean
make
file mydemo_fasync.ko test
scp -P 2222 mydemo_fasync.ko test martin@127.0.0.1:~/
加载并创建设备节点
该教学驱动只调用 alloc_chrdev_region() 和 cdev_add(),没有创建 class/device,所以需要手动创建设备节点。
insmod ./mydemo_fasync.ko
grep -w mydemo_dev /proc/devices
本次实际分配到主设备号 247:
mknod /dev/mydemo0 c 247 0
chmod 666 /dev/mydemo0
ls -l /dev/mydemo0
动态主设备号不是固定值;重新加载后必须以 /proc/devices 为准。
验证 FASYNC
终端 A:
./test
程序保持运行并等待 SIGIO。终端 B:
echo "hello fasync" > /dev/mydemo0
内核日志关键证据:
demodrv_open: major=247, minor=0, device=mydemo_dev0
demodrv_write kill fasync
demodrv_write:mydemo_dev0 pid=315, actual_write =13
demodrv_read:mydemo_dev0, pid=389, actual_readed=13
PID 315 是写者,PID 389 是监听程序。监听程序没有轮询读取,而是在 kill_fasync() 发送 SIGIO 后调用 read(),因此异步通知闭环成立。
结束实验
先用 Ctrl-C 停止测试程序,再执行:
rmmod mydemo_fasync
手动创建的 /dev/mydemo0 不会随模块自动删除;重新加载模块后应重新核对设备号。
关联
- 字符设备驱动 MOC
- FASYNC 与 SIGIO 如何实现字符设备异步通知
- 设备节点缺失或设备号失配导致 FASYNC 测试失败
- 未初始化 fasync 指针可能导致 fasync_insert_entry Oops
待验证
- 用用户态 GDB 类比实验分析未初始化
fasync指针,再进入内核vmlinux做偏移定位。