• 検索結果がありません。

1 Level1 Linux kernel kernel (config) kernel 11 Kernel linux kernel /usr/src/ linux- - The Linux Kernel Archives kernel /usr/src/ /usr/src/li

N/A
N/A
Protected

Academic year: 2021

シェア "1 Level1 Linux kernel kernel (config) kernel 11 Kernel linux kernel /usr/src/ linux- - The Linux Kernel Archives kernel /usr/src/ /usr/src/li"

Copied!
33
0
0

読み込み中.... (全文を見る)

全文

(1)

UNIX

実験

テーマ 

Unix Kernel

チーム:サンダース

提出日:2006 年 7 月 1 日

チームメンバー 学籍番号 045709E 上原 直久 学籍番号 045713C 大城 和也 学籍番号 045736B 知念 栄作 学籍番号 045739G 友寄雄一朗

(2)

1

Level1

Linux kernelを構築する。kernel のソースの場所を見つけ、それを設定 (config)し、 コンパイルせよ。コンパイルした kernel が正しく動く事を確認 せよ。

1.1

Kernel

の再構築手順

1.1.1 ソースの場所

kernelのソースは通常,/usr/src/のディレクトリ下に linux-リリース番号-の形で置かれている.ディレクトリ下に何も無い場合やバージョンアップを 行うなら, The Linux Kernel Archives などのサイトから kernel のソースを ダウンロードして展開しておく. 今回の場合はバージョンのアップもかねて linux-2.6.17 をダウンロードし て/usr/src/に展開している.なお、ここからの説明はすべて/usr/src/linux-2.6.17での作業としている. 1.1.2 前準備 まず、kernel の再構築する前に現在使っている kernel との区別をつけるため, それとモジュールのインストールの際に既存のモジュールを上書きを避けるた めにリリースの設定を 変更しておく.設定は Makefile の EXTRAVERSION の内容を書き換える事で変更する.ここでは -mycustum と設定しておく. ---VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 17 EXTRAVERSION = -mycustom ・・・ ---次に kernel の構築環境の初期化を行っておく.この初期化は、make mr-properコマンドを実行すればよい. ---%make mrproper ---このコマンドは.config ファイルも削除するので前の設定を用いたいのなら ばバックアップをとっておく.ちなみに新しくダウンロードしてきた kernel においては特にこの作業をする必要は無い.

(3)

1.1.3 Kernel Configuration では、メインの kernel の設定へ kernelの設定方法は以下のように,いくつかあるので使いやすい方を選ぶ. • make config   コンソールで kernel の設定を行う.設定は一つずつ [y/m/n] のい ずれかを入力する ことでその設定をオンにするかモジュールにす るかオフにするかを選択する.また,’ ?’ を入力することでその機 能がどの様なものなのかの説明をするヘルプ情報を見る事ができ る. 同じようなものに make oldconfig というものもあり,これは昔 の.config を用いて,まだ設定されていない項目だけ設定を求める ものである.バージョンアップの際 などの時に使われることが 多い. • make menuconfig   コンソールにでるテキストベースのメニューの画面を見て設定を 行う.make config とは違ってそれぞれの項目 を自分で選択して 設定を行う.なお,このコマンドを使う際には ncurses というコ ンソール画面 制御のパッケージが導入されている必要がある.画 面は図 1 のようになる. 図 1: menuconfig

(4)

• make xconfig   X Window ベースの設定画面で GUI で設定を行う事ができる.2.4 まででは tcl/tk が導入されている環境で利用が可能.2.6 からは 新しく,KDE/Qt 環境で使用できる qconf という設定ツールが用 意された.下の図 2 を見ればわかるように,これは項目を選択す れば下のウィンドウにヘルプ情報が表示されたり,ツリー表示に するなどわかりやすいようにしている. 図 2: qconf • make gconfig

  xconfig と同様に X Window ベースの GUI で設定を行う.ただし, これは gnome/gtk の環境で使えるようなっている.これも 2.6 か ら導入されたものである

この他にも全ての設定を Yes とする make allyesconfig やその逆の make all-noconfig、できるだけモジュールにする make allmodconfig などがある.ま

(5)

これらである程度の設定を行ってもらい,細かい設定を上のコマンドを用い るなどをすれば設定が容易になるだろう. 実験環境では gconfig は動かなかった.設定は xconfig をメインに使った. 1.1.4 設定項目 kernelの設定項目は非常に多い.以下の表 1 は大まかな設定の内容を記述 している. 表 1: 設定内容 64-bit kernel 64ビット用のカーネルにするかどうかの設定

Processor suport PPC系列の processor の選択や multi processor のサポート Code maturity level option 開発中や試験的に使用している機能の項目の選

択をできるようにする         General setup 一般設定の項目

Loadable module support ローダブルモジュールを利用するための設定.モジュール のロードに関する項目や kernel のモジュールの自動読みこ みなどがある.        Block layer ブロックデバイスのサイズのサポートなど

Platform support Prossecer Typeによって変化する.

Kernel options サーバ向けかデスクトップ向けかの設定 (?).  Bus options 拡張カードバスに関する設定 Advanced setup さらに細かい設定を行えるようにするかどうかの設定  Nerworking TCP/IPを使用するかどうかなどのネットワークの設定項目 Device Drivers デバイスドライバに関する設定.さまざまなデバイスの必要 かどうかを設定する.         File systems サポートするファイルシステムに関する設定. Library routines CRCチェックに関する設定 Instrumentation Support プロファイリングのサポート.2.6 では試験的に用いられている.  Kernel hacking 強制リブートやダンプなどの機能をつける.主に kernel のデ

バッグに利用する機能.        Security options セキュリティ機能に関する項目

Cryptographic options 暗号化アルゴリズムに関する設定

今回の実験の関連の設定では Loadable module support にある Module unloadingの設定を ON にしておく,また,いらないと思われるモジュール は排除して今後のコンパイルを早められるようにした.

(6)

1.1.5 Kernelのコンパイル

設定が終ったら kernel をコンパイルする.

kernelが 2.6 の場合は kernel のコンパイルは make コマンドを実行するだ けでよい.

---%make

---makeをすると kernel と module のコンパイルが同時に行われる.もし, 別々でコンパイルしたいのなら ---%make zImage %make modules ---上のようにすることでコンパイルが可能.2.6 より下のバージョンでコンパ イルを行う場合は make zImage,make modules を行う前に make dep をして 変更をカーネルツリーに反映させる必要がある.コンパイルが通れば vmlinux や System.map,arch/powerpc/boot/zImage などが生成されているはずだ. モジュールをインストールする. ---%make modules_install ---これによって/lib/modules のディレクトリ下に設定したリリースの名前の フォルダができ,モジュールがインストールされる. kernelをインストール (/boot にコピー) する. ---%cp arch/powerpc/boot/zImage /boot/vmlinuz-2.6.17.mycustom %cp vmlinux /boot/vmlinux-2.6.17.mycustom %cp System.map-2.6.17.mycustom

---この時,zImage 及び vmlinux というファイルが kernel 本体である.zImage は vmlinux を圧縮したものである.System.map は symbol 情報を格納した ファイルである.これらを/boot のディレクトリ下に適当に名前を変更して コピーする.

initrdの作成

---%mkinitrd -i initrd-2.6.17.mycustom -k vmlinux-2.6.17.mycustom -M

%System.map-2.6.17.mycustom

(7)

---Suseの場合は以上のようにして作成する,もしくは mkinitrd コマンドだ けを行う事で現在/boot にあるそれぞれの kernel についての initrd を作成し てくれる.

initrdはブートローダに RAM ディスクをロードする機能を与える.そし てこの RAM ディスクは root として mount され,そこから各種のプログラ ムを起動する事ができる.これを使う事によって SCSI や FireWire などの機 器からの kernel の起動が可能となっている.今回はそれらを用いるわけでも ないので必要性は薄いと思われるが作成しておく事にする. 1.1.6 Boot loaderの編集 kernelのコピーが終ったら,次はその kernel をブートさせるための設定を ブートローダに加える必要がある.ブートローダにはいくつかの種類がある がここでは lilo の設定を説明する.lilo の設定ファイルは/etc/lilo.conf であ る.これを見てみると以下のようになっている. ---activate timeout = 80 default = linux boot = /dev/hda3 image = /boot/vmlinux

###Don’t change this comment - YaST2 identifier: Original name: linux### label = linux

append = " quiet sysrq=1" initrd = /boot/initrd root = /dev/hda6 ---作成した kernel の設定を追加する. ---activate timeout = 80 default = linux boot = /dev/hda3 image = /boot/vmlinux

###Don’t change this comment - YaST2 identifier: Original name: linux### label = linux

append = " quiet sysrq=1" initrd = /boot/initrd root = /dev/hda6 ---|image = /boot/vmlinux-2.6.17.mycustom | |### mycustom linux ### | | label = mycustom | | append = " quiet sysrq=1" | | initrd = /boot/initrd-2.6.17.mycustom | | root = /dev/hda6 |

(8)

--- 四角で囲っているところが追加した部分.label は PC を起動した際にど の kernel をブートするのかを選択する時に表示される名前.image はブート する kernel 本体を指してしている.append は kernel のブート時に kernel に 渡す引数で,root がこの linux の通常時にルートとして扱われるデバイスを 表している. 設定が終ったらその設定を反映させる ---%/sbin/lilo ---これでエラーがでなかったら再起動をする.起動したら label で指定した 項目が新しくでているのでそれを入力する.これで起動できれば kernel の再 構築は成功である. 最後に新しい kernel となっているかを確認する. } ---%uname -r 2.6.17-mycustom ---ちゃんと設定した kernel が動いている事が伺える.

1.2

考察

kernelを再構築するメリットには主にカーネルのアップデートや、kernel の設定を変更する事によってサポートされて (して) なかったデバイスドライ バが使用できるようになったり、また逆に必要の無いデバイスドライバの排 除、モジュール化などによってカーネルの起動を早めるといったことが上げ られる。この他にも今回の実験のようにシステムコールの追加やカーネルの デバッグなどの開発の作業の際にも kernel の 再構築は行われる.

今回,kernel を再構築した後,apparmor の起動が failed となった.このモ ジュールは SUSE 特有 (?) のセキュリティの機能らしく,どうやら kernel を 変更した際にその kernel には含まれていなかった模様.まだ試しては無いが ソースを持ってきて組み込めば大丈夫だと思われる.

(9)

2

Level2

open system callを処理する部分を見つけよ。Linux のシステムコールは、 1、アプリケーションが呼び出すライブラリ 2、ライブラリが呼び出す trap 3、trap を受け取り振り分ける部分 4、実際に kernel 内部で処理する部分 がある。それぞれ、どこにあるのか調べよ。

2.1

1,

アプリーケーションが呼び出すライブラリ

作成したアプリケーションがライブラリと結合されるのはリンカ (ld) が実 行され、実行ファイルが生成されるときである。 ライブラリ自体は ld がライブラリ参照に使うファイル(/usr/lib/libc.so)か ら/lib/libc.so.6 と /usr/lib/libc nonshared.a であることがわかる。

コメントで書かれているように、ほとんどのプログラムで使用される shared libraryは前者の libc.so.6、いくつかの機能で使用される static library は後 者の libc nonshared.a である。

/usr/lib/libc.so

---/* GNU ld script

   Use the shared librarys but some functions are only in    the staticc library. so try that secondarily. */ OUTPUT_FORMAT(elf32-i386) GROUP(/lib/libc.so.6 /usr/lib/libc_nonshared.a ) ---ライブラリに含まれる関数の中には、それ自体がさらにシステムコールを呼 び出しているものがある。これには、直接システムコールを呼び出すのに比 べて次のような利点がある。 1、より使いやすい形で関数を利用できる。 システムコールは非常に一般化された形で書いてあるので、様々な要求に対 応できるかわりに利用が難しい。ライブラリ関数ではそれを使いやすいもの にするために、用途を限定したいくつかの関数を作ってその中からシステム コールを呼び出している。

(10)

2、移植性が高まる ライブラリは複数のアーキテクチャやカーネルなどでも同一の関数を提供す るので、システムコールの実装が違う別カーネルや、アーキテクチャが全く 違う別ハード上でもソースを変更することなく、または最小限のソース変更 で移植できる。

2.2

ライブラリがよびだす trap

ライブラリ内でシステムコールを呼び出すには、レジスタに必要な引数を ストアしてアセンブラの sc 命令を実行する。(powerpc の場合。i386 は int 0x80命令) sc命令は、呼び出されたプログラムの直後のアドレスを格納し、0xc00 番地 (powerpc の場合。i386 は 0x80 番地)に格納されたシステムコールの割り込 みベクタに処理を移す。 —trap init— linux/arch/(アーキテクチャ名)/kernel/trap.c に記述されている、trap の 初期化(割り込みベクタのレジスタ配置)を行う部分。 ppcでは、割り込みベクタ自体が 0xc00 番地に格納されるよう、head.S に直 に記述してある(ppc はそのような記述が可能)ので、trap init は空のダミー 関数になっている。

2.3

trap

を受け取り振り分ける部分

0xc00番地の割り込みベクタでは、レジスタをカーネルスタックに退避し、 transfer to handlerを呼ぶ。

transfet to handlerから DoSyscall に処理が移る。DoSyscall はシステムコー ル番号からテーブルを参照し、実際のシステムコールを呼び出す。

テーブルはソースファイル中では linux/arch/(アーキテクチャ名)/kernel/misc.S の中にある・・・と様々な場所に書いてあったが、どうやらテーブルのみ別 に分離されたらしく、systbl.S というファイル(powerpc の場合。i386 では syscall table.S)に別にとられていた。

(11)

0xc00割込み(powerpc の場合) linux/arch/powerpc/kernel/head_32.S---.. . /* System call */ . = 0xc00 SystemCall: EXCEPTION_PROLOG EXC_XFER_EE_LITE(0xc00, DoSyscall)

/* Single step - not used on 601 */

EXCEPTION(0xd00, SingleStep, single_step_exception, EXC_XFER_STD) EXCEPTION(0xe00, Trap_0e, unknown_exception, EXC_XFER_EE)

.. . ---他アーキテクチャでは基本的に linux/arch/(アーキテクチャ名)/kernel/head.S というファイルに記述されている。 linux/arch/powrpc/kernel/entry_32.S---.. . /*

* Handle a system call. */ .stabs "arch/powerpc/kernel/",N_SO,0,0,0f .stabs "entry_32.S",N_SO,0,0,0f 0: _GLOBAL(DoSyscall) stw r0,THREAD+LAST_SYSCALL(r2) stw r3,ORIG_GPR3(r1) li r12,0 stw r12,RESULT(r1) lwz r11,_CCR(r1) /* Clear SO bit in CR */ rlwinm r11,r11,0,4,2 stw r11,_CCR(r1) #ifdef SHOW_SYSCALLS

(12)

bl do_show_syscall

#endif /* SHOW_SYSCALLS */

rlwinm r10,r1,0,0,(31-THREAD_SHIFT) /* current_thread_info() */ lwz r11,TI_FLAGS(r10) andi. r11,r11,_TIF_SYSCALL_T_OR_A bne-syscall_dotrace .. . ---他アーキテクチャでは基本的に linux/arch/(アーキテクチャ名)/kernel/entry.S というファイルに記述されている。

2.4

実際に kernel 内部で処理する部分

それぞれの関数の実体は、基本的に C 言語で書かれており、linux/fs/ 以下 や linux/kernel 以下など kernel ソース内に散在している。

2.5

Level2

考察

システムコールを呼び出すまでに必要なソース群は数回ジャンプしており、 結局いくつかのソースファイルを見て回ることになった。 機械語レベルに関わる箇所が多いためにアセンブラを使用している箇所が多 かったが、極端に複雑な記述はなかったように思える。 今回調査しきれなかったのは途中で出てきた transfer to handler。DoSyscall を呼び出すことまではわかったが、これ自身が何をしているものなのかは突 き止めることができなかった。powerpc に関するソース中でしか出てこない ので、アーキテクチャ固有の、必要な機能だとは思われる。 linux-2.6.16.18/Documentation/powerpc/cpu features.txt 内には、それらし き記述があるが、カーネルソースにはそれらしき記述が見つからなかった。 linux-2.6.16.18/Documentation/powerpc/cpu features.txtの一部 ---.. .

(13)

After detecting the processor type, the kernel patches out sections of code that shouldn’t be used by writing nop’s over it. Using cpufeatures requires just 2 macros (found in include/asm-ppc/cputable.h), as seen in head.S transfer_to_handler:

#ifdef CONFIG_ALTIVEC BEGIN_FTR_SECTION

mfspr r22,SPRN_VRSAVE /* if G4, save vrsave register value */ stw r22,THREAD_VRSAVE(r23)

END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) #endif /* CONFIG_ALTIVEC */

If CPU 0 supports Altivec, the code is left untouched. If it doesn’t, both instructions are replaced with nop’s.

.. .

---cpu features.txt の記述によれば、PowerPC G4 に搭載されたベクトル演算

ユニット AltiVec を使用する場合にのみ有効になるマクロ(それ以外の場合 は含まれる全命令が nop になる)らしいが、今回との関わりが見つけられな かった。

3

Level3

Linuxには、リモートデバッグという機能がある。gdb のリモートデバッ グに関して調べよ。 これは、二つのコンピュータ (VMware のような仮想コンピュータでも良 い) 接続して、片方から片方の kernel(などのプログラム) をデバッグする方 法である。これを動作させるためには、Linux のカーネルを作り直す必要が あると思われる。 リモートデバッグオプションを有効にし、実際に、動作させてみよ。 いくつかの system call をリモートデバッグのブレークポイントの対象に し、実際に止めて観察せよ。

(14)

3.1

gdb

とは

実行中のプログラムの内部で何が起こっているのか、プログラムがクラッ シュしたときに何が起こっていたのかを知るためのツールである。 実際のバグを発見できるようにするために 4 つのこと(さらに、これらを 支援するため¿に他のことも)行う。 • プログラムを、その動作に影響を与える可能性のある様々なことを指定 して起動する • 指定された条件が成立したときにプログラムを停止する • 停止したときにプログラムが何を行っていたかを調べる • プログラムの内部を変更することによって、 1 つのバグの影響を試験 的に修正して、ほかのバグについて調べる gdbでは C, C++, Modula-2 などで書かれたプログラムのデバッグが行な える。

3.2

リモートデバッグとは

プログラムを実行中のマシンとは別のマシンからデバッグを行うことであ る。 オペレーティング・システムのカーネルのデバッグや、フル機能を持つデ バッガを実行するのに十分な機能を持つ汎用的なオペレーティング・システ ムを持たない小規模なシステムでのデバッグを行う場合にリモートデバッグ を行う。 1台はデバッグ環境のためのホストで, すべてのソースとすべてのシンボル を含んだバイナリのコピーを持っている。 もう 1 台はターゲットマシンで, 同一のプログラムのコピー (ただしデバッグ情報は 取り除いてあるもの) を 単に実行するためのものが必要になる。 通常は2台のマシン間での通信を行うためにはスタブをプログラムソース 含める必要がある。しかし、gdbserver を使用することによってスタブをリン クすることなくリモートでバッグができる。

(15)

3.3

カーネルのデバッグ

カーネルをデバッグするにはコンフィグの ”CONFIG DEBUG INFO ”を 有効にし、再コンパイルを行う。

再コンパイルしたカーネルを利用し以下の用に gdb を実行すると、

---prompt> gdb vmlinux

GNU gdb Red Hat Linux (6.3.0.0-1.122rh) Copyright 2004 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions.

There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "ppc-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".

(gdb)

---のように gdb のプロンプト画面に移る。

ここでシステムコール sys open で breakpoint を作成してみる

---(gdb) break sys_open

Breakpoint 1 at 0xc0080bec: file fs/open.c, line 1107.

---そして実行してみる。

---(gdb) run

Starting program: /usr/src/redhat/SOURCES/linux-2.6.17.debug/vmlinux Program terminated with signal SIGKILL, Killed.

The program no longer exists.

You can’t do that without a process to debug.

---すると sys open でブレイクする前にプログラムが止まってしまった。

3.4

カーネルをリモートデバッグしてみる

ターゲットとなるマシンで gdbserver を実行する。

ここでは Fedora Core 5 が動いている iBook で gdbserver を実行する。

---prompt> gdbserver host:9321 vmlinux

[root@nw0436 linux-2.6.17.debug]# gdbserver host:9321 vmlinux Process vmlinux created; pid = 31896

Child terminated with signal = 9 Listening on port 9321

(16)

---子プロセスが終了したとメッセージが出たが、そのまま作業を続けること にする。次にホストとなるマシンで gdb 実行し、リモートデバッグをする。 SUSE Linuxが動いている別の iBook から gdb を実行し、ターゲットに接続 してみた。

---(gdb) target remote 133.13.54.206:9321 \\

Remote debugging using 133.13.54.206:9321 \\

Remote communication error: 接続が相手からリセットされました。

---と、相手側から接続が遮断された。

3.5

考察

ローカルでカーネルで gdb を実行し sys open でブレイクポイントを作成 したが、sys open が呼び出される前にプログラムが終了した原因は、カーネ ルをただは実行しただけではシステムコール sys open は呼び出されない為だ と思われる。なんらかの操作でシステムコールを呼び出せる方法を見つけれ ばうまく sys open でブレイクしてくれるものと思われる。 リモートデバッグで接続されなかったはの gdbserver を実行した段階で、子 プロセスが終了しているのに問題があると思われる。kernel のリモートデバッ グの方法として kgdb を使用した方法がいくつか紹介さ、「gdb -k」とすると kgdbモードになるらしいが、今回使用した Fedora と SUSE には gdb に「-k」 が存在しなかった。

4

Level4

新しいシステムコールを提案し、実装してみよ。 kernelの config の過程で、使用するシステムコールの番号等を指定するファ イルがある。これを使うと、 1. アプリケーションが呼び出すライブラリ (libc) 2. ライブラリが呼び出す trap 3. trapを受け取り振り分ける部分 4. 実際に kernel 内部で処理する部分 のうち、3番目は、自動的に生成される。 もちろん、テストするアプリケーションも必要である。

(17)

アプリケーションからは、trap をアセンブラで直接起動しても良いし、libc を修正して、普通の C の関数に見えるようにしても良い。

4.1

その1「システムコール番号対応表の修正」

カーネル内部では、システムコールはその関数名に対応付けされた番号で 管理されている。カーネルソース中では linux/include/asm /unistd.h このファイルにシステムコールと番号群の対応がマクロの形で記述されてい るので追加するシステムコールに関して追加する。 追加するにはシステムコールの空き番号に #define NR (システムコール名) 空き番号 という行を追加する。 システムコール対応の最後にある syscalls というのはシステムコールの総数 を表すだけで実装されていない関数なので、それを除いたものが空き番号に なる。 linux-2.6.16.18/include/asm(-powerpc)/unistd.hの一部 ---.. . #define __NR_restart_syscall 0 #define __NR_exit 1 #define __NR_fork 2 #define __NR_read 3 #define __NR_write 4 #define __NR_open 5 .. . #define __NR_spu_run 278 #define __NR_spu_create 279 #define __NR_pselect6 280 #define __NR_ppoll 281 #define __NR_unshare 282 #define __NR_syscalls 283

(18)

.. . ---これに test というシステムコールを追加する場合、以下のようになる。 ---.. . #define __NR_spu_create 279 #define __NR_pselect6 280 #define __NR_ppoll 281 #define __NR_unshare 282 /* added systemcall */ #define __NR_test 283 #define __NR_syscalls 284 .. .

---4.2

その2「呼び出しテーブルの修正」

メモリ上では全システムコール(へのジャンプ用アドレス)は特定の箇所 に順番通りに固まっている。それの記述は linux2.4.16.18/arch/powerpc/kernel/systbl.S 内にある。このファイルはアセンブラによるシステムコールのテーブルになっ ており、テーブルの記述自体はマクロを使っている。i386 アーキテクチャ側 のテーブル(linux2.4.16.18/arch/i386/kernel/syscall table.S)はそうはなっ ていないが、これは powerpc が G5 から 64 ビットプロセッサを採用したため である。マクロを利用することで G4 以前の powerpc 用と G5 用を、ある程 度共通のソースを用いて記述している。 システムコールを追加する場合に、複数あるマクロのどれかを利用すること になるが、G4 以前の(32 ビットプロセッサ時代の)powerpc であればどれを 利用しても大きな差はない。それぞれが何に対応するのかは調べきれなかっ たが、ソースを読んだ感じでは

(19)

CAMPAT SYS -> ファイルシステムや時間のカウントを使用するシステム コールに使用されている。

PPC SYS -> 不明。fork 系、clone、newuname、rtas が該当する。プロセス 作成に関係するものか。

OLDSYS -> stat 系、debug setcontext が該当する。情報取得システムコー ル SYS32ONLY -> 不明。sigaction、sigsuspend、sigreturn、mmap2、trun-cate64、ftruncate64、sendfile64 が該当する。名前からして 32 ビットプロ セッサのみで使用できるシステムコールだと思われる。 SYSX -> 32 ビットプロセッサと 64 ビットプロセッサで全く別の関数が用 意されているもの。これのみ引数が3つあり、それぞれに対応するシステム コール名を入れる。 となっている。32 ビットプロセッサではこの内 SYSCALL,COMPAT SYS, OLDSYS,SYS32ONLYの3つが最終的に同じ文に展開される。 PPC SYSと SYSX を利用する理由がなければ、 SYSCAL(システムコール名) とすればよい。 linux-2.4.16.18/arch/powerpc/kernel/systbl.S ---.. . SYSCALL(inotify_add_watch) SYSCALL(inotify_rm_watch) SYSCALL(spu_run) SYSCALL(spu_create) COMPAT_SYS(pselect6) COMPAT_SYS(ppoll) SYSCALL(unshare) ---これに test という名前のシステムコールを追加する場合、以下のようになる。 ファイルの最後には改行を入れておく。なくてもコンパイルに問題はないが、 アセンブラは警告を吐く。

(20)

---.. . SYSCALL(inotify_add_watch) SYSCALL(inotify_rm_watch) SYSCALL(spu_run) SYSCALL(spu_create) COMPAT_SYS(pselect6) COMPAT_SYS(ppoll) SYSCALL(unshare) SYSCALL(test)

---4.3

その3「システムコール本体の実装」

linux/kernel/sys.c に実際のシステムコール本体を記述する。形式は 関数型 sys 名前 (引数) というように、普通に関数を作る形でよい。ただ、実際にはソース中の他の システムコールは asmlinkage関数型 sys 名前 (引数) というように実装されている。asmlinkage はマクロであり、linux-2.6.16.18/include/linux/linkage.h に定義されている。 linux-2.6.16.18/include/linux/linkage.hの一部 ---.. . #ifdef __cplusplus

#define CPP_ASMLINKAGE extern "C" #else

#define CPP_ASMLINKAGE #endif

(21)

#define asmlinkage CPP_ASMLINKAGE #endif .. . ---これによれば、asmlinkage は C++用のオプションで、C 言語で書かれている 関数を考慮して、C++の場合は関数に C 言語互換を持たせるための [extern ”C”]を追加する、というもののようである。 Cコンパイラを使用するのであれば記述がなくても問題ないが、関数を C 言 語で記述し、C++コンパイラを使う可能性があるなら必要なオプションであ る。 今回追加した test システムコールは以下のようになる。

/* added system calls */---int sys_test(char *mess, */---int len)

{

char *kmess;

if ((kmess = vmalloc(len+1))==0) return -ENOMEM;

copy_from_user(kmess, mess, len); kmess[len] = ’\0’; console_print(kmess); vfree(kmess); return 0; } ---このシステムコールは、引数として文字列へのアドレスとその長さを受け取 る。受け取った文字列を console print()(実体は printk)を利用して出力す るものである。(出力先は普通カーネルログバッファ中になる)

ここまで終了したら、カーネルをコンパイルする。これより後は、コンパイ ルしたカーネルを使用する。

(22)

4.4

その4「システムコールを利用するプログラムの作成」

システムコールを利用する関数を別に自作する。 システムコールを動かすには、syscall マクロを利用できる。 システムコールの対応表を修正した [unistd.h] と [errno.h] をインクルードし、 ヘッダ部分で syscall(引数の数)(型、システムコール名、引数・・・) とすれば、プログラム中で システムコール名(引数) のように普通の関数のように利用できる。 syscallマクロは linux-2.6.16.18/include/asm-powerpc/unistd.h 内に定義さ れており、システムコールを呼び出す関数へと展開してくれる。 今回の例であれば、システムコール名が [test]、引数が 2 つなのでシステム コールを呼び出すプログラムは以下のようになる。 ---#include<stdio.h> #include</usr/src/linux-2.6.16.18/include/asm-powerpc/unistd.h> #include<errno.h> #include<string.h>

_syscall2(int, test, char *, mess, int ,len);

int main(){

char message[] = "Print straight to console\n";

if (test(message, strlen(message))==-1)

printf("Error occurred in printcons() call\n");

}

---また、今回は使用していないが、アセンブラを利用する方法もある。

(23)

意されており、引数を対応するレジスタに格納して実行することでシステム コールを利用することができる。レジスタ番号と引数の対応は以下の通り r0システムコール番号 r3第1引数 r4第2引数 r5第3引数 r6第4引数 r7第5引数 呼び出しの例---li r0, 1 //システムコール番号、1 は exit システムコール li r3, 0 //第1引数。exit システムコールは引数を1つとる sc //システムコール実行 ---i386であれば、命令は [int 0x80] となる。i386 でのレジスタと引数の対応は 以下の通り eaxシステムコール番号 ebx第1引数 ecx第2引数 edx第3引数 esi第4引数 edi第5引数

4.5

その5「実行結果」

プログラムをコンパイルし実行すると、標準出力には出力されないが dmseg コマンドでみることのできるカーネルログに以下のような出力がみられる。 dmsegコマンドの出力の一部 ---.. .

(24)

74

SFW2-INext-DROP-DEFLT IN=eth0 OUT= MAC=01:00:5e:00:00:fb:00:0a: SFW2-INext-DROP-DEFLT IN=eth0 OUT= MAC=01:00:5e:00:00:fb:00:0a: Print straight to console

---この「Print straight to console」というのが今回のシステムコールからの出 力である。よって、追加したシステムコールは正常に動作している。

4.6

Level4

考察

システムコールに関してはカーネル中で多数のマクロが用意されており、 また、実装形式自体がわかりやすいものになっている。 このおかげで、カーネルにシステムコールを追加する作業は非常に容易いも のになっている。 この部分は、常に更新されているカーネルソースの拡張性の高さ、ソースの 簡潔さが出ている部分だろう。

5

Level5

何か、OS 内部の情報を取得するシステムコールを作成せよ。ps などでと れる情報でもよいし、そうでないものでも良い。

5.1

実装手順

実装手順は、Level4 で行ったシステムコールの実装と全く同じものを使う。 実装するシステムコールは、syscalltotal という名前で Level4 の test の後(284 番目)に追加し、実装されているシステムコールの総数を int 型で返す機能 を持つとする。 各ソースの修正箇所は以下の通りである。 システムコール番号対応表修正 linux-2.6.16.18/kernel/sys.c---.

(25)

.

#define __NR_pselect6 280 #define __NR_ppoll 281 #define __NR_unshare 282

/* added system calls */ #define __NR_test 283 #define __NR_syscalltotal 284 #define __NR_syscalls 285 . . . ---システムコールテーブル修正 linux-2.6.16.18/arch/powerpc/kernel/systbl.S---. . . SYSCALL(inotify_rm_watch) SYSCALL(spu_run) SYSCALL(spu_create) COMPAT_SYS(pselect6) COMPAT_SYS(ppoll) SYSCALL(unshare) SYSCALL(test) SYSCALL(syscalltotal) ---\end{verbaatim} \noindent システムコール実装 \begin{verbatim} linux-2.6.16.18/kernel/sys.c---. . . int sys_syscalltotal() {

(26)

return __NR_syscalls; } ---システムコール呼び出しプログラム 実行要プログラム「sct.c」---#include<stdio.h> #include</usr/src/linux-2.6.16.18/include/asm-powerpc/unistd.h> #include<errno.h> #include<string.h> _syscall0(int, syscalltotal); int main(){

printf("system call total = %d\n",syscalltotal());

}

---5.2

実行結果

作成した sct.c をコンパイルし、実行すると以下のような出力が得られる。 ---unix_kernel/syscalltest> ./sct

system call total = 286 unix_kernel/syscalltest> ---今回のシステムコールの追加で元々284 つあったシステムコールを 286 個 に増やした。なので、この出力は正しく、システムコールは正常に動作して いると考えられる。

5.3

level5

考察

osの内部情報としてはカレントプロセスのプロセス ID やリーダープロセ スの ID をとることもできる。だが、他のシステムコールでその機能は実装さ れているため(getpid など)、今回のようなシステムコールの実装になった。

(27)

6

level6

ヌル・デバイスドライバ (何もしないデバイスドライバ) を作成せよ. デバイスは,/dev にあるのでノードによってアクセスされる.ノード番号 を指定するファイルが,システムコールと同じようにあるので,それを調査 せよ.

6.1

デバイスドライバとは?

デバイスドライバとは,パソコンに接続されている周辺装置を OS によっ て制御するために用意されたプログラムである。OS からの要求を特定の周 辺機器コントローラが理解できるよう翻訳することによって,OS とハード ウェアを結ぶものである. Linuxでは,デバイスをファイルとして扱える.よって,ユーザはファイ ル操作命令でデバイスを操作できる.ファイル操作命令のような一般的な命 令に対して各々のデバイスドライバは,その命令に対応した独自の関数で処 理している. また,デバイスドライバには以下の種類がある. • ブロック型ドライバ • キャラクタ型ドライバ • ネットワークインターフェイスデバイスドライバ デバイスドライバはカーネルと一体となって動作するので,libc は使えない.

6.2

プログラム・ソース

6.2.1 宣言部 これらは,modinfo というコマンドでそこに記述した情報が表示されるもの で,プログラムの説明である.MODULE LICENSE を記述しないと insmod のときに警告が出る. ---14 MODULE_LICENSE("GPL"); 15 MODULE_AUTHOR(DRIVER_AUTHOR); 16 MODULE_DESCRIPTION(DRIVER_DESC);

(28)

---6.2.2 file operations構造体

---25 struct file_operations hello_fops =

26 { 27 .owner = THIS_MODULE 28 }; ---ユーザからのファイル操作命令をデバイスドライバ独自の命令 (関数) と結 びつける役割がある.つまり,関数へのポインタを集めたテーブルのような ものである.例として,ユーザがデバイスファイルを open 命令で開こうと すると,file operations 構造体に登録された .open = xxx という xxx 関数が 呼び出される.今回はヌル・デバイスドライバなので,特に設定していない.

6.2.3 static int hello init(void)

insmodされたときに呼び出される関数.ここで,メジャー番号,マイナー 番号を割り当てて,デバイスの登録を行う.つまり,モジュールの初期化を 行っている.

---31 static int hello_init(void)

32 {         ・         ・ ---MKDEVでデバイス番号の作成を行っている.デバイス番号はメジャー番 号 12 ビット + マイナー番号 20 ビットの 32 ビットで表される.次に regis-ter chrdev region 関数でデバイス番号の割当を行い,成功したら 0 を返す. もし,メジャー番号を 0 にして動的割当にするなら,alloc chrdev region 関 数を使う.

---36 dev_number=MKDEV(major,minor); /*デバイス番号の生成*/

37 if(MAJOR(dev_number)){

38 result = register_chrdev_region(dev_number, DEVICE_INSTANCE, devname); 39 } else

40 result = alloc_chrdev_region(&dev_number, 0, DEVICE_INSTANCE, devname);

---cdev alloc 関数で ---cdev 構造体のメモリ確保を行っている.---cdev 構造体は キャラクタデバイスを表す構造体で,これがカーネルに登録される.失敗す ることもあるのでそのときは,先ほど確保したデバイス番号を開放する.

opsは file operations 構造体をさすポインタで,これでデバイスドライバが ファイル操作命令を受け取ることができる.kobject set name は名前の通り, kobjectという基本的なデータ型に名前を付けている.また,cdev の owner

(29)

フィールドも設定しなければならない.これはアンロードの時のモジュール の保護に使われる.

---49 device = cdev_alloc(); /* cdev構造体の領域の確保*/

        ・ /*cdev構造体の初期化*/ 55 device->ops = &fops; 56 kobject_set_name(&device->kobj, "simple_cdev%d",dev_number); 57 device->owner=THIS_MODULE; ---cdev add でカーネルへデバイスの登録を行う. ---59 result = cdev_add(device, dev_number, DEVICE_INSTANCE); /*デバイスの登録 */         ・ 65 return 0; 66 } ---また,75 行目にある ---75 module_init(hello_init); ---は,マクロでこれで init module(マクロを使わなければこの名前を使わな ければならない) に好きな名前が付けられる.

6.2.4 static int hello exit(void)

ここは,rmmod が実行されたときに呼び出される関数である.

cdev delでカーネルからデバイスドライバを取り除く.unregister chrdev region() でデバイス番号を開放する. ---71 cdev_del(device); 72 unregister_chrdev_region(dev_number, ); ---また,76 行目にある ---76 module_exit(hello_exit); ---は,マクロでこれで clean module(マクロを指定しなければこの名前にし ないといけない) に好きな名前が付けられる.

(30)

6.3

デバイスドライバの追加手順

まず,kernel のコンパイル時の config で,「Loadable module support」項 目の「Module unloading」を有効にしないと,rmmod できないのでこれを 有効にする. デバイスドライバプログラムがあるディレクリに以下の Makefile を作成 する. ---Makefile の中身---# xxx.o はデバイスドライバのプログラムのオブジェクトファイル. # KERNELRELEASEにはカーネルのバージョン名 (2.6.xx-xx) が入るが,それは # ここで定義されていないので,最初は必ず else 以降の文が実行される.ここが # 真となるのは,再帰的にこの Makefile が呼び出された時である. ifneq ($(KERNELRELEASE),) obj-m := xxx.o # 最初,make が実行されると以下が実行される.その後,make の再帰的な実行に # よって,上が実行される. else

KERNELDIR ?= /lib/modules/$(shell unname -r)/build PWD := S(shell pwd)

default:

$(MAKE) -C $(KERNELDIR) M=$(PWD) modules endif ---あとは,そのディレクリで make をするとコンパイルできる. ---%make ---コンパイルすると xxx.ko というモジュールオブジェクトファイルが作成さ れる.そして,スーパーユーザになって,以下のコマンドを実行してモジュー ルをロードする. ---%/sbin/insmod xxx.ko ---ロードしたモジュールは以下のコマンドで確認できる./proc/devices は ロードしているモジュール名とモジュール番号が確認できる. ---%cat /proc/devices Character devices:    ・    ・ 1000 hello    ・

(31)

---ロードしたモジュールをアンロードするには以下のコマンドを実行する. すると,アンロードしたことが確認できる. ---%/sbin/rmmod xxx.ko %cat /proc/devices Character devices:    ・    ・ ---ロードしたモジュールを利用するには,デバイスファイルを作成しなけれ ばならない.デバイスファイルを消すには,普通に rm コマンドを使えば良 い.コマンドは以下の通りである.rm はモジュールがアンロードしてから行 うと良い. ---%/bin/mknod /dev/xxx c 1000 0 %ls -l /dev/xxx %rm /dev/xxx %ls -l /dev/xxx ---私たちのプログラムは printk を用いて,文字列を出力していた.これは, 以下のコマンドで確認できる.dmesg はリングバッファの表示ができる. ---%dmesg | tail -3 hello, world! Success register good bye!bye!

---6.4

ノード番号を指定するファイル

メジャー番号は,/usr/src/linux/inclue/linux/major.h にある. ロードされているモジュールは,/proc/devices に記述されている.また, /usr/src/linux/Documentation/devices.txt にはメジャー番号やマイナー番 号の割当が記述されている.

6.5

考察

カーネルのバージョン 2.6 からデバイスドライバの登録方法が新しくなっ ているが,2.4 でも使われていた register chrdev() でも登録できるようになっ ている. また,sysfs という仮想ファイルシステムにはデバイスドライバ情報、ド ライバクラス情報、バス情報があり,sysfs をマウントするとその情報が参照 できる.起動時にマウントされていなければ,以下のようにしてマウントで きる.

(32)

---%mount -t sysfs sysfs 適当なディレクトリ

---また,2.6 ではメジャー番号が,今まで 8 ビットで割り当てていたものが 12ビットとなり,1∼4095 まで割り当てることができる.0 は動的割当とな る (今まで通り).マイナー番号も 20 ビットと拡張されている.

7

参考文献

参考文献

[1] Debugging with GDB - Table of Contents

http://www.asahi-net.or.jp/ ∼wg5k-ickw/html/online/gdb-4.18/gdb toc.html [2] ファイヤープロジェクト http://www.h7.dion.ne.jp/∼matsu/index.html GDB http://www.h7.dion.ne.jp/∼matsu/feature/gdb/index.html [3] FreeBSDハンドブック http://docs.freebsd.org/doc/2.2.8-RELEASE/usr/share/doc/ja/handbook/handbook.html リモート GDB を使ったオンラインカーネルデバッグ http://docs.freebsd.org/doc/2.2.8-RELEASE/usr/share/doc/ja/handbook/handbook297.html [4] 「2005 年後期 UNIX 実験」 http://www.ie.u-ryukyu.ac.jp/∼j03057/unix/index.html [5] 「linuxchix.org」 http://www.linuxdhix.org [6] 「68user’s page」 http://x68000.q-e-d.net/∼68user/ [7] 「Jun’s Homepage」 http://www.nk.rim.or.jp/∼jun/index.html [8] 「patch:powerpc:wire up the *at system calls」

http://patchwork.ozlabs.org/linuxppc64/patch?id=4378 [9] Linux-2.6デバイスドライバ - Si-Linux Wiki

(33)

http://www.si-linux.co.jp/wiki/silinux/index.php? Linux-2.6%A5%C7%A5%D0%A5%A4%A5%B9%A5%C9%A5%E9%A5%A4%A5%D0 [10] 制御系技術者のための Linux 活用技術 http://w3-pre.apc.ehdo.go.jp/22116/ [11] [Linuxウォッチ] 第 2 回 次期カーネル 2.6 構築時の注意 http://itpro.nikkeibp.co.jp/members/SI/oss/20031024/3/ [12] iTmediaエンタープライズ:PD 思考法の基礎と情報収集 (その 2) http://www.itmedia.co.jp/enterprise/articles/0512/15/news008.html [13] WULS/040628デバイスドライバ作成その 1 http://www.wakayama-u.ac.jp/∼miw/linux/pukiwiki/pukiwiki.php?WULS%2F040628 [14] tos052 Writing our first character device

http://www.cs.bgu.ac.il/∼tos052/ [15] CodeDump: A Character Device

http://www.de-brauwer.be/wiki/wikka.php?wakka=CharacterDevice [16] LWN:Driver porting:Char devices and large dev t

http://lwn.net/Articles/49684/

[17] LWN:Linux Device Drivers, Third Edition http://lwn.net/Kernel/LDD3/

参照

関連したドキュメント

In particular, Proposition 2.1 tells you the size of a maximal collection of disjoint separating curves on S , as there is always a subgroup of rank rkK = rkI generated by Dehn

Since the data measurement work in the Lamb wave-based damage detection is not time consuming, it is reasonable that the density function should be estimated by using robust

BOUNDARY INVARIANTS AND THE BERGMAN KERNEL 153 defining function r = r F , which was constructed in [F2] as a smooth approx- imate solution to the (complex) Monge-Amp` ere

Abstract. Recently, the Riemann problem in the interior domain of a smooth Jordan curve was solved by transforming its boundary condition to a Fredholm integral equation of the

We construct a kernel which, when added to the Bergman kernel, eliminates all such poles, and in this way we successfully remove the obstruction to regularity of the Bergman

In Section 4, we establish parabolic Harnack principle and the two-sided estimates for Green functions of the finite range jump processes as well as H¨ older continuity of

Keywords and phrases: symmetric jump process, metric measure space, heat kernel estimate, stability, Dirichlet form, cut-o↵ Sobolev inequality, capacity, Faber-Krahn inequality,

Keywords: symmetric Markov process, pseudo-differential operator, diffusion process, jump process, L´evy system, hitting probability, parabolic function, a priori H¨older