Yoctoベースの組み込みLinuxがビルドエラーになったときに試したこと

この記事は [Linuxアドベントカレンダー2021](https://qiita.com/advent-calendar/2021/linux) の24日目の記事なります。
前日の23日目は @leosuke さんによる
**【元Linux向けアプリケーション開発者がおすすめする本「C言語によるスーパーLinuxプログラミング」の紹介】**でした。
私は知らなかった本で有用な知識が得られると感じました。是非、読んでみたいと思いました。

#自己紹介
組み込みソフトウェアエンジニアでベアメタルな開発がほとんどです。
Linuxを適用した組み込み製品開発の経験はありません。
今回、学習目的で組み込みLinuxの環境構築にチャレンジしたのでそのプロセスを共有したいと考えました。
つっこみどころが多々あると思いますが暖かい気持ちで読んでいただけるとありがたいです。

#やったこと
ラズパイ４　ModelB　８GでYoctoベースの組み込みLinux「Wind River Linux(バージョン: 10.19.45.19)」を動かせました。
**基本、APSさんの記事とおりに実施**しました。
実施したことは次の２つです。

1. カーネルモジュールのビルド
1. Wind River Linuxのビルド

実施しましたがビルドエラー⇔調査・対策の試行錯誤がありました。
この調査・対策の試行錯誤を共有すれば何か役に立つかもしれないと考えました。

次は当時のツイートです。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Linux開発経験ないおじさんが、最近Linux学び始めました。<br>わからないことだらけでしたが、構築のトラブルを調査⇄対応の試行錯誤で少成長を感じられました。 <a href="https://t.co/utZjwsLt3s">pic.twitter.com/utZjwsLt3s</a></p>&mdash; k-abe@組み込みソフトウェアの人 (@juraruming) <a href="https://twitter.com/juraruming/status/1447935291168264196?ref_src=twsrc%5Etfw">October 12, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


APSさんの記事は次を参考にしました。
カーネルモジュールのビルドは次を参考にしました。

https://www.aps-web.jp/academy/wr-linux/23638/

Wind River Linuxのビルドは次を参考にしました。

https://www.aps-web.jp/academy/wr-linux/18676/#raspi_microsd_init

# 環境
## ホストPC環境
Windows PCにVirtualBoxをインストールし、ゲストOSをUbuntu 18.04.5 Desktop　６４bitにしました。
APSさんのWebページだとCentOS 7を使っていましたがサポート終了のニュースが流れていました。
Ubuntu 18.04はWind River Linux　10.19.45.19に対応しているので決めました。

ホストPCの推奨条件:

https://docs.windriver.com/bundle/Wind_River_Linux_Release_Notes_LTS_19_tki1589820771450/page/mmo1403548582698.html

LinuxホストPCの推奨条件:

https://docs.windriver.com/bundle/Wind_River_Linux_Release_Notes_LTS_19_tki1589820771450/page/mmo1403548614263.html

disk空き容量は５００Gバイトくらいあると安心かと思います。


## ホストPCのスペック
ホストPCのスペックは次のとおりです。

 - OS: Windows 10 Pro バージョン20H2
 - プロセッサ: Intel(R) Core(TM) i7-8700 CPU @3.20GHz 
 - メモリ: 8G
 - 64bit
 - SSD 1T

このホストPCにVirtualBox　6.1をインストールしました。

## VirtualBoxの設定
VirtualBoxの設定は次のとおりです。

設定全体像:

![VirtualBox.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/171866/9744e97f-016c-f70b-79d1-699d33195e52.png)

メモリ:
メモリは設定可能上限値の５G程度にしました。

![memory.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/171866/9e35d37e-de48-2584-29c4-f7a410fb8e64.png)


プロセッサ:
環境構築当初は6コアに設定していましたが後述する理由で最終的に１コアに設定変更しました。

![cpu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/171866/b39f5325-e1a1-e7af-0595-f6966713ec6e.png)


ディスプレイ:
こちらはデフォルトのままです。
![display.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/171866/5df234e4-d8fc-4c90-0113-ac41d45ca430.png)

ネットワーク:
ブリッジアダプターに変更しました。
![net.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/171866/7bdf3091-817e-eb87-bb5b-f76cd5544f18.png)


## ラズパイ環境
ラズパイ４の必要条件は次になります。

次のリンクの【Raspberry Pi 4 Model B で組み込みLinuxディストリビューション開発】のとおりです。

https://www.aps-web.jp/academy/wr-linux/18676/#raspi_microsd_init

 - Raspberry Pi 4 Model B+ メモリ 4GB
 - microSDカード 16GB ⇛ 32Gを使いました。
 - microSDカードリーダ
 - USB-C給電ケーブル
 - 有線LANケーブル
 - USBマウス
 - USBキーボード
 - microHDMI to HDMI変換ケーブル
 - HDMIケーブル
 - HDMI対応ディスプレイ


# カーネルモジュールのビルド
カーネルモジュールのビルドは次のリンクを参照しそのまま実行していきました。

https://www.aps-web.jp/academy/wr-linux/23638/

リンクの【Wind River LinuxでLinuxディストリビューションとSDKをビルドする】で次のコマンドを実行します。

```
bitbake wrlinux-image-glibc-std -c populate_sdk
```

【エラー】
ビルドがエラーで失敗していました。
dmesgでエラー内容を確認すると【Out of memory】が発生していました。

```
[ 1095.180723] [  12067]  1000 12067    25572      134    61440       38             0 tar
[ 1095.180725] [  12068]  1000 12068  2055002  1155100 10846208   174013             0 xz
[ 1095.180726] oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0,global_oom,task_memcg=/,task=xz,pid=12068,uid=1000
[ 1095.180833] Out of memory: Killed process 12068 (xz) total-vm:8220008kB, anon-rss:4620400kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:10592kB oom_score_adj:0
[ 1095.347151] oom_reaper: reaped process 12068 (xz), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB
[ 1671.482412] sched: RT throttling activated
```

【対策】
UbuntuのGUIをオフにしました。
次のコマンドを実行しUbuntuのGUIをオフにしてCUIでビルドするようにします。
メモリを節約してビルドすることが狙いでした。

```
sudo systemctl set-default multi-user.target 
```

参考情報：

https://bitto.jp/posts/%E6%8A%80%E8%A1%93/linux/ubuntu-multi-user/

【対策効果確認】
この対策でbitbakeコマンドが正常に終了することができました。

次は[APSさんのリンク](https://www.aps-web.jp/academy/wr-linux/23638/)の【カーネルモジュールのビルド準備をする】の次のコマンドでエラーが発生しました。

```
make modules_prepare
```

【エラー】

```
koji@koji-VirtualBox:/opt/windriver/wrlinux/19.45/sysroots/aarch64-wrs-linux/usr/src/kernel$ make modules_prepare
  HOSTCC  scripts/extract-cert
scripts/extract-cert.c:21:10: fatal error: openssl/bio.h: そのようなファイルやディレクトリはありません
 #include <openssl/bio.h>
          ^~~~~~~~~~~~~~~
compilation terminated.
scripts/Makefile.host:90: recipe for target 'scripts/extract-cert' failed
make[1]: *** [scripts/extract-cert] Error 1
Makefile:1097: recipe for target 'scripts' failed
make: *** [scripts] Error 2
```

調査した結果、次のモジュールをインストールすれば良いことがわかったのでインストールしました。

 - openssh-server
 - libssl-dev

【対策】
SSHサーバのインストール

```
sudo apt-get install openssh-server
```

参考情報：

https://aquarius-train.hatenablog.com/entry/SSH%E3%81%AE%E8%A8%AD%E5%AE%9A%E6%89%8B%E9%A0%86%28Ubuntu18_04%29%E3%81%A8Windows%E3%81%8B%E3%82%89%E3%81%AE%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E7%A2%BA%E8%AA%8D%E6%89%8B%E9%A0%86

【対策】
libssl-devのインストール

```
sudo apt-get install libssl-dev
```

参考情報：

https://github.com/TinkerBoard/debian_kernel/issues/26

【対策効果確認】
対策後、make modules_prepareコマンドが正常に終了しました。

```
koji@koji-VirtualBox:/opt/windriver/wrlinux/19.45/sysroots/aarch64-wrs-linux/usr/src/kernel$ make modules_prepare
  HOSTCC  scripts/extract-cert
  CC      scripts/mod/empty.o
  HOSTCC  scripts/mod/mk_elfconfig
  MKELF   scripts/mod/elfconfig.h
  HOSTCC  scripts/mod/modpost.o
  CC      scripts/mod/devicetable-offsets.s
  UPD     scripts/mod/devicetable-offsets.h
  HOSTCC  scripts/mod/file2alias.o
  HOSTCC  scripts/mod/sumversion.o
  HOSTLD  scripts/mod/modpost
  VDSOA   arch/arm64/kernel/vdso/gettimeofday.o
  VDSOA   arch/arm64/kernel/vdso/note.o
  VDSOA   arch/arm64/kernel/vdso/sigreturn.o
  LD      arch/arm64/kernel/vdso/vdso.so.dbg
  VDSOSYM include/generated/vdso-offsets.h
koji@koji-VirtualBox:/opt/windriver/wrlinux/19.45/sysroots/aarch64-wrs-linux/usr/src/kernel$
```

次のリンクを参照し、Makefile, hello.cを作成します。

https://docs.windriver.com/bundle/Wind_River_Linux_Tutorial_Building_a_Kernel_Module_with_the_SDK_LTS_1/page/wrt1539286031817.html

作成したhello.cをビルドしカーネルモジュール(hello.ko)を作成します。

```
koji@koji-VirtualBox:~/hello_kernel_module$ make
make -C /opt/windriver/wrlinux/19.45/sysroots/aarch64-wrs-linux/usr/src/kernel M=/home/koji/hello_kernel_module modules
make[1]: ディレクトリ '/opt/windriver/wrlinux/19.45/sysroots/aarch64-wrs-linux/lib/modules/5.2.60-yocto-standard/build' に入ります
  CC [M]  /home/koji/hello_kernel_module/hello.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/koji/hello_kernel_module/hello.mod.o
  LD [M]  /home/koji/hello_kernel_module/hello.ko
make[1]: ディレクトリ '/opt/windriver/wrlinux/19.45/sysroots/aarch64-wrs-linux/lib/modules/5.2.60-yocto-standard/build' から出ます
koji@koji-VirtualBox:~/hello_kernel_module$ ls
Makefile        hello.c   hello.mod.c  hello.o
Module.symvers  hello.ko  hello.mod.o  modules.order
```

#Wind River Linuxのビルド
次はWind River Linuxのビルドをおこないます。
次のリンクの【GUIを備えたLinuxを生成するための設定】とおりに設定しビルドします。

```
bitbake wrlinux-image-glibc-std -c populate_sdk_ex
```

参考情報：

https://www.aps-web.jp/academy/wr-linux/18676/#raspi_microsd_init

エラーとなってしまいましたが、最後までビルドを継続するようオプション(-k)を追加して再びビルドします。

```
koji@koji-VirtualBox:‾/wrlinux_bcm-2xx-rpi4_prj/rpi-gui$ bitbake wrlinux-image-glibc-std -c -k populate_sdk_ext
```

オプションの位置を変えてやってみます。

```
koji@koji-VirtualBox:‾/wrlinux_bcm-2xx-rpi4_prj/rpi-gui$ bitbake -k wrlinux-image-glibc-std -c populate_sdk_ext
```

エラーになりました。

```
Summary: 1 task failed:
  virtual:multilib:lib32:/home/koji/wrlinux_bcm-2xx-rpi4_prj/layers/oe-core/meta/recipes-support/boost/boost_1.71.0.bb:do_compile
Summary: There were 43 WARNING messages shown.
Summary: There was 1 ERROR message shown, returning a non-zero exit code.
```

dmesgで確認したところ、【Out of memory】が確認できました。

【対策】
10分毎にWidowsのフリーソフト・メモリクリーナの設定を40%掃除に設定しbitbakeしてみました。

【対策効果確認】
エラーは解消されませんでした。

【対策】
メモリクリーナの設定を70%掃除に設定しbitbakeしてみました。

【対策効果確認】
エラーは解消されませんでした。

【対策】
【ビルドの並列度】に関する設定を変更しbitbakeを実行してみました。
【BB_NUMBER_THREADS】は**同時に処理するパッケージ数**ということだそうです。
【PARALLEL_MAKE】は**パッケージのビルドに割り当てるスレッド数**ということだそうです。

```
conf/local.conf
BB_NUMBER_THREADS = "1"
PARALLEL_MAKE = "1"
```

【対策効果確認】
エラーは解消しませんでした。


参考動画: 再生時間帯 0:２５〜１:45

https://youtu.be/5bdzq9oxpAE

https://qiita.com/byuu/items/95086d07e317dfe64ee2

【対策】
local.confの記述が間違っているかもしれないと思い、次のように修正しbitbakeコマンドを実行しました。

```
PARALLEL_MAKE = "-j 1"
```

【対策効果確認】
エラーは解消しませんでした。

他に何か対応策はないか調べていたところ次の概念を知りました。

 - OOM killer (out of memory killer) 
 - オーバーコミット

参考情報

https://passingloop.tumblr.com/post/11957331420/overcommit-and-oom-killer

参考情報

https://next49.hatenadiary.jp/entry/20191204/1575459045

デフォルト設定は次の値でした。

```
koji@koji-VirtualBox:~/wrlinux_bcm-2xx-rpi4_prj/rpi-gui$ cat /proc/sys/vm/overcommit_memory
0
```

これは次の意味になります。

 - オーバーコミット有効
 - 1回の malloc で確保できるのは実際に利用可能なメモリの大きさまで

```
koji@koji-VirtualBox:~/wrlinux_bcm-2xx-rpi4_prj/rpi-gui$ cat /proc/sys/vm/overcommit_ratio
50
```

こちらは次の意味になります。

 - メモリ容量の50%でOOM Killerが呼び出される

【対策】
次の値に変更してbitbakeします。

 - overcommit_memory 0 ⇛ 2(オーバーコミット無効)に変更
 - overcommit_ratioは50のままで変更なし

次のコマンドを実行し設定します。

```
% sudo su
# echo 2 > /proc/sys/vm/overcommit_memory
```

【対策効果確認】
Out of memoryは解消されませんでした。


【対策】
次の値に変更してbitbakeします。

 - overcommit_memory 0 ⇛ 2(オーバーコミット無効)に変更
 - overcommit_ratio　50 ⇛ 90(メモリ容量の90%でOOM Killerが呼び出される) ★今回の変更点

【対策効果確認】
Out of memoryは解消されませんでした。


【対策】
次の設定変更をおこないbitbakeします。

 - VirtualBoxのプロセッサ数を6から1にする。
 - overcommit_memory 0 ⇛ 2(オーバーコミット無効)に変更
 - overcommit_ratio　50 ⇛ 90(メモリ容量の90%でOOM Killerが呼び出される)
 - conf/local.conf は前の変更を適用している。
 BB_NUMBER_THREADS = "1"
 PARALLEL_MAKE = "-j 1"

【対策効果確認】
bitbakeコマンドが成功しました。:cracker::cracker::cracker:

次の階層にWind River Linuxのイメージファイルが作成できていることを確認できました(次のmicroSDへ書き込みで使うファイルのみ抜粋しています)。

```
koji@koji-VirtualBox:~/wrlinux_bcm-2xx-rpi4_prj/rpi-gui$ ls tmp-glibc/deploy/images/bcm-2xxx-rpi4/
 〜 略 〜
Image-bcm-2xxx-rpi4.bin
 〜 略 〜
bcm2711-rpi-4-b.dtb
 〜 略 〜
wrlinux-image-glibc-std-bcm-2xxx-rpi4.tar.bz2
 〜 略 〜
```

[APSさんのリンク](https://www.aps-web.jp/academy/wr-linux/18676/)の【ビルド結果をmicroSDへ書き込み、起動する】のコマンドを実行し、Raspberry Pi 4 Model B起動用のmicroSDカードにファイルを書き込みます。

結果、冒頭のツイート画像のようにWind River Linuxが起動しました。
途中の作業工程で設定した【GUI】の画面になっています。

# まとめ
いろいろとエラーは発生しましたが結果としてLinuxを構築することができました。
APSさんの記事、ネットにある先人の皆さんの情報共有に感謝です。
環境構築しなければわからなかった知識も得ることができました。

 - GUIをオフにする設定
 - OOM killer (out of memory killer) の概念
 - オーバーコミットの概念
 - ビルドの並列度に関する設定(BB_NUMBER_THREADS, PARALLEL_MAKE)
 - VirtualBoxなど少ないリソースでYoctoベースの組み込みLinuxをビルドする際に気をつける勘所

それと強く思ったのは**Yoctoベースの組み込みLinuxの構築はできるだけハイスペックPCでやったほうが良い**、ということです^^;
長文を最後まで読んでいただきありがとうございました。
Linux環境構築を始めてみようと思っている方、悩んでいた方の参考になれば嬉しいです。


[Linuxアドベントカレンダー2021](https://qiita.com/advent-calendar/2021/linux)  の25日目は@kentaostさんによる記事です。