Rumah  >  Artikel  >  Tutorial sistem  >  Bagaimanakah penggunaan CPU dikira dalam Linux?

Bagaimanakah penggunaan CPU dikira dalam Linux?

WBOY
WBOYke hadapan
2024-02-15 11:15:11910semak imbas

Apabila melihat status berjalan perkhidmatan dalam talian pada pelayan dalam talian, kebanyakan orang suka menggunakan arahan teratas terlebih dahulu untuk melihat penggunaan CPU keseluruhan sistem semasa. Sebagai contoh, untuk mesin rawak, maklumat penggunaan yang dipaparkan oleh arahan atas adalah seperti berikut:

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

Hasil keluaran ini adalah mudah untuk dikatakan, tetapi tidak begitu mudah untuk memahami segala-galanya walaupun rumit. Contohnya:

Soalan 1: Bagaimanakah output maklumat penggunaan mengikut bahagian atas dikira Adakah ia tepat?
Soalan 2: Lajur ni bagus Ia mengeluarkan overhed CPU semasa memproses?
Soalan 3: wa mewakili io wait, jadi adakah CPU sibuk atau melahu dalam tempoh ini?

Hari ini kami mempunyai kajian mendalam tentang statistik penggunaan cpu. Melalui kajian hari ini, anda bukan sahaja akan memahami butiran pelaksanaan statistik penggunaan CPU, tetapi juga mempunyai pemahaman yang lebih mendalam tentang penunjuk seperti tunggu bagus dan io.

Hari ini kita mulakan dengan pemikiran kita sendiri!

1. Fikirkan dulu

Mengetepikan pelaksanaan Linux, jika anda mempunyai keperluan berikut, anda mempunyai pelayan empat teras dengan empat proses berjalan padanya.

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

Membolehkan anda mereka bentuk dan mengira penggunaan CPU keseluruhan sistem Ia menyokong output seperti arahan teratas dan memenuhi keperluan berikut:

  • Penggunaan CPU hendaklah setepat mungkin
  • Ia adalah perlu untuk mencerminkan status CPU serta-merta pada tahap kedua sebanyak mungkin.

Anda boleh berhenti dan berfikir selama beberapa minit.

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

Okey, tamatkan pemikiran. Selepas memikirkannya, anda akan mendapati bahawa keperluan yang kelihatan mudah ini sebenarnya agak rumit.

Satu idea ialah menjumlahkan masa pelaksanaan semua proses dan kemudian membahagikannya dengan jumlah masa pelaksanaan sistem * 4.

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

Tiada masalah dengan idea ini Anda boleh menggunakan kaedah ini untuk mengira penggunaan CPU dalam jangka masa yang panjang, dan statistiknya cukup tepat.

Tetapi selagi anda telah menggunakan bahagian atas, anda akan tahu bahawa output penggunaan CPU oleh bahagian atas tidak tetap untuk masa yang lama, tetapi akan dikemas kini secara dinamik dalam unit 3 saat secara lalai (selang masa ini boleh ditetapkan menggunakan -d ). Penyelesaian kami boleh mencerminkan jumlah penggunaan, tetapi sukar untuk mencerminkan keadaan serta-merta ini. Anda mungkin fikir saya boleh mengiranya sebagai satu setiap 3 saat, bukan? Tetapi pada titik manakah tempoh 3 saat ini bermula. Butirannya sukar dikawal.

Inti kepada persoalan pemikiran sebelum ini ialah bagaimana menyelesaikan masalah serta-merta. Apabila ia datang kepada keadaan sementara, anda mungkin mempunyai idea lain. Kemudian saya akan menggunakan pensampelan segera untuk melihat berapa banyak teras yang sedang sibuk. Jika dua daripada empat teras sibuk, penggunaannya ialah 50%.

Jalur pemikiran ini juga ke arah yang betul, tetapi terdapat dua masalah:

  • Nombor yang anda kira semuanya adalah gandaan 25%;
  • Nilai serta-merta ini boleh menyebabkan perubahan liar dalam paparan penggunaan CPU.

Sebagai contoh, gambar di bawah:

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

Daripada keadaan serta-merta t1, penggunaan CPU sistem sudah pasti 100%, tetapi dari perspektif t2, penggunaan telah menjadi 0%. Idea ini berada dalam arah yang betul, tetapi jelas pengiraan kasar ini tidak dapat berfungsi dengan elegan seperti arahan teratas.

Mari kita perbaiki dan gabungkan dua idea di atas, mungkin kita boleh selesaikan masalah kita. Dari segi persampelan, kita tetapkan tempoh lebih halus, tetapi dari segi pengiraan, kita tetapkan tempoh itu lebih kasar.

Kami memperkenalkan konsep tempoh pakai, masa, seperti pensampelan setiap 1 milisaat. Jika CPU sedang berjalan pada saat pensampelan, 1 ms ini direkodkan seperti yang digunakan. Pada masa ini, penggunaan CPU serta-merta akan diperoleh dan disimpan.

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

Apabila mengira penggunaan CPU dalam masa 3 saat, seperti julat masa t1 dan t2 dalam gambar di atas. Kemudian tambah semua nilai serta-merta dalam tempoh ini dan ambil purata. Ini boleh menyelesaikan masalah di atas, statistiknya agak tepat, dan masalah nilai serta-merta berayun dengan kuat dan terlalu kasar (hanya boleh berubah dalam unit 25%) dapat dielakkan.

Sesetengah pelajar mungkin bertanya, bagaimana jika CPU bertukar antara dua persampelan, seperti yang ditunjukkan dalam gambar di bawah.

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

Apabila titik pensampelan semasa tiba, proses A baru sahaja selesai dilaksanakan Untuk masa yang singkat, ia tidak dikira oleh titik pensampelan sebelumnya, dan tidak boleh dikira kali ini. Untuk proses B, ia sebenarnya hanya bermula untuk tempoh yang singkat Nampaknya terlalu banyak untuk merekodkan semua 1 ms.

Masalah ini memang wujud, tetapi kerana pensampelan kami sekali 1 ms, dan apabila kami benar-benar menyemak dan menggunakannya, ia sekurang-kurangnya pada tahap kedua, yang akan merangkumi maklumat daripada beribu-ribu titik pensampelan, jadi ralat ini tidak Ia akan mempengaruhi pemahaman kita tentang keadaan keseluruhan.

Malah, inilah cara Linux mengira penggunaan CPU sistem. Walaupun mungkin terdapat ralat, ia sudah cukup untuk digunakan sebagai data statistik. Dari segi pelaksanaan, Linux mengumpul semua nilai serta-merta ke dalam data tertentu, dan bukannya menyimpan banyak salinan data serta-merta.

Seterusnya, mari kita masuk ke Linux untuk melihat pelaksanaan spesifiknya bagi statistik penggunaan CPU sistem.

2. Di manakah data penggunaan arahan atas

Pelaksanaan Linux yang kami nyatakan dalam bahagian sebelumnya adalah untuk mengumpul nilai segera​​​kepada data tertentu Nilai ini didedahkan kepada keadaan pengguna oleh kernel melalui fail pseudo /proc/stat. Linux menggunakannya semasa mengira penggunaan CPU sistem.

Secara keseluruhan, butiran dalaman bagi kerja arahan teratas ditunjukkan dalam rajah di bawah.

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

arahan atas mengakses /proc/stat untuk mendapatkan pelbagai nilai penggunaan CPU

  • Inti memanggil fungsi stat_open untuk mengendalikan akses kepada /proc/stat;

  • Data yang diakses oleh kernel berasal dari tatasusunan kernel_cpustat dan diringkaskan

  • Cetak output ke mod pengguna.

Seterusnya, mari kembangkan setiap langkah dan lihat dengan lebih dekat.

Dengan menggunakan strace untuk mengesan pelbagai panggilan sistem bagi arahan atas, anda boleh melihat panggilannya ke fail.

# strace top
...
openat(AT_FDCWD, "/proc/stat", O_RDONLY) = 4
openat(AT_FDCWD, "/proc/2351514/stat", O_RDONLY) = 8
openat(AT_FDCWD, "/proc/2393539/stat", O_RDONLY) = 8
...

Selain /proc/stat, terdapat juga /proc/{pid}/stat yang dipecahkan mengikut setiap proses, yang digunakan untuk mengira penggunaan cpu bagi setiap proses.

Inti mentakrifkan fungsi pemprosesan untuk setiap fail pseudo Kaedah pemprosesan fail /proc/stat ialah proc_stat_operations.

//file:fs/proc/stat.c
static int __init proc_stat_init(void)
{
 proc_create("stat", 0, NULL, &proc_stat_operations);
 return 0;
}

static const struct file_operations proc_stat_operations = {
 .open  = stat_open,
 ...
};

proc_stat_operations mengandungi kaedah operasi yang sepadan dengan fail ini. Apabila fail /proc/stat dibuka, stat_open akan dipanggil. stat_open memanggil single_open_size dan show_stat dalam urutan untuk mengeluarkan kandungan data. Mari lihat kodnya:

//file:fs/proc/stat.c
static int show_stat(struct seq_file *p, void *v)
{
 u64 user, nice, system, idle, iowait, irq, softirq, steal;

 for_each_possible_cpu(i) {
  struct kernel_cpustat *kcs = &kcpustat_cpu(i);

  user += kcs->cpustat[CPUTIME_USER];
  nice += kcs->cpustat[CPUTIME_NICE];
  system += kcs->cpustat[CPUTIME_SYSTEM];
  idle += get_idle_time(kcs, i);
  iowait += get_iowait_time(kcs, i);
  irq += kcs->cpustat[CPUTIME_IRQ];
  softirq += kcs->cpustat[CPUTIME_SOFTIRQ];
  ...
 }

 //转换成节拍数并打印出来
 seq_put_decimal_ull(p, "cpu  ", nsec_to_clock_t(user));
 seq_put_decimal_ull(p, " ", nsec_to_clock_t(nice));
 seq_put_decimal_ull(p, " ", nsec_to_clock_t(system));
 seq_put_decimal_ull(p, " ", nsec_to_clock_t(idle));
 seq_put_decimal_ull(p, " ", nsec_to_clock_t(iowait));
 seq_put_decimal_ull(p, " ", nsec_to_clock_t(irq));
 seq_put_decimal_ull(p, " ", nsec_to_clock_t(softirq));
 ...
}

Dalam kod di atas, for_each_possible_cpu sedang merentasi pembolehubah kcpustat_cpu yang menyimpan data penggunaan cpu. Pembolehubah ini ialah pembolehubah percpu, yang menyediakan elemen tatasusunan untuk setiap teras logik. Ia menyimpan pelbagai acara yang sepadan dengan teras semasa, termasuk pengguna, bagus, sistem, idel, iowait, irq, softirq, dll.

Dalam gelung ini, tambahkan setiap penggunaan setiap teras. Akhirnya, data dikeluarkan melalui seq_put_decimal_ull.

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

Perhatikan bahawa dalam kernel, setiap kali sebenarnya direkodkan dalam nanosaat, tetapi semuanya ditukar kepada unit rentak apabila dikeluarkan. Bagi panjang unit rentak, kami akan memperkenalkannya dalam bahagian seterusnya. Ringkasnya, output /proc/stat dibaca daripada pembolehubah percpu kernel_cpustat.

Mari kita lihat apabila data dalam pembolehubah ini ditambah.

三、统计数据怎么来的

前面我们提到内核是以采样的方式来统计 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 加起来才对。


Atas ialah kandungan terperinci Bagaimanakah penggunaan CPU dikira dalam Linux?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:lxlinux.net. Jika ada pelanggaran, sila hubungi admin@php.cn Padam