最近我对linux内核如何处理信号感到很困惑。
我知道当进程从内核返回时(准备在用户模式下运行),内核调用用户注册的信号处理程序。最常见的情况是系统调用或内核调度任务。所以我写了一个没有太多系统调用的 cpu 绑定程序。我在一台负载很低的机器上运行程序。
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#define USECREQ 10
#define LOOPS 1000
void event_handler(int signum)
{
static unsigned long cnt = 0;
static struct timeval tsFirst;
if (cnt == 0)
{
gettimeofday(&tsFirst, 0);
}
cnt++;
if (cnt >= LOOPS)
{
struct timeval tsNow;
struct timeval diff;
setitimer(ITIMER_REAL, NULL, NULL);
gettimeofday(&tsNow, 0);
timersub(&tsNow, &tsFirst, &diff);
unsigned long long udiff = (diff.tv_sec * 1000000) + diff.tv_usec;
double delta = (double)(udiff / cnt) / 1000000;
int hz = (unsigned)(1.0 / delta);
printf("kernel timer interrupt frequency is approx. %d Hz", hz);
if (hz >= (int)(1.0 / ((double)(USECREQ) / 1000000)))
{
printf(" or higher");
}
printf("\n");
exit(0);
}
}
int main(int argc, char **argv)
{
struct sigaction sa;
struct itimerval timer;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = &event_handler;
sigaction(SIGALRM, &sa, NULL);
timer.it_value.tv_sec = 0;
timer.it_value.tv_usec = USECREQ;
timer.it_interval.tv_sec = 0;
timer.it_interval.tv_usec = USECREQ;
setitimer(ITIMER_REAL, &timer, NULL);
while (1) ;
}
由于机器的负载很低(24核,没有其他任务),我认为没有太多的调度事件。
我认为内核到/从用户模式切换的唯一时间是时钟中断,大约是 4ms 或 10ms,这意味着内核不会在 1 秒内调用 event_handler 超过 1s/4ms = 250 次。但令人惊讶的是,输出大约是 100000,只需要大约 .这是否意味着大约有 111111 个内核调度事件?
// output
[root@my-centos ~]# time ./a.out
kernel timer interrupt frequency is approx. 111111 Hz or higher
real 0m0.011s
user 0m0.009s
sys 0m0.001s
内核传递信号(不生成)进行处理的确切时间是多少?
首先,这一行是错误的,因为您在除法运算后将类型转换为 double(将在 int 中)
double delta = (double)(udiff / cnt) / 1000000;
应该是:
double delta = ((double)udiff / cnt) / 1000000;
我很确定变量“delta”的值会非常接近 USECREQ/1000000,这意味着大约。 0.000010 秒。 BUT 这条线也是错误的。通过将“delta”除以 cnt,您持有 AVG。进程收到的所有 SIGALRM 的 TIME(以秒为单位),但您需要累积时间。
要知道在第二行传递了多少中断应该是:
double delta = (double)udiff / 1000000;
将在 100 HZ 左右。