linux CPU绑定进程如何在内核模式和用户模式之间切换

wixjitnu  于 2023-05-16  发布在  Linux
关注(0)|答案(1)|浏览(138)

最近我对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核,没有其他任务),我认为没有太多的调度事件。
我认为唯一的内核到/从用户模式切换是时钟中断,大约是4 ms或10 ms,这意味着内核不会在1秒内调用event_handler超过1 s/4 ms = 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

时间内核传递信号(而不是生成)来处理的确切时间是多少?

gupuwyp2

gupuwyp21#

首先,这一行是错误的,因为你在除法运算之后类型转换为double(这将是int)

double delta = (double)(udiff / cnt) / 1000000;

它应该是:

double delta = ((double)udiff / cnt) / 1000000;

我非常确定变量'delta'的值将非常接近USECREQ/1000000,这意味着大约。0.000010秒。“但是……这条线也是错的。通过将'delta'除以cnt,您持有 AVG。进程收到的所有SIGALRM的TIME(以秒为单位),但需要累计时间。
要知道在第二行中传递了多少个中断,应该是:

double delta = (double)udiff / 1000000;

大约100 HZ。

相关问题