Download presentation
Presentation is loading. Please wait.
1
システムプログラミング 第6回、7回、8回 情報工学科 篠埜 功
2
今日からの内容 Linuxのシステムコールを用いたCプログラムの作成 今日の内容 Cプログラムのmain関数の引数について
Usage メッセージについて システムコールのエラーメッセージについて
3
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 $
4
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()で取得できるのでそれを使えばよい。
5
スタートアップルーチン すべてのC言語プログラムは、実行形式ファイルにするときに、スタートアップルーチンとリンクされる。
スタートアップルーチンは、/usr/lib/crt1.oにある。 $ nm /usr/lib/crt1.o で確認。この出力結果に U main という行が含まれており、スタートアップルーチンがmain関数を呼び出していることがここに反映されている。 スタートアップルーチン部分で引数の個数、引数の文字列配列、環境変数配列をmain関数に渡す。
6
コマンドのオプションについて オプションはハイフンのあとに1文字(-oなど) オプションのあとにオプションの引数があることもある
(例) gcc –o main main.c など。 いくつかのオプションをまとめて記述することもある 例 ls –la は、ls –l –a をまとめて書いたものである。 --helpのように、ハイフンが2つの場合もある。(これは-h –e –l –pを-helpと書いた場合との区別のため)
7
Usage メッセージ オプションが正しく与えられなかった場合 (2) コマンドの引数に過不足があった場合
例えば、 $ cp のようにcpコマンドを引数無しで実行すると、Usageメッセージが表示される。
8
例(打ち込んで確認) (実行例) $ 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
9
システムコールについて システムコールとは、カーネル内のコードを呼び出すためのC関数
ファイル、ネットワーク、キーボード等、ハードウェアとのやりとりはすべてカーネルが行う。ユーザはシステムコールを通じてカーネルにハードウェア操作を依頼する。 例えば、ファイルからデータを読み出す場合、getc, fgetcなどのライブラリ関数を呼ぶが、どんなライブラリ関数を呼んだとしても、最終的にはreadシステムコールが呼ばれ、カーネル内部のコードが実行される。
10
システムコールの実装 システムコールは、Cの関数であり、ライブラリファイル /usr/lib/libc.a に入っている。
$ ar t /usr/lib/libc.a | less でlibc.aの中身のオブジェクトファイルリストが表示される。write.o, read.oなどが入っている。
11
システムコールの番号 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関数が番号の設定を行うので番号は知らなくてよい。
12
システムコールのマニュアル システムコールはmanコマンドでマニュアルを表示できる。 例えば、 $ man -S 2 write
$ man write とすると、writeコマンドのマニュアルが表示される。 $ man –a write とすると、writeの名前のマニュアルがすべて(PAGERがlessの場合はqを押すごとに)順番に表示される。 manコマンドの-Sの引数には分類番号を入れる。1はコマンド、2はシステムコール、3はライブラリ関数となっている。
13
システムコールのエラーメッセージ システムコールがエラーになった場合、エラー番号がerrnoという外部変数に代入されている。この番号を用いてエラーメッセージを出力するライブラリ関数perrorがあるのでそれを用いる。(errnoを直接使うプログラムは書かないほうがよい。) 例えば、$ ls aaa のように、lsコマンドで存在しないファイル名を指定した場合に、そのようなファイルは存在しないといったメッセージが出力される。ここではperror関数が用いられている。(そのようにlsコマンドが実装されている。)
14
ライブラリ関数perror void perror (char *s)
15
ファイルの1文字目を表示する例 (入力して実行、後で詳述)
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.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;
16
演習課題 さきほどのプログラムを参考にして、テキストファイルの2文字目まで表示するプログラムをopenシステムコール, readシステムコールを使って作成せよ。 (さきほどと同様、printfは使うことにする。)
17
ファイル、プロセス OS(Linux)は、ファイルとプロセスを木構造で管理する。 まず、ファイルの木構造について学習する。
18
ファイル操作のシステムコール ファイルはOS(Linux)が管理している。 ファイルの操作等を行うためのシステムコールが提供される。
open, creat, close, read, write, lseek, dup, dup2等がある。 今日はopen, close, read, writeの解説を行う。この前に、まず、ファイルの基本事項の説明を行う。
19
ファイルとは UNIX系OSではファイルは1つの木構造で管理される。ルートディレクトリ以外は、すべてのファイルには親(ディレクトリ)がある。
ディレクトリもファイルの一種であり、そのディレクトリの子供のファイルの名前を保持しているファイルである。ディレクトリファイルも、(ルートディレクトリ以外は)何らかのディレクトリの子供である。 すべてのファイルはルートディレクトリから親子関係を辿ることによって到達できる。
20
デバイスファイル UNIXでは、端末、ディスク、磁気テープ、プリンタなどの周辺機器やメモリなどの装置のそれぞれに対応するファイルがある。特殊ファイル、あるいはスペシャルファイルとも呼ばれる。これらのファイルは通常/devディレクトリ以下にある。 デバイスは次の2種類に分けられる。 キャラクタデバイス --- 端末やプリンタなど、文字単位で入出力を行う装置 ブロックデバイス --- ディスクや磁気テープ装置など、データをある程度まとまったブロック単位で処理する装置 デバイスファイルも普通のファイルと同様に扱える(UNIXの利点)
21
ファイル名、パス名 ファイルには、名前がある。それをファイル名という。ディレクトリが違えば同じファイル名であっても別のファイルである。
パス名は、経路(パス)をファイル名の前につけたものである。 ファイル名は255文字以内、パス名は4095文字以内。大文字、小文字、数字、ピリオド、ハイフン、アンダーバーが使える。ファイル名に日本語は使えるが、現状では使わないのが無難。
22
絶対パス、相対パス 絶対パス --- ルートディレクトリから下向きに辿るパス
相対パス --- 現在のディレクトリからの相対的な経路で表すパス名、上に行くこともある。 ピリオド --- 現在のディレクトリ(current directory)を表す。 ピリオド2つ --- 親ディレクトリを表す。 (例) ../.. は2つ上の親のディレクトリを表す。
23
ピリオドの役割 ピリオド . はカレントディレクトリを表す。カレントディレクトリの実行形式ファイルを実行するとき、 $ ./test
のようにして実行する。単にファイル名を $ test のように打った場合は、testという実行形式ファイルを、シェル(例えばtcsh)が、シェル変数PATHに格納されているディレクトリを順番に探すことになる。PATHに . が指定されていない場合は、カレントディレクトリにtestがあっても実行できない。また、testという名前の実行ファイルがシェルの内部コマンドだったり(実際は違うが)、他のPATHの(順番が先になっている)ディレクトリにあると、そちらが実行される。(実際に、/usr/bin/testが存在する。)
24
ホームディレクトリ ログイン時のディレクトリをホームディレクトリという。通常、/homeの下に作成される。(rootというユーザ(スーパーユーザ)のホームディレクトリは/rootである)。演習室では、(私sasanoの場合)/home/sitの下にある。ディレクトリを変更するコマンドがcd(シェルの内部コマンド)であり、これはchdirシステムコールを使って実現されている。ホームディレクトリはチルダ~で表される。
25
ファイルの保護 ファイルを他人(他のユーザ)に見られないように、permissionの指定ができる。 $ ls –al で確認できる。
d rwx rwx rwx の最初の桁以外は変更できる。 最初がdだとディレクトリ、-は一般のファイル。 そのあとは、owner, group, otherに対する読み書き実行の許可を表す。 (例) -rw-r—r--だと、一般ファイルで、所有者は読み書き、グループメンバーとその他のユーザは読み出しのみ許可。
26
Permissionの変更 Permissionの変更はchmodコマンドで行う。 $ chmod モード ファイル名
モードは、rwxの組を3桁の2進数と考え、これを8進1桁で表わし、8進3桁で指定することができる。例えば、rw-r--r--にしたい場合は644とする。 chmodは、chmodシステムコールを使って実現されている。
27
ファイルの所有者 ファイルには所有者、グループが関連づけられている。 $ ls –l で確認できる。
ファイルの所有者の変更はchownコマンド、グループの変更はchgrpコマンドで行う。これらはchownシステムコールを使って実現されている。ただし、所有者とスーパーユーザ以外は変更できない。
28
ディレクトリファイル ディレクトリファイルには、ファイル名からiノード番号(iノードはindex nodeの略)への対応がファイルの数だけ格納されている。(実際のデータ構造は、ハッシュテーブルや線形リストなど。) ファイルのiノード番号は $ ls –li 等、lsに-iオプションを与えると確認できる。
29
ファイルシステムの実装 ファイルシステムは、ブートブロック、スーパーブロック、iノードリスト、データブロックの4つの領域から構成される。
ブートブロック --- OSを起動するためのプログラムを格納。 スーパーブロック --- ファイルシステムの大きさ、ブロック数、ブロックサイズ、空きブロック、iノードリストの大きさ、空きiノードリストの情報などを格納。 iノードリスト --- iノードのリスト。iノードには各ファイルの種類、所有者、permission、変更時刻、ファイルサイズ、データブロック内の場所などが格納されている。 データブロック --- ファイルの中身が格納されている。
30
openシステムコール ファイルからのデータの読み込み、ファイルへのデータの書き込みをするには、まずファイルをオープンする。これを行うのがopenシステムコールである。 openシステムコールを使う場合、types.h, stat.h, fcntl.hをインクルードする。
31
openシステムコールの引数 openシステムコールは、パス名、フラグの2引数あるいは、これらにモードを加えた3引数で呼び出す。
パス名で指定されたファイルを、フラグに従ってオープンし、ファイル記述子(file descriptor, int型)を返す。フラグがO_CREATの場合、モードが必要。 ファイル記述子は、利用者ファイル記述子表の 何番目かを表す。 オープンするとは、データ入出力用のバッファを確保し、利用者ファイル記述子表中の1つの構造体を割り当て、構造体の各メンバーに初期値を設定することをいう。 利用者ファイル記述子表についてはdupシステムコールの説明時に説明する。
32
openシステムコールの代表的なフラグ O_RDONLY --- 読みだしのみ O_WRONLY --- 書き込みのみ
O_RDWR --- 読み出し、書き込みの両方を行う O_CREAT --- ファイルが存在しない場合作成する。第3引数のモードでファイルのpermission等を設定する。 これらのフラグはfcntl.hに記述されているので、インクルードして使う。
33
モード openシステムコールの第3引数に与えられるモードでは、ファイルのpermissionおよびセットユーザIDビット、セットグループIDビット、stickyビット(/tmpなどで使用)を12桁の2進数で表す。代表的な数値はstat.hでマクロとして提供されているが、数値で直接指定してよい。 (例)S_IRWXU --- 所有者はread, write, executeができる。 その他にもあるが、 $ man –S 2 open で確認。
34
readシステムコール ファイルからデータを読み出すためのシステムコールがreadシステムコールである。
unistd.hをインクルードする。 ファイル記述子、データ格納領域へのポインタ、読み出しバイト数を引数に与える。返り値は読み出したバイト数。これが0のときはファイルの最後まで読み終わっているということになる。 readシステムコールが正常終了しなかった場合は-1が返ってくる。このときはperrorでエラーメッセージを表示する。
35
writeシステムコール ファイルへデータを書き込むためのシステムコールがwriteシステムコールである。
unistd.hをインクルードする。 ファイル記述子、書き込むデータが格納されている領域へのポインタ、書き込みバイト数を引数に与える。返り値は書きこんだバイト数。 writeシステムコールが正常終了しなかった場合は-1が返ってくる。このときはperrorでエラーメッセージを表示する。
36
closeシステムコール closeシステムは、ファイル記述子を引数に受け取り、そのファイルをクローズする。
クローズするとは、入出力用バッファを解放し、利用者記述子表内の構造体を解放することをいう。 (同時に開けるファイル数に制限があるので、閉じるのがよい。閉じなければプロセス終了時に閉じられる。)
37
例1:テキストファイルの先頭にaを書き込む
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main (void) { int fd, n; char c = 'a'; if ((fd = open ("test", O_WRONLY)) == -1) { perror ("open"); exit(1); } /* 続き */ if (write (fd, &c, 1) != 1){ perror ("write"); exit(1); } if (close (fd) == -1) { perror ("close"); return 0;
38
演習課題2 テキストファイル(ファイル名はtestなど)の先頭文字を読み取り、その文字を2文字目に書きこむ。Openシステムコールの第2引数(フラグ)はO_RDWRにする。 read, writeシステムコールを呼ぶたびに、読み書きのためのポインタ(システムコール内部のポインタ)が1つ進むので、readで読み取ったあとにwriteで書き込めば、2文字目に書きこまれることになる。
39
例2 /* 続き */ if ((fd = open (argv[1], O_RDONLY)) == -1) {
perror ("open"); exit(1); } while ((n = read (fd, &c, 1) ) > 0) printf ("%c", c); if (n==-1) { perror ("read"); if (close (fd) == -1) { perror ("close"); return 0; /* テキストファイル全部表示 */ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main (int argc, char * argv []) { int fd, n; char c; if (argc!=2) { fprintf (stderr, "Usage: %s filename\n", argv[0]); exit(1); }
40
複数バイトずつ読み込む readシステムコールで、複数バイト単位で読み込むこともできる。 readシステムコールの返り値は、
(1) 正の場合、読み込んだバイト数を表す。 (2) 0 の場合、ファイルの内容を既に全部読み終わっていたことを表す。 (3) -1の場合、システムコールが何らかの理由で正常終了しなかったことを表す。この場合はライブラリ関数perrorでエラー内容を表示するべき。 例えば、ファイルサイズ260バイトのファイルを100バイト単位で読み込むと、最後の回は60バイト(返り値も60)になり、その後は返り値は0となる。
41
例3 /* 続き */ if ((fd = open (argv[1], O_RDONLY)) /* テキストファイル全部表示 */
== -1) { perror ("open"); exit(1); } while ((n = read (fd, c, 100) ) > 0) if (write (1, c, n) != n) { perror ("write"); exit(1); }; if (n == -1) { perror ("read"); if (close (fd) == -1) { perror ("close"); return 0; } /* テキストファイル全部表示 */ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main (int argc, char * argv []) { int fd, n; char c[100]; if (argc!=2) { fprintf (stderr, "Usage: %s filename\n", argv[0]); exit(1); } 1は標準出力
42
例4 /* 標準入力を標準出力へ書きだす*/ #include <stdio.h>
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main (int argc, char * argv []) { int fd, n; char c[100]; while ((n = read (0, c, 100) ) > 0) if (write (1, c, n) != n) { perror ("write"); exit(1); }; /* 続き */ if (n == -1) { perror ("read"); exit(1); } return 0; 0は標準入力 1は標準出力
43
新しいファイルの作成 新しいファイルの作成は、openシステムコールの第2引数のフラグにO_CREATを指定する。(ファイルが存在していたらそのファイルを使う。存在しなければファイルを新しく作る。) 他のフラグと組み合わせて指定できる。組み合わせるときはビットのor演算子|を用いる。 例えば、write onlyで開きたい場合は、openシステムコールの第2引数に O_WRONLY | O_CREAT を指定する。さらに、存在しているときに内容を消したいときは O_TRUNCをさらに追加で指定する。
44
例5 /* 続き */ if ((fd = open (argv[1], O_WRONLY | O_CREAT |
O_TRUNC, 0644)) == -1) { perror ("open"); exit(1); } while ((n = read (0, c, 100) ) > 0) if (write (fd, c, n) != n) { perror ("write"); exit(1); }; if (n == -1) { perror ("read"); if (close (fd) == -1) { perror ("close"); return 0; } /* 入力をファイルへ書きだす*/ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main (int argc, char * argv []) { int fd, n; char c[100]; if (argc!=2) { fprintf (stderr, "Usage: %s filename\n", argv[0]); exit(1); } 0は標準入力
45
演習課題3 実行ファイルの引数に2つのファイル名を受けとり、1つ目のファイル(存在するファイル)のコピーを2つ目のファイルに作成するプログラムを作成せよ。(コピーコマンド) コピー先ファイルのopen時の引数 第2引数 --- O_WRONLY | O_CREAT | O_TRUNC 第3引数 (8進表記) とせよ。これにより、コピー先のファイル名が存在していたら、内容が消去されてから書き込まれる。
46
レポート課題3 catコマンドの以下の機能を、システムコール(open, close, read, write)を使って実装せよ。
引数無しの場合 --- 標準入力を標準出力へコピー 引数がある場合(1個以上のファイル名を引数にとる) --- それらのファイルの内容を結合したものを標準出力に書き出す。 catコマンドを使った場合と挙動を比較し、同じであることを確認したのち提出すること。
47
レポートの提出方法 □ 下記のファイルを作成し、提出 kadai3.c, kadai3.txt □ 提出方法
システムプログラミング講義用の課題提出用フォルダ内にあるkadai3というフォルダの中に自分の学籍番号を名前とするフォルダを作成し、その中に上記ファイルを置く。kadai3.txt内に学籍番号、氏名、日付、および作成したプログラムの簡単な説明を記載する。 □ 提出期限 12月8日の講義開始時間まで。締め切り後に提出した場合、成績への反映を保証しない。
48
参考: ライブラリ関数を使った場合 int main (int argc, char * argv []) {
FILE *fp; if (argc==1) filecopy (stdin, stdout); else while (--argc > 0) if ((fp = fopen (*++argv, "r")) == NULL) { printf ("cat: can't open %s\n", *argv); return 1; } else { filecopy (fp, stdout); fclose (fp); } return 0; #include <stdio.h> void filecopy (FILE *fpin, FILE *fpout) { int c; while ((c = getc(fpin)) != EOF) putc (c, fpout); } この実装では1バイトずつコピーしている。複数バイトまとめて読み込んで書き込んだ方が速い。
Similar presentations
© 2024 slidesplayer.net Inc.
All rights reserved.