51
ププププププププププ プ2プ systemcall workshop プププププププ

プログラミングの基礎 第 2 回 systemcall

  • Upload
    talib

  • View
    56

  • Download
    0

Embed Size (px)

DESCRIPTION

プログラミングの基礎 第 2 回 systemcall. workshop 資料作成委員会. 本ワークショップの目標. システムコールの仕組みを理解する カーネルモードとユーザモード ソフトウェア割り込み 演習 実 際にシステムコールを直接呼んでみる システムコールを使ったプログラミング. table of contents. システムコール概要 カーネルモードユーザーモード ソフトウェア割り込み システムコール実例 演習:. プログラミングとは?. コンピュータのデバイスを操作する命令手順を書くこと. Hello!. メモリ. ディスプレイ - PowerPoint PPT Presentation

Citation preview

Page 1: プログラミングの基礎 第 2 回 systemcall

プログラミングの基礎 第 2 回systemcall

workshop 資料作成委員会

Page 2: プログラミングの基礎 第 2 回 systemcall

本ワークショップの目標 システムコールの仕組みを理解する

カーネルモードとユーザモード ソフトウェア割り込み

演習 実際にシステムコールを直接呼んでみる システムコールを使ったプログラミング

Page 3: プログラミングの基礎 第 2 回 systemcall

table of contents

1. システムコール概要2. カーネルモードユーザーモード3. ソフトウェア割り込み4. システムコール実例5. 演習:

Page 4: プログラミングの基礎 第 2 回 systemcall

プログラミングとは? コンピュータのデバイスを操作する命令手

順を書くこと

プログラミング

メモリディスプレイに出力する命令手順

ディスプレイデバイス

Hello!

Page 5: プログラミングの基礎 第 2 回 systemcall

OS はすべてのデバイスを管理する OS 上でプログラミングするには?

OS に対してデバイスを操作する命令を発行

OS

画面に文字を出力するプログラム

Page 6: プログラミングの基礎 第 2 回 systemcall

プログラム例 画面に” Hello!” を出力する printf

プログラミング

ディスプレイデバイス

Hello!write()

出力関数( OS の機能 )

出力命令を発行

実際に制御

Page 7: プログラミングの基礎 第 2 回 systemcall

書いたプログラムを動かす!

ソースコード オブジェクトコード

コンパイル リンクhello.ohello.c a.out

実行プログラム

Hello!

実行

Page 8: プログラミングの基礎 第 2 回 systemcall

a.out の構成 元の hello.o よりもサイズが大きくなる

hello.o

a.out

C ランタイムオブジェクトlibC への参照

自分で書いた部分

Page 9: プログラミングの基礎 第 2 回 systemcall

実習:a.out から C ランタイムと libc を削除する

Page 10: プログラミングの基礎 第 2 回 systemcall

前準備

Hello, world! を作ってね! FreeBSD か Linux ホストでお願いします.

#include <stdio.h>#define MESSAGE “Hello, world!¥n”

int main() { printf(MESSAGE); return(123);}

Example program

ちなみに,この時点でコードサイズは…11368 バイト!

Page 11: プログラミングの基礎 第 2 回 systemcall

これから… 邪魔なリンクファイル (crt*   C ランタイ

ムファイル ) を消そう そのかわり、 C ランタイムがやってくれてい

たことは全部自分でやらなければいけません。 これにより、プログラムがどのように動く(動

かされている)のか知ることができます。 いらないセクションを消そう

Page 12: プログラミングの基礎 第 2 回 systemcall

邪魔なリンクファイルを消そう! 先ほどの説明で, crt* ファイルがコンパ

イル時にリンクされていることがわかりました.

リンクしないように, -nostartfiles をつけてみましょう.

Page 13: プログラミングの基礎 第 2 回 systemcall

21:55 [0] skk@aries% gcc -v hello.c Using built-in specs.Configured with: FreeBSD/i386 system compilerThread model: posixgcc 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.oGNU ld version 2.15 [FreeBSD] 2004-05-23 Supported emulations: elf_i386_fbsd

Page 14: プログラミングの基礎 第 2 回 systemcall

21:55 [0] skk@aries% gcc -v -nostartfiles hello.c Using built-in specs.Configured with: FreeBSD/i386 system compilerThread model: posixgcc 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'

Page 15: プログラミングの基礎 第 2 回 systemcall

warning: cannot find entry symbol _start; defaulting to 0000000008048200

「 _start というシンボルが見つからないー.仕方ないから, .text セクションの先頭アドレスを開始アドレスにしちゃうもんね.」という意味。 _start は crt1.o ( C ランタイム)の中にある関数で、

プログラム実行時に最初に呼び出される。今回は crt1.o を丸ごと消したいので使えない。

プログラムの開始アドレスを _start じゃなく,main にしたい!

Page 16: プログラミングの基礎 第 2 回 systemcall

開始アドレスの変更 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'

Page 17: プログラミングの基礎 第 2 回 systemcall

printf() がない??• -lgcc -lc を削除したことに注目しましょう。

前者は gcc 固有ライブラリ、後者は libc ライブラリをリンクするためのものです。

ですが今回はとっちゃいます。なぜでしょう? ライブラリとは

ライブラリは、便利な関数を集めていつでも使えるようにしておいたファイルです。 libc にはさまざまな C 言語の標準関数が含まれています。

printf, もその一つなのです。 逆に言うと、 Hello World! を表示するだけのプログラムでは、 printf の

機能だけ使えればいいので、ライブラリ全てをリンクするのは無駄です。

そこで printf に相当する機能を自分で作ります。

 

-> そうだ! write() だ!

Page 18: プログラミングの基礎 第 2 回 systemcall

C ランタイムと LibC の役割 C ランタイム

C 言語で書かれたプログラムを動作させる環境

LibC C 言語から OS の機能を抽象化して使いやすくす

C ランタイム

USE

Page 19: プログラミングの基礎 第 2 回 systemcall

LibC の役割 OS の機能を抽象化してくれる!

OS

LibC

printf は僕が持ってます

システムコール

OS とのインタフェース( 窓口 ) です

Page 20: プログラミングの基礎 第 2 回 systemcall

C ライブラリとシステムコール C ライブラリはシステムコールのラッパー

を提供する より使いやすく可搬性を高めるため

デバイス

カーネル

プロセス プロセス プロセス

システムコールユーザモード

libc

普通は C ライブラリの中でシステムコールを呼んでく

れる

Page 21: プログラミングの基礎 第 2 回 systemcall

システムコール概要

Page 22: プログラミングの基礎 第 2 回 systemcall

システムコールとは? OS(OS のカーネル)の機能を呼び出すた

めに使用される機構のこと システムをカーネルに制御を移すための特別

な命令を実行し、カーネルの機能を呼ぶ

プロセス OS 資源の利用( デバイス、メモリ空間など )

システムコールの実行

Page 23: プログラミングの基礎 第 2 回 systemcall

プロセスとかカーネルって? プロセス

アプリケーションの実行単位 カーネル

アプリケーションが動作する実行環境を提供

デバイス

カーネル

プロセス プロセス プロセス

Page 24: プログラミングの基礎 第 2 回 systemcall

システムコールがあると何がうれしいの? システムのセキュリテが向上する

カーネルが処理を実行する前に処理要求の正当性を確認できる

プログラミングが楽になる ハードウェアに関する低水準レイヤについて

覚えなくてよくなる プログラムの可搬性が向上

カーネルが同じインタフェースを提供する限りにおいて

Page 25: プログラミングの基礎 第 2 回 systemcall

プログラミングの基礎における意味 プログラミングする上で、 OS の存在を意

識できるようになる この API を呼ぶと、カーネルに制御が移り OS

の資源を利用できる

プロセス カーネル

制御の移り変わり

普通にプログラミングする場合、システムコールを直接呼ぶことは

しません

Page 26: プログラミングの基礎 第 2 回 systemcall

システムコールの特徴 CPU の実行権限を切り替える 割り込みによる実行 OS とアプリケーションの中間層

詳細は、この後に解説

Page 27: プログラミングの基礎 第 2 回 systemcall

カーネルモードとユーザーモード

Page 28: プログラミングの基礎 第 2 回 systemcall

概要 カーネルモードとユーザモード

セキュリティと安全性のため異なる特権状態で命令を実行する

異なる CPU のランレベルを利用する Linux では特権モードと非特権モードの 2 つ

を使い分ける

Page 29: プログラミングの基礎 第 2 回 systemcall

カーネルモード あらゆるハードウェア資源にアクセス可能 オペレーティングシステムの実行モード

デバイス

カーネル

プロセス プロセス プロセス

システムコール

ユーザモード

カーネルモード

Page 30: プログラミングの基礎 第 2 回 systemcall

ユーザモード ハードウェア資源へのアクセスを制限・監視下でプログラムを実行

通常のプログラムの実行モード

デバイス

カーネル

プロセス プロセス プロセス

システムコール

ユーザモード

カーネルモード

Page 31: プログラミングの基礎 第 2 回 systemcall

CPU のランレベル 多くの CPU は 2 つ以上の実行モードを保有 実行レベルを使い分けることで安全性、安定

性を向上させる Intel 80x 86 は 4 つの実行リング ( 特権の階

層 ) を持つ

ランレベル 0 をカーネルモードそれ以上をユーザモードに割り当てる

Page 32: プログラミングの基礎 第 2 回 systemcall

CPU ランレベル移行 コールゲートによるランレベルの移行

コールゲートを解した呼び出しだけが許される 特権レベルの低いコードセグメントから特権レベルの

高いコードセグメントの呼び出し

Page 33: プログラミングの基礎 第 2 回 systemcall

コールゲート OS がランレベルの移行をコントロールできる機

構 ゲートを経由しないと移行できない 呼び出せる特権レベルのゲートは現動作レベル以下だ

特権レベル3のコールゲート

移行先

Page 34: プログラミングの基礎 第 2 回 systemcall

システムコールの本質 CPU ランレベルを切り替えて、プログラム

がデバイスを操作できるようにする仕組み

プロセス 1

システムコールハンドラ

ユーザモード

カーネルモード

システムコールデバイスを操作できる領域

Page 35: プログラミングの基礎 第 2 回 systemcall

ランモードの切り替え方法 ソフトウェア割り込み(システムコールが

使う) タイマ割り込み デバイス割り込み 例外割り込み

プロセス 1

システムコールハンドラ

ユーザモード

カーネルモード

システムコール

スケジューラ

プロセス 1

タイマ割り込み

割り込みハンドラ

プロセス 1

デバイス割り込み

Page 36: プログラミングの基礎 第 2 回 systemcall

ソフトウェア割り込み

Page 37: プログラミングの基礎 第 2 回 systemcall

概要 システムコールはソフトウェア割り込み

で行 割り込みベクターは 0x80 システムコールの引数は,すべてレジスタで渡される

システムコールを実行する手順1. レジスタに必要な値を設定2. int 0x80 を実行する

Page 38: プログラミングの基礎 第 2 回 systemcall

システムコールが利用するレジスタ

Page 39: プログラミングの基礎 第 2 回 systemcall

システムコールの流れ Linux の場合

1. システムコール番号を eax レジスタにセット2. 必要に応じてほかの引数もレジスタにセット3. int 0x80 ソフトウェア割り込みを発行4. カーネルモードスタック上にレジスタ内容を退避

5. システムコールサービスルーチンを呼び出す6. システムコールの実処理7. ハンドラから抜ける

Page 40: プログラミングの基礎 第 2 回 systemcall

システムコールの流れ図

system_call:・・・sys_xyz()・・・iret

sys_xyz(){・・・}

xyz(){

・・・

int 0x80

・・・

}

・ ・ ・

xyz()

・ ・ ・

アプリケーションプログラムからのシステムコール発行

libc 標準ライブラリのラッパールーチン

システムコールハンドラ

システムコールサービスルーチン

ユーザモード カーネルモード

eax 38ebx "param1"

(38: sys_xyzのシステムコール 番号とする )

システムコール番号や引数をセット

Page 41: プログラミングの基礎 第 2 回 systemcall

C ライブラリとシステムコール C ライブラリはシステムコールのラッパー

を提供する より使いやすく可搬性を高めるため

デバイス

カーネル

プロセス プロセス プロセス

システムコールユーザモード

libc

普通は C ライブラリの中でシステムコールを呼んでく

れる

Page 42: プログラミングの基礎 第 2 回 systemcall

システムコール実例

Page 43: プログラミングの基礎 第 2 回 systemcall

システムコール リスト Linux

#define __NR_exit 1#define __NR_fork 2#define __NR_read 3#define __NR_write 4#define __NR_open 5#define __NR_close 6#define __NR_waitpid 7#define __NR_creat 8#define __NR_link 9#define __NR_unlink 10#define __NR_execve 11#define __NR_chdir 12#define __NR_time 13#define __NR_mknod 14#define __NR_chmod 15#define __NR_lchown 16

Page 44: プログラミングの基礎 第 2 回 systemcall

システムコール リスト FreeBSD

#define SYS_syscall 0#define SYS_exit 1#define SYS_fork 2#define SYS_read 3#define SYS_write 4#define SYS_open 5#define SYS_close 6#define SYS_wait4 7/* 8 is old creat */#define SYS_link 9#define SYS_unlink 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

Page 45: プログラミングの基礎 第 2 回 systemcall

strate でシステムコールをトレースする strace って?

システムコールのトレースを行ってくれる! strace の仕組み

システムコールの enter と exit をフックして引数と返り値を出力する OS のデバック用インタフェースを用いる

システムコールユーザモード

カーネルモード

Strace

フック フック

Page 46: プログラミングの基礎 第 2 回 systemcall

strace: emacs on linux の一部出力

execve("/usr/bin/emacs", ["emacs"], [/* 21 vars */]) = 0uname({sys="Linux", node="einstein", ...}) = 0brk(0) = 0x8424000old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40017000access("/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) = 0read(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

Page 47: プログラミングの基礎 第 2 回 systemcall

演習:直接システムコールを呼ぼう

システムコールを使った helloworld 出力(前回既にできた人は必要なし )

Page 48: プログラミングの基礎 第 2 回 systemcall

システムコールを呼ぼう システムコールには,番号がついていま

す. Linux: /usr/include/asm/unistd.h FreeBSD: /usr/include/sys/syscall.h

#define SYS_syscall 0#define SYS_exit 1#define SYS_fork 2#define SYS_read 3#define SYS_write 4#define SYS_open 5#define SYS_close 6#define SYS_wait4 7

$FreeBSD: src/sys/sys/syscall.h,v 1.178.2.1 2005/11/21 01:36:27 csjp Exp $

Page 49: プログラミングの基礎 第 2 回 systemcall

システムコール〜引数の渡し方〜 Linux FreeBSD

EAX レジスタ システムコール番号EBX レジスタ 第一引数ECX レジスタ 第二引数EDX レジスタ 第三引数ESI レジスタ 第四引数EDI レジスタ 第五引数

引数の順番と逆にスタックに積んでいく

mov $4, %eaxmov $1, %ebxmov buf, %ecxmov length, %edxint 0x80

push lengthpush bufpush $1push $4int 0x80

Page 50: プログラミングの基礎 第 2 回 systemcall

Linux FreeBSDconst 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);}

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);}

システムコールで helloworld

Page 51: プログラミングの基礎 第 2 回 systemcall

コンパイル&実行!

% gcc -o helloworld helloworld% ./helloworld

hello world