こんにちは。構築担当の下地です。
夏休みは名古屋に行って、矢場とんや山本屋本店でグルメめぐりをしていました。
少し暑かったですが名古屋めし美味しかったです。
さて、今日はLinuxにおけるCPUのモードにお話します。
宜しくお願いします。
ユーザモードとカーネルモード
CPUにはユーザモードとカーネルモードの2つのモードがあります。
通常、OSは各種プログラムをプロセスという単位で実行しますが、プロセスはユーザモードで動作します。
そして、プロセスからデバイス(ストレージやネットワークアダプタなど)を操作したい場合に、デバイスドライバを介してデバイスにアクセスします。
デバイスドライバはカーネルモードで動作します。
なぜ2つのモードがあるのか
仮にプロセスが自由にデバイスへアクセスできるとすると、複数のプロセスが1つのデバイスを同時に操作することができてしまいます。
そういった事態を防ぐため、Linuxではプロセスから直接デバイスへアクセスできないようになっています。
具体的には、ユーザモードとカーネルモードという2つのモードを用意し、カーネルモードで動作しているときのみデバイスにアクセスできるようにします。
デバイスの操作以外にも、プロセスから直接操作できると困る処理がたくさんあります。
・プロセスの管理
・プロセススケジューラ
・メモリの管理
などです。
これらもカーネルモードで動作します。
このようなカーネルモードで動作するOSの核となる処理をまとめたプログラムをカーネルと呼んでいます。
システムコールと割り込み
先に書いたように、プロセスは直接デバイスを含むカーネルが提供する機能を使うことはできません。
それらを使いたければ、すべて「システムコール」と呼ばれる特殊な処理を介す必要があります。
システムコールには次のような種類があります。
・プロセス生成、削除
・メモリ確保、解放
・プロセス間通信
・ネットワーク
・ファイルシステム操作
・デバイスアクセス
プロセスは通常ユーザモードで実行していますが、カーネルに処理を依頼するためにシステムコールを発行すると、CPUにおいて「割り込み」というイベントが発生します。
これによりCPUはユーザモードからカーネルモードに遷移して、依頼内容に応じてカーネルモードで処理が開始されます。
カーネルモードでの処理が完了すると再びユーザモードに戻ってプロセスの動作を継続します。
システムコールの様子を実際に見てみる
プロセスがどんなシステムコールを発行するかは、straceコマンドで見ることができます。
実際にやってみましょう。
まず簡単なプログラムを作成します。
いわゆるhello worldです。
#include <stdio.h>
int main(void)
{
puts("hello world");
return 0;
}
書けたらコンパイルして動かしてみます。
# cc -o hello hello.c
# ./hello
hello world
OKでした。
ではstraceコマンドで、このプログラムがどんなシステムコールを発行するかを見てみます。
# strace -o hello.log ./hello
hello world
# cat hello.log
execve("./hello", ["./hello"], [/* 22 vars */]) = 0
brk(NULL) = 0x8ac000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1129f99000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=26393, ...}) = 0
mmap(NULL, 26393, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f1129f92000
close(3) = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "177ELF2113 3 >