組込み Linux としての Nerves

前日17日はautoracexのオーガナイザー @torifukukaiou の [「kochi.ex #6 Livebook で Nerves ハンズオン！」](https://qiita.com/torifukukaiou/items/78dd9c97eb414d672479) イベントレポートでした！

---

おはこんばんにちは、[OkazaKirin.beam](https://okazakirin-beam.connpass.com/) の pojiro です。
今日は Nerves の組込み Linux としての側面の紹介にトライしてみます！

# 組込み Linux って？

炊飯器、給湯器、車載、体温計や血圧計などなどいたるところに組込まれているコンピュータがあります。これらは各分野の要求（低消費電力や低価格など）を満たすためにリソースが限られている『マイコン』が用いられていると思います。

一方で、組込み 『Linux』 はそれらよりもリソースを用いて処理をするような分野で使われています。

## どんなところで使われているのか？

私がパッと思いつくのは

* サイネージや電光掲示板[^1]
* DSP
* ルーター
* 自動販売機

[^1]: 最近ではWindows IoTを使っていることもあるみたいです。

です。その他だと以前、くら寿司のお皿の画像認識の一部にラズパイ4が使われていることが話題になりました。これも組込み Linux だと想像されます。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">くら寿司のCoral Edge TPU事例が出ました!!全国数百店舗で大規模に運用、お皿の画像認識部分でEdge TPUを使っています。僕も微力ながら記事化に協力させていただきました〜<a href="https://t.co/BITHt8tYkc">https://t.co/BITHt8tYkc</a> <a href="https://t.co/dlY94tpOx6">pic.twitter.com/dlY94tpOx6</a></p>&mdash; Hayato Y (@hayatoy82) <a href="https://twitter.com/hayatoy82/status/1321929559625719808?ref_src=twsrc%5Etfw">October 29, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

これらは、画像描画・認識、ネットワーク処理、数値演算などを１つのコンピュータで同時に担うため、マイコンよりもリソースが多いボード上に組込み Linux を構築して用いられていると考えられます。

## Nervesについて

Nerves は [Buildroot](https://buildroot.org/) を用いて構成される組込み Linux です。その組込み Linux 上で Erlang VM を動作させています。本記事では Nerves の組込み Linux としての側面を紹介してみます。対象にするターゲットマシン rpi4 です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369713/408dba75-2029-75d5-73a1-77b2d9716e25.png)

# Nerves に内包される Linux コマンド

まずはどんな Linux コマンドを持っているのか見てみます。まず `which` はあるかな？

```elixir
iex(1)> cmd "which" # cmd は IEx シェルから Linux コマンドを直で叩ける Nerves 独自の関数です
/bin/sh: which: not found
127
```

残念。`which` はありません。なので代わりに `type` を使います。

```elixir
iex(3)> cmd "type"
0
iex(4)> cmd "type sh"
sh is /bin/sh
0
```

Nerves 上で使用したい Linux コマンドの有無を調べるには `type` が使えます。`find`もあります。

```elixir
iex(5)> cmd "type find"
find is /usr/bin/find
0
```

しかしながら、こんなふうに調べていたら埒が開かないので、`/bin`, `/sbin`, `/usr/bin`をまとめて調べてしまいましょう。

```elixir
iex(12)> Enum.map(["/bin", "/sbin", "/usr/bin"], fn path -> cmd("ls -la #{path}/*") end)
lrwxrwxrwx    1 root     root             7 Nov  6 21:43 /bin/umount -> busybox
lrwxrwxrwx    1 root     root             7 Nov  6 21:43 /bin/sleep -> busybox
lrwxrwxrwx    1 root     root             7 Nov  6 21:43 /bin/sh -> busybox
lrwxrwxrwx    1 root     root             7 Nov  6 21:43 /bin/rmdir -> busybox
lrwxrwxrwx    1 root     root             7 Nov  6 21:43 /bin/rm -> busybox
lrwxrwxrwx    1 root     root             7 Nov  6 21:43 /bin/pwd -> busybox
lrwxrwxrwx    1 root     root             7 Nov  6 21:43 /bin/ps -> busybox
lrwxrwxrwx    1 root     root             7 Nov  6 21:43 /bin/mv -> busybox
lrwxrwxrwx    1 root     root             7 Nov  6 21:43 /bin/mount -> busybox
lrwxrwxrwx    1 root     root             7 Nov  6 21:43 /bin/mknod -> busybox
lrwxrwxrwx    1 root     root             7 Nov  6 21:43 /bin/mkdir -> busybox
lrwxrwxrwx    1 root     root             7 Nov  6 21:43 /bin/ls -> busybox
lrwxrwxrwx    1 root     root             7 Nov  6 21:43 /bin/kill -> busybox
lrwxrwxrwx    1 root     root             7 Nov  6 21:43 /bin/grep -> busybox
lrwxrwxrwx    1 root     root             7 Nov  6 21:43 /bin/dmesg -> busybox
lrwxrwxrwx    1 root     root             7 Nov  6 21:43 /bin/df -> busybox
lrwxrwxrwx    1 root     root             7 Nov  6 21:43 /bin/dd -> busybox
lrwxrwxrwx    1 root     root             7 Nov  6 21:43 /bin/date -> busybox
lrwxrwxrwx    1 root     root             7 Nov  6 21:43 /bin/cp -> busybox
lrwxrwxrwx    1 root     root             7 Nov  6 21:43 /bin/cat -> busybox
-rwsr-xr-x    1 root     root        280448 Nov  6 21:43 /bin/busybox
lrwxrwxrwx    1 root     root             7 Nov  6 21:43 /bin/ash -> busybox
lrwxrwxrwx    1 root     root            14 Nov  6 21:43 /sbin/uevent -> ../bin/busybox
lrwxrwxrwx    1 root     root            14 Nov  6 21:43 /sbin/udhcpc -> ../bin/busybox
lrwxrwxrwx    1 root     root            14 Nov  6 21:43 /sbin/sysctl -> ../bin/busybox
lrwxrwxrwx    1 root     root            14 Nov  6 21:43 /sbin/rmmod -> ../bin/busybox
lrwxrwxrwx    1 root     root            14 Nov  6 21:43 /sbin/reboot -> ../bin/busybox
lrwxrwxrwx    1 root     root            14 Nov  6 21:43 /sbin/poweroff -> ../bin/busybox
lrwxrwxrwx    1 root     root            14 Nov  6 21:43 /sbin/modprobe -> ../bin/busybox
lrwxrwxrwx    1 root     root            14 Nov  6 21:43 /sbin/modinfo -> ../bin/busybox
lrwxrwxrwx    1 root     root            14 Nov  6 21:43 /sbin/lsmod -> ../bin/busybox
lrwxrwxrwx    1 root     root            14 Nov  6 21:43 /sbin/iptunnel -> ../bin/busybox
lrwxrwxrwx    1 root     root            14 Nov  6 21:43 /sbin/iprule -> ../bin/busybox
lrwxrwxrwx    1 root     root            14 Nov  6 21:43 /sbin/iproute -> ../bin/busybox
lrwxrwxrwx    1 root     root            14 Nov  6 21:43 /sbin/ipneigh -> ../bin/busybox
lrwxrwxrwx    1 root     root            14 Nov  6 21:43 /sbin/iplink -> ../bin/busybox
lrwxrwxrwx    1 root     root            14 Nov  6 21:43 /sbin/ipaddr -> ../bin/busybox
lrwxrwxrwx    1 root     root            14 Nov  6 21:43 /sbin/ip -> ../bin/busybox
-rwxr-xr-x    1 root     root         48088 Nov  6 21:43 /sbin/init
lrwxrwxrwx    1 root     root            14 Nov  6 21:43 /sbin/ifconfig -> ../bin/busybox
lrwxrwxrwx    1 root     root            14 Nov  6 21:43 /sbin/halt -> ../bin/busybox
-rwxr-xr-x    1 root     root         10016 Nov  6 21:43 /usr/bin/vcmailbox
-rwxr-xr-x    1 root     root         46960 Nov  6 21:43 /usr/bin/vchiq_test
-rwxr-xr-x    1 root     root         10016 Nov  6 21:43 /usr/bin/vcgencmd
lrwxrwxrwx    1 root     root            17 Nov  6 21:43 /usr/bin/unzip -> ../../bin/busybox
-rwxr-xr-x    1 root     root         31320 Nov  6 21:43 /usr/bin/tvservice
lrwxrwxrwx    1 root     root            17 Nov  6 21:43 /usr/bin/tail -> ../../bin/busybox
-rwxr-xr-x    1 root     root         10016 Nov  6 21:43 /usr/bin/slencheck
lrwxrwxrwx    1 root     root            17 Nov  6 21:43 /usr/bin/sha256sum -> ../../bin/busybox
-rwxr-xr-x    1 root     root         14808 Nov  6 21:43 /usr/bin/rngtest
-rwxr-xr-x    1 root     root          5920 Nov  6 21:43 /usr/bin/randstat
-rwxr-xr-x    1 root     root         44184 Nov  6 21:43 /usr/bin/pigs
-rwxr-xr-x    1 root     root         14144 Nov  6 21:43 /usr/bin/pigpiod
-rwxr-xr-x    1 root     root         10016 Nov  6 21:43 /usr/bin/pig2vcd
-rwxr-xr-x    1 root     root         22312 Nov  6 21:43 /usr/bin/odbcinst
-rwxr-xr-x    1 root     root         10016 Nov  6 21:43 /usr/bin/odbc_config
-rwxr-xr-x    1 root     root         19040 Nov  6 21:43 /usr/bin/nbtty
-rwxr-xr-x    1 root     root          5363 Nov  6 21:43 /usr/bin/ldd
lrwxrwxrwx    1 root     root            17 Nov  6 21:43 /usr/bin/killall -> ../../bin/busybox
-rwxr-xr-x    1 root     root         26408 Nov  6 21:43 /usr/bin/iusql
-rwxr-xr-x    1 root     root         30512 Nov  6 21:43 /usr/bin/isql
-rwxr-xr-x    1 root     root           859 Nov  6 21:43 /usr/bin/img2fwup
lrwxrwxrwx    1 root     root            17 Nov  6 21:43 /usr/bin/id -> ../../bin/busybox
-rwxr-xr-x    1 root     root        288824 Nov  6 21:43 /usr/bin/fwup
lrwxrwxrwx    1 root     root            17 Nov  6 21:43 /usr/bin/free -> ../../bin/busybox
lrwxrwxrwx    1 root     root            17 Nov  6 21:43 /usr/bin/find -> ../../bin/busybox
lrwxrwxrwx    1 root     root            17 Nov  6 21:43 /usr/bin/env -> ../../bin/busybox
lrwxrwxrwx    1 root     root             9 Nov  6 21:43 /usr/bin/dtparam -> dtoverlay
-rwxr-xr-x    1 root     root           280 Nov  6 21:43 /usr/bin/dtoverlay-pre
-rwxr-xr-x    1 root     root           282 Nov  6 21:43 /usr/bin/dtoverlay-post
-rwxr-xr-x    1 root     root         30584 Nov  6 21:43 /usr/bin/dtoverlay
-rwxr-xr-x    1 root     root         10016 Nov  6 21:43 /usr/bin/dtmerge
-rwxr-xr-x    1 root     root         38784 Nov  6 21:43 /usr/bin/dltest
-rwxr-xr-x    1 root     root         31088 Nov  6 21:43 /usr/bin/boardid
-rwxr-xr-x    1 root     root         30656 Nov  6 21:43 /usr/bin/aserver
-rwxr-xr-x    1 root     root         75680 Nov  6 21:43 /usr/bin/arecord
-rwxr-xr-x    1 root     root         75680 Nov  6 21:43 /usr/bin/aplay
-rwxr-xr-x    1 root     root         55216 Nov  6 21:43 /usr/bin/amixer
lrwxrwxrwx    1 root     root            17 Nov  6 21:43 /usr/bin/[[ -> ../../bin/busybox
lrwxrwxrwx    1 root     root            17 Nov  6 21:43 /usr/bin/[ -> ../../bin/busybox
```

リストにすると多いように見えますがそこまで多くないですね。あと、`busybox`へのsymlink になっているコマンドが結構あります。

Nerves は Buildroot を使って構成する組込み Linux と述べましたが、ファームウェアイメージが大きくならないよう用意されているコマンドが限られています。また、そのために `busybox` も使われています。`busybox`は各コマンドのバイナリサイズの総和が大きくならないよう各コマンド機能を１つに固めたバイナリです（各コマンドもオプションがフルにサポートされていません[^2]）。その[ドキュメント](https://busybox.net/about.html)には

> The Swiss Army Knife of Embedded Linux

と書かれており組込み Linux 向けに作られていることが分かります。かっこいいですね！

[^2]: たとえば、`echo` コマンドは ash の built-in コマンドで `-n` オプションをサポートしませんし、 `ps` コマンドがサポートするオプションは `wlT` のみです。

## FAQ

> 使いたいコマンドがないのだけど、どうしたらいい？

大丈夫です。Nerves は目的に合わせカスタマイズができます。 see. [Customizing Your Own Nerves System](https://hexdocs.pm/nerves/customizing-systems.html) 

# Nerves のファイルシステム

次は `mount` コマンドでファイルシステムを確認してみます。

```elixir
iex(19)> cmd "mount"
/dev/root on / type squashfs (ro,relatime,errors=continue) # ルートファイルシステム
devtmpfs on /dev type devtmpfs (rw,nosuid,noexec,relatime,size=1024k,nr_inodes=430743,mode=755)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000)
tmpfs on /tmp type tmpfs (rw,nosuid,nodev,noexec,relatime,size=377524k)
tmpfs on /run type tmpfs (rw,nosuid,nodev,noexec,relatime,size=188764k,mode=755)
/dev/mmcblk0p1 on /boot type vfat (ro,nosuid,nodev,noexec,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro)
pstore on /sys/fs/pstore type pstore (rw,nosuid,nodev,noexec,relatime)
tmpfs on /sys/fs/cgroup type tmpfs (rw,nosuid,nodev,noexec,relatime,size=1024k,mode=755)
cpu on /sys/fs/cgroup/cpu type cgroup (rw,nosuid,nodev,noexec,relatime,cpu)
/dev/mmcblk0p3 on /root type f2fs (rw,lazytime,relatime,background_gc=on,discard,no_heap,user_xattr,inline_xattr,acl,inline_data,inline_dentry,flush_merge,extent_cache,mode=adaptive,active_logs=6,alloc_mode=reuse,checkpoint_merge,fsync_mode=posix,discard_unit=block)
```

Nerves のルートファイルシステムは [`SquashFS`](https://docs.kernel.org/filesystems/squashfs.html) です。
SquashFSは Read Only（ro マウントされる）であり、これが組込み機器に要求される電断対策になっています。

読み書き可能なファイルシステムですと、ファイル書き込み中に電源を断つとファイル破損が起きる可能性があります（Linux サーバが停電により起動できなくなる場合もファイル破損が起きています、電気的にサーバマシンが壊れている可能性もありますが、、）。

電断対策はいくつか選択肢（電断を検知しUPS持続中に正常終了させるなど）がありますが、Nerves はそもそもファイル書き込みをしないことによりファイル破損を起こさない選択肢をとっています。これにより Nerves は電源をぶちぶち切ることができる Linux になっています。

> そうはいっても、永続化させたいデータはあるのでは？

はい、そのため `/root` を rpi4 では f2fs で rw マウントしています。
このパーティションは読み書きができますので、ユーザが残したいデータを保存することができますし、 iex シェルのヒストリやvintage_net の設定等が保存されます。（「rpi4では」と書いたのは、nerves_systemu_***によって異なるからです。そうはいっても `f2fs` か `ext4` のいずれかとは思います。）
※ `/root`書き込み中に電断が起きればファイル破損の可能性はあります。なので `/root`には常にがしがし書き込むことはせず、ユーザ操作に呼応した書き込み頻度に保つことが必要かなと思います。（SD は書き込み上限回数もありますし）

# Nerves のシェル

Nerves の内包する Linux コマンドの節で `type` を使いました。type はシェルの built-in コマンドです。でも Nerves が使っているシェル何でしょうか？これは `/etc/shells`ファイルを確認することで分かります。

```elixir
iex(27)> cmd "cat /etc/shells"
/bin/ash
0
```

`ash`です。`bash`を前提に built-in コマンドを呼ぶと動作しないので、この点は注意が必要です。

# Nerves の /sbin/init

Linux（カーネル） はルートファイルシステムをマウントすると

> プロセス ID を 1 として init と呼ばれるプログラムを起動します。この時点がユーザー空間の開始です。

refs. Linuxシステムの仕組み p.125

Nerves はこの init プログラムを独自の [`erlinit`](https://github.com/nerves-project/erlinit) に置き換えています。
erlinit の README.md の冒頭には以下の記載があります。

> This is a replacement for /sbin/init that launches an Erlang/OTP release.

Nerves は Linux のユーザー空間での処理の殆どを Erlang VM 上の Elixir の世界で実行させるコンセプトがあるため、このようにしていると想像されます。`ps`コマンドを実行し `/sbin/init` に紐づくプロセスを抽出すると以下になります。（捨象したプロセスはカーネル管理下にあるもの

```elixir
iex(1)> cmd "ps -wlT"
S   UID   PID  PPID   VSZ   RSS TTY   STIME TIME     CMD
S     0     1     0  2172   252 ttyS0 05:00 00:00:00 /sbin/init
S     0    72     1  2008   216 ttyS0 05:00 00:00:00 /usr/bin/nbtty /srv/erlang/erts-13.1.2/bin/erlexec -config /srv/erlang/release
S     0    80     1  5476   332 0:0   05:00 00:00:00 /usr/sbin/rngd
S     0    81    72  2008    80 0:0   05:00 00:00:00 /usr/bin/nbtty /srv/erlang/erts-13.1.2/bin/erlexec -config /srv/erlang/release
S     0    82    81 2497m  116m pts0  05:00 00:00:00 /srv/erlang/erts-13.1.2/bin/beam.smp -Bc -C multi_time_warp -sbwt none -sbwtdc
S     0    83    81 2497m  116m pts0  05:00 00:00:00 {sys_sig_dispatc} /srv/erlang/erts-13.1.2/bin/beam.smp -Bc -C multi_time_warp
S     0    84    81 2497m  116m pts0  05:00 00:00:00 {sys_msg_dispatc} /srv/erlang/erts-13.1.2/bin/beam.smp -Bc -C multi_time_warp
S     0    85    81 2497m  116m pts0  05:00 00:00:00 {async_1} /srv/erlang/erts-13.1.2/bin/beam.smp -Bc -C multi_time_warp -sbwt no
S     0    87    81 2497m  116m pts0  05:00 00:00:00 {1_scheduler} /srv/erlang/erts-13.1.2/bin/beam.smp -Bc -C multi_time_warp -sbw
S     0    88    81 2497m  116m pts0  05:00 00:00:00 {2_scheduler} /srv/erlang/erts-13.1.2/bin/beam.smp -Bc -C multi_time_warp -sbw
R     0    89    81 2497m  116m pts0  05:00 00:00:01 {3_scheduler} /srv/erlang/erts-13.1.2/bin/beam.smp -Bc -C multi_time_warp -sbw
S     0    90    81 2497m  116m pts0  05:00 00:00:00 {4_scheduler} /srv/erlang/erts-13.1.2/bin/beam.smp -Bc -C multi_time_warp -sbw
S     0    91    81 2497m  116m pts0  05:00 00:00:00 {1_dirty_cpu_sch} /srv/erlang/erts-13.1.2/bin/beam.smp -Bc -C multi_time_warp
S     0    92    81 2497m  116m pts0  05:00 00:00:00 {2_dirty_cpu_sch} /srv/erlang/erts-13.1.2/bin/beam.smp -Bc -C multi_time_warp
S     0    93    81 2497m  116m pts0  05:00 00:00:00 {3_dirty_cpu_sch} /srv/erlang/erts-13.1.2/bin/beam.smp -Bc -C multi_time_warp
S     0    94    81 2497m  116m pts0  05:00 00:00:00 {4_dirty_cpu_sch} /srv/erlang/erts-13.1.2/bin/beam.smp -Bc -C multi_time_warp
S     0    95    81 2497m  116m pts0  05:00 00:00:00 {1_dirty_io_sche} /srv/erlang/erts-13.1.2/bin/beam.smp -Bc -C multi_time_warp
S     0    96    81 2497m  116m pts0  05:00 00:00:00 {2_dirty_io_sche} /srv/erlang/erts-13.1.2/bin/beam.smp -Bc -C multi_time_warp
S     0    97    81 2497m  116m pts0  05:00 00:00:00 {3_dirty_io_sche} /srv/erlang/erts-13.1.2/bin/beam.smp -Bc -C multi_time_warp
S     0    98    81 2497m  116m pts0  05:00 00:00:00 {4_dirty_io_sche} /srv/erlang/erts-13.1.2/bin/beam.smp -Bc -C multi_time_warp
S     0    99    81 2497m  116m pts0  05:00 00:00:00 {5_dirty_io_sche} /srv/erlang/erts-13.1.2/bin/beam.smp -Bc -C multi_time_warp
S     0   100    81 2497m  116m pts0  05:00 00:00:00 {6_dirty_io_sche} /srv/erlang/erts-13.1.2/bin/beam.smp -Bc -C multi_time_warp
S     0   101    81 2497m  116m pts0  05:00 00:00:00 {7_dirty_io_sche} /srv/erlang/erts-13.1.2/bin/beam.smp -Bc -C multi_time_warp
S     0   102    81 2497m  116m pts0  05:00 00:00:00 {8_dirty_io_sche} /srv/erlang/erts-13.1.2/bin/beam.smp -Bc -C multi_time_warp
S     0   103    81 2497m  116m pts0  05:00 00:00:00 {9_dirty_io_sche} /srv/erlang/erts-13.1.2/bin/beam.smp -Bc -C multi_time_warp
S     0   104    81 2497m  116m pts0  05:00 00:00:00 {10_dirty_io_sch} /srv/erlang/erts-13.1.2/bin/beam.smp -Bc -C multi_time_warp
S     0   105    81 2497m  116m pts0  05:00 00:00:00 {1_aux} /srv/erlang/erts-13.1.2/bin/beam.smp -Bc -C multi_time_warp -sbwt none
S     0   106    81 2497m  116m pts0  05:00 00:00:00 {0_poller} /srv/erlang/erts-13.1.2/bin/beam.smp -Bc -C multi_time_warp -sbwt n
S     0    86    82  2144  1168 0:0   05:00 00:00:00 erl_child_setup 1024
S     0   112    86  2008   224 0:0   05:00 00:00:00 heart -pid 82 -ht 30
S     0   115    86  2176  1024 0:0   05:00 00:00:00 /srv/erlang/lib/nerves_uevent-0.1.0/priv/uevent modprobe
S     0   122    86  2000   212 0:0   05:00 00:00:00 /srv/erlang/lib/nerves_logging-0.2.0/priv/kmsg_tailer
S     0   125    86  2192   256 0:0   05:00 00:00:00 /srv/erlang/lib/vintage_net-0.12.2/priv/if_monitor
S     0   139    86  2008   216 0:0   05:00 00:00:00 /srv/erlang/lib/muontrap-1.1.0/priv/muontrap -- /usr/sbin/ntpd -n -S /srv/erla
S     0   140   139  2396   280 0:0   05:00 00:00:00 /usr/sbin/ntpd -n -S /srv/erlang/lib/nerves_time-0.4.5/priv/ntpd_script -p 0.p

S     0   270    86  2008   204 0:0   05:00 00:00:00 /srv/erlang/lib/muontrap-1.1.0/priv/muontrap -- /usr/sbin/wpa_supplicant -i wl
S     0   271   270  8432  6580 0:0   05:00 00:00:00 /usr/sbin/wpa_supplicant -i wlan0 -Dnl80211,wext -c /tmp/vintage_net/wpa_suppl
S     0   274    86  2008   224 0:0   05:00 00:00:00 /srv/erlang/lib/muontrap-1.1.0/priv/muontrap -- /sbin/udhcpc -f -i eth0 -x hos
S     0   275   274  2396   276 0:0   05:00 00:00:00 /sbin/udhcpc -f -i eth0 -x hostname:nerves-051f -s /srv/erlang/lib/beam_notify
R     0   297    86  2396   280 0:0   05:01 00:00:00 ps -wlT
```

眺めてみると、

1. カーネルは `/sbin/init`（`erlinit`） を PID 1 で実行
1. `erlinit` は `/usr/bin/nbtty`(PID 72),  `/usr/sbin/rngd`(PID 80) を実行
1. `nbtty` は `/srv/erlang/erts-13.1.2/bin/erlexec`（PID 81）を実行
1. `erl_child_setup`（PID 86）は `heart`, `ntpd`, `wpa_supplicant`, `udhcpc`を実行

のようなことが分かります。
* heart は [nerves_heart](https://github.com/nerves-project/nerves_heart)で、Erlang VM 起動時に VM 自身の死活監視をするプロセスとして実行されます。
* `ntpd`は[nerves_time](https://github.com/nerves-time/nerves_time)により muontrap でくるまれて呼び出され時刻合わせをします。
* `wpa_suppulicant`, `udhcpc`は [vintage_net](https://github.com/nerves-networking/vintage_net) 関連により muontrap にくるまれて呼び出されています。それぞれ WiFiとDHCPクライアント をつかさどっています。

プロセスについて今の私の知識で分かることはここまでです。
分かっていないこととして `beam.smp` を引数付きで実行しているプロセス達です。この調査は今後の私の課題です🦾

2022/12/22 追記： [erlinit が起動する beam.smp プロセスについて](https://qiita.com/pojiro/items/ed6e4aa57e65ebb25d30) を書きました。

# まとめ？

発散感がありますが、組込み Linux としての Nerves の側面を紹介してみました。
Nerves は組込み Linux 開発のゲームチェンジャーだと私は思っています。これからも Nerves をやっていきましょう！

この記事は以前調べてまとめた「[Nerves tips, /bin/sh & nerves_heart](https://docs.google.com/presentation/d/1KQGRrgbLSzd6ukiKaWrpzx0VvrBLTeWXrEU61xp0rw0/edit?usp=sharing)」も参考にしています。興味があればそっちも見ていただければ思います〜。

明日は @myasu の担当日です！お楽しみに〜！