【问题标题】:Signal handling in kernel-space内核空间中的信号处理
【发布时间】:2013-11-15 10:08:24
【问题描述】:

我编写了一个使用SIGALRM 和信号处理程序的程序。

我现在正尝试将此作为测试模块添加到内核中。
我发现我必须用它们的底层系统调用替换 libc 提供的许多函数。示例是 timer_createsys_timer_create timer_settimesys_timer_settime 等等。

但是,我遇到了sigaction 的问题。
编译内核抛出如下错误
arch/arm/mach-vexpress/cpufreq_test.c:157:2: error: implicit declaration of function 'sys_sigaction' [-Werror=implicit-function-declaration]

我在下面附上了相关的代码块

int estimate_from_cycles() {
    timer_t timer;
    struct itimerspec old;

    struct sigaction sig_action;
    struct sigevent sig_event;
    sigset_t sig_mask;

    memset(&sig_action, 0, sizeof(struct sigaction));
    sig_action.sa_handler = alarm_handler;
    sigemptyset(&sig_action.sa_mask);

    VERBOSE("Blocking signal %d\n", SIGALRM);
    sigemptyset(&sig_mask);
    sigaddset(&sig_mask, SIGALRM);

    if(sys_sigaction(SIGALRM, &sig_action, NULL)) {
            ERROR("Could not assign sigaction\n");
            return -1;
    }

    if (sigprocmask(SIG_SETMASK, &sig_mask, NULL) == -1) {
            ERROR("sigprocmask failed\n");
            return -1;
    }

    memset (&sig_event, 0, sizeof (struct sigevent));
    sig_event.sigev_notify = SIGEV_SIGNAL;
    sig_event.sigev_signo = SIGALRM;
    sig_event.sigev_value.sival_ptr = &timer;


    if (sys_timer_create(CLOCK_PROCESS_CPUTIME_ID, &sig_event, &timer)) {
            ERROR("Could not create timer\n");
            return -1;
    }

    if (sigprocmask(SIG_UNBLOCK, &sig_mask, NULL) == -1) {
            ERROR("sigprocmask unblock failed\n");
            return -1;
    }

    cycles = 0;
    VERBOSE("Entering main loop\n");

    if(sys_timer_settime(timer, 0, &time_period, &old)) {
            ERROR("Could not set timer\n");
            return -1;
    }

    while(1) {
            ADD(CYCLES_REGISTER, 1);
    }
    return 0;
}

这种获取用户空间代码并单独更改调用的方法是否足以在内核空间中运行代码?

【问题讨论】:

  • 不,不是。内核模块必须运行的环境与用户级程序运行的环境有很大不同,某些可以调用的函数的名称只是一个相当大的冰山一角......
  • 那么在内核空间中执行信号处理/警报之类的正确方法是什么?
  • 我不这么认为。你能举例说明如何向内核发送信号吗?

标签: c linux linux-kernel signals


【解决方案1】:

是这样一种获取用户空间代码并更改调用的方法吗 单独足以在内核空间中运行代码?

当然不是!您所做的是直接从内核空间调用系统调用的实现,但是保证它们的 SYS_function 具有与系统调用相同的函数定义。正确的方法是搜索执行您需要的正确内核例程。除非您正在编写驱动程序或内核功能,否则您不需要编写内核代码。系统调用只能从用户空间调用。它们的主要目的是提供一种安全的方式来访问操作系统提供的低级机制,例如文件系统、套接字等。

关于信号。您有一个TERRIBLE 的想法,即尝试使用来自内核空间的信号系统调用来接收信号。一个进程向另一个进程发送信号,而信号意味着在用户空间中使用,因此在用户空间进程之间使用。通常,当您向另一个进程发送信号时会发生什么情况,如果信号未被屏蔽,则接收进程将停止并执行信号处理程序。请注意,为了实现此结果,需要在用户空间和内核空间之间进行两次切换。

但是,内核的内部任务与用户空间的结构完全相同,但存在一些差异(例如内存映射、父进程等)。当然,您不能从用户进程向内核线程发送信号(想象一下,如果您向关键组件发送 SIGKILL 会发生什么)。由于内核线程具有与用户空间线程相同的结构,因此它们可以接收信号,但其默认行为是丢弃它们,除非另有说明。

我建议您更改代码以尝试从内核空间向用户空间发送信号,而不是尝试接收信号。 (你将如何向内核空间发送信号?你会指定哪个 pid?)。这可能是一个很好的起点:http://people.ee.ethz.ch/~arkeller/linux/kernel_user_space_howto.html#toc6

sys_sigaction 有问题,因为这是系统调用的旧定义。正确的定义应该是sys_rt_sigaction。 来自内核源代码 3.12:

 #ifdef CONFIG_OLD_SIGACTION
 asmlinkage long sys_sigaction(int, const struct old_sigaction __user *,
                                 struct old_sigaction __user *);
 #endif

 #ifndef CONFIG_ODD_RT_SIGACTION
 asmlinkage long sys_rt_sigaction(int,
                                  const struct sigaction __user *,
                                  struct sigaction __user *,
                                  size_t);
 #endif

顺便说一句,你不应该调用它们中的任何一个,它们应该是从用户空间调用的。

【讨论】:

  • 大多数系统调用可能与它们的 libc 实现非常相似,因为 libc 只是围绕这些实现。我最终使用的几乎所有系统调用都具有相同的签名和签名背后的相同想法。关于信号,我认为您误解了..我没有尝试从 userspace 向内核发送信号..我正在尝试将模块作为尝试接收的内核的一部分运行一个信号......都在内核本身......但我看到你正确提出的一个困难......我会指定pid什么......不确定......
  • 我已经正确理解了你想要做什么,我试图解释为什么这是一个坏主意。如果您想测试内核空间的信号,最好和最简单的方法是将它们发送到用户空间。出于这个原因,我指出了那篇文章。
【解决方案2】:

您正在内核空间中工作,因此您应该开始像在内核空间中工作一样思考,而不是尝试将用户空间黑客移植到内核中。如果您需要在内核空间中调用sys_* 系列函数,99.95% 的情况下,您已经在做非常非常错误的事情了。

不是while (1),而是让它打破一个易失性变量的循环并启动一个简单地休眠并在完成时更改变量值的线程。

void some_function(volatile int *condition) {
    sleep(x);
    *condition = 0;
}

volatile int condition = 1;
start_thread(some_function, &condition);
while(condition) {
        ADD(CYCLES_REGISTER, 1);
}

但是,在没有大量黑客攻击的情况下,在像 Linux 这样的抢占式内核上,您正在做的事情(我假设您正在尝试获取 CPU 运行的周期数)本质上是不可能的。如果您保持中断打开,您的循环计数将不准确,因为您的内核线程可能随时被关闭。如果你关闭中断,其他线程将不会运行,你的代码只会无限循环并挂起内核。

您确定不能简单地使用内核中的 BogoMIPs 值吗?它本质上是您要测量的内容,但内核在启动过程的早期就进行了测量,并且做得正确。

【讨论】:

  • 是的,你是对的。理想情况下,我应该使用 BogoMIPS 代码,但我已经尝试过多次,但我从来没有真正觉得我理解发生了什么。所以我决定编写我自己的一段代码来做类似的事情。我不使用条件的原因是我的代码,我知道确切循环需要多少个周期。我猜我也可以通过指令集找出与条件相同的情况..
  • 不,它仍然可能不准确。不要忘记内核可以上下文切换你的线程。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-07-02
  • 2018-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多