Linux カーネルについて全く知識がなかったので、これ一冊で完全理解 Linuxカーネル超入門 という本を参考に概要をまとめてみようと思います。
Linux カーネルとは
Linux カーネルはOSの核となる基本的かつ重要なソフトウェアです。
- アプリケーションとコンピュータのハードウェアを結び付ける仕事
- コンピュータにはHDD, CPU, メモリなどアプリケーションが動作する際に必要になるハードウェア資源が何種類もあるため、それらが一元的に管理されていないと取り合いになってしまうためカーネルが必要
環境
ハンズオンをするための環境はお手軽にサーバを作成し、ターミナルも触れる AWS Cloud9 1 を利用するのがおすすめです。
- AWS Cloud9
- Ubuntu 18.04
カーネルの実体
-
uname -r
で動いているカーネルのバージョンを表示する -
/boot
ディレクトリにあるvmlinuz-x.xx.x-xxxx-aws
がカーネルの実体- ファイルサイズが 8.5MB ほどしかないことが分かる
$ 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)と呼びます。
ローダブル・カーネル・モジュールは/lib/modules/カーネルバージョン/kernel/
配下に置かれています。
$ ls -F /lib/modules/5.3.0-1033-aws/kernel/
arch/ crypto/ drivers/ fs/ lib/ net/ virt/ wireguard/ zfs/
カーネルに既にロードされているモジュールはlsmod
コマンドを用いて一覧表示可です
$ 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
コマンドで表示します。
$ 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]
<以下略>
1レコードが1プロセスにあたり、複数のプロセスが動いていることがわかると思います。
メモリ管理
Linux では各プロセスが使用するメモリ空間を物理アドレスから分離し、プロセス単位の分離を実現すると共に、実質的に使用可能なメモリ量を増大させています。メモリ空間はページ(決まった大きさの単位)に分けられていて、プロセスが仮想メモリ空間を利用するだけ、ページ単位で物理メモリを割り当てています。これを ページング方式 と呼びます。
ページ回収
Linux はプロセスがディスクに対してファイルを読み書きする時に、カーネルがその内容をページキャッシュというメモリ領域に一時的に保存することで同じファイルにアクセスする時にキャッシュ上のファイルを利用できるので処理が高速化します。
ページキャッシュは空きメモリが少なくなると捨てられ、キャッシュを解放して空きメモリにすることをページ回収と呼日ます。
ページキャッシュの使用量はfree
コマンドの buff/cached にあたる部分になります。(以下の例だと554MB)
$ 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
に3を書き込むとページキャッシュを開放します。事前にsync
コマンドでディスクにファイルを反映しておくとよいです。
$ 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
ディレクトリ以下に存在します。
$ 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
が先頭になっています。これはブロックデバイスを意味し、ブロック形式でデータをやり取りする機器(ハードディスクドライブ/メモリ領域などのアドレス指定可能な機器)になります。
$ ls -l /dev/xvda
brw-rw---- 1 root disk 202, 0 Sep 22 00:02 /dev/xvda
lsblk
コマンドでブロックデバイスを表示することもできます。
$ 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
が先頭になっています。これはキャラクタデバイスを意味し、システムが1バイトずつデータを転送する機器(キーボードなど)になります。
$ 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のイベントを表示します。
$ udevadm monitor
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing
KERNEL - the kernel uevent
<以下略>
ファイルシステム
ファイルシステムとはコンピュータのリソースを操作するための Linuxカーネルの機能です。簡単にいうとファイルをとり扱うための枠組みと方法
です3。ファイルとは、主に補助記憶装置に格納されたデータを指しますが、デバイスやプロセス、カーネル内の情報といったものもファイルとして提供するファイルシステムもあります。
ファイルシステムの種類
- ディスク
- ext4
- ext3
- XFS
- ネットワークファイル共有
- NFS
- SMB/CIFS
- 特殊用途
- procfs, sysfs
- tmpfs
- FUSE
sudo parted -l
コマンドで全てのブロックデバイスのパーティション情報を表示します。
$ 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
にカーネルで対応しているファイルシステムの一覧があります。
$ 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 が管理する情報の1つに iノードがあります。iノードはファイルとディスクの情報を関連付ける役割を担っています。ディレクトリは、iノード情報を保持することで間接的にファイルを管理しています。
ls -i
で左端に表示される数字(280257)が iノード番号にあたります。
$ ls -il
total 4
280257 -rw-r--r-- 1 ubuntu ubuntu 569 Aug 28 05:46 README.md
iノード情報を詳しく確認するには stat
コマンドを利用します。
$ 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
シグナル
カーネルからプログラムに対して、終了や中断、休止などの要求を通知する仕組み。プロセスはシグナルを受け取ると、シグナルの種類ごとに登録してある処理を実行する。
$ 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