オペレーティングシステムの役割
オペレーティングシステム(OS)はハードウェアとアプリケーションの間で動作する基盤ソフトウェアです。主な機能は以下の通り:
- ハードウェアドライバを通じて物理デバイスを管理
- システムコールを介してアプリケーションにサービスを提供
- リソースの効率的な配分と保護を実現
システムコールとライブラリ関数
システムコールはカーネルと直接通信する低レベルインターフェースです。例えばファイル読み込みでは:
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
この関数はカーネル内でsys_read()を実行します。アプリケーション開発者は通常、fread()のような高レベルのC標準ライブラリ関数を使用します。ライブラリ関数はシステムコールをラップし、開発者の利便性を向上させます。
プロセスの実体構造
実行中のプログラムはプロセスとして管理されます。Linuxではtask_struct構造体がプロセスのすべての情報を保持します。
プロセス状態の詳細
| 状態コード | 意味 | 特徴 |
|---|---|---|
| R | 実行可能状態 | CPU実行待ちまたは実行中(R+はフォアグラウンド実行) |
| S | インタラプティブル待機 | イベント待ち(キーボード入力など) |
| D | アンインタラプティブル待機 | I/O操作中(信号で中断不可) |
| T | 停止状態 | デバッガによる一時停止など |
| Z | ゾンビ状態 | 終了した子プロセスの残骸(親プロセスが回収待ち) |
ゾンビプロセスの発生メカニズム
子プロセスが終了後、親プロセスがwait()システムコールを呼び出さないとゾンビ状態が発生します。initプロセス(PID=1)は孤児プロセスを回収する役割を持ちます:
while (1) {
pid_t child = waitpid(-1, &status, WNOHANG);
if (child > 0) cleanup_zombie(child);
}
仮想アドレス空間の仕組み
各プロセスは独立した仮想メモリ空間を持ち、物理メモリへのマッピングはページテーブルで管理されます。
コピー・オン・ライトの実装
fork()実行時のメモリ最適化例:
int main() {
int value = 100;
pid_t pid = fork();
if (pid == 0) { // 子プロセス
value = 200; // 書き込み時にメモリ領域を分離
printf("Child: %d\n", value);
} else { // 親プロセス
sleep(1);
printf("Parent: %d\n", value);
}
return 0;
}
実行結果:
Child: 200 Parent: 100
この動作はコピー・オン・ライト(COW)と呼ばれ、メモリ使用量を最適化します。
環境変数の管理
環境変数はプロセスの実行環境を設定する文字列の集合です。主な用途:
PATH: 実行可能ファイルの検索パスHOME: ユーザーディレクトリのパスSHELL: 使用シェルのパス
カスタムコマンドをPATHに追加する方法:
export PATH="$PATH:/home/user/bin"
この設定はセッション終了時にリセットされます。永続化する場合は~/.bashrcに記述します。
環境変数の取得方法
C言語でのアクセス例:
#include <stdlib.h>
int main(int argc, char *argv[], char *envp[]) {
for (int i = 0; envp[i]; i++) {
printf("%s\n", envp[i]);
}
return 0;
}