You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何中断Linux内核模块且不终止关联的用户态进程

问题描述

我编写了一个实现设备驱动的Linux内核模块,该模块使用down_interruptible处理共享资源访问。需要实现两种场景:中断内核模块的等待操作,但不终止用户态进程(内核中断后用户态进程自行结束):

  1. 用户态程序知晓结束时机,主动中断内核模块的等待;
  2. 用户按下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给用户态。

线程场景注意事项
  1. 信号传递规则:Linux信号默认发送给整个进程,会被任意未阻塞该信号的线程捕获。若写入操作在子线程执行,需确保子线程未阻塞目标信号(如SIGINT/SIGUSR1),否则内核等待不会被唤醒。
  2. 文件描述符安全:多线程共享设备文件描述符时,需避免并发write操作导致的逻辑混乱,必要时在用户态加锁。
  3. 线程退出逻辑:内核等待中断后,子线程需正确退出,主线程要等待子线程结束后再关闭文件描述符,避免资源泄漏。

线程版用户态示例:

#!/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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 12:17:11