システムプログラミング 第6回、7回 main関数の引数 usageメッセージ システムコールのエラーメッセージ ファイル 情報工学科 篠埜 功
今日からの内容 Linuxのシステムコールを用いたCプログラムの作成 今日の内容 Cプログラムのmain関数の引数について Usage メッセージについて システムコールのエラーメッセージについて
Cのmain関数の引数について /* コマンド名および引数を表示 */ #include<stdio.h> int main (int argc, char *argv[ ]) { for ( ; *argv; argv++) printf ("%s\n", *argv); return 0; } (実行例) $ gcc 1.c $ ./a.out abc 234 ./a.out abc 234 $
main関数の第3引数 #include<stdio.h> main関数は第3引数に環境変数の情報の配列が渡される場合がある。(ISO規格で定められているわけではなく、処理系依存。) #include<stdio.h> int main (int argc, char *argv[ ], char *envp[ ]) { for ( ; *envp; envp++) printf ("%s\n", *envp); return 0; } 演習室の環境では第3引数を受け取れる。第3引数はなくてもよい。環境変数はライブラリ関数getenv()で取得できるのでそれを使えばよい。
スタートアップルーチン C言語プログラムは、実行形式ファイルにするときにスタートアップルーチンとリンクされる。 スタートアップルーチンは、/usr/lib/crt1.oにある。 $ nm /usr/lib/crt1.o で確認。この出力結果に U main という行が含まれており、スタートアップルーチンがmain関数を呼び出していることがここに反映されている。 スタートアップルーチン部分で引数の個数、引数の文字列配列、環境変数配列をmain関数に渡す。
コマンドのオプションについて オプションはハイフンのあとに1文字(-oなど) オプションのあとにオプションの引数があることもある (例) gcc –o main main.c など。 いくつかのオプションをまとめて記述することもある 例 ls -la は、ls -l -a をまとめて書いたものである。 --helpのように、ハイフンが2つの場合もある。(これは-h -e -l -pを-helpと書いた場合との区別のため)
Usage メッセージ オプションが正しく与えられなかった場合 (2) コマンドの引数に過不足があった場合 例えば、 $ cp のようにcpコマンドを引数無しで実行すると、Usageメッセージが表示される。
例(打ち込んで確認) (実行例) $ gcc usage.c $ ./a.out Usage: ./a.out filename #include<stdio.h> int main (int argc, char * argv[]) { if (argc!=2) fprintf (stderr, "Usage: %s filename\n", argv[0]); return 0; } (実行例) $ gcc usage.c $ ./a.out Usage: ./a.out filename
システムコールについて システムコールとは、カーネル内のコードを呼び出すためのC関数 ファイル、ネットワーク、キーボード等、ハードウェアとのやりとりはすべてカーネルが行う。ユーザはシステムコールを通じてカーネルにハードウェア操作を依頼する。 例えば、ファイルからデータを読み出す場合、getc, fgetcなどのライブラリ関数を呼ぶが、どんなライブラリ関数を呼んだとしても、最終的にはreadシステムコールが呼ばれ、カーネル内部のコードが実行される。
システムコールの実装 システムコールは、Cの関数であり、ライブラリファイル /usr/lib/libc.a に入っている。 $ ar t /usr/lib/libc.a | less でlibc.aの中身のオブジェクトファイルリストが表示される。write.o, read.oなどが入っている。
システムコールの番号 1 exit 2 fork 3 read 4 write 5 open 6 close 7 wait 8 creat 9 link 10 unlink 11 exec 12 chdir … 今後の講義でシステムコールを少しずつ紹介する。各システムコールのC関数が番号の設定を行うので番号は知らなくてよい。
システムコールのマニュアル システムコールはmanコマンドでマニュアルを表示できる。 例えば、 $ man -S 2 write $ man write とすると、writeコマンドのマニュアルが表示される。 $ man –a write とすると、writeの名前のマニュアルがすべて(PAGERがlessの場合はqを押すごとに)順番に表示される。 manコマンドの-Sの引数には分類番号を入れる。1はコマンド、2はシステムコール、3はライブラリ関数となっている。
システムコールのエラーメッセージ システムコールがエラーになった場合、エラー番号がerrnoという外部変数に代入されている。この番号を用いてエラーメッセージを出力するライブラリ関数perrorがあるのでそれを用いる。(errnoを直接使うプログラムは書かないほうがよい。) 例えば、$ ls aaa のように、lsコマンドで存在しないファイル名を指定した場合に、そのようなファイルは存在しないといったメッセージが出力される。ここではperror関数が用いられている。(そのようにlsコマンドが実装されている。)
ライブラリ関数perror void perror (char *s)
ファイルの1文字目を表示する例 (入力して実行、後で詳述) #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #define ERR -1 int main (void) { int fd, n; char c; if ((fd = open ("test", O_RDONLY)) == ERR) { perror ("open"); exit(1);} /* 続き */ if ( (n = read (fd, &c, 1) ) > 0 ) printf ("1文字目は%cです。\n", c); if (n==ERR) { perror ("read"); exit(1); } if (close (fd) == ERR) { perror ("close"); return 0;
演習課題 さきほどのプログラムを参考にして、テキストファイルの2文字目まで表示するプログラムをopenシステムコール, readシステムコールを使って作成せよ。 (さきほどと同様、printfは使うことにする。)
ファイル、プロセス OS(Linux)は、ファイルとプロセスを木構造で管理する。 まず、ファイルの木構造について学習する。
ファイル操作のシステムコール ファイルはOS(Linux)が管理している。 ファイルの操作等を行うためのシステムコールが提供される。 open, creat, close, read, write, lseek, dup, dup2等がある。 今日はopen, close, read, writeの解説を行う。この前に、まず、ファイルの基本事項の説明を行う。
ファイルとは UNIX系OSではファイルは1つの木構造で管理される。ルートディレクトリ以外は、すべてのファイルには親(ディレクトリ)がある。 ディレクトリもファイルの一種であり、そのディレクトリの子供のファイルの名前を保持しているファイルである。ディレクトリファイルも、(ルートディレクトリ以外は)何らかのディレクトリの子供である。 すべてのファイルはルートディレクトリから親子関係を辿ることによって到達できる。
デバイスファイル UNIXでは、端末、ディスク、磁気テープ、プリンタなどの周辺機器やメモリなどの装置のそれぞれに対応するファイルがある。特殊ファイル、あるいはスペシャルファイルとも呼ばれる。デバイスドライバに対するインタフェースであり、ファイルを扱うシステムコールで操作できる。これらのファイルは通常/devディレクトリ以下にある。 デバイスは次の2種類に分けられる。 キャラクタデバイス --- 端末やプリンタなど、文字単位で入出力を行う装置 ブロックデバイス --- ディスクや磁気テープ装置など、データをある程度まとまったブロック単位で処理する装置
ファイル名、パス名 ファイルには、名前がある。それをファイル名という。ディレクトリが違えば同じファイル名であっても別のファイルである。 パス名は、経路(パス)をファイル名の前につけたものである。 ファイル名は255文字以内、パス名は4095文字以内。大文字、小文字、数字、ピリオド、ハイフン、アンダーバーが使える。ファイル名に日本語は使えるが、現状では使わないのが無難。
絶対パス、相対パス 絶対パス --- ルートディレクトリから下向きに辿るパス 相対パス --- 現在のディレクトリからの相対的な経路で表すパス名、上に行くこともある。 ピリオド --- 現在のディレクトリ(current directory)を表す。 ピリオド2つ --- 親ディレクトリを表す。 (例) ../.. は2つ上の親のディレクトリを表す。
ピリオドの役割 ピリオド . はカレントディレクトリを表す。カレントディレクトリの実行形式ファイルを実行するとき、 $ ./test のようにして実行する。単にファイル名を $ test のように打った場合は、testという実行形式ファイルを、シェル(例えばtcsh)が、シェル変数PATHに格納されているディレクトリを順番に探すことになる。PATHに . が指定されていない場合は、カレントディレクトリにtestがあっても実行できない。また、testという名前の実行ファイルがシェルの内部コマンドだったり(実際は違うが)、他のPATHの(順番が先になっている)ディレクトリにあると、そちらが実行される。(実際に、/usr/bin/testが存在する。)
ホームディレクトリ ログイン時のディレクトリをホームディレクトリという。通常、/homeの下に作成される。(rootというユーザ(スーパーユーザ)のホームディレクトリは/rootである)。演習室では、(私sasanoの場合)/home/sitの下にある。ディレクトリを変更するコマンドがcd(シェルの内部コマンド)であり、これはchdirシステムコールを使って実現されている。ホームディレクトリはチルダ~で表される。
ファイルの保護 ファイルを他人(他のユーザ)に見られないように、permissionの指定ができる。 $ ls –al で確認できる。 d rwx rwx rwx の最初の桁以外は変更できる。 最初がdだとディレクトリ、-は一般のファイル。 そのあとは、owner, group, otherに対する読み書き実行の許可を表す。 (例) -rw-r—r--だと、一般ファイルで、所有者は読み書き、グループメンバーとその他のユーザは読み出しのみ許可。
Permissionの変更 Permissionの変更はchmodコマンドで行う。 $ chmod モード ファイル名 モードは、rwxの組を3桁の2進数と考え、これを8進1桁で表わし、8進3桁で指定することができる。例えば、rw-r--r--にしたい場合は644とする。 chmodは、chmodシステムコールを使って実現されている。
ファイルの所有者 ファイルには所有者、グループが関連づけられている。 $ ls –l で確認できる。 ファイルの所有者の変更はchownコマンド、グループの変更はchgrpコマンドで行う。これらはchownシステムコールを使って実現されている。ただし、所有者とスーパーユーザ以外は変更できない。