内核空间中的信号处理

时间:2013-11-04 17:37:54

标签: c linux linux-kernel signals

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

我现在正在尝试将其添加为内核中的测试模块 我发现我必须用timer_create sys_timer_createtimer_settime取代libc提供的许多函数及其基础系统调用...示例为sys_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;
}

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

2 个答案:

答案 0 :(得分:5)

  

是采用用户空间代码和更改呼叫的方法   单独足以在内核空间中运行代码吗?

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

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

但是,内核的内部任务具有完全相同的用户空间结构,但存在一些差异(例如内存映射,父进程等)。当然,您无法从用户进程向内核线程发送信号(想象一下如果将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
顺便说一句,你应该调用它们中的任何一个,它们应该从用户空间调用。

答案 1 :(得分:1)

你在内核空间工作,所以你应该开始考虑你在内核空间工作,而不是试图将用户空间入侵到内核。如果你需要在内核空间中调用sys_*函数系列,99.95%的时间,你已经做了非常非常错误的事情。

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

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);
}

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

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