システム・プログラム 電子・情報工学系 新城 靖 <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/syspro-2001/2001-04-16
あるいは、次のページから手繰っていくこともできます。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/
http://www.is.tsukuba.ac.jp/~yas/index-j.html
この講義では、Unix の API (Application Programming Interface) を利用し てプログラムを作成する。API は、次の3つに分類される。
この講義で扱う Unix の API は、他のものと比較すると簡単になっている。 複雑なものには、Xウインドウ・システム、MS-Windows (Win32 API) などが ある。
カーネル ( kernel ) とは、直接ハードウェアを制御する、オペレーティング・システムの中心部 である。たとえば、ハードディスクやキーボードとの入出力を行ったり、ネットワー クを制御したりする。
UNIXのカーネルは、ファイル、プロセス、プロセス間通信といったシステムの 基本的な機能を提供する。具体的には、ファイルのアクセス権の確認、プロ セスの保護、CPU資源のスケジューリング、パイプ、ソケット、 TCP/IPによるプロセス間通信機能等がある。
シェル ( shell ) とは、カーネルを取り囲んでいる殻という意味である。シェルは、利用者と対話 を行い、利用者からの入力を解析し、それにしたがってプログラム(コマンド) を実行します。シェルのことを、 コマンド・インタプリタ (command interpreter ) と呼ぶこともある。
実験では、シェルを作ることで、システムの仕組みを学ぶ。
標準入力、標準出力、標準エラー、パイプによる結合、入出力の切り換えは、 シェルの仕事である。
コマンド ( command ) とは、利用者がシェルに与える指令である。UNIXのシェルは、指令を受け取っ ても、具体的な仕事をほとんど何もしない。cd, exit, set, umask のような、 自分自身の状態を変えたりプログラムの実行環境を変えるものを除いて、全部 外部のプログラムを実行する。シェル自身が実行するコマンドを 組込みコマンド ( built-in command )、 そうでないものを 外部コマンド ( ) とう。
UNIXでは、ディレクトリの一覧表、ファイルのコピー、ファイルの画面への表 示といった基本的な仕事も、それぞれ ls, cp, cat といった名前の外部コマ ンドにより実行される。
UNIXを使って仕事をする人(利用者、ユーザ)にとって大事なものは、シェル とコマンドである。UNIXの上でプログラムを書く人 ( プログラマ ) プログラマにとって大事なことは、 システム・コール ( system call ) と ライブラリ ( library ) である。
システム・コールというのは、カーネル(システム)の機能を呼出すことである。 たとえば、ファイルの入力出力でいうと、単純な読込み read() や、単純な書 込み write() がシステム・コールである。システム・コールをより使いやすい 形にしたものが、ライブラリである。たとえば、ファイルの内容を1行読み込む fgets()や、書式付きの出力を行うprintf() などがライブラリになっている。
システム・コールもライブラリも、C言語からは、両方とも関数呼出しの形で 使えるようになっていて、一見、区別がつかない。ただしマニュアルを見ると、 システム・コールは2章、ライブラリは、3章に入っている。man コマンドで 見た時に、最初の行に"READ(2)"のように、"(2)" となっていれば、システム・ コール、"PRINTF(3)" のように、"(3)" になっていれば、ライブラリである。
システム・コールは、トラップ命令(trap instruction)を含んでいる。これ は、カーネルに制御を移す働きがある。この部分は、アセンブリ言語で書かれ ている。ライブラリ関数は、アセンブリ言語で書かかれているものもあるが、 大部分はC言語で書かれている。
システム・コールとライブラリの集合を API (Application Programming Interface) という。 たとえC言語で書かれていても、API が違うとコンパイルして機械語に変換す ることはできても、実行できない。
ライブラリやシステム・コールは、コンパイルされてオブジェクト・コードの 形で保存されている。これらは、実行形式を作る時に 抜き出され、アプリケーション・プログラマから作成した オブジェクト・コードと結合される。この操作を、 リンク(link) ( リンケージ・エディット(linkage edit)、 結合編集 ) という。
リンクには、静的リンクと動的リンクの2種類がある。 静的リンク(static link) では、プログラム実行する前(実行形式を作成する時点)で、ライブラリ関数の 呼出しをすべて機械語の手続き呼び出し命令にしたり、データへの参照を絶対 番地に変換する。 動的リンク(dynamiclink) では、プログラム実行中に必要に応じてリンク処理を行う。
動的リンクを行う時には、しばしば 共有ライブラリ(shared library) を使う。これは、複数のプロセスで共通に使うライブラリを動的リンクで共有 するものである。動的リンクを使うことで、実行時の環境に応じてプログラム の動きを変えることができる。この機能を使って、たとえば、古いバージョン のシステムでコンパイルされた実行形式をバージョンのシステムで動作させる ことができる。また、共有ライブラリを使うと、1つひとつの実行形式が小さ くなるので、メモリの節約にななる。
デーモン ( daemon ) とは、本来オペレーティング・システムの一部であるが、他のプログラムと 同じようにプロセスとして活動しているプログラムである。たとえば、プリント・ デーモンは、カーネルと同じように、いろいろな利用者からの仕事(プリント) を請け負う。デーモンというのはUNIXの言葉で、一般的には、 サーバ・プロセス ( server process ) とう。これは、サービスを提供するプロセスを意味する。
最近では、 GUI(Graphical User Interface) を提供するコンピュータが普通である。GUIを提供するための中心的なシス テムであるウィンドウ・システムである。ウィンドウ・システムも、シェルに 並んでコンピュータの利用者に対する見え方を決定する重要な要素である。
ウィンドウ・システムは、オペレーティング・システムとは独立した存在とし て扱わることもありますが、目的や役割を考えるとオペレーティング・システ ムと共通している。 O2 で動いているウィンドウ・システムは、Xウィンドウである。Xウィンド ウでは、モニタやキーボードを管理するサーバ・プロセスとそれらを利用する クライアント・プロセスに分かれている。プロセスとは、一言で言えば、プログラムの実行時のイメージ。ここで、 プ ログラムというのは、CPUが実行できる機械命令の集まり(実行形式、ロー ド・モジュール)のことである。
1つのプログラムを同時に2個動かすことを考えるとプログラムとプロセスの 違いがはっきりする。プロセス(process)は、プロセッサ(processor,CPU)と関 係ある。
プロセスには、「利用者の代理」として計算機の中に入り込むものという側面 がある。
プロセスの操作
UNIXでプロセスを観察するには、ps コマンドを使う。
---------------------------------------------------------------------- % psPID TTY TIME CMD 5815 ttyq1 0:00 tcsh 5830 ttyq1 0:00 emacs 5841 ttyq1 0:00 ps % ps -f
UID PID PPID C STIME TTY TIME CMD yas 5815 5814 1 21:40:00 ttyq1 0:00 -tcsh yas 5830 5815 0 21:41:25 ttyq1 0:00 mule -nw yas 5842 5815 3 21:42:20 ttyq1 0:00 ps -f % ps -u $USER
PID TTY TIME CMD 5838 pts/2 0:00 tcsh 5815 pts/1 0:00 tcsh 5830 pts/1 0:00 emacs 5843 pts/1 0:00 ps %
----------------------------------------------------------------------
図2 ソース・プログラムから実行形式まで
cc -c
で実行されるプログラム
cpp(the C language preprocessor)
ccom(C Compiler)
as(Assembler)
図3 cc -c が実行するプログラム
コンパイルの観察。---------------------------------------------------------------------- % nl -ba today.c1 #define TODAY "Tuesday" 2 main() 3 { 4 printf("Today is %s.\n",TODAY ); 5 } % cc today.c
% ./a.out
Today is Tuesday. %
----------------------------------------------------------------------
---------------------------------------------------------------------- % cc -v today.c/usr/lib/cfe -D_MIPS_FPSET=16 -D_MIPS_ISA=2 -D_ABIO32=1 -D_MIPS_SIM=_ABIO32 -D_M IPS_SZINT=32 -D_MIPS_SZLONG=32 -D_MIPS_SZPTR=32 -D__EXTENSIONS__ -DLANGUAGE_C -D _LANGUAGE_C -D__INLINE_INTRINSICS -Dsgi -D__sgi -Dunix -Dmips -Dhost_mips -D__un ix -D__host_mips -D_SVR4_SOURCE -D_MODERN_C -D_SGI_SOURCE -D_PIC -D__DSO__ -DSYS TYPE_SVR4 -D_SYSTYPE_SVR4 -D_LONGLONG -D__mips=2 -I -D_MIPSEB -DMIPSEB -D__STDC_ _=1 -I/usr/include today.c -Xv -D_CFE -Amachine(mips) -Asystem(unix) -call_share d -G 0 -std -XS/tmp/ctmsta001TP -mips2 -EB -Xg0 -O1 > /tmp/ctmfa001TP cfe: main /usr/lib/cfe phase time: 0.03u 0.04s 0:00.1 100% /usr/lib/ugen -v -G 0 -pic2 -mips2 -EB -g0 -O1 /tmp/ctmfa001TP -o /tmp/ctmca001T P -t /tmp/ctmsta001TP -temp /tmp/ctmgta001TP ugen: main /usr/lib/ugen phase time: 0.01u 0.04s 0:00.1 100% /usr/lib/as1 -t5_ll_sc_bug -elf -pic2 -v -G 0 -p0 -mips2 -EB -g0 -O1 /tmp/ctmca0 01TP -o today.o -t /tmp/ctmsta001TP as1: main /usr/lib/as1 phase time: 0.00u 0.02s 0:00.1 25% /usr/lib/ld -elf -_SYSTYPE_SVR4 -require_dynamic_link _rld_new_interface -no_unr esolved -Wx,-G 0 -mips2 -call_shared -g0 -KPIC -L/usr/lib/ -nocount /usr/lib/crt 1.o -count today.o -nocount -lc /usr/lib/crtn.o /usr/lib/ld phase time: 0.02u 0.04s 0:00.1 67% %
----------------------------------------------------------------------
---------------------------------------------------------------------- % cc -E today.c > today.i% nl -ba today.i
1 # 1 "today.c" 2 3 main() 4 { 5 printf("Today is %s.\n","Tuesday" ); 6 } %
----------------------------------------------------------------------
---------------------------------------------------------------------- % nl -ba today.s1 .verstamp 7 20 2 .option pic2 3 .rdata 4 .align 2 5 .align 0 6 $$5: 7 .ascii "Today is %s.\X0A\X00" 8 .rdata 9 .align 2 10 .align 0 11 $$6: 12 .ascii "Tuesday\X00" 13 .text 14 .align 2 15 .file 2 "today.c" 16 .globl main 17 .loc 2 3 18 # 1 #define TODAY "Tuesday" 19 # 2 main() 20 # 3 { 21 .ent main 2 22 main: 23 .option O1 24 .set noreorder 25 .cpload $25 26 .set reorder 27 subu $sp, 32 28 sw $31, 28($sp) 29 .cprestore 24 30 .mask 0x90000000, -4 31 .frame $sp, 32, $31 32 .loc 2 3 33 .loc 2 4 34 # 4 printf("Today is %s.\n",TODAY ); 35 la $4, $$5 36 la $5, $$6 37 .livereg 0x0C00000E,0x00000000 38 jal printf 39 .loc 2 5 40 # 5 } 41 move $2, $0 42 .livereg 0x2000FF0E,0x00000FFF 43 lw $31, 28($sp) 44 addu $sp, 32 45 j $31 46 .end main %
----------------------------------------------------------------------
---------------------------------------------------------------------- % cc -c today.c% nm today.o
Symbols from today.o: [Index] Value Size Class Type Section Name [0] | 0| |File |ref=4 |Text | today. c [1] | 0| |Proc |end=3 int |Text | main [2] | 68| |End |ref=1 |Text | main [3] | 0| |End |ref=0 |Text | today. c [4] | 0| |Proc |ref=1 |Text | main [5] | 0| |Proc | |Undefined| printf [6] | 0| |Global | |Undefined| _gp_di sp % cc -v -o today today.o
/usr/lib/ld -elf -o today -_SYSTYPE_SVR4 -require_dynamic_link _rld_new_interfac e -no_unresolved -Wx,-G 0 -mips2 -call_shared -g0 -KPIC -L/usr/lib/ -nocount /us r/lib/crt1.o -count today.o -nocount -lc /usr/lib/crtn.o /usr/lib/ld phase time: 0.02u 0.05s 0:00.1 88% %
----------------------------------------------------------------------
open(),close(),read(),write()
ライブラリ関数の例
fopen(),fclose(),fread(),fwrite(),printf(),scanf(),getchar(),putchar()
マクロ定義の例
getchar(),putchar(),stdin,stdout,stderr
ヘッダ・ファイル stdio.h の観察
---------------------------------------------------------------------- % egrep getchar /usr/include/stdio.hextern int getchar(void); extern int getchar_unlocked(void); #define getchar() getc_locked(stdin) #define getchar_locked() getc_locked(stdin) #define getchar_unlocked() getc_unlocked(stdin) #define getchar() getc(stdin) #define getchar_unlocked() getc_unlocked(stdin) #define getchar() getc(stdin) % egrep stdin /usr/include/stdio.h
#define stdin (&__iob[0]) #define getchar() getc_locked(stdin) #define getchar_locked() getc_locked(stdin) #define getchar_unlocked() getc_unlocked(stdin) #define getchar() getc(stdin) #define getchar_unlocked() getc_unlocked(stdin) #define getchar() getc(stdin) %
----------------------------------------------------------------------
---------------------------------------------------------------------- 1: /* 2: write-hello-c.c -- writeシステムコールの利用 3: ~yas/syspro/file/write-hello-c.c 4: $Header: /home/lab2/OS/yas/syspro-2001/file/RCS/write-hello-c.c,v 1.1 2001/04/15 12:02:00 yas Exp $ 5: Start: 2001/04/15 21:00:35 6: */ 7: 8: char hello[] = { 'h','e','l','l','o','\n' }; 9: 10: main() 11: { 12: write( 1,hello,6 ); 13: } ----------------------------------------------------------------------
実行例。
---------------------------------------------------------------------- % cp ~yas/syspro/file/write-hello-c.c .% make write-hello-c
cc write-hello-c.c -o write-hello-c % ./write-hello-c
hello %
----------------------------------------------------------------------
---------------------------------------------------------------------- 1: # 2: # write-hello-s.s -- writeシステムコールの利用(アセンブリ言語) 3: # ~yas/syspro/file/write-hello-s.s 4: # $Header: /home/lab2/OS/yas/syspro-2001/file/RCS/write-hello-s.s,v 1.1 2001/04/15 12:20:49 yas Exp $ 5: # Start: 2001/04/15 21:09:47 6: 7: .text 8: .align 2 9: .globl main 10: main: 11: addiu $sp,$sp,-32 12: sw $31,28($sp) 13: sw $gp,24($sp) 14: 15: li $4, 1 16: la $5, hello 17: li $6, 6 18: 19: li $2, 1004 # write system call 20: syscall 21: 22: lw $31,28($sp) 23: lw $gp,24($sp) 24: addiu $sp,$sp,32 25: jr $31 26: 27: .data 28: .align 2 29: hello: 30: .byte 104 31: .byte 101 32: .byte 108 33: .byte 108 34: .byte 111 35: .byte 10 ----------------------------------------------------------------------
実行例。
---------------------------------------------------------------------- % cp ~yas/syspro/file/write-hello-s.s .% make write-hello-s
cc write-hello-s.s -o write-hello-s % ./write-hello-s
hello %
----------------------------------------------------------------------
---------------------------------------------------------------------- int myputchar(int c) { ... write(1,...,...); ... } int myputs(char *s) { ... write(1,...,...); ... } ----------------------------------------------------------------------main() 関数を付けて、正しく動いていることがわかるようにしなさい。
---------------------------------------------------------------------- int mygetchar() { ... read(0,...,...); ... } ----------------------------------------------------------------------main() 関数を付けて、正しく動いていることがわかるようにしなさい。
注意:ライブラリ関数 gets() には、バッファの長さ以上のデータが入力され た時に、問題がある。今後、決して使ってはいけない。