Linux は世界のサーバ・クラウド・スマートフォン (Android)・組み込み機器・スーパーコンピュータの大半を動かしているオープンソース OS — と言いたくなるが、厳密には Linux は「カーネル」だけを指す名前で、日常的に触る「Linux」は GNU ユーザランド + ディストリビューション固有のグルー が積み上がった全体。本稿はこの 3 層構造、syscall で切れたカーネル/ユーザ境界、「すべてはファイル」、プロセス・権限・シェル、そして namespaces + cgroups からコンテナへの自然な発展 までを通しで扱う。
難しく見えても本質は次の 3 つだけ。(1) 「Linux」と呼ぶときは 厳密にはカーネル (= エンジン部分) のみを指し、Ubuntu や Fedora といったディストリはそのエンジンに座席や内装を付けた 完成車。(2) Linux の合言葉は 「すべてはファイル」 — 設定もデバイスもプロセス情報も、全部 open / read / write で読み書きできる「ファイル」として扱える。(3) 世界の サーバの 95%、スマホの 70% (Android)、スパコンの 100% が Linux で動いている = インフラの土台 として現代を支えている。— ここを土台に各章を順に開いていけばいい。
「カーネルだけ」と「OS 全体」の二重の意味 #
「Linux」という単語の多層性を最初に分解する。
「Linux を入れた」と言うとき、多くの人が思い浮かべるのは Ubuntu や Fedora といったディストリ全体。だが厳密に「Linux」が指すのは エンジン (= カーネル) 部分だけで、Linus Torvalds + コミュニティが管理している。これに GNU プロジェクトのツール群 (`ls`, `cp`, `bash` などの座席や内装に相当) と、ディストリ独自のグルー (Ubuntu の Snap や apt 設定など、ナビや内装のチューニング) を載せた 「完成車」がディストリ。だから「Linux」と言ったとき、相手によって意味する範囲が違うことがある、と最初に覚えておくと混乱しない。
| 層 | 内容 | 例 |
|---|---|---|
| カーネル | プロセス管理 / メモリ管理 / FS / ネットワーク / デバイスドライバ。唯一「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 を載せている。kernel.org 的には Linux だが、「GNU/Linux」とは別物として扱われることが多い。カーネル自体は GPLv2 で、これが Android メーカーがカーネル変更を公開する義務の根拠になっている。
アーキテクチャ — カーネルとユーザランドはシステムコールで切れている #
Linux (UNIX 系全般) の設計の核は、カーネル空間とユーザ空間が CPU の特権レベルで物理的に分離されている こと。ユーザプロセスはハードウェアに直接触れず、必ず システムコール という決められた入り口を通してカーネルに依頼する。
ユーザのアプリは 「ファイルを開きたい」「ネットに繋ぎたい」と思っても、自分でハードウェアに触れない。必ずカーネルという「役所」に申請書を出して頼む。この申請書のフォーマットが システムコール (syscall) で、約 400 種類あらかじめ決まっている (read / write / open / fork / socket …)。アプリが暴走しても役所側は守られているし、役所からアプリが何を頼んだかは strace で全部見える。「Linux でやっていることは、結局この syscall の組み合わせ」と理解すると、後の話が一気に繋がる。
[ Application ] nginx / PostgreSQL / Firefox / Python スクリプト / vim / Docker クライアント
[ User Land ] bash/zsh / ls,cp,cat,grep / glibc/musl / systemd / sshd / cron / Wayland/Xorg
───── system call interface (read / write / open / fork / exec / mmap / socket / ...) ─────
[ Kernel ] Process/Sched (CFS/EEVDF, cgroups, namespaces)
Memory (MMU, page cache, slab, OOM killer, swap)
VFS/FS (ext4/XFS/Btrfs, tmpfs, procfs, FUSE, overlayfs, io_uring)
Network (TCP/IP, netfilter/nftables, XDP/eBPF, routing, socket)
+ Device Drivers (NIC, GPU, NVMe, USB, ACPI, audio — modprobe で動的ロード)
[ Hardware ] CPU / メモリ / ストレージ / NIC / GPU / 周辺機器
# syscall は ~400 個 / 例: read=0, write=1, open=2, fork=57, execve=59, mmap=9 (x86_64)押さえどころ:
- カーネルは決して「アプリの一部」ではない。アプリは
read()write()open()fork()socket()といった 400 ほどのシステムコールを通してしかカーネルにアクセスできない。strace で実行を追える のはこの境界が明確だから - GNU ユーザランドは Linux カーネルとは別プロジェクト。
lsやcatは GNU coreutils が提供し、Linux カーネルと独立に開発される。Alpine Linux のように musl + BusyBox で軽量化すると、同じカーネルでも全く別の OS の使用感になる - systemd は「カーネルとユーザランドの間の現代的な接着剤」 — プロセス管理 / cgroups / ロギング / ネットワーク設定 / DNS / 起動順序を統合した、ユーザ空間の PID 1 デーモン
- ドライバの動的ロード —
lsmodで見えるカーネルモジュールが、必要に応じてmodprobeで読み込まれる。実行中に新しいデバイスが繋がっても再起動なしで動く
# システムコールを実況中継 (アプリが何をカーネルに頼んでいるか)
$ 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すべてはファイル — UNIX 最大の抽象 #
「すべてはファイル」(Everything is a file) は UNIX 哲学の中で最も影響力が大きい設計判断。通常ファイル、ディレクトリ、デバイス、パイプ、ソケット、シンボリックリンク が全て同じ read()/write()/open()/close() の API で扱えるため、プログラムが「相手が何か」を意識せずに合成可能になった。
家の蛇口は 水道水でも井戸水でも温泉でも、同じ「ひねる」操作で使える。Linux はこれと同じ思想で、ハードディスクのファイル / シリアルポート / プロセス情報 / ネットワーク接続 / 設定 / カーネルの内部状態を、全部「ファイル」として open / read / write で扱えるようにした。だから cat /proc/cpuinfo で CPU 情報が読めるし、echo 1 > /sys/.../ip_forward でカーネル設定を変えられる。「全部ファイル → 全部 cat / grep / シェルで処理可能」というのが、Linux で何でもスクリプト化できる本当の理由。
grep foo /var/log/syslog も grep foo < /dev/ttyS0 (シリアルポート) も cat hello | grep foo も 同じ read() の呼び出し — これが UNIX のシンプルさと強さの本体。
| ls -l 先頭 | タイプ | 例 |
|---|---|---|
- |
regular — 通常ファイル | /etc/hosts, /bin/ls |
d |
directory — 名前→inode 一覧 | /home, /etc, /tmp |
l |
symlink — 他パスへの参照 | /lib → /usr/lib |
c |
char device — バイト単位 I/O | /dev/tty, /dev/null |
b |
block device — ブロック単位 I/O | /dev/sda, /dev/nvme0n1 |
p |
FIFO — 名前付きパイプ | mkfifo で作成 |
s |
socket — UNIX socket | /tmp/.X11-unix/X0 |
特殊ファイルシステム — /dev /proc /sys #
- /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のような操作も可
ディスクには存在しない。カーネルが実行時に生成する仮想 FS。プロセスが終わったり再起動すれば内容は消えるので、保存したいなら別の場所にコピーする。
# ファイルタイプ確認
$ 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 # 同じことプロセスとシグナル — fork/exec と PID 1 #
Linux のプロセスモデルは UNIX 由来の独特な設計 — 「fork() でコピー → exec() で別プログラムに置換」。
Windows のプロセス起動が「新しい人を雇って仕事させる」ような直接的なモデルなのに対し、UNIX/Linux は 「まず自分のクローンを作り (fork)、そのクローンに別の衣装を着せて (exec) 別プログラムに変身させる」という独特な 2 段階モデル。シェルで ls と打つと、bash は自分のクローンを作って → そのクローンを ls プログラムに変身させて → クローンが終わるのを待つ。こうすると親 (bash) の環境変数や開いているファイルが、自然に子に受け継がれる — シェルが入出力を自由にリダイレクト・パイプできるのもこれが理由。
これが「全プロセスは 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 種類が定義されている。
この 2 つだけはプロセスがハンドラを設定できない。ユーザがどうしても止められる手段を残すための設計。kill -9 は cleanup が走らないのでリソースリークの危険があり、まずは SIGTERM で礼儀正しく頼むのが定石。
# プロセスツリーを見る
$ 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 空間を食い潰すと新規プロセスが起動できなくなる。
権限モデル — rwx → setuid → capabilities → namespaces #
UNIX の権限モデルは UID + GID + 各ファイルの 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 権限で動く必要があった → 実行時だけ所有者の権限になる。乱用するとローカル特権昇格の温床 で、検出 (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 / Network / UID) を見せる + CPU/メモリ/I/O を制限 → これが コンテナの正体 (§07) |
# 基本の権限
$ 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 の安全な編集シェルがすべてを繋ぐ — pipe と標準入出力 #
Linux/UNIX の生産性の本質は、小さな単機能プログラムを | で繋ぎ合わせて何でも作れる こと。これを実現しているのが「すべてはファイル」 + 標準入出力 (stdin / stdout / stderr) + パイプ (|)。
カウンター式の店で、皿洗い → 切り分け → 盛り付け → 提供と、専門スタッフが ベルトコンベアで料理を流し合うのと同じ。各コマンドは 1 つの仕事しかしない小さな職人で、| がベルトコンベア。cat ログ | grep エラー | sort -u | wc -l なら 4 人の職人が「中身を読む → エラー行を抜く → 重複削除 → 数える」を流れ作業で 1 行にまとめる。この組み合わせ自由度こそが Linux で何でも書ける本当の理由。「コマンドが多すぎて覚えられない」ではなく、「小さい部品の組み立て方を覚える」と発想を変えると一気に楽になる。
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 はこれでビルド成功/失敗を判定する。bash の set -euo pipefail は「未定義変数で死ぬ / エラー終了で即停止 / パイプ途中の失敗も検出」というスクリプトの安全装備の定型。
環境変数 (PATH, HOME, LANG, LD_LIBRARY_PATH …) は親プロセスから子プロセスにコピーで伝わる。export FOO=bar でその後起動する子プロセスに渡る。
ディストリビューション — どれを選ぶか #
「Linux を入れる」 = 「ディストリを選ぶ」。カーネルは同じでも、パッケージマネージャ・リリースサイクル・標準デーモン・コミュニティの色が違う。
たくさんあって選びきれないが、実は 3 つの大系統しかない。Debian 系 (Ubuntu, Debian) = 個人 / 開発機 / クラウドの定番、apt 使用。RHEL 系 (RHEL, Rocky, Fedora) = 企業本番の定番、dnf 使用。Arch 系 (Arch, Manjaro) = 最新志向 / 学習用、pacman 使用。これに「コンテナ用に Alpine」が加わる。「迷ったら Ubuntu LTS」が一番無難で、文献も豊富で躓きにくい。慣れたら本番要件に合わせて RHEL 系か他を選ぶ、という順序で問題ない。
| ディストリ | 系統 | パッケージ | 特徴 / 使われる場面 |
|---|---|---|---|
| Debian | 元祖 | apt (.deb) |
安定第一・コミュニティ駆動・サーバ用途で長期に強い |
| Ubuntu | Debian 派生 | apt |
デスクトップで最普及・LTS リリース・Canonical サポート |
| RHEL | 商用 | 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 | 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
コンテナ革命 — namespaces + cgroups の組合せ #
Docker / Kubernetes が席巻した 2010 年代後半は、Linux にとって何が起きたのか? 答えは「Linux カーネルにもとから入っていた 2 つの機能を組合せて使い始めただけ」。
仮想マシン (VM) が 「土地ごと買って一軒家を建てる」とすれば、コンテナは 「マンションの 1 室を借りる」。同じ建物 (ホスト OS のカーネル) を共有しつつ、各部屋は壁 (namespaces) と契約条件 (cgroups) で他から見えなくなっている。だから 軽い (= マンションの 1 室は土地買うより安い) / 速い (= 入居も退去も即時)。代償として 建物自体に欠陥 (= カーネル脆弱性) が出ると全室が影響を受ける — 一軒家 (VM) のような物理的隔離はない、という点だけ理解しておく。Docker / Kubernetes はこの「マンション管理」の仕組みを自動化したもの。
- 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 (積層 FS) を組合せると「ホスト OS の上に独立したミニ OS をプロセス単位で動かす」 = コンテナ になる。Docker は最初これらを lxc 経由で使い、後に runc を作って独自実装、現代は OCI runtime 標準に至っている。
ハードウェア仮想化 (Hyper-V, KVM, VMware) を経由しないから — カーネルはホストと共有し、プロセス分離だけでハードウェアエミュレーションはしない。代償として カーネル脆弱性が出るとコンテナの分離が崩れる (CVE-2022-0185, CVE-2024-1086 などが有名)。
$ docker inspect --format '{{.State.Pid}}' my-container # ホスト上の PID
$ ls /proc/<PID>/ns # その PID の namespaces
$ nsenter -t <PID> -n -p ip addr # NS に入って ip 確認
$ cat /sys/fs/cgroup/system.slice/docker-<ID>.scope/cpu.stat # cgroups の CPU 統計Kubernetes はこの上にもう 1 段「コンテナをクラスタで自動配置・自動修復・自動スケール」する制御平面を被せたもの。Linux カーネルがコンテナを動かし、Kubernetes がそれを管理する という 2 層構造。
現代の 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 | 急成長。Microsoft が公式に Linux カーネルを Windows に同梱という時代 |
「サーバとモバイルとあらゆる埋め込み機器を支えているが、デスクトップだけは少数派」。Windows と macOS は OS 製品として競合するが、Linux はインフラとしての OS で別レイヤを占めている。
Linux は「Linus Torvalds が 1991 年に趣味で書いたカーネル」から始まり、世界の半分以上のコンピューティングを支える OS のエコシステム全体を指す呼称になった。理解の鍵は 「カーネル = Linux 本体」「ユーザランド = GNU + ディストリ」「両者は syscall で物理分離されている」 という 3 層構造、そして 「すべてはファイル」「fork/exec」「pipe + 終了コード」「rwx → capabilities → namespaces」 という UNIX 由来の設計判断が現代までそのまま生き続けていることを押さえることにある。
クラウドもコンテナもスマートフォンもサーバレスも、どこを掘っても Linux の syscall と namespaces と cgroups に行き着く。Linux を 1 度ちゃんと理解すると、現代インフラのほとんどに同じ語彙で対峙できる。