技术知识库

使用 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 不会随模块自动删除;重新加载模块后应重新核对设备号。

关联

待验证

  • 用用户态 GDB 类比实验分析未初始化 fasync 指针,再进入内核 vmlinux 做偏移定位。