システム・プログラム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
この時、chdir(2) を使って、探すディレクトリを "." で参照できるようにし なさい。ディレクトリの内容の読み込みには、getdents(2) か、 opendir(3) を使いなさい。2 / 2 usr 27650 local 29966 bin 29970 tcsh