【问题标题】:How to send signal from kernel to user space如何从内核向用户空间发送信号
【发布时间】:2015-07-26 22:33:28
【问题描述】:

我的内核模块代码需要将信号 [def.] 发送到用户平台程序,以将其执行转移到注册的信号处理程序。

我知道如何在两个用户登陆进程之间发送信号,但我在网上找不到关于上述任务的任何示例。

具体来说,我的预期任务可能需要如下界面(一旦error != 1,代码行int a=10 不应执行):

void __init m_start(){
    ...
    if(error){
        send_signal_to_userland_process(SIGILL)
    }
    int a = 10;
    ...
}

module_init(m_start())

【问题讨论】:

  • 你为什么要这样做?你在编码什么样的内核模块?只供玩,还是供他人认真使用?您可能应该编辑您的问题以进一步激发它,或者放弃这个想法(正如我回答的那样)......
  • 内核编程离题了。 VtC 并转移到 SO。

标签: linux-kernel signals


【解决方案1】:

我过去使用的一个示例,用于从内核空间的硬件中断向用户空间发送信号。就是这样:

内核空间

#include <asm/siginfo.h>    //siginfo
#include <linux/rcupdate.h> //rcu_read_lock
#include <linux/sched.h>    //find_task_by_pid_type

static int pid; // Stores application PID in user space

#define SIG_TEST 44

需要一些“包含”和定义。基本上,您需要用户空间中应用程序的 PID。

struct siginfo info;
struct task_struct *t;

memset(&info, 0, sizeof(struct siginfo));
info.si_signo = SIG_TEST;
// This is bit of a trickery: SI_QUEUE is normally used by sigqueue from user space,    and kernel space should use SI_KERNEL. 
// But if SI_KERNEL is used the real_time data  is not delivered to the user space signal handler function. */
info.si_code = SI_QUEUE;
// real time signals may have 32 bits of data.
info.si_int = 1234; // Any value you want to send
rcu_read_lock();
// find the task with that pid
t = pid_task(find_pid_ns(pid, &init_pid_ns), PIDTYPE_PID);
if (t != NULL) {
    rcu_read_unlock();      
    if (send_sig_info(SIG_TEST, &info, t) < 0) // send signal
        printk("send_sig_info error\n");
} else {
     printk("pid_task error\n");
     rcu_read_unlock();
    //return -ENODEV;
}

前面的代码准备好信号结构并发送它。请记住,您需要应用程序的 PID。在我的例子中,来自用户空间的应用程序通过 ioctl 驱动程序发送它的 PID:

static long dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
    ioctl_arg_t args;
    switch (cmd) {
        case IOCTL_SET_VARIABLES:
              if (copy_from_user(&args, (ioctl_arg_t *)arg, sizeof(ioctl_arg_t))) return -EACCES;
              pid = args.pid;
        break;

用户空间

定义并实现回调函数:

#define SIG_TEST 44

void signalFunction(int n, siginfo_t *info, void *unused) {
    printf("received value %d\n", info->si_int);
}

在主程序中:

int  fd = open("/dev/YourModule", O_RDWR); 
if (fd < 0) return -1;

args.pid       = getpid();
ioctl(fd, IOCTL_SET_VARIABLES, &args); // send the our PID as argument

struct sigaction sig;

sig.sa_sigaction = signalFunction; // Callback function
sig.sa_flags = SA_SIGINFO;
sigaction(SIG_TEST, &sig, NULL);

希望对你有所帮助,虽然答案有点长,但很容易理解。

【讨论】:

    【解决方案2】:

    您可以使用例如kill_pid(在&lt;linux/sched.h&gt; 中声明)向指定进程发送信号。要为其形成参数,请参阅sys_kill 的实现(在kernel/signal.c 中定义为SYSCALL_DEFINE2(kill))。

    注意,从内核向当前进程发送信号几乎是没有用的:内核代码应该在用户空间程序看到信号触发之前返回。

    【讨论】:

    • 你能详细说明一下吗:“内核代码应该在用户空间程序看到信号触发之前返回。” (我的理解是 current 进程应该看到信号被触发,如果它存储在它检查的内存中的某个位置;并且通过看到它,用户登陆进程可以跳转到运行信号处理程序,但是常规代码)。
    • 信号处理不能中断内核代码。返回用户空间后,系统调用检查信号,并运行适当的机制(检查信号是否被阻塞、忽略、调用信号处理程序等)。但是用户空间代码会立即被信号中断。
    【解决方案3】:

    你的界面违背了 Linux 的精神。不要那样做.....system call(尤其是与您的驱动程序相关的那些)应该只以errno 失败(请参阅syscalls(2)...); 考虑eventfd(2)netlink(7) 用于此类异步内核 用户级通信(并期望用户代码能够poll(2) 他们)。

    kernel module 可能无法加载。我不熟悉细节(从未编写过任何内核模块),但this hello2.c 示例表明模块初始化函数可以在失败时返回非零错误代码。

    人们真的期待信号(这是一个困难而痛苦的概念)的行为与signal(7) 中记录的一样,而您想要做的事情并不适合该图片。所以一个表现良好的内核模块不应该向进程异步发送任何信号

    如果您的内核模块表现不佳,您的用户会很生气并且不会使用它。

    如果您想分叉您的实验内核(例如用于研究目的),不要期望它会被大量使用;只有这样你才能真正打破你打算做的信号行为,并且你可以编写不适合内核模块图片的东西(例如添加一个新的系统调用)。另见kernelnewbies

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-26
      • 2021-08-03
      • 1970-01-01
      • 2014-05-13
      • 1970-01-01
      • 2014-01-19
      相关资源
      最近更新 更多