如何从内核向用户空间发送信号

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何从内核向用户空间发送信号相关的知识,希望对你有一定的参考价值。

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

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

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

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

module_init(m_start())
答案

我过去用来从内核空间中的硬件中断向用户空间发送信号的示例。这恰好如下:

核心空间

#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
");
} else {
     printk("pid_task error
");
     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
", 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);

我希望它有所帮助,尽管答案有点长,但很容易理解。

另一答案

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

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

另一答案

您的界面违反了Linux的精神。不要这样做.....一个system call(特别是与你的司机相关的那些)应该只能用errno失败(参见syscalls(2) ......);考虑使用eventfd(2)netlink(7)进行此类异步内核用户空间通信(并期望用户代码能够使用poll(2))。

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

人们真的期待信号(这是一个困难和痛苦的概念)在signal(7)中记录的行为,你想做的事情不适合那张照片。因此,性能良好的内核模块不应该异步地向进程发送任何信号。

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

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

以上是关于如何从内核向用户空间发送信号的主要内容,如果未能解决你的问题,请参考以下文章

cpu 如何向内核发送有关硬件异常的信号?

uevent 从内核发送到用户空间 (udev)

Linux进程信号

Linux进程信号

linux 信号处理

如何从信号处理程序内部向其他进程发送通知?