top始终显示load 1

观察到一个奇怪的现象,同样的Raspberry Pi 4,区别是一个Pi4是2G版本,使用TF卡;另一个Pi4是8G版本,直接使用外接的SSD移动硬盘。 两个树莓派都是使用Ubuntu 20.04 for Raspberry Pi 64bit版本,操作系统原先是安装在TF卡,然后通过dd命令clone到另一个使用SSD移动硬盘的树莓派。

照理说两个系统应该完全一致,但是发现还是有点差异

  • 8G版本,可以连接无线访问internet,我发现这个主机始终有一个负载Load 1存在,但是,却看不到运行的进程,系统进程基本都是sleep的

top - 22:54:18 up 1 day,  5:02,  2 users,  load average: 1.00, 1.00, 1.00
Tasks: 129 total,   1 running, 128 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.1 us,  0.1 sy,  0.0 ni, 99.8 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :   7810.3 total,   7246.5 free,    173.0 used,    390.8 buff/cache
MiB Swap:      0.0 total,      0.0 free,      0.0 used.   7522.3 avail Mem

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
   4079 ubuntu    20   0   10700   3176   2720 R   0.7   0.0   0:00.15 top
   3852 root      20   0       0      0      0 I   0.3   0.0   0:00.10 [kworker/0:1-events]
      1 root      20   0  167624  10796   7336 S   0.0   0.1   0:06.50 /sbin/init fixrtc
      2 root      20   0       0      0      0 S   0.0   0.0   0:00.06 [kthreadd]
      3 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 [rcu_gp]
      4 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 [rcu_par_gp]
      6 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 [kworker/0:0H-kblockd]
      8 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 [mm_percpu_wq]
      9 root      20   0       0      0      0 S   0.0   0.0   0:00.08 [ksoftirqd/0]
     10 root      20   0       0      0      0 I   0.0   0.0   0:02.40 [rcu_preempt]
     11 root      rt   0       0      0      0 S   0.0   0.0   0:00.60 [migration/0]
     12 root     -51   0       0      0      0 S   0.0   0.0   0:00.00 [idle_inject/0]
     14 root      20   0       0      0      0 S   0.0   0.0   0:00.00 [cpuhp/0]
     15 root      20   0       0      0      0 S   0.0   0.0   0:00.00 [cpuhp/1]
     16 root     -51   0       0      0      0 S   0.0   0.0   0:00.00 [idle_inject/1]
     17 root      rt   0       0      0      0 S   0.0   0.0   0:00.60 [migration/1]
     18 root      20   0       0      0      0 S   0.0   0.0   0:01.06 [ksoftirqd/1]
     20 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 [kworker/1:0H-kblockd]
     21 root      20   0       0      0      0 S   0.0   0.0   0:00.00 [cpuhp/2]
     22 root     -51   0       0      0      0 S   0.0   0.0   0:00.00 [idle_inject/2]
     23 root      rt   0       0      0      0 S   0.0   0.0   0:00.60 [migration/2]
     24 root      20   0       0      0      0 S   0.0   0.0   0:00.04 [ksoftirqd/2]
     26 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 [kworker/2:0H-kblockd]
     27 root      20   0       0      0      0 S   0.0   0.0   0:00.00 [cpuhp/3]
     28 root     -51   0       0      0      0 S   0.0   0.0   0:00.00 [idle_inject/3]
     29 root      rt   0       0      0      0 S   0.0   0.0   0:00.58 [migration/3]
     30 root      20   0       0      0      0 S   0.0   0.0   0:00.05 [ksoftirqd/3]
     32 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 [kworker/3:0H-kblockd]
     33 root      20   0       0      0      0 S   0.0   0.0   0:00.04 [kdevtmpfs]
     34 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 [netns]
     35 root      20   0       0      0      0 S   0.0   0.0   0:00.00 [rcu_tasks_kthre]
     36 root      20   0       0      0      0 S   0.0   0.0   0:00.00 [kauditd]
     38 root      20   0       0      0      0 S   0.0   0.0   0:00.05 [khungtaskd]
  • 2G版本,暂时不能连接无线访问internet,则系统负载接近0

top - 22:55:15 up 2 days,  7:45,  1 user,  load average: 0.00, 0.00, 0.00
Tasks: 124 total,   1 running, 123 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.1 us,  0.1 sy,  0.0 ni, 99.8 id,  0.0 wa,  0.0 hi,  0.1 si,  0.0 st
MiB Mem :   1848.2 total,   1169.1 free,    167.2 used,    511.9 buff/cache
MiB Swap:      0.0 total,      0.0 free,      0.0 used.   1645.7 avail Mem

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
   8241 ubuntu    20   0   10696   3224   2640 R   0.7   0.2   0:00.15 top
   8234 root      20   0       0      0      0 I   0.3   0.0   0:00.04 [kworker/0:1-events]
      1 root      20   0  167568  10740   7236 S   0.0   0.6   0:11.38 /sbin/init fixrtc splash
      2 root      20   0       0      0      0 S   0.0   0.0   0:00.40 [kthreadd]
      3 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 [rcu_gp]
      4 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 [rcu_par_gp]
      8 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 [mm_percpu_wq]
      9 root      20   0       0      0      0 S   0.0   0.0   0:01.99 [ksoftirqd/0]
     10 root      20   0       0      0      0 I   0.0   0.0   0:09.14 [rcu_preempt]
     11 root      rt   0       0      0      0 S   0.0   0.0   0:01.18 [migration/0]
     12 root     -51   0       0      0      0 S   0.0   0.0   0:00.00 [idle_inject/0]
     14 root      20   0       0      0      0 S   0.0   0.0   0:00.00 [cpuhp/0]
     15 root      20   0       0      0      0 S   0.0   0.0   0:00.00 [cpuhp/1]
     16 root     -51   0       0      0      0 S   0.0   0.0   0:00.00 [idle_inject/1]
     17 root      rt   0       0      0      0 S   0.0   0.0   0:01.16 [migration/1]
     18 root      20   0       0      0      0 S   0.0   0.0   0:03.78 [ksoftirqd/1]
     21 root      20   0       0      0      0 S   0.0   0.0   0:00.00 [cpuhp/2]
     22 root     -51   0       0      0      0 S   0.0   0.0   0:00.00 [idle_inject/2]
     23 root      rt   0       0      0      0 S   0.0   0.0   0:01.19 [migration/2]
     24 root      20   0       0      0      0 S   0.0   0.0   0:00.74 [ksoftirqd/2]
     27 root      20   0       0      0      0 S   0.0   0.0   0:00.00 [cpuhp/3]
     28 root     -51   0       0      0      0 S   0.0   0.0   0:00.00 [idle_inject/3]
     29 root      rt   0       0      0      0 S   0.0   0.0   0:01.16 [migration/3]
     30 root      20   0       0      0      0 S   0.0   0.0   0:00.66 [ksoftirqd/3]
     33 root      20   0       0      0      0 S   0.0   0.0   0:00.04 [kdevtmpfs]
     34 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 [netns]
     35 root      20   0       0      0      0 S   0.0   0.0   0:00.00 [rcu_tasks_kthre]
     36 root      20   0       0      0      0 S   0.0   0.0   0:00.00 [kauditd]
     39 root      20   0       0      0      0 S   0.0   0.0   0:00.10 [khungtaskd]
     40 root      20   0       0      0      0 S   0.0   0.0   0:00.00 [oom_reaper]
     41 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 [writeback]
     42 root      20   0       0      0      0 S   0.0   0.0   0:00.00 [kcompactd0]
     43 root      25   5       0      0      0 S   0.0   0.0   0:00.00 [ksmd]

我比较奇怪,这个Load 1到底是什么原因导致的,为何看不到运行进程,也没有看到有读写磁盘的iowait。

Load概念

虽然我们都知道在Linux上top命令显示的Load是一个统计数据,表明队列中有多少进程,通常有多少运行的进程占用cpu资源或者iowait,就会显示在Load上。但是,我们很少会仔细研究这个Load的计算方法,只知道这个值不能高于CPU核心数量,超出则表明系统负载过高。

做一个简单的比喻(假设你是一座大桥的交通调度员):

  • 对于一个单核心处理器就类似多通道高速路的一条通道

  • 假设大桥的一个通道能够摆满6台汽车依次通过,则当桥上的一个通告有3台车,则负载就是 0.5(3/6=0.5));如果一个通道有8台车,则有2辆车排在桥通道外面,则负载就是(8/6=1.7)

  • 对于多处理器,相当于有多条车道,则原先一个通道只能承载6辆车,多出的2辆车现在不需要堆积在桥外,可以从其他通道通过。所以对于多处理器,高于1.0的负载也不需要担心。

排查

回到我们现在的问题,我们看到top显示并没有运行的进程,那么占用top中的Load是什么呢?

实际上Load计算不仅包含了运行进程,也包含了等待CPU资源的进程,例如一个进程在等待IO - 磁盘或者网络。

  • 通过 ps -e v 命令可以检查进程状态

state    The state is given by a sequence of characters, for example, "RWNA". The      first character indicates the run state of the process:
D    Marks a process in disk (or other short term, uninterruptible) wait.
I    Marks a process that is idle (sleeping for longer than about 20 seconds).  
L    Marks a process that is waiting to acquire a lock.
R    Marks a runnable process.
S    Marks a process that is sleeping for less than about 20 seconds.
T    Marks a stopped process.
W    Marks an idle interrupt thread.
Z    Marks a dead process (a "zombie").

需要注意我们输出中进程是 RD 状态的,就是影响负载的进程。

  • cat /proc/loadavg 可以查看当前负载:

/proc/loadavg
          The first three fields in this file are load average figures giving
          the  number  of jobs in the run queue (state R) or waiting for disk
          I/O (state D) averaged over 1, 5, and 15  minutes.

注意,Load Average是只在运行队列和等待IO的任务数量。

  • 过滤出RD 状态进程

ps -e v | grep -E ' R | D ' | grep -v grep

输出可以看到有一个D住进程

   7195 ?        D      0:01      0     0     0     0  0.0 [kworker/2:1+events_freezable]

为什么有一个 kworker 出现了D?

sudo cat /proc/7195/stack

查看这个进程堆栈可以看到,是存在io等待,但是这个io等待是位于 mmc 也就是在等待SD卡设备

[<0>] __switch_to+0x104/0x170
[<0>] mmc_wait_for_req_done+0x30/0x170
[<0>] mmc_wait_for_req+0xb0/0x108
[<0>] mmc_wait_for_cmd+0x7c/0xb0
[<0>] mmc_io_rw_direct_host+0xa0/0x138
[<0>] sdio_reset+0x74/0x98
[<0>] mmc_rescan+0x33c/0x3b8
[<0>] process_one_work+0x1c0/0x458
[<0>] worker_thread+0x50/0x428
[<0>] kthread+0x104/0x130
[<0>] ret_from_fork+0x10/0x1c

原因是我配置了从外接USB存储设备启动,但是eeprom配置启动顺序是先TF卡后USB设备,启动时我确实看到屏幕闪过mmc超时,可能是这个原因导致有内核进程始终在等待mmc返回信息(但是没有SD卡)。

为了测试验证这个问题是偶发还是一直存在,我先重启一次系统,看看这个问题是否消除。果然,重启发现系统依然负载是1,依然是同样的进程D住:

$ ps -e v | grep -E ' R | D '
    168 ?        D      0:00      0     0     0     0  0.0 [kworker/1:3+events_freezable]

注意到D住的进程号会变化,有时候是168,过一会再检查D住的是100,其中100的D住进程和重启前检查一样,是有关mmc的

$ sudo cat /proc/168/stack
[<0>] __switch_to+0x104/0x170
[<0>] worker_thread+0xdc/0x428
[<0>] kthread+0x104/0x130
[<0>] ret_from_fork+0x10/0x1c
# cat /proc/168/stat
168 (kworker/1:3-events) I 2 0 0 0 -1 69238880 0 0 0 0 0 77 0 0 20 0 1 0 327 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 1 0 0 17 1 0 0 0 0 0 0 0 0 0 0 0 0 0

进程100如下:

# ps -e v | grep D
    PID TTY      STAT   TIME  MAJFL   TRS   DRS   RSS %MEM COMMAND
    100 ?        D      0:01      0     0     0     0  0.0 [kworker/1:1+events_freezable]

# cat /proc/100/stack
[<0>] __switch_to+0x104/0x170
[<0>] mmc_wait_for_req_done+0x30/0x170
[<0>] mmc_wait_for_req+0xb0/0x108
[<0>] mmc_wait_for_cmd+0x7c/0xb0
[<0>] mmc_io_rw_direct_host+0xa0/0x138
[<0>] sdio_reset+0x74/0x98
[<0>] mmc_rescan+0x33c/0x3b8
[<0>] process_one_work+0x1c0/0x458
[<0>] worker_thread+0x50/0x428
[<0>] kthread+0x104/0x130
[<0>] ret_from_fork+0x10/0x1c

# cat /proc/100/stat
100 (kworker/1:1-events) I 2 0 0 0 -1 69238880 0 0 0 0 0 158 0 0 20 0 1 0 82 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 1 0 0 17 1 0 0 0 0 0 0 0 0 0 0 0 0 0

上述这个问题是因为我配置了 树莓派4 USB启动Ubuntu Server 20.04 ,当前epprom通过USB存储启动依然会先读取SD卡,如果SD卡不存在,就会导致系统内核进程始终D住。这个问题,我暂时通过配置仅从USB外接存储启动来规避。

参考

Last updated