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

Post on 12-Jan-2016

57 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

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

TRANSCRIPT

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

workshop 資料作成委員会

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

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

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

table of contents

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

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

順を書くこと

プログラミング

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

ディスプレイデバイス

Hello!

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

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

OS

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

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

プログラミング

ディスプレイデバイス

Hello!write()

出力関数( OS の機能 )

出力命令を発行

実際に制御

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

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

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

実行プログラム

Hello!

実行

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

hello.o

a.out

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

自分で書いた部分

実習: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 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

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'

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 言語で書かれたプログラムを動作させる環境

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

C ランタイム

USE

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

OS

LibC

printf は僕が持ってます

システムコール

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

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

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

デバイス

カーネル

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

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

libc

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

れる

システムコール概要

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

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

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

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

システムコールの実行

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

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

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

デバイス

カーネル

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

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

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

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

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

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

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

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

の資源を利用できる

プロセス カーネル

制御の移り変わり

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

しません

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

詳細は、この後に解説

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

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

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

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

を使い分ける

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

デバイス

カーネル

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

システムコール

ユーザモード

カーネルモード

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

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

デバイス

カーネル

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

システムコール

ユーザモード

カーネルモード

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

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

層 ) を持つ

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

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

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

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

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

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

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

移行先

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

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

プロセス 1

システムコールハンドラ

ユーザモード

カーネルモード

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

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

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

プロセス 1

システムコールハンドラ

ユーザモード

カーネルモード

システムコール

スケジューラ

プロセス 1

タイマ割り込み

割り込みハンドラ

プロセス 1

デバイス割り込み

ソフトウェア割り込み

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

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

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

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

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

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

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

システムコールの流れ図

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

sys_xyz(){・・・}

xyz(){

・・・

int 0x80

・・・

}

・ ・ ・

xyz()

・ ・ ・

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

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

システムコールハンドラ

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

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

eax 38ebx "param1"

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

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

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

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

デバイス

カーネル

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

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

libc

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

れる

システムコール実例

システムコール リスト 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

システムコール リスト 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

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

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

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

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

カーネルモード

Strace

フック フック

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

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

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

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

す. 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 $

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

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

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

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

push lengthpush bufpush $1push $4int 0x80

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

コンパイル&実行!

% gcc -o helloworld helloworld% ./helloworld

hello world

top related