プログラミングの基礎 第2回
systemcall
workshop 資料作成委員会
本ワークショップの目標
システムコールの仕組みを理解する
カーネルモードとユーザモード
ソフトウェア割り込み
演習
実際にシステムコールを直接呼んでみる
システムコールを使ったプログラミング
table of contents
1.
2.
3.
4.
5.
システムコール概要
カーネルモードユーザーモード
ソフトウェア割り込み
システムコール実例
演習:
プログラミングとは?
コンピュータのデバイスを操作する命令手順
を書くこと
Hello!
メモリ
プログラミング
ディスプレイに
出力する命令手順
ディスプレイ
デバイス
OSはすべてのデバイスを管理する
OS上でプログラミングするには?
OSに対してデバイスを操作する命令を発行
OS
画面に文字を
出力する
プログラム
プログラム例
画面に”Hello!”を出力するprintf
プログラミング
出力命令を発行
Hello!
write()
実際に制御
出力関数(OSの機能)
ディスプレイ
デバイス
書いたプログラムを動かす!
ソースコード
hello.c
実行プログラム
オブジェクトコード
コンパイル
hello.o
リンク
a.out
実行
Hello!
a.outの構成
元のhello.oよりもサイズが大きくなる
a.out
Cランタイムオブジェクト
libCへの参照
hello.o
自分で書いた部分
実習:
a.outからCランタイムとlibcを削除する
前準備
Hello, world! を作ってね!
FreeBSD か Linux ホストでお願いします.
#include <stdio.h>
#define MESSAGE “Hello, world!¥n”
int main() {
printf(MESSAGE);
return(123);
}
Example program
ちなみに,この時点でコードサイズは…
11368 バイト!
これから…
邪魔なリンクファイル (crt* Cランタイムファ
イル) を消そう
そのかわり、Cランタイムがやってくれていたことは
全部自分でやらなければいけません。
これにより、プログラムがどのように動く(動かされて
いる)のか知ることができます。
いらないセクションを消そう
邪魔なリンクファイルを消そう!
先ほどの説明で,crt* ファイルがコンパイル
時にリンクされていることがわかりました.
リンクしないように,-nostartfiles をつけてみ
ましょう.
21:55 [0] skk@aries% gcc -v hello.c
Using built-in specs.
Configured with: FreeBSD/i386 system compiler
Thread model: posix
gcc version 3.4.4 [FreeBSD] 20050518
・・・
/usr/bin/ld -V -dynamic-linker /libexec/ld-elf.so.1
/usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtbegin.o L/usr/lib /var/tmp//ccdlOU3m.o -lgcc -lc -lgcc
/usr/lib/crtend.o /usr/lib/crtn.o
GNU ld version 2.15 [FreeBSD] 2004-05-23
Supported emulations:
elf_i386_fbsd
21:55 [0] skk@aries% gcc -v -nostartfiles hello.c
Using built-in specs.
Configured with: FreeBSD/i386 system compiler
Thread model: posix
gcc version 3.4.4 [FreeBSD] 20050518
/usr/libexec/cc1 -quiet -v -D_LONGLONG hello.c -quiet dumpbase hello.c -auxbase hello –
・・・
elf_i386_fbsd
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting
to 0000000008048200
/usr/lib/libc.so: undefined reference to `environ'
/usr/lib/libc.so: undefined reference to `__progname'
warning: cannot find entry symbol _start; defaulting to
0000000008048200
「_start というシンボルが見つからないー.仕方ない
から,.text セクションの先頭アドレスを開始アドレス
にしちゃうもんね.」という意味。
_startはcrt1.o(Cランタイム)の中にある関数で、プログラ
ム実行時に最初に呼び出される。今回はcrt1.oを丸ごと消
したいので使えない。
プログラムの開始アドレスを _start じゃなく,main
にしたい!
開始アドレスの変更
ld コマンドの -e オプションで変更できます!
-e の後ろには,シンボル名を指定します.
% gcc -c hello.c
% ld -e main -o a.out hello.o
hello.o(.text+0x25): In function `main':
: undefined reference to `printf'
printf() がない??
•
-lgcc -lc を削除したことに注目しましょう。
前者はgcc固有ライブラリ、後者はlibcライブラリをリンクするためのものです。
ですが今回はとっちゃいます。なぜでしょう?
ライブラリとは
ライブラリは、便利な関数を集めていつでも使えるようにしておいたファイル
です。
libcにはさまざまなC言語の標準関数が含まれています。
printf,もその一つなのです。
逆に言うと、Hello World!を表示するだけのプログラムでは、printfの機能
だけ使えればいいので、ライブラリ全てをリンクするのは無駄です。
そこで
printfに相当する機能を自分で作ります。
-> そうだ! write() だ!
CランタイムとLibCの役割
Cランタイム
C言語で書かれたプログラムを動作させる環境
USE
Cランタイム
LibC
C言語からOSの機能を抽象化して使いやすくする
LibCの役割
OSの機能を抽象化してくれる!
printfは僕が持っ
てます
LibC
システムコール
OS
OSとのインタフェース
(窓口)です
Cライブラリとシステムコール
Cライブラリはシステムコールのラッパーを提
供する
より使いやすく可搬性を高めるため
プロセス
プロセス
プロセス
libc
ユーザモード
システムコール
カーネル
デバイス
普通はCライブラリの
中でシステムコール
を呼んでくれる
システムコール概要
システムコールとは?
OS(OSのカーネル)の機能を呼び出すために
使用される機構のこと
システムをカーネルに制御を移すための特別な命
令を実行し、カーネルの機能を呼ぶ
システムコールの実行
プロセス
OS資源の利用
(デバイス、メモリ空間など)
プロセスとかカーネルって?
プロセス
アプリケーションの実行単位
カーネル
アプリケーションが動作する実行環境を提供
プロセス
プロセス
プロセス
カーネル
デバイス
システムコールがあると何がうれしいの?
システムのセキュリテが向上する
カーネルが処理を実行する前に処理要求の正当
性を確認できる
プログラミングが楽になる
ハードウェアに関する低水準レイヤについて覚え
なくてよくなる
プログラムの可搬性が向上
カーネルが同じインタフェースを提供する限りにお
いて
プログラミングの基礎における意味
プログラミングする上で、OSの存在を意識で
きるようになる
このAPIを呼ぶと、カーネルに制御が移りOSの資
源を利用できる
プロセス
カーネル
制御の移り変わり
普通にプログラミングする
場合、システムコールを
直接呼ぶことはしません
システムコールの特徴
CPUの実行権限を切り替える
割り込みによる実行
OSとアプリケーションの中間層
詳細は、この後に解説
カーネルモードとユーザーモード
概要
カーネルモードとユーザモード
セキュリティと安全性のため異なる特権状態で命
令を実行する
異なるCPUのランレベルを利用する
Linuxでは特権モードと非特権モードの2つを使い
分ける
カーネルモード
あらゆるハードウェア資源にアクセス可能
オペレーティングシステムの実行モード
プロセス
プロセス
プロセス
ユーザモード
システムコール
カーネルモード
カーネル
デバイス
ユーザモード
ハードウェア資源へのアクセスを制限・監視下
でプログラムを実行
通常のプログラムの実行モード
プロセス
プロセス
プロセス
ユーザモード
システムコール
カーネルモード
カーネル
デバイス
CPUのランレベル
多くのCPUは2つ以上の実行モードを保有
実行レベルを使い分けることで安全性、安定性を
向上させる
Intel 80x86は4つの実行リング(特権の階層)を
持つ
ランレベル0をカーネルモード
それ以上をユーザモードに割り当てる
CPUランレベル移行
コールゲートによるランレベルの移行
コールゲートを解した呼び出しだけが許される
特権レベルの低いコードセグメントから特権レベルの高い
コードセグメントの呼び出し
コールゲート
OSがランレベルの移行をコントロールできる機構
ゲートを経由しないと移行できない
呼び出せる特権レベルのゲートは現動作レベル以下だけ
移行先
特権レベル3の
コールゲート
システムコールの本質
CPUランレベルを切り替えて、プログラムがデ
バイスを操作できるようにする仕組み
プロセス1
ユーザモード
カーネルモード
デバイスを操作
できる領域
システムコール
システムコール
ハンドラ
ランモードの切り替え方法
ソフトウェア割り込み(システムコールが使う)
タイマ割り込み
デバイス割り込み
例外割り込み
プロセス1
プロセス1
プロセス1
ユーザモード
カーネルモード
システムコール
タイマ割り込み
システムコール
ハンドラ
スケジューラ
デバイス割り込み
割り込みハンドラ
ソフトウェア割り込み
概要
システムコールはソフトウェア割り込みで行
割り込みベクターは 0x80
システムコールの引数は,すべてレジスタで渡さ
れる
システムコールを実行する手順
1. レジスタに必要な値を設定
2. int 0x80 を実行する
システムコールが利用するレジスタ
システムコールの流れ
Linuxの場合
1.
2.
3.
4.
5.
6.
7.
システムコール番号をeaxレジスタにセット
必要に応じてほかの引数もレジスタにセット
int 0x80ソフトウェア割り込みを発行
カーネルモードスタック上にレジスタ内容を退避
システムコールサービスルーチンを呼び出す
システムコールの実処理
ハンドラから抜ける
システムコールの流れ図
eax
ebx
ユーザモード
・・・
xyz()
・・・
38 (38: sys_xyzのシステムコール
番号とする)
"param1"
システムコール番号
や引数をセット
xyz(){
・・・
int 0x80
・・・
}
アプリケーション
libc標準ライブラリ
プログラムからの
のラッパールーチン
システムコール発行
カーネルモード
system_call:
・・・
sys_xyz()
・・・
iret
sys_xyz(){
・・・
}
システムコール
ハンドラ
システムコール
サービスルーチン
Cライブラリとシステムコール
Cライブラリはシステムコールのラッパーを提
供する
より使いやすく可搬性を高めるため
プロセス
プロセス
プロセス
libc
ユーザモード
システムコール
カーネル
デバイス
普通はCライブラリの
中でシステムコール
を呼んでくれる
システムコール実例
システムコール リスト
Linux
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
__NR_exit
__NR_fork
__NR_read
__NR_write
__NR_open
__NR_close
__NR_waitpid
__NR_creat
__NR_link
__NR_unlink
__NR_execve
__NR_chdir
__NR_time
__NR_mknod
__NR_chmod
__NR_lchown
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
システムコール リスト
FreeBSD
#define SYS_syscall
#define SYS_exit
#define SYS_fork
#define SYS_read
#define SYS_write
#define SYS_open
#define SYS_close
#define SYS_wait4
/* 8 is old creat */
#define SYS_link
#define SYS_unlink
0
1
2
3
4
5
6
7
9
10
/* 11 is obsolete execv */
#define SYS_chdir
12
#define SYS_fchdir
13
#define SYS_mknod
14
#define SYS_chmod
15
#define SYS_chown
16
#define SYS_break
17
/* 18 is old getfsstat */
/* 19 is old lseek */
#define SYS_getpid
20
#define SYS_mount
21
strateでシステムコールをトレースする
straceって?
システムコールのトレースを行ってくれる!
straceの仕組み
システムコールのenterとexitをフックして引数と
返り値を出力する
OSのデバック用インタフェースを用いる
Strace
ユーザモード
カーネルモード
フック
フック
システムコール
strace: emacs on linuxの一部出力
execve("/usr/bin/emacs", ["emacs"], [/* 21 vars */]) = 0
uname({sys="Linux", node="einstein", ...}) = 0
brk(0)
= 0x8424000
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0
x40017000
access("/etc/ld.so.nohwcap", F_OK)
= -1 ENOENT (No
such file or directory)
open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT
(No such file or directory)
close(3)
=0
read(3,
"\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\220
\342"..., 512) = 512
演習:直接システムコールを呼ぼう
システムコールを使ったhelloworld出力
(前回既にできた人は必要なし)
システムコールを呼ぼう
システムコールには,番号がついています.
Linux: /usr/include/asm/unistd.h
FreeBSD: /usr/include/sys/syscall.h
#define
#define
#define
#define
#define
#define
#define
#define
SYS_syscall
SYS_exit
SYS_fork
SYS_read
SYS_write
SYS_open
SYS_close
SYS_wait4
0
1
2
3
4
5
6
7
$FreeBSD: src/sys/sys/syscall.h,v 1.178.2.1 2005/11/21 01:36:27 csjp Exp $
システムコール〜引数の渡し方〜
Linux
EAX
EBX
ECX
EDX
ESI
EDI
レジスタ
レジスタ
レジスタ
レジスタ
レジスタ
レジスタ
システムコール番号
第一引数
第二引数
第三引数
第四引数
第五引数
mov $4, %eax
mov $1, %ebx
mov buf, %ecx
mov length, %edx
int 0x80
FreeBSD
引数の順番と逆にスタッ
クに積んでいく
push length
push buf
push $1
push $4
int 0x80
システムコールでhelloworld
Linux
const char message[] = "hello world¥n";
int writes(const char *buf, int len) {
int ret;
asm(
"int $0x80"
: "=a" (ret)
: "a" (4),
"b" (1),
"c" (buf),
"d" (len)
);
return(ret);
}
int main() {
int ret;
ret = writes(message, sizeof(message));
return(ret);
}
FreeBSD
const char message[] = "hello world¥n";
int writes(const char *buf, int len) {
int ret;
asm("nop" :: "b"(len));
asm ("pushl %ebx");
asm("nop" :: "c"(buf));
asm("pushl %ecx");
asm("pushl $1");
asm("movl $0x4, %eax");
asm("pushl %eax");
asm("int $0x80");
asm("addl $12, %esp");
return(ret);
}
int main() {
int ret;
ret = writes(message, sizeof(message));
return(ret);
コンパイル&実行!
% gcc -o helloworld helloworld
% ./helloworld
hello world
© Copyright 2026 ExpyDoc