Linux #
Linux は、世界の サーバ・クラウド・スマートフォン (Android)・組み込み機器・スーパーコンピュータ の大半を動かしているオープンソースの OS — と言いたくなるが、厳密には Linux は「カーネル」だけを指す名前で、ユーザが日常的に触る「Linux」は GNU ユーザランド + ディストリビューション固有のグルー が積み上がった全体である。Richard Stallman が「GNU/Linux」と呼ぶことに固執するのも、この層構造の事実を反映している。
それでも「Linux」は実用的には 「Linux カーネルが動いている UNIX 系 OS のエコシステム全体」を指す呼称として広く通じる。本稿は最初にこの 3 層構造を整理し、次にカーネルとユーザランドがシステムコールで切れているという UNIX/Linux の根本設計、「すべてはファイル」という統一抽象、プロセス / 権限 / シェルという日常の使用感を作っている層、そして namespaces + cgroups から自然に出てきたコンテナ革命 までを、「Linux を 1 枚絵で理解する」 粒度で扱う。
1. Linux とは何 — 「カーネルだけ」と「OS 全体」の二重の意味 #
「Linux」という単語の多層性を最初に分解する。
| 層 | 内容 | 例 |
|---|---|---|
| カーネル | プロセス管理 / メモリ管理 / ファイルシステム / ネットワーク / デバイスドライバ。唯一「Linus Torvalds とコミュニティが管理する Linux」 | linux-6.x (kernel.org) |
| ユーザランド (GNU + 周辺) | ライブラリ (glibc / musl)、シェル (bash / zsh)、coreutils (ls cp cat)、GNU ツールチェーン (gcc / binutils) |
GNU project + util-linux + systemd |
| ディストリビューション | カーネル + ユーザランド + パッケージマネージャ + 独自の設定 / 起動スクリプト / リリースポリシー | Debian, Ubuntu, RHEL, Fedora, Arch, Alpine, Android |
つまり「Ubuntu を入れた」 = 「Linux カーネル + GNU ツールチェーン + Debian パッケージング + Canonical 独自の Snap / Netplan / 設定」を 1 つにまとめたディストリを入れた、ということ。Android も Linux カーネルを使っているが、GNU ユーザランドではなく Bionic libc + Java/ART を載せているため「Linux」と呼ぶかどうかは微妙な扱いになる (kernel.org 的には Linux)。
「自由ソフトウェア (Free Software)」 vs 「オープンソース (Open Source)」の哲学的議論はこの背景にあるが、実用上はどちらも GPL/MIT/Apache 等のライセンスでソースが公開されている点が本質。Linux カーネル自体は GPLv2 で、これが Android メーカーがカーネル変更を公開する義務の根拠になっている。
2. アーキテクチャ — カーネルとユーザランドはシステムコールで切れている #
Linux (UNIX 系全般) の設計の核は、カーネル空間とユーザ空間が CPU の特権レベルで物理的に分離されていること。ユーザプロセスはハードウェアに直接触れない、必ず システムコール という決められた入り口を通してカーネルに依頼する。
押さえどころ:
- カーネルは決して「アプリの一部」ではない。アプリは
read()write()open()fork()socket()といった 400 ほどのシステムコールを通してしかカーネルにアクセスできない。「strace で実行を追える」のはこの境界が明確だから。 - GNU ユーザランドは「Linux カーネルとは別プロジェクト」。
lsやcatは GNU coreutils が提供しており、Linux カーネルとは独立に開発されている。Alpine Linux のようにユーザランドを musl + BusyBox で軽量化 すると、同じカーネルでも全く別の OS の使用感になる。 - systemd は「カーネルとユーザランドの間の現代的な接着剤」。プロセス管理 / cgroups / ロギング / ネットワーク設定 / DNS / 起動順序 — 従来は別個に存在していた仕組みを統合した。サービスを
systemctl start nginxで起動するときに動いているのはユーザ空間の PID 1 デーモン。 - ドライバの動的ロード —
lsmodで見えるkernel moduleが、必要に応じてmodprobeで読み込まれている。Linux カーネルの実行中に新しいデバイスが繋がっても再起動なしで動くのはこの仕組み。
# システムコールを実況中継 (アプリが何をカーネルに頼んでいるか)
strace -f -e trace=openat,read,write ls /tmp
# カーネルバージョンとビルド設定
uname -a
cat /proc/version
# ロード済みカーネルモジュール
lsmod
# システム全体のシステムコール統計 (要 perf)
sudo perf stat -e 'syscalls:sys_enter_*' -a sleep 5
3. すべてはファイル — UNIX が世界に貢献した最大の抽象 #
「すべてはファイル」(Everything is a file) は UNIX 哲学の中で最も影響力が大きい設計判断。通常ファイル、ディレクトリ、デバイス、パイプ、ソケット、シンボリックリンク が全て同じ read()/write()/open()/close() の API で扱えることで、プログラムが「相手が何か」を意識せずに合成可能になった。grep foo /var/log/syslog も grep foo < /dev/ttyS0 (シリアルポート) も cat hello | grep foo も同じ read() の呼び出し — これが UNIX のシンプルさと強さの本体。
特に重要な概念:
/devはハードウェアと仮想デバイスへの入口。echo "hello" > /dev/tty1で 1 番目の仮想コンソールに出力する、dd if=/dev/zero of=test.bin bs=1M count=100で 100 MB のゼロ詰めファイルを作る — どちらも普通のファイルへの書き込みと同じ syscallで動く。/procはカーネルがその場で生成する仮想 FS。/proc/PID/mapsでプロセスのメモリマッピング、/proc/PID/fd/でそのプロセスが開いているファイルディスクリプタが見える。ディスク上には何もない、catした瞬間にカーネルがテキストを生成している。/sysはカーネル内部のオブジェクト階層 (デバイス・バス・クラス) をディレクトリツリーとして公開するもの。NIC の MAC アドレスを変更するecho 02:11:22:33:44:55 > /sys/class/net/eth0/addressのような操作が可能。- シンボリックリンク (
lで始まる) は他のパスへの参照で、Windows のショートカットに似ているがカーネルが透過的に解決する点が違う。/lib → /usr/libのような統合や、Deployer のcurrent/ → releases/56のような切替に多用される。
# ファイルタイプ確認
ls -l /dev/null /etc/hosts /lib /tmp/.X11-unix/X0
stat -c '%F %n' /dev/sda /proc/meminfo /sys/class/net/eth0
# プロセス情報を /proc から拾う
ls /proc/$$/fd # 自分のシェルが開いている FD
cat /proc/$$/maps # メモリマッピング
cat /proc/$$/status # 状態 (VmRSS 等)
# sysfs / sysctl で動的に設定変更
sudo sysctl -w net.ipv4.ip_forward=1
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward # 同じこと
4. プロセスとシグナル — fork/exec と PID 1 #
Linux のプロセスモデルは 「fork() でコピー → exec() で別プログラムに置換」 という UNIX 由来の独特な設計。bash から ls を起動するとき、シェルは:
fork()で自分のコピーを作る (子プロセス, 親と同じメモリ・FD を持つ)- 子プロセスが
execve("/bin/ls", ...)で自分自身を ls プログラムに置換 (PID は変わらず) - 親シェルは
waitpid()で子の終了を待つ
これが 「全プロセスは PID 1 から派生する 1 本の系図 (プロセスツリー) を成す」 の根拠。pstree で見ると、systemd (PID 1) → sshd → bash → vim のように、自分の祖先を遡れる。親プロセスが先に死ぬと、子は孤児になり PID 1 (systemd) が引き取るルール (init による reaping) が現代でも健在。
シグナルはプロセス間の非同期通知。Ctrl+C で送られる SIGINT、kill PID (デフォルト SIGTERM)、kill -9 PID (SIGKILL — プロセスから捕捉不能, 強制終了)、メモリエラー SIGSEGV など 31 種類が定義されている。SIGKILL と SIGSTOP だけはプロセスがハンドラを設定できない (ユーザがどうしても止められる手段を残すため)。
# プロセスツリーを見る
pstree -p $$ # 自分のシェルから祖先まで
# 状態とリソース
ps -ef # 全プロセス (UNIX 形式)
ps auxf # ツリー表示 (BSD 形式)
top / htop # リアルタイム
ss -tnp / lsof -p PID # PID が開いている接続/ファイル
# シグナル送信
kill -SIGTERM PID # 礼儀正しく頼む (アプリは cleanup できる)
kill -9 PID # 強制終了 (リソースリークの危険)
kill -SIGUSR1 PID # アプリ独自の通知 (nginx のログローテーション等)
ゾンビ (Z 状態) = 子が exit したが親が wait() を呼んでいないプロセス。リソースは解放済みで PID とプロセステーブルエントリだけ残っている状態。ゾンビが大量発生する場合は親プロセスのバグ (子の終了を待っていない) で、PID 空間を食い潰すと新規プロセスが起動できなくなる。
5. 権限モデル — UID/GID/rwx → setuid → capabilities → namespaces #
UNIX の権限モデルは UID (ユーザ ID) + GID (グループ ID) + 各ファイルの rwx 9 bit という単純な仕組みから出発した。ls -l の -rwxr-xr-x の 9 文字は 「所有者 rwx / グループ rwx / その他 rwx」 の並び。
UID 0 = root がすべての権限を持つ特権ユーザで、普段の作業はできるだけ非 root で行う + 必要な瞬間だけ sudo が現代のセキュリティ運用の基本。
しかし**「rwx だけでは粒度が足りない」**シーンが何度もあり、段階的に拡張されてきた:
| 機能 | 何を解決したか |
|---|---|
| setuid / setgid bit | passwd コマンドが /etc/shadow を更新するために、一般ユーザの実行でも root 権限で動く必要があった → 実行ファイルに setuid bit を立てると実行時だけ所有者の権限になる。乱用するとローカル特権昇格の温床になるため、検出 (find / -perm -4000) が侵入後解析の定番 |
| POSIX ACL | rwx の「所有者・グループ・その他」3 段階では 複数ユーザに別々の権限が与えられない → setfacl でファイル単位に細かく |
| Capabilities | 「root の権限を全部寄越せ」ではなく 40 個ほどの能力に分割 (CAP_NET_BIND_SERVICE = 1024 番未満ポートを開ける, CAP_NET_ADMIN = ネットワーク設定, CAP_SYS_ADMIN = なんでも) → デーモンに必要最小限だけ与える ことが可能に |
| MAC (Mandatory Access Control) | rwx は DAC (Discretionary) で「ファイルの所有者が自由に決める」 → SELinux / AppArmor がシステム全体のポリシーで「この binary はこの操作だけ」を強制 |
| Namespaces + cgroups | 「プロセスごとに別の世界 (PID 空間 / FS / ネットワーク / UID マッピング) を見せる」「CPU / メモリ / I/O を制限する」 → これがコンテナの正体 (§7) |
# 基本の権限
ls -l /etc/shadow # `-rw-r-----` root:shadow → 一般ユーザは見えない
chmod 600 ~/.ssh/id_ed25519 # SSH 秘密鍵は 600 (= rw------- 所有者のみ)
# setuid を探す (システムに何があるか把握する基本)
find / -perm -4000 -type f 2>/dev/null
# capabilities (root を分割する)
sudo setcap cap_net_bind_service=+ep /usr/bin/python3.11
# → python が root でなくても 80/443 を bind 可能
# SELinux (RHEL 系) のステータス確認
sestatus
ls -lZ /var/www/html
# sudo の設定 (NOPASSWD は限定的に)
sudo visudo # /etc/sudoers の安全な編集
6. シェルがすべてを繋ぐ — pipe と標準入出力 #
Linux/UNIX の生産性の本質は、小さな単機能プログラムを | で繋ぎ合わせて何でも作れること。これを実現しているのが 「すべてはファイル」 + 標準入出力 (stdin / stdout / stderr) + パイプ (|)。
┌──────┐ stdout stdin ┌──────┐ stdout stdin ┌──────┐
│ ls │ ─────────────────→│ grep │ ────────────────→│ wc │
└──────┘ └──────┘ └──────┘
ls | grep '\.log$' | wc -l は 3 つの独立プロセスを同時に起動し、stdout を次のプロセスの stdin に直結することで「カレントディレクトリの .log 拡張子のファイル数」を 1 行で計算する。カーネルがパイプ (FIFO バッファ) を仲介しており、プロセス間メモリは共有しない。
| 要素 | 意味 |
|---|---|
cmd > file |
stdout をファイルに 上書き |
cmd >> file |
stdout を 追記 |
cmd 2> err.log |
stderr のみをファイルへ |
cmd 2>&1 |
stderr を stdout にマージ |
cmd < file |
stdin をファイルから |
cmd1 | cmd2 |
cmd1 の stdout を cmd2 の stdin に |
cmd1 ; cmd2 |
順次実行 (前者の終了コードを問わず) |
cmd1 && cmd2 |
前者が 成功 (exit 0) したときだけ後者 |
cmd1 || cmd2 |
前者が 失敗 したときだけ後者 |
終了コード (exit code) はプログラム間の最重要な戻り値。0 = 成功、それ以外 = 何らかの失敗。シェルスクリプトはこれで分岐し、CI/CD はこれで「ビルド成功か失敗か」を判定する。
環境変数 (PATH, HOME, LANG, LD_LIBRARY_PATH …) は親プロセスから子プロセスにコピーで伝わる。export FOO=bar でその後起動する子プロセスに渡る。bash の set -euo pipefail は「未定義変数で死ぬ / エラー終了で即停止 / パイプ途中の失敗も検出」というスクリプトの安全装備の定型。
7. ディストリビューション — どれを選ぶか #
「Linux を入れる」 = 「ディストリを選ぶ」。カーネルは同じでも、パッケージマネージャ・リリースサイクル・標準デーモン・コミュニティの色が違う。
| ディストリ | 系統 | パッケージ | 特徴 / 使われる場面 |
|---|---|---|---|
| Debian | 元祖 | apt (.deb) |
安定第一・コミュニティ駆動・サーバ用途で長期に強い |
| Ubuntu | Debian 派生 | apt |
デスクトップで最普及・LTS リリース・ビジネス向け Canonical サポート |
| RHEL (Red Hat Enterprise Linux) | 商用 | dnf (.rpm) |
エンタープライズ実本番標準・サブスクリプション・10 年サポート |
| Fedora | RHEL 上流 | dnf |
最新機能の人柱・RHEL に降りていく前のテストベッド |
| CentOS Stream / Rocky / AlmaLinux | RHEL 互換 | dnf |
旧 CentOS の後継 — Rocky / Alma がバイナリ互換 |
| Arch Linux | 独立系 | pacman |
ローリングリリース・最新志向・自分で組む派・ArchWiki が世界一の Linux 文書 |
| Alpine Linux | 独立系 | apk |
musl + BusyBox で極小 (~5 MB) ・Docker イメージのデフォルトベースとして爆発的普及 |
| Android | (独自) | Linux カーネル + Bionic libc + Java/ART — 世界最普及の Linux | |
| WSL2 (Windows 上) | Microsoft | 親ディストリに依る | Windows 上で動く Linux カーネル (Hyper-V VM) — 開発環境として急速に普及 |
選び方の指針:
- 本番サーバ → RHEL / Rocky / Ubuntu LTS (10 年サポート・エンタープライズ実績)
- 開発機 / デスクトップ → Ubuntu / Fedora / Arch (好み次第)
- コンテナベース → Alpine (小さい) または Debian slim / Ubuntu minimal (互換性重視)
- 古いハードの再生 → 軽量派生 (Lubuntu, MX Linux …)
- 学習用 → Arch (
pacstrapで 0 から組む経験) または Debian (デフォルトで最小限)
8. コンテナ革命 — namespaces + cgroups から自然に出てきた #
Docker / Kubernetes が席巻した 2010 年代後半は、Linux にとって何が起きたのか? 答えは 「Linux カーネルにもとから入っていた 2 つの機能を組合せて使い始めただけ」:
- namespaces (PID / network / mount / UTS / IPC / user) — プロセスごとに別の世界を見せる
- PID namespace: コンテナ内では
psで自分のプロセスしか見えない - network namespace: 独自の eth0 / ルーティングテーブルを持つ
- mount namespace: 独自のファイルシステムを
/として見る - user namespace: コンテナ内 root がホストから見ると非 root (rootless container)
- PID namespace: コンテナ内では
- cgroups (control groups) — CPU / メモリ / I/O / pid 数をプロセス群単位で制限・計測
これに overlayfs (積層ファイルシステム) を組合せると、「ホスト OS の上に独立したミニ OS をプロセス単位で動かす」 = コンテナ になる。Docker は最初これらを lxc 経由で使い、後に runc を作って独自実装、現代は OCI runtime 標準 に至っている。
「コンテナは仮想マシンより軽い」のは、ハードウェア仮想化 (Hyper-V, KVM, VMware) を経由しないから — カーネルはホストと共有し、プロセス分離だけでハードウェアエミュレーションはしない。代償としてカーネル脆弱性が出るとコンテナの分離が崩れる (CVE-2022-0185, CVE-2024-1086 などが有名)。
# コンテナの中身を「ただの Linux プロセス」として見る
docker inspect --format '{{.State.Pid}}' my-container # ホスト上の PID
ls /proc/<PID>/ns # その PID の namespaces
nsenter -t <PID> -n -p ip addr # ネットワーク namespace に入って ip 確認
cat /sys/fs/cgroup/system.slice/docker-<ID>.scope/cpu.stat # cgroups の CPU 統計
Kubernetes はこの上にもう 1 段「コンテナをクラスタで自動配置・自動修復・自動スケール」する制御平面を被せたもの。Linux カーネルがコンテナを動かし、Kubernetes がそれを管理する という 2 層構造。
9. 現代の Linux はどこで動いているか #
| 場所 | シェア / 状況 |
|---|---|
| クラウドサーバ | 95% 以上。AWS EC2 / Azure VM / GCP CE のデフォルトイメージは Linux。マネージド DB・Kubernetes ワーカー・Lambda・Fargate もすべて Linux 上 |
| スマートフォン (Android) | 世界の 70%+ が Android = Linux カーネル。世界で最も普及した OS |
| 組み込み機器 | ルータ・テレビ・冷蔵庫・自動車・産業機器・スマートウォッチ・IoT — 無数に動いているが見えない |
| スーパーコンピュータ | TOP500 の 100% が Linux (2017 以降)。HPC は事実上 Linux しかない |
| デスクトップ | ~3-5%。Windows / macOS が圧倒的、ただし開発者・科学者・エンジニアコミュニティでは大きい |
| WSL2 (Windows 上) | 急成長。Microsoft が公式に Linux カーネルを Windows に同梱 という時代 |
「サーバとモバイルとあらゆる埋め込み機器を支えているが、デスクトップだけは少数派」というのが Linux の現代の立ち位置。Windows と macOS は OS 製品として競合するが、Linux はインフラとしての OS で別レイヤを占めている。
Linux は 「Linus Torvalds が 1991 年に趣味で書いたカーネル」 から始まり、「世界の半分以上のコンピューティングを支える OS のエコシステム全体」を指す呼称になった。理解の鍵は 「カーネル = Linux 本体」「ユーザランド = GNU + ディストリ」「両者は syscall で物理分離されている」 という 3 層構造、そして 「すべてはファイル」「fork/exec」「pipe + 終了コード」「rwx → capabilities → namespaces」 という UNIX 由来の設計判断が現代までそのまま生き続けていることを押さえることにある。
クラウドもコンテナもスマートフォンも、「Linux の設計思想がそのまま現代インフラに通用している」事実の上に成立している。コンテナは「Linux の機能を新しい組み方で使い始めただけ」、Kubernetes は「Linux 上のコンテナを管理するもう 1 段」、サーバレスは「Linux カーネル上の極小 VM (Firecracker 等) で関数を動かすもの」 — どこを掘っても Linux の syscall と namespaces と cgroups に行き着く。Linux を 1 度ちゃんと理解すると、現代インフラのほとんどに同じ語彙で対峙できるようになる。