>  기사  >  시스템 튜토리얼  >  Linux에서 CPU 사용률은 어떻게 계산됩니까?

Linux에서 CPU 사용률은 어떻게 계산됩니까?

WBOY
WBOY앞으로
2024-02-15 11:15:11910검색

온라인 서버에서 온라인 서비스의 실행 상태를 관찰할 때 대부분의 사람들은 현재 시스템의 전체 CPU 사용률을 확인하기 위해 top 명령을 먼저 사용하는 것을 선호합니다. 예를 들어 임의 머신의 경우 top 명령으로 표시되는 활용도 정보는 다음과 같습니다.

Linux 中 CPU 利用率是如何算出来的?

이 출력 결과는 말 그대로 간단하지만, 복잡하더라도 모든 것을 이해하기는 쉽지 않습니다. 예:

질문 1: 상단에서 출력되는 활용 정보는 어떻게 계산되나요?
질문 2: ni 열은 처리 시 CPU 오버헤드를 출력하는 것이 좋습니다.
질문 3: wa는 io 대기를 나타냅니다. 그러면 이 기간 동안 CPU가 사용 중입니까 아니면 유휴 상태입니까?

오늘은 CPU 사용률 통계에 대해 심층적으로 연구했습니다. 오늘의 연구를 통해 CPU 사용률 통계의 구현 세부 사항을 이해할 수 있을 뿐만 아니라 nice 및 io wait와 같은 지표에 대한 더 깊은 이해를 갖게 됩니다.

오늘은 각자의 생각부터 시작해보겠습니다!

1. 먼저 생각해보세요

Linux 구현은 제쳐두고, 다음 요구 사항이 있는 경우 4개의 프로세스가 실행되는 쿼드 코어 서버가 있는 것입니다.

Linux 中 CPU 利用率是如何算出来的?

전체 시스템의 CPU 사용률을 설계하고 계산할 수 있으며 top 명령과 같은 출력을 지원하며 다음 요구 사항을 충족합니다.

  • CPU 사용량은 최대한 정확해야 합니다.
  • 순간적인 CPU 상태를 최대한 2차 레벨에 반영하는 것이 필요합니다.

몇 분 동안 멈춰서 생각해 보세요.

Linux 中 CPU 利用率是如何算出来的?

좋아, 생각 끝. 곰곰이 생각해보면 간단해 보이는 이 요구 사항이 실제로는 다소 복잡하다는 것을 알게 될 것입니다.

한 가지 아이디어는 모든 프로세스의 실행 시간을 더한 다음 이를 총 시스템 실행 시간 * 4로 나누는 것입니다.

Linux 中 CPU 利用率是如何算出来的?

이 방법을 사용하면 장기간에 걸쳐 CPU 사용률을 계산할 수 있으며 통계도 충분히 정확합니다.

하지만 top을 사용했다면 top에 의해 출력된 CPU 사용률이 오랫동안 일정하지 않고 기본적으로 3초 단위로 동적으로 업데이트된다는 것을 알게 될 것입니다. (이 시간 간격은 -d를 사용하여 설정할 수 있습니다. ). 우리 솔루션은 전체 사용량을 반영할 수 있지만, 이러한 순간적인 상태를 반영하는 것은 어렵습니다. 3초에 1개로 셀 수 있다고 생각하실 수도 있겠네요. 그런데 이 3초의 시간은 어느 시점에서 시작되는가. 세분성은 제어하기 어렵습니다.

앞서 생각하는 질문의 핵심은 순간적인 문제를 어떻게 해결하느냐입니다. 일시적인 상태에 관해서는 다른 아이디어가 있을 수 있습니다. 그런 다음 인스턴트 샘플링을 사용하여 현재 사용 중인 코어 수를 확인합니다. 4개의 코어 중 2개가 사용 중인 경우 사용률은 50%입니다.

이런 생각도 올바른 방향이지만 두 가지 문제가 있습니다.

  • 계산한 숫자는 모두 25%의 배수입니다.
  • 이 순간 값으로 인해 CPU 사용량 표시가 크게 바뀔 수 있습니다.

예를 들어 아래 사진은

Linux 中 CPU 利用率是如何算出来的?

t1의 순간 상태에서는 시스템의 CPU 사용률이 의심할 바 없이 100%인데, t2의 관점에서는 사용률이 0%가 되었습니다. 아이디어는 올바른 방향이지만 분명히 이 조잡한 계산은 최상위 명령만큼 우아하게 작동할 수 없습니다.

이를 개선하고 위의 두 가지 아이디어를 결합하면 문제를 해결할 수 있을 것입니다. 샘플링 측면에서는 주기를 더 세밀하게 설정했지만, 계산 측면에서는 주기를 더 성기게 설정했습니다.

1ms마다 샘플링하는 등 채택 기간, 타이밍 개념을 소개합니다. 샘플링 순간에 CPU가 실행 중이면 이 1ms가 사용된 것으로 기록됩니다. 이때 순간적인 CPU 사용량을 얻어서 저장하게 됩니다.

Linux 中 CPU 利用率是如何算出来的?

위 그림의 t1 및 t2 시간 범위와 같이 3초 이내의 CPU 사용량을 계산할 때. 그런 다음 이 기간 동안의 모든 순간 값을 더하고 평균값을 취합니다. 이를 통해 위의 문제를 해결할 수 있고, 통계가 비교적 정확하며, 순시값이 격렬하게 진동하고 너무 거칠게(25% 단위로만 변경 가능) 발생하는 문제를 피할 수 있습니다.

아래 그림과 같이 두 샘플링 사이에 CPU가 변경되면 어떻게 되는지 궁금해하는 학생들도 있습니다.

Linux 中 CPU 利用率是如何算出来的?

현재 샘플링 지점에 도달하면 프로세스 A가 잠시 동안 실행을 마쳤으며 이전 샘플링 지점에서도 계산되지 않았으며 이번에도 계산될 수 없습니다. 프로세스 B의 경우 실제로는 짧은 시간 동안만 시작되었으므로 1ms를 모두 기록하기에는 좀 무리인 것 같습니다.

이 문제는 존재하지만 우리 샘플링은 한 번 1ms이고 실제로 확인하고 사용할 때 적어도 수천 개의 샘플링 지점의 정보를 포함하는 두 번째 수준이므로 이 오류는 발생하지 않습니다. 전반적인 상황을 파악하는 데 영향을 미칩니다.

실제로 Linux에서 시스템 CPU 사용률을 계산하는 방법은 다음과 같습니다. 비록 오류가 있을 수 있지만 통계자료로 활용하기에는 충분합니다. 구현 측면에서 리눅스는 실제로 순간 데이터의 많은 사본을 저장하는 것이 아니라 모든 순간 값을 특정 데이터에 축적합니다.

다음으로 Linux에 들어가서 시스템 CPU 사용률 통계의 구체적인 구현을 살펴보겠습니다.

2. top 명령어 사용 데이터는 어디에 있나요

이전 섹션에서 언급한 Linux의 구현은 순간적인 값을 특정 데이터에 누적하는 것입니다. 이 값은 /proc/stat 의사 파일을 통해 커널에 의해 사용자 상태에 노출됩니다. Linux는 시스템 CPU 사용률을 계산할 때 이를 사용합니다.

전체적으로 최고 사령부 작업의 내부 세부 사항은 아래 그림과 같습니다.

Linux 中 CPU 利用率是如何算出来的?

top 명령은 /proc/stat에 액세스하여 다양한 CPU 사용률 값을 얻습니다.

  • 커널은 /proc/stat에 대한 액세스를 처리하기 위해 stat_open 함수를 호출합니다.

  • 커널에서 액세스하는 데이터는 kernel_cpustat 배열에서 제공되며 요약됩니다.

  • 사용자 모드로 출력을 인쇄합니다.

다음으로 각 단계를 확장하여 자세히 살펴보겠습니다.

strace를 사용하여 top 명령의 다양한 시스템 호출을 추적하면 파일에 대한 호출을 볼 수 있습니다.

으아악

/proc/stat 외에도 각 프로세스별로 분류된 /proc/{pid}/stat도 있으며, 이는 각 프로세스의 CPU 사용률을 계산하는 데 사용됩니다.

커널은 각 의사 파일에 대한 처리 기능을 정의합니다. /proc/stat 파일의 처리 방법은 proc_stat_Operations입니다.

으아악

proc_stat_Operations에는 이 파일에 해당하는 작업 방법이 포함되어 있습니다. /proc/stat 파일이 열리면 stat_open이 호출됩니다. stat_open은 Single_open_size와 show_stat를 차례로 호출하여 데이터 내용을 출력합니다. 코드를 살펴보겠습니다:

으아악

위 코드에서 for_each_possible_cpu는 CPU 사용량 데이터를 저장하는 kcpustat_cpu 변수를 순회합니다. 이 변수는 각 논리 코어에 대한 배열 요소를 준비하는 percpu 변수입니다. user, nice, system, idel, iowait, irq, softirq 등 현재 코어에 해당하는 다양한 이벤트를 저장합니다.

이 루프에서는 각 코어의 각 사용량을 더합니다. 마지막으로 seq_put_decimal_ull을 통해 데이터가 출력된다.

Linux 中 CPU 利用率是如何算出来的?커널에서는 각 시간이 실제로 나노초 단위로 기록되지만 출력 시 모두 비트 단위로 변환된다는 점에 유의하세요. 비트 단위의 길이에 대해서는 다음 섹션에서 소개하겠습니다. 즉, /proc/stat의 출력은 percpu 변수 kernel_cpustat에서 읽혀집니다.

이 변수의 데이터가 언제 추가되는지 살펴보겠습니다.

三、统计数据怎么来的

前面我们提到内核是以采样的方式来统计 cpu 使用率的。这个采样周期依赖的是 Linux 时间子系统中的定时器。

Linux 内核每隔固定周期会发出 timer interrupt (IRQ 0),这有点像乐谱中的节拍的概念。每隔一段时间,就打出一个拍子,Linux 就响应之并处理一些事情。

Linux 中 CPU 利用率是如何算出来的?

一个节拍的长度是多长时间,是通过 CONFIG_HZ 来定义的。它定义的方式是每一秒有几次 timer interrupts。不同的系统中这个节拍的大小可能不同,通常在 1 ms 到 10 ms 之间。可以在自己的 Linux config 文件中找到它的配置。

# grep ^CONFIG_HZ /boot/config-5.4.56.bsk.10-amd64
CONFIG_HZ=1000

从上述结果中可以看出,我的机器每秒要打出 1000 次节拍。也就是每 1 ms 一次。

每次当时间中断到来的时候,都会调用 update_process_times 来更新系统时间。更新后的时间都存储在我们前面提到的 percpu 变量 kcpustat_cpu 中。

Linux 中 CPU 利用率是如何算出来的?

我们来详细看下汇总过程 update_process_times 的源码,它位于 kernel/time/timer.c 文件中。

//file:kernel/time/timer.c
void update_process_times(int user_tick)
{
 struct task_struct *p = current;

 //进行时间累积处理
 account_process_tick(p, user_tick);
 ...
}

这个函数的参数 user_tick 指的是采样的瞬间是处于内核态还是用户态。接下来调用 account_process_tick。

//file:kernel/sched/cputime.c
void account_process_tick(struct task_struct *p, int user_tick)
{
 cputime = TICK_NSEC;
 ...

 if (user_tick)
  //3.1 统计用户态时间
  account_user_time(p, cputime);
 else if ((p != rq->idle) || (irq_count() != HARDIRQ_OFFSET))
  //3.2 统计内核态时间
  account_system_time(p, HARDIRQ_OFFSET, cputime);
 else
  //3.3 统计空闲时间
  account_idle_time(cputime);
}

在这个函数中,首先设置 cputime = TICK_NSEC, 一个 TICK_NSEC 的定义是一个节拍所占的纳秒数。接下来根据判断结果分别执行 account_user_time、account_system_time 和 account_idle_time 来统计用户态、内核态和空闲时间。

3.1 用户态时间统计

//file:kernel/sched/cputime.c
void account_user_time(struct task_struct *p, u64 cputime)
{
 //分两种种情况统计用户态 CPU 的使用情况
 int index;
 index = (task_nice(p) > 0) ? CPUTIME_NICE : CPUTIME_USER;

 //将时间累积到 /proc/stat 中
 task_group_account_field(p, index, cputime);
 ......
}

account_user_time 函数主要分两种情况统计:

  • 如果进程的 nice 值大于 0,那么将会增加到 CPU 统计结构的 nice 字段中。
  • 如果进程的 nice 值小于等于 0,那么增加到 CPU 统计结构的 user 字段中。

看到这里,开篇的问题 2 就有答案了,其实用户态的时间不只是 user 字段,nice 也是。之所以要把 nice 分出来,是为了让 Linux 用户更一目了然地看到调过 nice 的进程所占的 cpu 周期有多少。

我们平时如果想要观察系统的用户态消耗的时间的话,应该是将 top 中输出的 user 和 nice 加起来一并考虑,而不是只看 user!

接着调用 task_group_account_field 来把时间加到前面我们用到的 kernel_cpustat 内核变量中。

//file:kernel/sched/cputime.c
static inline void task_group_account_field(struct task_struct *p, int index,
      u64 tmp)
{
 __this_cpu_add(kernel_cpustat.cpustat[index], tmp);
 ...
}

3.2 内核态时间统计

我们再来看内核态时间是如何统计的,找到 account_system_time 的代码。

//file:kernel/sched/cputime.c
void account_system_time(struct task_struct *p, int hardirq_offset, u64 cputime)
{
 if (hardirq_count() - hardirq_offset)
  index = CPUTIME_IRQ;
 else if (in_serving_softirq())
  index = CPUTIME_SOFTIRQ;
 else
  index = CPUTIME_SYSTEM;

 account_system_index_time(p, cputime, index);
}

内核态的时间主要分 3 种情况进行统计。

  • 如果当前处于硬中断执行上下文, 那么统计到 irq 字段中;
  • 如果当前处于软中断执行上下文, 那么统计到 softirq 字段中;
  • 否则统计到 system 字段中。

判断好要加到哪个统计项中后,依次调用 account_system_index_time、task_group_account_field 来将这段时间加到内核变量 kernel_cpustat 中。

//file:kernel/sched/cputime.c
static inline void task_group_account_field(struct task_struct *p, int index,
      u64 tmp)
{ 
 __this_cpu_add(kernel_cpustat.cpustat[index], tmp);
}

3.3 空闲时间的累积

没错,在内核变量 kernel_cpustat 中不仅仅是统计了各种用户态、内核态的使用时间,空闲也一并统计起来了。

如果在采样的瞬间,cpu 既不在内核态也不在用户态的话,就将当前节拍的时间都累加到 idle 中。

//file:kernel/sched/cputime.c
void account_idle_time(u64 cputime)
{
 u64 *cpustat = kcpustat_this_cpu->cpustat;
 struct rq *rq = this_rq();

 if (atomic_read(&rq->nr_iowait) > 0)
  cpustat[CPUTIME_IOWAIT] += cputime;
 else
  cpustat[CPUTIME_IDLE] += cputime;
}

在 cpu 空闲的情况下,进一步判断当前是不是在等待 IO(例如磁盘 IO),如果是的话这段空闲时间会加到 iowait 中,否则就加到 idle 中。从这里,我们可以看到 iowait 其实是 cpu 的空闲时间,只不过是在等待 IO 完成而已。

看到这里,开篇问题 3 也有非常明确的答案了,io wait 其实是 cpu 在空闲状态的一项统计,只不过这种状态和 idle 的区别是 cpu 是因为等待 io 而空闲。

四、总结

本文深入分析了 Linux 统计系统 CPU 利用率的内部原理。全文的内容可以用如下一张图来汇总:

Linux 中 CPU 利用率是如何算出来的?

Linux 中的定时器会以某个固定节拍,比如 1 ms 一次采样各个 cpu 核的使用情况,然后将当前节拍的所有时间都累加到 user/nice/system/irq/softirq/io_wait/idle 中的某一项上。

top 命令是读取的 /proc/stat 中输出的 cpu 各项利用率数据,而这个数据在内核中是根据 kernel_cpustat 来汇总并输出的。

回到开篇问题 1,top 输出的利用率信息是如何计算出来的,它精确吗?

/proc/stat 文件输出的是某个时间点的各个指标所占用的节拍数。如果想像 top 那样输出一个百分比,计算过程是分两个时间点 t1, t2 分别获取一下 stat 文件中的相关输出,然后经过个简单的算术运算便可以算出当前的 cpu 利用率。

再说是否精确。这个统计方法是采样的,只要是采样,肯定就不是百分之百精确。但由于我们查看 cpu 使用率的时候往往都是计算 1 秒甚至更长一段时间的使用情况,这其中会包含很多采样点,所以查看整体情况是问题不大的。

另外从本文,我们也学到了 top 中输出的 cpu 时间项目其实大致可以分为三类:

第****一类:用户态消耗时间,包括 user 和 nice。如果想看用户态的消耗,要将 user 和 nice 加起来看才对。

第二类:内核态消耗时间,包括 irq、softirq 和 system。

第三类:空闲时间,包括 io_wait 和 idle。其中 io_wait 也是 cpu 的空闲状态,只不过是在等 io 完成而已。如果只是想看 cpu 到底有多闲,应该把 io_wait 和 idle 加起来才对。


위 내용은 Linux에서 CPU 사용률은 어떻게 계산됩니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 lxlinux.net에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제