システム・プログラムI
電子・情報工学系
新城 靖
<yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/syspro1-1998/1998-05-12
あるいは、次のページから手繰っていくこともできます。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/
http://www.hlla.is.tsukuba.ac.jp/~yas/index-j.html
例題のファイルは、簡単にコピーできる。
ディレクトリを作ろう。
fread(), fwrite() を使う。
ポータビリティ(別のOSやCPUのコンピュータでも使う)を考える時には、 文字コード、バイトオーダー、浮動小数点の形式、構造体の穴に注意する。 XDR ライブラリを使えば、ポータビリティが高くなる。(XDR については、3 学期の「分散システム」で。)
----------------------------------------------------------------------
1:
2: /*
3: utmp-print.c -- /var/adm/utmp の内容を表示するプログラム
4: ~/syspro1/file/utmp-print.c
5: $Header: /home/lab2/OS/yas/syspro1-1998/file/RCS/utmp-print.c,v 1.4 1998/05/11 14:54:49 yas Exp $
6: Start: 1995/03/06 09:01:09
7: */
8:
9: #include <stdio.h> /* FILE, NULL, stderr */
10: #include <utmp.h> /* struct utmp */
11: #include <time.h> /* ctime() */
12:
13: #if 0
14: struct utmp
15: {
16: char ut_user[8] ; /* User login name */
17: char ut_id[4] ; /* /etc/inittab id(usually line #) */
18: char ut_line[12] ; /* device name (console, lnxx) */
19: short ut_pid ; /* leave short for compatiblity - process id */
20: short ut_type ; /* type of entry */
21: struct exit_status ut_exit ;/* The exit status of a process
22: * marked as DEAD_PROCESS.
23: */
24: time_t ut_time ; /* time entry was made */
25: } ;
26: #endif
27:
28: void utmp_print_file();
29: void utmp_print( struct utmp *u );
30: void print_char_array( char a[], int len );
31:
32: main()
33: {
34: utmp_print_file();
35: }
36:
37: void utmp_print_file()
38: {
39: struct utmp entry;
40: FILE *fp ;
41: fp = fopen( UTMP_FILE, "r" );
42: if( fp == NULL )
43: {
44: perror( UTMP_FILE );
45: exit( -1 );
46: }
47: while( fread(&entry, sizeof(entry),1,fp ) == 1 )
48: {
49: utmp_print( &entry );
50: }
51: fclose( fp );
52: }
53:
54: void utmp_print( struct utmp *u )
55: {
56: print_char_array( u->ut_user, sizeof(u->ut_user) );
57: print_char_array( u->ut_id, sizeof(u->ut_id) );
58: print_char_array( u->ut_line, sizeof(u->ut_line) );
59: printf("%6d ", u->ut_pid );
60: printf("%6d ", u->ut_type );
61: printf("%s",ctime(&u->ut_time) );
62: }
63:
64: void print_char_array( char a[], int len )
65: {
66: int i ;
67: for( i=0 ; i<len && a[i] ; i++ )
68: putchar( a[i] );
69: for( ; i<len ; i++ )
70: putchar(' ');
71: putchar(' ');
72: }
----------------------------------------------------------------------
実行結果
---------------------------------------------------------------------- % ./utmp-printsystem boot 0 2 Sun Apr 26 05:32:18 1998 run-level 2 0 1 Sun Apr 26 05:32:18 1998 lnsyscon link 32 8 Sun Apr 26 05:32:18 1998 rc2 s2 35 8 Sun Apr 26 05:33:27 1998 LOGIN t1 ttyd1 703 6 Sun Apr 26 05:33:28 1998 rlogin q0 ttyq0 11481 8 Mon May 11 23:24:53 1998 yas q1 ttyq1 11506 7 Mon May 11 23:24:45 1998 yas q2 ttyq2 11521 7 Mon May 11 23:24:59 1998 i97???? q3 ttyq3 265 8 Sat May 9 18:01:24 1998 t??? q4 ttyq4 28706 8 Wed May 6 13:55:54 1998 s??? q5 ttyq5 2818 8 Sun Apr 26 16:40:12 1998 s??? q6 ttyq6 2889 8 Sun Apr 26 17:05:52 1998 s??? q7 ttyq7 2890 8 Sun Apr 26 17:05:52 1998 % who
yas ttyq1 5月 11日 23時24分 yas ttyq2 5月 11日 23時24分 %
----------------------------------------------------------------------
UNIXでは、ファイルを read() したり write() したりするたびに、ファイル を読み書きする位置(先頭からのバイト数)が移動する。この位置は、シーク・ ポインタやファイル・ポインタと呼ばれる。(FILE *とは違うので注意。)
ファイルを先頭から順にアクセスするのではなく、ランダムにアクセスしたい 時には、まず読み書きしたい場所にシーク・ポインタを移動させる。これを行 うのが、lseek() システム・コールである。
引数は、ファイル記述子、オフセット(バイト数)、移動方法である。移動方法 (whence)には、次の3種類のいずれかを指定する。off_t lseek(int fildes, off_t offset, int whence);
----------------------------------------------------------------------
1:
2: /*
3: wtmp-last10.c -- /var/adm/wtmpの最後の10個を表示するプログラム
4: ~yas/syspro1/file/wtmp-last10.c
5: $Header: /home/lab2/OS/yas/syspro1/file/RCS/wtmp-last10.c,v 1.3 1998/05/11 15:09:23 yas Exp $
6: Start: 1995/03/06 09:01:09
7: */
8:
9: #include <stdio.h> /* FILE, NULL, stderr */
10: #include <utmp.h> /* struct utmp */
11: #include <sys/types.h> /* open(),lseek() */
12: #include <sys/stat.h> /* open() */
13: #include <fcntl.h> /* open() */
14: #include <unistd.h> /* lseek() */
15:
16: void wtmp_last10_print_file();
17: void utmp_print( struct utmp *u );
18: void print_char_array( char a[], int len );
19:
20: main()
21: {
22: wtmp_last10_print_file();
23: }
24:
25: void wtmp_last10_print_file()
26: {
27: struct utmp entry;
28: int fd, i ;
29: off_t offset ;
30:
31: if( (fd = open( WTMP_FILE, O_RDONLY )) == -1 )
32: {
33: perror( WTMP_FILE );
34: exit( -1 );
35: }
36: for( i=-1 ; i>-10; i-- )
37: {
38: offset = sizeof(struct utmp)*i ;
39:
40: if( lseek( fd, offset, SEEK_END ) == -1 )
41: {
42: perror("lseek");
43: exit( -1 );
44: }
45: if( read(fd, &entry, sizeof(entry)) != sizeof(entry) )
46: {
47: perror("read");
48: exit( -1 );
49: }
50: utmp_print( &entry );
51: }
52: close( fd );
53: }
54:
55: void utmp_print( struct utmp *u )
56: {
57: print_char_array( u->ut_user, sizeof(u->ut_user) );
58: print_char_array( u->ut_id, sizeof(u->ut_id) );
59: print_char_array( u->ut_line, sizeof(u->ut_line) );
60: printf("%6d ", u->ut_pid );
61: printf("%6d ", u->ut_type );
62: printf("%s",ctime(&u->ut_time) );
63: }
64:
65: void print_char_array( char a[], int len )
66: {
67: int i ;
68: for( i=0 ; i<len && a[i] ; i++ )
69: putchar( a[i] );
70: for( ; i<len ; i++ )
71: putchar(' ');
72: putchar(' ');
73: }
74:
75: /* ------------------------ utmp-print.c ------------------------ */
76: static char rcsid[] =
77: "$Header: /home/lab2/OS/yas/syspro1/file/RCS/wtmp-last10.c,v 1.3 1998/05/11 15:09:23 yas Exp $" ;
----------------------------------------------------------------------
実行例。
---------------------------------------------------------------------- % ./wtmp-last10yas q4 ttyq4 16360 7 Tue May 12 00:07:23 1998 rlogin q4 ttyq4 16360 6 Tue May 12 00:07:23 1998 telnet q3 ttyq3 16201 8 Mon May 11 23:47:57 1998 e96???? q3 ttyq3 16202 7 Mon May 11 23:43:51 1998 telnet q3 ttyq3 16202 6 Mon May 11 23:43:47 1998 a??? q1 ttyq1 16110 7 Mon May 11 23:33:52 1998 telnet q1 ttyq1 16110 6 Mon May 11 23:33:49 1998 telnet q1 ttyq1 16063 8 Mon May 11 23:33:43 1998 a??? q1 ttyq1 16064 7 Mon May 11 23:08:18 1998 % last | head -10
yas ttyq4 hlla-gw.hlla.is.tsuku Tue May 12 00:07 still logged in e96???? ttyq3 130.158.125.185 Mon May 11 23:43 - 23:47 (00:04) a??? ttyq1 AMIcc-01p08.ppp.xxxxx Mon May 11 23:33 still logged in a??? ttyq1 AMIcc-01p08.ppp.xxxxx Mon May 11 23:08 - 23:33 (00:25) k????? ttyq0 balsam-serv Mon May 11 22:31 still logged in t???? ttyq1 adonis7 Mon May 11 21:49 - 21:49 (00:00) to???? ttyq0 icho.ipe.tsukuba.ac.j Mon May 11 21:38 - 22:01 (00:23) m???? ttyq0 azalea11 Mon May 11 21:06 - 21:17 (00:11) to???? ttyq4 icho.ipe.tsukuba.ac.j Mon May 11 19:06 - 19:09 (00:03) m???? ttyq4 azalea11 Mon May 11 18:01 - 18:07 (00:05) %
----------------------------------------------------------------------
ファイルの内容を操作する方法として、read(), write() とは全く違った方法 がある。それは、ファイルの内容をメモリにマップ(map, 張り付ける)し、 ファイルの内容を配列のようにして扱う方法である。
----------------------------------------------------------------------
1:
2: /*
3: mmap-head.c -- mmap(2) を使った head プログラム
4: $Header: /home/lab2/OS/yas/syspro1/file/RCS/mmap-head.c,v 1.1 1998/05/11 16:06:46 yas Exp $
5: ~yas/syspro1/file/mmap-head.c
6: <-- mmap-cat.c,v 1.1 1997/05/12 22:56:02 yas Exp $
7: Start: 1998/05/12 01:06:15
8: */
9:
10: #include <stdio.h> /* stderr */
11: #include <sys/types.h> /* open(),mmap() */
12: #include <sys/stat.h> /* open() */
13: #include <fcntl.h> /* open() */
14: #include <sys/mman.h> /* mmap() */
15:
16: extern void mmap_head( char *name );
17: extern char *mmap_file( char *name /* in */, size_t *filesizep /* out */);
18:
19: void main( int argc, char *argv[] )
20: {
21: if( argc != 2 )
22: {
23: fprintf( stderr,"Usage: %% %s filename\n",argv[0] );
24: exit( 1 );
25: }
26: mmap_head( argv[1] );
27: }
28:
29: void mmap_head( char *name )
30: {
31: size_t size,i ; /* unsigned int */
32: char *file_address ;
33: int nlcount ;
34:
35: file_address = mmap_file( name,&size );
36: if( ((int)file_address) == -1 )
37: {
38: perror( name );
39: exit( 1 );
40: }
41: for( i = 0, nlcount=0 ; i<size ; i ++ )
42: {
43: if( file_address[i] == '\n' )
44: {
45: nlcount ++ ;
46: if( nlcount >= 10 )
47: {
48: i++ ;
49: break;
50: }
51: }
52: }
53: write( 1, file_address, i );
54: }
55:
56: char *mmap_file( char *name /* in */, size_t *filesizep /* out */)
57: {
58: int fd ;
59: struct stat buf ;
60: size_t size,p ; /* unsigned int */
61: off_t off ; /* long */
62: caddr_t addr ; /* char * */
63: if( (fd = open( name,O_RDONLY )) == -1 )
64: return( (char *)-1 );
65: fstat( fd, &buf );
66: size = buf.st_size ;
67: addr = 0 ; off = 0 ;
68: addr = mmap( addr, size, PROT_READ, MAP_PRIVATE, fd, off );
69: if( (int)addr == -1 )
70: {
71: close( fd );
72: return( (char *)-1 );
73: }
74: close( fd );
75: if( filesizep )
76: *filesizep = size ;
77: return( addr );
78: }
----------------------------------------------------------------------
実行例。
---------------------------------------------------------------------- % ./mmap-head /etc/passwdroot:aBcDefGhijklmN:0:0:Super-User:/:/bin/csh sysadm:*:0:0:System V Administration:/usr/admin:/bin/sh diag:*:0:996:Hardware Diagnostics:/usr/diags:/bin/csh daemon:*:1:1:daemons:/:/dev/null bin:*:2:2:System Tools Owner:/bin:/dev/null uucp:*:3:5:UUCP Owner:/usr/lib/uucp:/bin/csh sys:*:4:0:System Activity Owner:/var/adm:/bin/sh adm:*:5:3:Accounting Files Owner:/var/adm:/bin/sh lp:*:9:9:Print Spooler Owner:/var/spool/lp:/bin/sh nuucp:*:10:10:Remote UUCP User:/var/spool/uucppublic:/usr/lib/uucp/uucico %
----------------------------------------------------------------------
UNIXのディレクトリは、ディスク中では、可変長の構造体になっている。C言 語では、直接的には可変長の構造体を扱うことはできない。
IRIX では、getdents(2) システム・コールを使うと、ディスク中に保存され たディレクトリに近いデータを得ることができる。(システム・コールは、シ ステムにより異なる。システムによっては、ディレクトリについても、read() が使えるものがある。SunOS, Solaris でも、getdents()。HP-UX では、 getdirentries() )
----------------------------------------------------------------------
1: /*
2: dir-getdents.c -- ディレクトリの内容を表示するプログラム
3: ~yas/syspro1/dir/dir-getdents.c
4: $Header: /home/lab2/OS/yas/syspro1/dir/RCS/dir-getdents.c,v 1.2 1998/05/11 17:18:01 yas Exp $
5: Start: 1995/03/07 21:44:51
6: */
7:
8: #include <stdio.h> /* fprintf(), stderr */
9: #include <sys/types.h> /* open(2) */
10: #include <sys/stat.h> /* open(2) */
11: #include <fcntl.h> /* open(2) */
12: #include <sys/dirent.h> /* getdents(2) */
13:
14: #if 0
15: typedef struct dirent { /* data from readdir() */
16: ino_t d_ino; /* inode number of entry */
17: off_t d_off; /* offset of disk directory entry */
18: unsigned short d_reclen; /* length of this record */
19: char d_name[1]; /* name of file */
20: } dirent_t;
21: #endif
22:
23:
24: extern void dir_list( char *dirname );
25: extern void xdump( unsigned char *buff, int n );
26: extern void xdump16( unsigned char *buff, int n );
27:
28: main( int argc, char *argv[] )
29: {
30: if( argc != 2 )
31: {
32: fprintf( stderr,"Usage:%% %s dirname \n",argv[0] );
33: exit( 1 );
34: }
35: dir_list( argv[1] );
36: }
37:
38: void dir_list( char *dirname )
39: {
40: int fd ;
41: struct dirent *p ;
42: char buff[BUFSIZ] ;
43: int rcount ;
44:
45: fd = open( dirname,O_RDONLY );
46: if( fd == -1 )
47: {
48: perror( dirname );
49: exit( 1 );
50: }
51:
52: while( (rcount=getdents(fd,(dirent_t *)buff,BUFSIZ)) >0 )
53: {
54: xdump( buff, rcount );
55: for( p = (struct dirent *)buff ; (char *)p < &buff[rcount] ;
56: p=(struct dirent *) ((int)p+(p->d_reclen)) )
57: {
58: printf("p:%d, ", (char *)p - buff );
59: printf("off:%u, ", p->d_off ); /* long */
60: printf("ino:%u, ", p->d_ino ); /* unsigned long */
61: printf("reclen:%d, ", p->d_reclen );
62: printf("name:%s\n", p->d_name );
63: }
64: }
65: close( fd );
66: }
67:
68: void xdump( unsigned char *buff, int n )
69: {
70: if( n<0 || n>100000 )
71: return;
72: for( ; n>0 ; n-=16, buff+=16 )
73: xdump16( buff,n>=16?16:n );
74: }
75:
76: void xdump16( unsigned char *buff, int n )
77: {
78: register int i ;
79: for( i=0 ; i<n ; i++ )
80: printf("%02x ",buff[i] );
81: for( i=n ; i<16 ; i++ )
82: printf(" ");
83: for( i=0 ; i<n ; i++ )
84: printf("%c",isprint(buff[i])?buff[i]:'#' );
85: for( i=n ; i<16 ; i++ )
86: printf(" ");
87: printf("\n");
88: }
89:
----------------------------------------------------------------------
実行例。
---------------------------------------------------------------------- % ./dir-getdents dir100 07 a5 60 00 00 17 2e 00 0c 2e 00 01 8f 70 21 ###`###.##.###p! 6d 3b 32 b4 00 10 2e 2e 00 00 00 00 00 07 a5 63 m;2###..#######c 6d 3b 32 b7 00 10 66 69 6c 65 32 00 00 07 a5 61 m;2###file2####a ff ff ff ff 00 10 66 69 6c 65 31 00 ######file1# p:0, off:5934, ino:501088, reclen:12, name:. p:12, off:1832596148, ino:26177569, reclen:16, name:.. p:28, off:1832596151, ino:501091, reclen:16, name:file2 p:44, off:4294967295, ino:501089, reclen:16, name:file1 %
----------------------------------------------------------------------
ディレクトリを読むためのライブラリ関数として、次のようなものがある。
----------------------------------------------------------------------
#include <sys/types.h>
#include <sys/dir.h>
DIR *opendir(char *filename);
struct direct *readdir(DIR *dirp);
long telldir(DIR *dirp);
void seekdir(DIR *dirp, long loc);
void rewinddir(DIR *dirp);
void closedir(DIR *dirp);
int dirfd(DIR *dirp)
----------------------------------------------------------------------
このライブラリ関数は、システム・コールと比較して移植性が高い。
----------------------------------------------------------------------
1: /*
2: dir-mkdir.c -- ディレクトリを作成するプログラム
3: ~yas/syspro1/dir/dir-mkdir.c
4: $Header: /home/lab2/OS/yas/syspro1/dir/RCS/dir-mkdir.c,v 1.2 1998/05/11 17:01:35 yas Exp $
5: Start: 1997/05/12 21:26:10
6: */
7:
8: #include <stdio.h>
9: #include <sys/stat.h>
10:
11: void main( int argc, char *argv[] )
12: {
13: if( argc != 2 )
14: {
15: fprintf( stderr,"Usage:%% %s dirname \n",argv[0] );
16: exit( 1 );
17: }
18: if( mkdir( argv[1],0777 ) == -1 )
19: {
20: perror( argv[1] );
21: }
22: }
----------------------------------------------------------------------
単純に man mkdir と打つと、mkdir(1) コマンドのマニュアルが表示される。
mkdir(2) システム・コールを見るには、次のように打つ。
% man 2 mkdir![]()
次のような長い表示をするUNIXの who コマンドと似た動きをするプログラム を作りなさい。
括弧の中は、ログインしてきたホスト名やXウィンドウのディスプレイの名前。---------------------------------------------------------------------- % whoyas ttyp0 Apr 28 20:32 (top:0.0) yas ttyp1 May 11 22:19 (revolution) yas ttyp2 May 11 20:03 (hlla0:0.0) % ----------------------------------------------------------------------
ヒント:utmp ではなく、utmpx を使うとよい。
wtmp ではなく wtmpx を使うと、ホスト名まで調べられる。
opendir(3) を用いて、次のような動きをするプログラムを作りなさい。
次のような長いファイル名が与えられたとする。
これに対して、次のようにディレクトリとファイルのinode番号(fileno)を表 示するプログラムを作りなさい。/usr/local/bin/tcsh
2 /
2 usr
27650 local
29966 bin
29970 tcsh
この時、chdir(2) を使って、探すディレクトリを "." で参照できるようにし
なさい。ディレクトリの内容の読み込みには、getdents(2) か、
opendir(3) を使いなさい。