如何中断Linux内核模块且不终止关联的用户态进程
问题描述
我编写了一个实现设备驱动的Linux内核模块,该模块使用down_interruptible处理共享资源访问。需要实现两种场景:中断内核模块的等待操作,但不终止用户态进程(内核中断后用户态进程自行结束):
- 用户态程序知晓结束时机,主动中断内核模块的等待;
- 用户按下Ctrl+C发送SIGINT时,用户态进程忽略该信号,但将中断传递给内核模块。
目前只能做到同时中断两者或都不中断,希望得到修正方案或更优实现。
复现代码
内核模块代码
#include <linux/cdev.h> #include <linux/device.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/module.h> #include <linux/semaphore.h> #include <linux/slab.h> #include <linux/types.h> #define NAME "testmod" #define MAJOR -1 static struct class *testmod_class; static struct cdev *testmod_cdev; struct semaphore *lock; static ssize_t read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { up(lock); printk(KERN_INFO "semaphore unlocked\n"); printk(KERN_INFO "semaphore count: %d\n", lock->count); return 0; } static ssize_t write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { ssize_t ret; if ((ret = down_interruptible(lock)) != 0) { printk(KERN_INFO "interrupt received\n"); } printk(KERN_INFO "semaphore count: %d\n", lock->count); ret = count; return ret; } static const struct file_operations fops = { .owner = THIS_MODULE, .read = read, .write = write, }; static int testmod_init(void) { int ret; if ((testmod_class = class_create(NAME)) == NULL) { ret = -1; goto out; } if ((testmod_cdev = cdev_alloc()) == NULL) { ret = -1; goto cleanup_class; } testmod_cdev->ops = &fops; testmod_cdev->owner = THIS_MODULE; if (cdev_add(testmod_cdev, MAJOR, 1) == -1) { ret = -1; goto cleanup_cdev; } if ((device_create(testmod_class, NULL, MAJOR, NULL, NAME)) == NULL) { ret = -1; goto cleanup_class; } if ((lock = kzalloc(sizeof(struct semaphore), GFP_KERNEL)) == NULL) { ret = -1; goto cleanup_dev; } sema_init(lock, 1); ret = 0; goto out; cleanup_dev: device_destroy(testmod_class, MAJOR); cleanup_class: class_destroy(testmod_class); cleanup_cdev: cdev_del(testmod_cdev); out: return ret; } static void testmod_exit(void) { device_destroy(testmod_class, MAJOR); class_destroy(testmod_class); cdev_del(testmod_cdev); } module_init(testmod_init); module_exit(testmod_exit); MODULE_LICENSE("GPL");
用户态应用代码
#!/usr/bin/env python import os from signal import signal, SIGINT, SIG_IGN signal(SIGINT, SIG_IGN) fd = os.open("/dev/testmod", os.O_WRONLY) while True: os.write(fd, b"1") os.close(fd)
解决方案
核心原理:down_interruptible会在进程收到信号时返回-ERESTARTSYS(对应用户态EINTR错误),只要用户态捕获信号并阻止进程退出,就能实现“只中断内核等待、保留进程”的效果。
场景1:用户态主动通知内核中断
内核模块修改
确保down_interruptible返回错误时,将-EINTR传递给用户态,让用户态感知中断:
static ssize_t write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { ssize_t ret; ret = down_interruptible(lock); if (ret != 0) { printk(KERN_INFO "interrupt received\n"); return -EINTR; // 返回EINTR给用户态 } printk(KERN_INFO "semaphore count: %d\n", lock->count); up(lock); // 必须释放信号量,避免死锁 return count; }
用户态修改
主动发送自定义信号给自己,触发内核等待中断,同时捕获信号避免进程退出:
#!/usr/bin/env python import os import signal import time def handle_signal(signum, frame): # 捕获信号,不执行操作,阻止进程退出 pass # 注册自定义信号处理(如SIGUSR1) signal.signal(signal.SIGUSR1, handle_signal) fd = os.open("/dev/testmod", os.O_WRONLY) # 模拟运行一段时间后主动触发中断 time.sleep(5) os.kill(os.getpid(), signal.SIGUSR1) # 处理内核返回的EINTR,自行退出 try: os.write(fd, b"1") except OSError as e: if e.errno == 4: # EINTR错误码 print("内核等待已中断,用户态准备退出") os.close(fd)
场景2:捕获SIGINT并传递给内核但保持进程运行
用户态修改
不要直接忽略SIGINT,注册信号处理函数阻止进程退出,让内核等待被信号唤醒:
#!/usr/bin/env python import os import signal def handle_sigint(signum, frame): print("收到SIGINT,传递给内核但保持进程运行") signal.signal(signal.SIGINT, handle_sigint) fd = os.open("/dev/testmod", os.O_WRONLY) running = True while running: try: os.write(fd, b"1") except OSError as e: if e.errno == 4: # EINTR print("内核等待已中断,用户态自行结束") running = False os.close(fd)
内核模块调整
同场景1的内核修改,确保返回-EINTR给用户态。
线程场景注意事项
- 信号传递规则:Linux信号默认发送给整个进程,会被任意未阻塞该信号的线程捕获。若写入操作在子线程执行,需确保子线程未阻塞目标信号(如SIGINT/SIGUSR1),否则内核等待不会被唤醒。
- 文件描述符安全:多线程共享设备文件描述符时,需避免并发
write操作导致的逻辑混乱,必要时在用户态加锁。 - 线程退出逻辑:内核等待中断后,子线程需正确退出,主线程要等待子线程结束后再关闭文件描述符,避免资源泄漏。
线程版用户态示例:
#!/usr/bin/env python import os import signal import threading running = True def handle_sigint(signum, frame): global running print("收到SIGINT,传递给内核") running = False def write_thread(fd): while running: try: os.write(fd, b"1") except OSError as e: if e.errno == 4: print("内核等待中断,线程退出") break signal.signal(signal.SIGINT, handle_sigint) fd = os.open("/dev/testmod", os.O_WRONLY) t = threading.Thread(target=write_thread, args=(fd,)) t.start() t.join() os.close(fd)
内容的提问来源于stack exchange,提问作者MattHusz
相关产品推荐
相关产品推荐

