システム・コールとライブラリによるファイルの扱い

システム・プログラム

                                       電子・情報工学系
                                       新城 靖
                                       <yas@is.tsukuba.ac.jp>

このページは、次の URL にあります。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/syspro-2001/2001-04-23
あるいは、次のページから手繰っていくこともできます。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/
http://www.is.tsukuba.ac.jp/~yas/index-j.html

■印刷配布資料

■復習

■今日の目標

■メモリ中のデータの表現

◆バイト・アドレッシング

メモリには、1バイトごとに番地が振られている。 (1ワードごと、1ビットごとにアドレスを振る方法も考えられる。)

◆整数

C言語では、いろいろなビット数(バイト数)の整数が使える。
char
8 ビット
short int
16 ビット
int
32 ビット (処理系依存)
long int
32 ビット
char も、8ビット(1バイト)の整数であることに注意。 unsigned を付けると符合無しになる。

Java では、char は、整数型とはまったく違う。相互に代入できない。 内部的には、16 ビットの Unicode。

◆バイト・オーダ

複数バイトの整数をメモリに置く時に、連続した複数番地の メモリを使う。 どういう順番で置くか。 数の大小比較の時に、最も効いてくる(most significant)なビット(を含むバ イト、上位バイト)の置き方。
ビッグエンディアン。番地が小さい方に上位バイトをを置く。
リトルエンディアン。番地が小さい方に下位バイトを置く。
例:0x12345678の置き方。

図2-1 バイト・オーダ(その1)

図2-1 バイト・オーダ(その1)

CPUのレジスタの中では、バイト・オーダは関係ない。

図2-2 バイト・オーダ(その2)

図2-2 バイト・オーダ(その2)

◆バイト・オーダを調べるプログラム

----------------------------------------------------------------------
   1:	/*
   2:	        byte-order.c -- バイト・オーダを調べるプログラム
   3:	        ~yas/syspro/cc/byte-order.c
   4:	        $Header: /home/lab2/OS/yas/syspro-2001/cc/byte-order.c,v 1.1 2001/04/22 12:21:46 yas Exp $
   5:	        Start: 2001/04/22 19:01:41
   6:	*/
   7:	
   8:	int i=0x12345678 ;
   9:	
  10:	main()
  11:	{
  12:	    char *p,c ;
  13:	        printf("&i == 0x%x\n",&i );
  14:	        p = (char *)&i ;
  15:	        printf(" p == 0x%x\n",p );
  16:	        c = *p ;
  17:	        printf(" c == 0x%x\n",c );
  18:	}
----------------------------------------------------------------------
実行結果。gdb で変数の番地を調べて、その番地の内容を x コマンドで表示 する。

----------------------------------------------------------------------
% gcc -g byte-order.c -o byte-order [←]
% ./byte-order  [←]
&i == 0x10012158
 p == 0x10012158
 c == 0x12
% gdb byte-order [←]
GNU gdb 4.17
Copyright 1998 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 "mips-sgi-irix6.3"...
(gdb) r[←]
Starting program: /home/lab2/OS/yas/syspro-2001/cc/byte-order 
&i == 0x10012158
 p == 0x10012158
 c == 0x12
Program exited with code 013.
Current language:  auto; currently asm
(gdb) p &i[←]
$2 = (int *) 0x10012158
(gdb) x 0x10012158[←]
0x10012158 : 0x12
(gdb) x/4b 0x10012158[←]
0x10012158 : 0x12    0x34    0x56    0x78
(gdb) q[←]
% []
----------------------------------------------------------------------

◆gdb コマンドカード

◆符合拡張

C言語では、関数呼出しの時、char も short も、int へ拡張する。符合付な ら、符合をつけたまま拡張する。

■文字の符号化

文字の符号化とは、文字とビット列(または整数)を対応させることであ る。対応のさせかたには何種類もある。
A	1
B	2
C	3
...
Z	26
モールス符合。

ポケットベル・コード。

コンピュータでよく使われる文字集合と文字の符号化の方法

厳密には、文字集合と文字コードは違う。JIS X 0208-1983符号もEUC-jpも、 Shift-JIS も文字集合は同じなので、相互に変換できる。

数の値と数を画面に文字として表示するために使う符合が違うことに注意。 数の0と文字の0の違い。

◆ISO Standard ISO8859-1 Latin-1

Latin-1コード表

8ビット符号。 20(16進)から7F(16進)までは、ASCII と同じ。 西ヨーロッパでよく使われている。

参考:

http://www.sandia.gov/sci_compute/iso_symbol.html
ISO Latin 1 Character HTML Entity Names)

◆JIS X 201

JIS X 201 コード表

◆漢字コード

JIS漢字コード表の一部

漢字1文字を、7ビットと7ビットの組でで表す。それぞれ 21〜7E に入るよ うになっている。16進で2121から747Eまでの範囲にある。ただし、半分は空い ている。

参考:

http://www.hlla.is.tsukuba.ac.jp/~yas/classes/ipe/nitiniti2-enshu-1996/1996-11-18/kanji-code.html
漢字コードに関する解説

◆C言語での文字

C言語では、ASCII で表せる文字については、シングルクォート 「''」で囲むことで、その文字コードの定数と同じ意味 になる。
  int x ;
  x = 'A' ;  /* (1) */
  x = 0x41 ; /* (2) */
漢字を「''」で囲んだ時に何が起るかは不明。 コンパイラはエラーを出さない。

◆C言語での文字列

「''」と「""」の違いを理解すること。
  int x ;
  x = 'A' ;  /* (1) */
  x = "A" ;  /* (3) */

char の配列として表現される。連続番地に置かれ、0終端されている。ダブ ルクォート「""」で囲むと、名前がない配列が作られ、その先頭番地が入る。

漢字を「""」で囲んだ時には、EUC だと比較的そのまま通る。 JIS だと、「"」や「\」と重なる漢字がある。

■ファイルのコピー(システムコールの利用)

file-copy.c は、システムコール open(), read(), write(), close() を使っ て引数で指定されたファイルを開き、その内容をコピーするプログラムである。
----------------------------------------------------------------------
   1:	
   2:	/*
   3:	        file-copy.c -- ファイルをコピーする簡単なプログラム
   4:	        ~yas/syspro/file/file-copy.c
   5:	        $Header: /home/lab2/OS/yas/syspro/file/RCS/file-copy.c,v 1.4 2001/04/22 08:28:45 yas Exp $
   6:	        Start: 1995/03/04 16:40:24
   7:	*/
   8:	
   9:	#include <stdio.h>      /* stderr */
  10:	#include <fcntl.h>      /* open(2) */
  11:	
  12:	extern void file_copy( char *from_name, char *to_name );
  13:	
  14:	main( int argc, char *argv[] )
  15:	{
  16:	        if( argc != 3 )
  17:	        {
  18:	            fprintf( stderr,"Usage: %s from to\n", argv[0] );
  19:	            exit( 1 );
  20:	        }
  21:	        file_copy( argv[1],argv[2] );
  22:	}
  23:	
  24:	#define BUFFERSIZE      1024
  25:	
  26:	void file_copy( char *from_name, char *to_name )
  27:	{
  28:	    int from_fd,to_fd ;
  29:	    char buffer[BUFFERSIZE] ;
  30:	    int rcount ;
  31:	    int wcount ;
  32:	
  33:	        from_fd = open( from_name,O_RDONLY );
  34:	        if( from_fd == -1 )
  35:	        {
  36:	            perror( from_name );
  37:	            exit( 1 );
  38:	        }
  39:	        to_fd = open( to_name,O_WRONLY|O_CREAT|O_TRUNC,0666 );
  40:	        if( to_fd == -1 )
  41:	        {
  42:	            perror( to_name );
  43:	            exit( 1 );
  44:	        }
  45:	        while( (rcount=read(from_fd,buffer,BUFFERSIZE)) > 0 )
  46:	        {
  47:	            if( (wcount=write(to_fd,buffer,rcount))!= rcount )
  48:	            {
  49:	                 perror( to_name );
  50:	                 exit( 1 );
  51:	            }
  52:	        }
  53:	        close( from_fd );
  54:	        close( to_fd ); 
  55:	}
----------------------------------------------------------------------

open()は、ファイルを開くシステム・コールである。O_RDONLYとは、 読み込み専用でファイルを開くことを意味している。open() システム・コー ルは、結果としてファイル記述子を返す。ファイル記述子は、負でない小さな 整数(だいたい0〜1024の範囲、最大はシステムによって違う)である。 ファイル記述子は、read()システム・コールやwrite()システム・コールで実 際にファイルを読み書きする時に使われる。

エラーが起きた時には、open() システム・コールは、-1 を返す。この時、エ ラーの番号が errno という変数に格納される。perror() は、errno変数を解 析して、より詳しいエラー・メッセージを表示する関数である。perror() の 引数は、エラー・メッセージとともに表示される文字列である。

ここでは、出力用のファイルを開いている。O_WRONLYは、書き込み専用でファ イルを開くことを意味している。O_CREATは、ファイルが存在しなければ作る ように指示するものである。ファイルが存在する場合、上書きされる。 O_TRUNCは、ファイルが存在している時には、その大きさを0にすることを指 示するものである。0666 (C言語で0から始まる数は、8進数)は、ファイルを 作る時のモードである。この数値に従って、ファイルのモード(ls -l で  rwxrwxrwx と表示される部分)が決定される。作成されるファイルのモードは、 ここで指定されたモードから現在の umask が落とされた(引かれた)値とな る。

read() システム・コールは、第1引数で指定されたファイル記述子のファイ ルを読み込み、それを第2引数の番地へ保存する。読み込むバイト数は、第3 引数で与えられる。結果として読み込んだバイト数を返す。通常は、BUFFERSIZE が返される。read() システム・コールの結果、ファイル上の読み書きする位 置が、実際に読み込んだバイト数だけずれる。ファイルの末尾近くや、ファイ ルが端末の時、BUFFERSIZE 以下の値が返される。ファイルの末尾に行き着くと  0 が返される。エラーが起きると、-1 が返される。

write() システム・コールは、第1引数で指定されたファイル記述子のファイ ルへデータを書き込む。書き込まれるデータは、第2引数で与えられた番地か ら、第3引数で与えらた大きさである。write() システム・コールは、結果と して書き込んだバイト数を返す。ファイル上の読み書きする位置が、実際に読 み込んだバイト数だけずれる。通常は、BUFFERSIZE が返される。空き容量不足 などで書き込みが失敗した時には、-1 を返す。エラーが起きると、-1 が返さ れる。

close() は、ファイルを閉じるシステム・コールである。このシステム・コールを実行してしまうと、そのファイル記述子は無効になる。すなわち、read() や write() を行うと、エラーになる。

----------------------------------------------------------------------
% mkdir file [←]
% cd file [←]
/home/lab2/OS/yas/syspro-2001/file/file
% cp ~yas/syspro/file/file-copy.c . [←]
% make file-copy [←]
cc     file-copy.c   -o file-copy
% ls [←]
file-copy    file-copy.c
% echo "This is file1" > file1 [←]
% cat file1 [←]
This is file1
% ./file-copy file1 file2 [←]
% ls [←]
file-copy    file-copy.c  file1        file2
% ls -l [←]
総ブロック数 30
-rwxr-xr-x    1 yas      lab        12616   4月 22日 17時19分  file-copy
-rw-r--r--    1 yas      lab         1096   4月 22日 17時19分  file-copy.c
-rw-r--r--    1 yas      lab           14   4月 22日 17時20分  file1
-rw-r--r--    1 yas      lab           14   4月 22日 17時20分  file2
% diff file1 file2 [←]
% cmp file1 file2 [←]
% cat file2 [←]
This is file1
% []
----------------------------------------------------------------------

■マニュアルの読み方

プログラムを作る時には、参考書だけでなく、付属のマニュアルを必ず参照す る必要がある。参考書とは微妙にコマンドや関数の使い方が違っていることが ある。

◆コマンドと同名のシステムコールのマニュアルを読む

単純に man mkdir と打つと、mkdir(1) コマンドのマニュアルが表示される。 mkdir(2) システム・コールを見るには、次のように打つ。
% man 2 mkdir [←]
Irix (O2) では、同名のコマンドやライブラリ関数が複数存在した場合、man コマンドはそれらを番号の若い順に全て表示しよとする。man mkdir の場合、 一度 mkdir(1) が表示されたあと、less をq で抜けると、mkdir(2) を表示し ようとする。

Solaris では、2章の mkdir を読むには、次のように -s オプションを使う。

% man -s2 mkdir [←]

◆manコマンドを快適に使うには

O2 は、メモリが足りなくて man コマンドが非常に遅い。 快適に man コマンドを使うには、次のような方法がある。

◆openシステム・コールのマニュアル


----------------------------------------------------------------------
% man open [←]

open(n)               Tcl Built-In Commands               open(n)
..
:q

Next page ('q' to quit):[←]

open(n)                     Tcl (7.0)                     open(n)

:q
Next page ('q' to quit):[←]


open(2)                                                                open(2)

NAME
     open - open for reading or writing

C SYNOPSIS
     #include 
     #include 
     #include 

     int open (const char *path, int oflag, ... /* mode_t mode */);

DESCRIPTION
     path points to a path name naming a file.  open opens a file descriptor
     for the named file and sets the file status flags according to the value
     of oflag.  oflag values are constructed by OR-ing Flags from the
     following list (only one of the first three flags below may be used):

     O_RDONLY
            Open for reading only.

     O_WRONLY
            Open for writing only.

     O_RDWR Open for reading and writing.
...
     O_CREAT If the file exists, this flag has no effect, except as noted under
            O_EXCL below.  Otherwise, the file is created and the owner ID of
            the file is set to the effective user IDs of the process, the
            group ID of the file is set to the effective group IDs of the
            process or to the group ID of the directory in which the file is
            being created.  This is determined as follows:
...
     O_TRUNC If the file exists, its length is truncated to 0 and the mode and
            owner are unchanged.  O_TRUNC has no effect on special files or
            directories.
...

     The named file is opened unless one or more of the following are true:

     EACCES The file does not exist and write permission is denied by the
            parent directory of the file to be created.

     EACCES O_CREAT or O_TRUNC is specified and write permission is denied.
...

SEE ALSO
     chmod(2), close(2), creat(2), dup(2), exec(2), fcntl(2),
     getdtablesize(2), getmsg(2), getrlimit(2), intro(2), lseek(2), putmsg(2),
     read(2), stat(2), stat(5), umask(2), write(2)

DIAGNOSTICS
     Upon successful completion, the file descriptor is returned.  Otherwise,
     a value of -1 is returned and errno is set to indicate the error.

----------------------------------------------------------------------

■ファイル記述子

ファイル記述子は、小さな整数である。
----------------------------------------------------------------------
   1:	/*
   2:	        fd-print.c -- ファイル記述子を表示するプログラム
   3:	        ~yas/syspro/file/fd-print.c
   4:	        $Header: /home/lab2/OS/yas/syspro/file/RCS/fd-print.c,v 1.3 2001/04/22 08:41:02 yas Exp $
   5:	        Start: 1997/04/21 18:23:13
   6:	*/
   7:	
   8:	#include <fcntl.h>
   9:	
  10:	void main( int argc, char *argv[] )
  11:	{
  12:	    int i ;
  13:	    int fd ;
  14:	        for( i=1 ; i<argc ; i++ )
  15:	        {
  16:	            fd = open( argv[i],O_RDONLY );
  17:	            printf("%s: %d\n",argv[i],fd );
  18:	        }
  19:	}
----------------------------------------------------------------------
----------------------------------------------------------------------
% ./fd-print fd-print.c  [←]
fd-print.c: 3
% ./fd-print fd-print.c /etc/passwd fd-print.c [←]
fd-print.c: 3
/etc/passwd: 4
fd-print.c: 5
% []
----------------------------------------------------------------------

■標準入出力を利用したファイルのコピー(システムコール)

プログラムが実行される時、open() システム・コールを使わなくても、次の 3つのファイル記述子は使える状態になっている。

0
標準入力(standard input)
1
標準出力(standard output)
2
標準エラー出力(standard error)

stdio-thru.cは、標準入力で指定されたファイルの内容を標準出力で指定され たファイルへコピーするプログラムである。

----------------------------------------------------------------------
   1:	
   2:	/*
   3:	        stdio-thru.c -- 標準入力から標準出力へのコピー
   4:	        ~yas/syspro/file/stdio-thru.c
   5:	        $Header: /home/lab2/OS/yas/syspro/file/RCS/stdio-thru.c,v 1.3 2001/04/22 08:45:56 yas Exp $
   6:	        Start: 1995/03/04 16:40:24
   7:	*/
   8:	
   9:	#include <stdio.h>
  10:	
  11:	extern void stdio_thru(void);
  12:	
  13:	main( int argc, char *argv[] )
  14:	{
  15:	        if( argc != 1 )
  16:	        {
  17:	            fprintf( stderr,"Usage: %s\n", argv[0] );
  18:	            exit( 1 );
  19:	        }
  20:	        stdio_thru();
  21:	}
  22:	
  23:	#define BUFFERSIZE      1024
  24:	
  25:	void stdio_thru()
  26:	{
  27:	    char buffer[BUFFERSIZE] ;
  28:	    int rcount ;
  29:	    int wcount ;
  30:	
  31:	        while( (rcount=read(0,buffer,BUFFERSIZE)) > 0 )
  32:	        {
  33:	            if( (wcount=write(1,buffer,rcount))!= rcount )
  34:	            {
  35:	                 perror("stdout");
  36:	                 exit( 1 );
  37:	            }
  38:	        }
  39:	        close( 0 );
  40:	        close( 1 );
  41:	}
----------------------------------------------------------------------
このプログラムは、引数をとらない。"<" や ">" は、シェルにより解釈され、 このプログラムには渡されない。

file_copy() とは異なり、stdio_thru() では、ファイルを開く操作(open()) を行うことなく、入出力(read(),write)を行っている。このような事が可能 な理由は、UNIXでは、ファイル記述子 0, 1, 2 は、シェルにより既に開 かれているからである。

ファイル記述子 0 は、なにもしないと端末のキーボードに割り当てられてい る。ファイル記述子 1, 2 は、端末の画面に割り当てられている。しかし、 "<"、">"、"|"を使うと別のファイル やパイプに切替えられる。

----------------------------------------------------------------------
% ./stdio-thru [←]
ljd[←]
ljd
sk[←]
sk
^D
% ls bbb [←]
bbb がアクセスできません: そのようなファイルまたはディレクトリはありません
% ./stdio-thru < stdio-thru.c > bbb [←]
% ls -l stdio-thru.c bbb [←]
-rw-r--r--    1 yas      lab          690   4月 22日 17時37分  bbb
-rw-r--r--    1 yas      lab          690   4月 22日 17時35分  stdio-thru.c
% []
----------------------------------------------------------------------

■文字単位のフィルタ(ライブラリ)

----------------------------------------------------------------------
   1:	/*
   2:	        filter-char.c -- 文字単位の簡単なフィルタ
   3:	        ~yas/syspro/file/filter-char.c
   4:	        $Header: /home/lab2/OS/yas/syspro-2001/file/RCS/filter-char.c,v 1.5 2001/04/22 11:15:55 yas Exp $
   5:	        Start: 1997/04/21 18:23:13
   6:	*/
   7:	
   8:	#include <stdio.h>      /* stdin, stdout, fopen(), fclose(), getc(), */
   9:	#include <ctype.h>      /* toupper() */
  10:	
  11:	extern void filter_char_upper( FILE *in, FILE *out );
  12:	
  13:	main( int argc, char *argv[] )
  14:	{
  15:	    int i ;
  16:	    FILE *fp ;
  17:	        if( argc == 1 )
  18:	            filter_char_upper( stdin, stdout );
  19:	        else
  20:	        {
  21:	            for( i=1 ; i< argc ; i++ )
  22:	            {
  23:	                if( (fp = fopen( argv[i],"r" )) == NULL )
  24:	                {
  25:	                    perror( argv[i] );
  26:	                    exit( -1 );
  27:	                }
  28:	                filter_char_upper( fp, stdout );
  29:	                fclose( fp );
  30:	            }
  31:	        }
  32:	}
  33:	
  34:	void filter_char_upper( FILE *in, FILE *out )
  35:	{
  36:	    int c ;     /* int型。char は、不可 */
  37:	        while( (c=getc(in)) != EOF )
  38:	        {
  39:	            putc( toupper(c),out );
  40:	        }
  41:	}
----------------------------------------------------------------------

実行例

----------------------------------------------------------------------
% ./filter-char [←]
abc[←]
ABC
asdfjkl;[←]
ASDFJKL;
^D
% []
----------------------------------------------------------------------

fopen()は、ファイルを開くライブラリ関数である。"r" は、 読み込み専用で ファイルを開くことを意味している。fopen()は、結果として FILE 構造体へ のポインタを返す。これは、stdio.h で宣言されている配列 _iob[] の要素へ のポインタである。これは、次のようなライブラリ関数で使われる。

    fgetc()           fputs()           getc()         getw()
    fgets()           fread()           ungetc()
    fprintf()         fscanf()
    fputc()           fwrite()          putc()         putc()
    fflush()          fseek()           ftell()
    frewind()         fclose()          fdopen()       freopen()
    ferrror()         feof()            clearerr()     fileno()

このような、struct FILE を使うライブラリ関数群は、次のような名前で呼ば れる。

putc() は、第1引数で指定された文字を第2引数で与えられたストリームへ 出力する。

fclose() は、ファイルを閉じるライブラリ関数である。

Java 風に書くとこうなる。

class FILE {
    int fgetc();
    int fputs(String str);
    size_t fread(byte buf, size_t size, size_t nmemb);
    ...
}

	FILE stdin ;
	FILE stdout ;
	int x = stdin.fgetc();
	stdout.fputs("hello");
引数の FILE *stream が、オブジェクトとなり、普通の引数からは消える。呼 出しの時には、オブジェクトを指定する。

■バッファと FILE 構造体(ライブラリ)

システムによって FILE 構造体の内容は異なる。
----------------------------------------------------------------------
   1:	/*
   2:	        filestruct-print.c -- _filestruct[] の内容を表示する
   3:	        ~yas/syspro/file/filestruct-print.c
   4:	        $Header: /home/lab2/OS/yas/syspro-2001/file/RCS/filestruct-print.c,v 1.4 2001/04/22 11:21:40 yas Exp $
   5:	        Start: 1997/05/05 18:31:55
   6:	*/
   7:	
   8:	#include <stdio.h>
   9:	
  10:	#if     0
  11:	typedef struct {
  12:	    int                  _cnt;
  13:	    unsigned char       *_ptr;
  14:	    unsigned char       *_base;
  15:	    unsigned short       _flag;
  16:	    unsigned char        _flag;
  17:	    unsigned char        _file;
  18:	} FILE;
  19:	extern FILE             __iob[100 ];    
  20:	#endif
  21:	
  22:	extern void filestruct_print( FILE *fp );
  23:	
  24:	main()
  25:	{
  26:	    FILE *fp ;
  27:	
  28:	        printf("stdin  == 0x%x == &__iob[%d]\n",stdin, stdin-&__iob[0] );
  29:	        printf("stdout == 0x%x == &__iob[%d]\n",stdout, stdout-&__iob[0] );
  30:	        printf("stderr == 0x%x == &__iob[%d]\n",stderr, stderr-&__iob[0] );
  31:	
  32:	        fp = fopen("filestruct-print.c","r");
  33:	        printf("fp     == 0x%x == &__iob[%d]\n",fp, fp-&__iob[0] );
  34:	
  35:	        printf("fopen()\n");
  36:	        filestruct_print( fp );
  37:	        getc( fp );
  38:	        printf("getc()\n");
  39:	        filestruct_print( fp );
  40:	        getc( fp );
  41:	        printf("getc()\n");
  42:	        filestruct_print( fp );
  43:	        fclose( fp );
  44:	        printf("fclose()\n");
  45:	        filestruct_print( fp );
  46:	}
  47:	
  48:	
  49:	void filestruct_print( FILE *fp )
  50:	{
  51:	        printf("cnt:%d, ptr:0x%x, base:0x%x, flag:0%x, flag:%d, file:%d\n",
  52:	               fp->_cnt, fp->_ptr, fp->_base, fp->_flag,
  53:	               fp->_flag,fp->_file);
  54:	}
----------------------------------------------------------------------
#define getc(p)         (--(p)->_cnt < 0 ? __filbuf(p) : (int)*(p)->_ptr++)
----------------------------------------------------------------------
_cnt
バッファに入っている文字数
_ptr
バッファの先頭番地(次に読む文字)
_base
バッファ用に確保されているメモリ領域の先頭番地
_file
UNIXのファイル記述子
実行結果

----------------------------------------------------------------------
% make filestruct-print [←]
cc     filestruct-print.c   -o filestruct-print
% ./filestruct-print  [←]
stdin  == 0xfb4f044 == &__iob[0]
stdout == 0xfb4f054 == &__iob[1]
stderr == 0xfb4f064 == &__iob[2]
fp     == 0xfb4f074 == &__iob[3]
fopen()
cnt:0, ptr:0x0, base:0x0, flag:01, flag:1, file:3
getc()
cnt:1252, ptr:0x10002009, base:0x10002008, flag:09, flag:9, file:3
getc()
cnt:1251, ptr:0x1000200a, base:0x10002008, flag:09, flag:9, file:3
fclose()
cnt:0, ptr:0x0, base:0x0, flag:00, flag:0, file:3
% []
----------------------------------------------------------------------

■ライブラリとシステムコールの混在

普通、同じファイルについては、ライブラリとシステム・コールを混ぜて使う ことはしない。たとえば、read(0,,) と getc(stdin)、write(1,,) と fprintf(stdout,,) を混ぜない。ライブラリは、バッファリングしているので、 タイミングが合わなくなる。

特殊な状況では、混ぜる必要がでてくる。 その時は、次のような関数を使う。

fileno()
FILE * で使われているファイル記述子を調べる。
fdopen()
ファイル記述子から FILE * を作る。
fflush()
FILE * の内部のバッファに溜っているデータを書き出す。

■キーボード入力のバッファリング

システムコールでも、キーボードからの入力ではバッファリングが 行われる。getchar() は、リターンが押されるまで、1文字も変えさない。 それまでは、バックスペースなどで編集することもできる。

この機能を抑止するには、stty cbreak とするシステムが多いが、Irix では 利かない。stty raw だと ^C も ^D も利かなくなるので、注意する。


----------------------------------------------------------------------
% cat [←]
abc[←]
abc
^D
% stty raw; cat [←]
aabbcc

			(^Dが利かない)
			(別の端末から ps して kill)
Terminated
% []
----------------------------------------------------------------------

■練習問題

★練習問題 6 teeプログラム

file-copy.c, stdio-thru.c の2つのプログラムを参考にして、UNIX の tee プログラムと同じような機能をもつプログラムを作りなさい。プログラムの名 前は、tee ではなく、別の名前(mytee) としなさい。tee の場合、標準の tee が動いているのか、自分で作った tee が動いているのか区別がつかない。 ただし、出力するファイルは、一つでもよい。 -i オプションや -a オプションには対応しなくてもよい。

mytee は、次のようにして、標準入力を標準出力にコピーしながら、同時にファ イルにも保存するものである。

% grep pattern file1 | ./mytee result [←]
この結果として、画面には、次のように grep コマンドを実行した時と同じ結果が表示される。

% grep pattern file1 [←]

同時に、ファイル reult には、画面に出力された結果とまったく同じものが 保存される。

tee コマンドの名前は、アルファベットの「T」に由来する。図形的に考えれ ば、動きを理解することができる。tee コマンドは、左(パイプラインで標準 入力)から入ったデータを下(引数で与えられたファイル)に保存しながら、 右(標準出力)にも出力する。tee コマンドは、時間のかかる処理の結果や、 後で参照したい中間結果をファイルに保存するために利用される。次の例は、 makeコマンドの実行結果を make.out へ保存すると同時に、それを user にメー ルで送るものである。

% make |& tee make.out | mailx -s make result user [←]
''

これにより、user は、メールが届いたことで、make コマンドの実行終了を知 ることができる。同時に、作業していたディレクトリでその結果を参照するこ とができる。

この課題では、入出力には、システムコールを使いなさい。

★練習問題 7 catプログラム

UNIXの cat コマンドと同様の動きをするプログラムを作りなさい。このプロ グラムの名前を mycat とする。引数として、なにも与えられなかった時には、 標準入力を標準出力にコピーしなさい。また、引数として "-" が指定された 時には、その位置で標準入力を標準出力にコピーしなさい。たとえば、次のよ うな場合、file1 とfile2 の内容の間に、標準入力の内容をコピーしなさい。

% mycat file1 - file2 [←]

この課題では、入出力には、システムコールを使いなさい。

★練習問題 8 フィルタのプログラム

高水準入出力ライブラリ関数を持ちいて、次のようなフィルタを作りなさい。

文字単位のフィルタでは、getc(), fgetc(), getchar() と putc(), fputc(), putchar() などを使うとよい。行単位のフィルタでは、 fgets(), fputs(), fprintf() などを使うとよい。 (gets(), fscanf(), scanf() は、セキュリティ・ホールになりやすいので、使 わない。)

★練習問題 9 manの表示

man コマンド、xman 、mule の ESC x man の使い方を練習しなさい。

man で表示された #includeの部分や定数を、Xウインドウ、または、mule の コピー&ペースト機能を使ってソース・プログラムに取り込みなさい。