0

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

我现在正在尝试将其添加为内核中的测试模块。
我发现我必须用它们的底层系统调用替换 libc 提供的许多函数。示例timer_createsys_timer_create timer_settimewithsys_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;
}

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

4

2 回答 2

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

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

于 2013-11-04T21:10:16.750 回答
1

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

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

IE

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 值吗?它本质上是您要测量的内容,但内核在引导过程的早期就进行了测量并且做得正确。

于 2013-11-04T22:53:24.487 回答