Linux カーネルについて概要をざっと理解する

Linux カーネルについて全く知識がなかったので、[これ一冊で完全理解 Linuxカーネル超入門](https://amzn.to/2LHp6p7) という本を参考に概要をまとめてみようと思います。

## Linux カーネルとは

Linux カーネルはOSの核となる基本的かつ重要なソフトウェアです。

- アプリケーションとコンピュータのハードウェアを結び付ける仕事
- コンピュータにはHDD, CPU, メモリなどアプリケーションが動作する際に必要になるハードウェア資源が何種類もあるため、それらが一元的に管理されていないと取り合いになってしまうためカーネルが必要

## 環境

ハンズオンをするための環境はお手軽にサーバを作成し、ターミナルも触れる [AWS Cloud9](https://aws.amazon.com/jp/cloud9/) [^1] を利用するのがおすすめです。

- AWS Cloud9
    - Ubuntu 18.04

## カーネルの実体

- `uname -r`で動いているカーネルのバージョンを表示する
- `/boot` ディレクトリにある `vmlinuz-x.xx.x-xxxx-aws` がカーネルの実体
    - ファイルサイズが 8.5MB ほどしかないことが分かる

```sh
$ uname -r
5.3.0-1033-aws
$ ls -lh /boot | grep vml
-rw------- 1 root root 7.6M Aug 27  2018 vmlinuz-4.15.0-1021-aws
-rw------- 1 root root 8.5M Aug  5 14:10 vmlinuz-5.3.0-1033-aws
-rw------- 1 root root 8.5M Sep  5 16:49 vmlinuz-5.3.0-1035-aws
```

Linux カーネルは必要不可欠ではない機能を分離して、別途オブジェクトファイルで管理し必要な時に使えるようにしています。

理由は仮にカーネルに機能を集約した場合、必要と思われる機能を全て最初からコンパイルし静的リンクしておく必要があり、その機能の多くは実際には使用されていないにも関わらずメモリに常駐し、新たな機能が必要になるとカーネル全体の再ビルドと再起動が必須となってしまうためです。

このように、OS動作中のカーネルを拡張するコードを含むオブジェクトファイルをローダブル・カーネル・モジュール(LKM)と呼びます。

- [ローダブル・カーネル・モジュール](https://ja.wikipedia.org/wiki/%E3%83%AD%E3%83%BC%E3%83%80%E3%83%96%E3%83%AB%E3%83%BB%E3%82%AB%E3%83%BC%E3%83%8D%E3%83%AB%E3%83%BB%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB)

ローダブル・カーネル・モジュールは`/lib/modules/カーネルバージョン/kernel/`配下に置かれています。

```sh
$ ls -F /lib/modules/5.3.0-1033-aws/kernel/
arch/  crypto/  drivers/  fs/  lib/  net/  virt/  wireguard/  zfs/
```

カーネルに既にロードされているモジュールは`lsmod`コマンドを用いて一覧表示可です

```sh
$ lsmod
Module                  Size  Used by
ufs                    81920  0
msdos                  20480  0
xfs                  1273856  0
xt_conntrack           16384  1
xt_MASQUERADE          20480  1
nf_conntrack_netlink    45056  0
nfnetlink              16384  2 nf_conntrack_netlink
xfrm_user              36864  1
xfrm_algo              16384  1 xfrm_user
<以下略>
```

## プロセス管理

### 実行中のプロセスの表示

実行中のプロセスは`ps`コマンドで表示します。

```sh
$ ps aux | head -n 10
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.1  0.7 225392  7804 ?        Ss   09:55   0:04 /sbin/init
root         2  0.0  0.0      0     0 ?        S    09:55   0:00 [kthreadd]
root         3  0.0  0.0      0     0 ?        I<   09:55   0:00 [rcu_gp]
root         4  0.0  0.0      0     0 ?        I<   09:55   0:00 [rcu_par_gp]
root         6  0.0  0.0      0     0 ?        I<   09:55   0:00 [kworker/0:0H-kb]
root         9  0.0  0.0      0     0 ?        I<   09:55   0:00 [mm_percpu_wq]
root        10  0.0  0.0      0     0 ?        S    09:55   0:00 [ksoftirqd/0]
root        11  0.0  0.0      0     0 ?        I    09:55   0:01 [rcu_sched]
root        12  0.0  0.0      0     0 ?        S    09:55   0:00 [migration/0]
<以下略>
```

１レコードが１プロセスにあたり、複数のプロセスが動いていることがわかると思います。

## メモリ管理

Linux では各プロセスが使用するメモリ空間を物理アドレスから分離し、プロセス単位の分離を実現すると共に、実質的に使用可能なメモリ量を増大させています。メモリ空間はページ(決まった大きさの単位)に分けられていて、プロセスが仮想メモリ空間を利用するだけ、ページ単位で物理メモリを割り当てています。これを [ページング方式](https://ja.wikipedia.org/wiki/%E3%83%9A%E3%83%BC%E3%82%B8%E3%83%B3%E3%82%B0%E6%96%B9%E5%BC%8F) と呼びます。


### ページ回収

Linux はプロセスがディスクに対してファイルを読み書きする時に、カーネルがその内容をページキャッシュというメモリ領域に一時的に保存することで同じファイルにアクセスする時にキャッシュ上のファイルを利用できるので処理が高速化します。
ページキャッシュは空きメモリが少なくなると捨てられ、キャッシュを解放して空きメモリにすることをページ回収と呼日ます。

ページキャッシュの使用量は`free`コマンドの buff/cached にあたる部分になります。(以下の例だと554MB)

```sh
$ free -m
              total        used        free      shared  buff/cache   available
Mem:            978         321         102           2         554         490
Swap:           488          63         425
```

`/proc/sys/vm/drop_caches`に３を書き込むとページキャッシュを開放します。事前に`sync`コマンドでディスクにファイルを反映しておくとよいです。

```sh
$ sync
$ sudo sh -c "echo 3 > /proc/sys/vm/drop_caches"
$ free -m
              total        used        free      shared  buff/cache   available
Mem:            978         325         526           2         126         520
Swap:           488          63         425
```
buff/cache の値が減り、free-Mem の値が増加しメモリが解放されていることが分かる。

## デバイス管理

各種周辺機器(デバイス)は、プログラムとデータをやり取りするときにデバイスファイルを利用します。Linux では、デバイスは抽象化されてファイルとして扱われます [^2]。

デバイスファイル一覧は、 `/dev` ディレクトリ以下に存在します。

```sh
$ ls -F /dev
autofs           log@                port      tty13  tty31  tty5   ttyS1      vcsa6
block/           loop-control        ppp       tty14  tty32  tty50  ttyS2      vcsu
btrfs-control    loop0               psaux     tty15  tty33  tty51  ttyS3      vcsu1
char/            loop1               ptmx      tty16  tty34  tty52  ttyprintk  vcsu2
console          loop2               pts/      tty17  tty35  tty53  udmabuf    vcsu3
core@            loop3               random    tty18  tty36  tty54  uinput     vcsu4
cpu_dma_latency  loop4               rfkill    tty19  tty37  tty55  urandom    vcsu5
cuse             loop5               rtc@      tty2   tty38  tty56  vcs        vcsu6
disk/            loop6               rtc0      tty20  tty39  tty57  vcs1       vfio/
ecryptfs         loop7               shm/      tty21  tty4   tty58  vcs2       vga_arbiter
fd@              mapper/             snapshot  tty22  tty40  tty59  vcs3       vhost-net
full             mcelog              stderr@   tty23  tty41  tty6   vcs4       vhost-vsock
fuse             mem                 stdin@    tty24  tty42  tty60  vcs5       xen/
hpet             memory_bandwidth    stdout@   tty25  tty43  tty61  vcs6       xvda
hugepages/       mqueue/             tty       tty26  tty44  tty62  vcsa       xvda1
hwrng            net/                tty0      tty27  tty45  tty63  vcsa1      zero
initctl@         network_latency     tty1      tty28  tty46  tty7   vcsa2      zfs
input/           network_throughput  tty10     tty29  tty47  tty8   vcsa3
kmsg             null                tty11     tty3   tty48  tty9   vcsa4
lightnvm/        nvram                tty12     tty30  tty49  tty50. vcsa5
```

### ブロックデバイス

`xvda`というデバイスファイルは、`b`が先頭になっています。これはブロックデバイスを意味し、ブロック形式でデータをやり取りする機器（ハードディスクドライブ/メモリ領域などのアドレス指定可能な機器）になります。

```sh
$ ls -l /dev/xvda
brw-rw---- 1 root disk 202, 0 Sep 22 00:02 /dev/xvda
```

`lsblk`コマンドでブロックデバイスを表示することもできます。

```sh
$ lsblk
NAME    MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
loop0     7:0    0 12.7M  1 loop /snap/amazon-ssm-agent/495
loop1     7:1    0 96.6M  1 loop /snap/core/9804
loop2     7:2    0 28.1M  1 loop /snap/amazon-ssm-agent/2012
loop3     7:3    0 97.1M  1 loop /snap/core/9993
xvda    202:0    0   10G  0 disk 
└─xvda1 202:1    0   10G  0 part /
```

### キャラクタデバイス

一方、`tty`というデバイスファイルは、`c`が先頭になっています。これはキャラクタデバイスを意味し、システムが１バイトずつデータを転送する機器（キーボードなど）になります。

```sh
$ ls -l /dev/tty
crw-rw-rw- 1 root tty 5, 0 Sep 22 00:02 /dev/tty
```

### udev

Linux ではデバイス管理ツールとして udev を利用しています。udev は Linux上のデーモンとして動作し、新しいデバイスがシステムに接続される、またはデバイスがシステムから接続を解除された場合、カーネルは udevに通知します。udev は通知を受けるとルールに基づいてデバイスファイルを作成します。`udevadm monitor` によって udevのイベントを表示します。

```sh
$ udevadm monitor
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing
KERNEL - the kernel uevent
<以下略>
```

## ファイルシステム

ファイルシステムとはコンピュータのリソースを操作するための Linuxカーネルの機能です。簡単にいうと`ファイルをとり扱うための枠組みと方法`です[^4]。ファイルとは、主に補助記憶装置に格納されたデータを指しますが、デバイスやプロセス、カーネル内の情報といったものもファイルとして提供するファイルシステムもあります。

### ファイルシステムの種類

- ディスク
    - ext4
    - ext3
    - XFS
- ネットワークファイル共有
    - NFS
    - SMB/CIFS
- 特殊用途
    - procfs, sysfs
    - tmpfs
    - FUSE


`sudo parted -l` コマンドで全てのブロックデバイスのパーティション情報を表示します。

```sh
$ sudo parted -l                                                                                                                                                                          
Model: Xen Virtual Block Device (xvd)
Disk /dev/xvda: 10.7GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags: 

Number  Start   End     Size    Type     File system  Flags
 1      1049kB  10.7GB  10.7GB  primary  ext4         boot
```

`/proc/filesystems `にカーネルで対応しているファイルシステムの一覧があります。

```sh
$ cat /proc/filesystems 
nodev   sysfs
nodev   tmpfs
nodev   bdev
nodev   proc
nodev   cgroup
nodev   cgroup2
nodev   cpuset
nodev   devtmpfs
nodev   configfs
nodev   debugfs
nodev   tracefs
nodev   securityfs
nodev   sockfs
nodev   bpf
nodev   pipefs
nodev   ramfs
nodev   hugetlbfs
nodev   devpts
        ext3
        ext2
        ext4
        squashfs
        vfat
nodev   ecryptfs
        fuseblk
nodev   fuse
nodev   fusectl
nodev   mqueue
nodev   pstore
        btrfs
nodev   autofs
nodev   overlay
nodev   aufs
```

### VFS

VFS（Virtual File System）とはアプリケーションが様々なファイルシステムに同じ方法でアクセスできるようにするためのカーネルの機能です。VFS があることによって、ローカルな記憶装置にもネットワーク上の記憶装置にも透過的にアクセスできるため違いを意識する必要がなくなります。

VFS が管理する情報の１つに iノードがあります。iノードはファイルとディスクの情報を関連付ける役割を担っています。ディレクトリは、iノード情報を保持することで間接的にファイルを管理しています。

`ls -i`で左端に表示される数字(280257)が iノード番号にあたります。

```sh
$ ls -il
total 4
280257 -rw-r--r-- 1 ubuntu ubuntu 569 Aug 28 05:46 README.md
```
iノード情報を詳しく確認するには `stat`コマンドを利用します。

```sh
$ stat README.md 
  File: README.md
  Size: 569             Blocks: 8          IO Block: 4096   regular file
Device: ca01h/51713d    Inode: 280257      Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1000/  ubuntu)   Gid: ( 1000/  ubuntu)
Access: 2020-08-28 06:25:56.732380517 +0000
Modify: 2020-08-28 05:46:13.000000000 +0000
Change: 2020-08-28 06:27:17.072779096 +0000
 Birth: -
```

## IO

### シグナル

カーネルからプログラムに対して、終了や中断、休止などの要求を通知する仕組み。プロセスはシグナルを受け取ると、シグナルの種類ごとに登録してある処理を実行する。


```sh
$ cat /proc/interrupts 
           CPU0       
  0:         43   IO-APIC   2-edge      timer
  1:          9  xen-pirq   1-ioapic-edge  i8042
  4:       3576  xen-pirq   4-ioapic-edge  ttyS0
  8:          2  xen-pirq   8-ioapic-edge  rtc0
  9:          0  xen-pirq   9-ioapic-level  acpi
 12:          3  xen-pirq  12-ioapic-edge  i8042
 14:          0   IO-APIC  14-edge      ata_piix
 15:          0   IO-APIC  15-edge      ata_piix
 48:     117991  xen-percpu    -virq      timer0
 49:          0  xen-percpu    -ipi       resched0
 50:          0  xen-percpu    -ipi       callfunc0
 51:          0  xen-percpu    -virq      debug0
 52:          0  xen-percpu    -ipi       callfuncsingle0
 53:          0  xen-percpu    -ipi       spinlock0
 54:        258   xen-dyn    -event     xenbus
 55:      30247   xen-dyn    -event     blkif
 56:      11722   xen-dyn    -event     eth0
NMI:          0   Non-maskable interrupts
LOC:          0   Local timer interrupts
SPU:          0   Spurious interrupts
PMI:          0   Performance monitoring interrupts
IWI:          0   IRQ work interrupts
RTR:          0   APIC ICR read retries
RES:          0   Rescheduling interrupts
CAL:          0   Function call interrupts
TLB:          0   TLB shootdowns
TRM:          0   Thermal event interrupts
THR:          0   Threshold APIC interrupts
DFR:          0   Deferred Error APIC interrupts
MCE:          0   Machine check exceptions
MCP:         13   Machine check polls
HYP:     161747   Hypervisor callback interrupts
ERR:          0
MIS:          0
PIN:          0   Posted-interrupt notification event
NPI:          0   Nested posted-interrupt event
```



[^1]: [AWS Cloud9](https://aws.amazon.com/jp/cloud9/) 
[^2]: https://users.miraclelinux.com/technet/document/linux/training/1_3_1.html
[^4]: https://eng-entrance.com/linux-make-filesystem

- [メモリ管理 - Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%A1%E3%83%A2%E3%83%AA%E7%AE%A1%E7%90%86)
- [カーネル - Wikipedia](https://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%BC%E3%83%8D%E3%83%AB)
- [第14章 タスクスケジューラのチューニング](https://manual.geeko.jp/ja/cha.tuning.taskscheduler.html)
- [仮想ファイルシステム - wikipedia](https://ja.wikipedia.org/wiki/%E4%BB%AE%E6%83%B3%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0)