システムコール
ユーザー空間のプログラムが OS カーネルの機能を呼び出すインターフェース
システムコールとは
システムコール (System Call, syscall) は、ユーザー空間のプログラムが OS カーネルの機能 (ファイル操作、ネットワーク、プロセス管理) を呼び出すインターフェースである。アプリケーションはハードウェアに直接アクセスできず、カーネルを経由する。
ユーザー空間とカーネル空間
CPU には特権レベルがあり、ユーザー空間のコードはカーネルのメモリやデバイスへ直接触れない。システムコールはこの境界を越える唯一の正規の入口で、呼び出した瞬間に CPU のモードがカーネル側へ切り替わり、処理が終わると元のモードへ戻る。
ユーザー空間 (アプリケーション)
Node.js, Python, Go ...
↓ システムコール
カーネル空間 (OS)
ファイルシステム, ネットワーク, メモリ管理
↓
ハードウェア
CPU, メモリ, ディスク, NIC
主要なシステムコール
カテゴリごとに代表的なものを挙げる。名前は Linux のものだが、ファイル / プロセス / ソケットという区分自体は他の Unix 系 OS にも共通である。
| カテゴリ | syscall | 説明 |
|---|---|---|
| ファイル | open, read, write, close | ファイル操作 |
| プロセス | fork, exec, wait, exit | プロセス管理 |
| ネットワーク | socket, bind, listen, accept | ソケット操作 |
| メモリ | mmap, brk | メモリ割り当て |
同じ処理でも OS ごとに呼び名や引数が異なるため、移植性が必要なコードは libc や言語のランタイムを経由し、直接 syscall を発行しない。
Node.js とシステムコール
高水準の API がどのシステムコールへ落ちるかを追うと、遅さの原因を当てやすくなる。
import { readFile } from 'fs/promises';
// readFile() → libuv → read() syscall → カーネル → ディスク
const data = await readFile('/etc/hosts', 'utf-8');
// fetch() → libuv → socket() + connect() + write() + read() syscall
const res = await fetch('https://api.example.com');
Node.js の fs, net, http モジュールは内部的にシステムコールを呼び出している。libuv がシステムコールを非同期に実行し、イベントループに結果を返す。
strace でシステムコールを観察
Linux では strace で、プロセスが実際に発行したシステムコールを引数と戻り値ごと観察できる。macOS に strace は無く、dtruss などが近い役割を担う。
# Node.js のシステムコールを表示
strace -f node -e "require('fs').readFileSync('/etc/hosts')"
# openat(AT_FDCWD, "/etc/hosts", O_RDONLY) = 3
# read(3, "127.0.0.1 localhost\n...", 8192) = 234
# close(3) = 0
-f は子プロセスとスレッドも追跡する。呼び出し回数と所要時間の内訳だけ見たいときは -c、対象を絞るときは -e trace=openat,read のように指定する。strace は 1 回ごとに呼び出しへ割り込む方式なので実行速度が大きく落ちる。本番プロセスへの常用は避け、再現環境で使う。
コンテナとシステムコール
コンテナ はホスト OS のカーネルを共有するため、コンテナ内のプロセスもホストのシステムコールを使う。裏を返せば、コンテナの分離の強さは「どのシステムコールを通すか」でほぼ決まる。seccomp はこの絞り込みを担う仕組みである。
// 特定の syscall だけを拒否する最小例 (既定は許可)
{
"defaultAction": "SCMP_ACT_ALLOW",
"syscalls": [
{ "names": ["reboot", "mount"], "action": "SCMP_ACT_ERRNO" }
]
}
Docker が既定で適用するプロファイルはこの逆で、defaultAction に SCMP_ACT_ERRNO を置いた許可リスト方式を採り、300 以上あるシステムコールのうち約 44 個を無効化する (2026 年 8 月時点)。許可リスト方式には、カーネルに後から追加されたシステムコールも自動的に拒否側へ入るという利点がある。逆に拒否リスト方式は、新しい呼び出しが素通りする。権限そのものの分離は 名前空間と cgroup が担い、seccomp はその上で入口の数を削る役割になる。
Lambda とシステムコール
Lambda の関数は分離された実行環境で動き、カーネルの設定変更やネットワークインターフェースの直接操作といった特権的な操作はできない。書き込める領域は /tmp で、容量は既定 512 MB、最大 10,240 MB まで 1 MB 刻みで設定できる (2026 年 8 月時点)。ここは同じ実行環境が再利用される間だけ残る一時領域なので、恒久的な保存先にはならない。
パフォーマンスへの影響
システムコールは呼び出しごとに CPU のモード遷移を伴う。これはプロセスの切り替え (プロセスとスレッド) とは別で、同じスレッドのまま特権レベルだけが上下する動きである。それでも引数の検証やキャッシュの汚れが積み上がるため、1 回あたりのコストは通常の関数呼び出しより桁違いに大きい。細かい read/write を大量に出すコードは、書き込みをまとめるだけで速くなることがある。
呼び出し回数そのものを減らす仕組みもカーネル側に用意されている。Linux は vDSO (virtual dynamic shared object) と呼ぶ小さな共有ライブラリを各プロセスのアドレス空間へ貼り付けており、gettimeofday のように頻繁で副作用のない呼び出しはカーネルへ入らずユーザー空間で完結する。I/O では io_uring が、ユーザー空間とカーネルで共有するリングバッファーに要求と完了を並べる方式を採り、要求 1 件ごとの呼び出しとバッファーのコピーを避ける (Linux 固有の仕組み)。
システムコールを意識すると、性能とセキュリティの両方が同じ視点で見えてくる。呼び出し回数を減らせば I/O の待ちが縮み、越えられる境界を絞れば侵害時の被害が縮む。どちらも「ユーザー空間とカーネル空間の境界を何回、どれだけ広く越えるか」という一つの問いに帰着する。
この記事は役に立ちましたか?
関連用語
プロセスとスレッドの違いとは - メモリ空間 / 生成コスト / 通信方法を比較
プロセスとスレッドの違いをメモリ空間 / 生成コスト / 通信方法の 3 軸で比較。Node.js での使い分け、Lambda の実行環境、データ競合とコンテナの PID 1 の落とし穴まで解説
Namespace と cgroup
Linux コンテナの基盤技術で、プロセスの隔離とリソース制限を実現するカーネル機能
コンテナ
アプリケーションとその依存関係をパッケージ化し、環境に依存しない一貫した実行環境を提供する仮想化技術
Linux
オープンソースで開発される OS。サーバーや組み込み機器で広く使われる
Elixir
高い並行性と耐障害性を持つ関数型言語。大量接続を捌くシステムに向く
C4 Model
ソフトウェアアーキテクチャを 4 つの抽象レベルで図示するモデル
関連する記事
Linux 本ガイド - コマンドライン / しくみ / 性能の 3 層で選ぶ技術書
Linux を学ぶ技術書の選び方を 3 層 (コマンドラインの操作 → カーネルのしくみ → 性能と運用) で整理。新しい Linux の教科書や [試して理解] Linux のしくみなどの定番書の使い分けと、学ぶ順番を解説します。
障害対応の夜に思い出す、あの本の 1 ページ
本番障害の緊迫した場面で、過去に読んだ技術書の知識が助けてくれることがあります。「いつか役立つ」知識が「今この瞬間」に変わる読書の価値を考えます。
技術書の知識を定着させる間隔反復法 - 読んだのに忘れる問題を解決する
技術書を読んでも内容を忘れてしまう原因を認知科学の観点から分析し、間隔反復法を使って知識を長期記憶に定着させる具体的な方法を紹介します。