システム・プログラムI 電子・情報工学系 新城 靖 <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/syspro1-1997/1997-05-13
あるいは、次のページから手繰っていくこともできます。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/
http://www.hlla.is.tsukuba.ac.jp/~yas/index-j.html
UNIXの who コマンドと似た動きをするプログラムを作りなさい。
UNIXのディレクトリは、ディスク中では、可変長の構造体になっている。C言 語では、直接的には可変長の構造体を扱うことはできない。
HP-UX では、getdirentries(2) システム・コールを使うと、ディスク中に保 存されたディレクトリに近いデータを得ることができる。(システム・コール は、システムにより異なる。システムによっては、ディレクトリについても、 read() が使えるものがある。SunOS, Solaris では、getdents()。)
---------------------------------------------------------------------- 1: /* 2: dir-getdirentries.c -- ディレクトリの内容を表示するプログラム 3: /usr/local/LECTURES/syspro-1997-shinjo/dir/dir-getdirentries.c 4: $Header: dir-getdirentries.c,v 1.4 97/05/12 20:17:29 yas Exp $ 5: Start: 1995/03/07 21:44:51 6: */ 7: 8: #include <stdio.h> /* fprintf(), stderr */ 9: #include <fcntl.h> /* open(2) */ 10: #include <ndir.h> /* getdirentries(2) */ 11: 12: #if 0 13: struct direct { 14: long d_fileno; /* file number of entry */ 15: short d_reclen; /* length of this record */ 16: short d_namlen; /* length of string in d_name */ 17: char d_name[MAXNAMLEN + 1]; /* name (up to MAXNAMLEN + 1) */ 18: }; 19: #endif 0 20: 21: main( argc,argv ) 22: int argc ; 23: char *argv[] ; 24: { 25: if( argc != 2 ) 26: { 27: fprintf( stderr,"Usage:%% %s dirname \n",argv[0] ); 28: exit( 1 ); 29: } 30: dir_list( argv[1] ); 31: } 32: 33: #define BUFFSIZE 1024 34: 35: dir_list( dirname ) 36: char *dirname ; 37: { 38: int fd ; 39: struct direct *p ; 40: char buff[BUFFSIZE] ; 41: int rcount ; 42: off_t offset ; 43: 44: fd = open( dirname,O_RDONLY ); 45: if( fd == -1 ) 46: { 47: perror( dirname ); 48: exit( 1 ); 49: } 50: 51: offset = 0 ; 52: while( (rcount=getdirentries(fd,buff,BUFFSIZE,&offset)) >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("fileno:%d, ", p->d_fileno ); 60: printf("reclen:%d, ", p->d_reclen ); 61: printf("namlen:%d, ", p->d_namlen ); 62: printf("name:%s\n", p->d_name ); 63: } 64: } 65: close( fd ); 66: free( buff ); 67: } 68: 69: xdump( buff,n ) 70: unsigned char *buff ; 71: int n ; 72: { 73: if( n<0 || n>100000 ) 74: return; 75: for( ; n>0 ; n-=16, buff+=16 ) 76: xdump16( buff,n>=16?16:n ); 77: } 78: 79: xdump16( buff,n ) 80: register unsigned char *buff ; 81: { 82: register int i ; 83: for( i=0 ; i<n ; i++ ) 84: printf("%02x ",buff[i] ); 85: for( i=n ; i<16 ; i++ ) 86: printf(" "); 87: for( i=0 ; i<n ; i++ ) 88: printf("%c",isprint(buff[i])?buff[i]:'#' ); 89: for( i=n ; i<16 ; i++ ) 90: printf(" "); 91: printf("\n"); 92: } 93: ----------------------------------------------------------------------実行例。
---------------------------------------------------------------------- % ls -ia dir1227366 . 184351 .. 227367 file1 227369 file2 % ./dir-getdirentries dir1
00 03 78 26 00 0c 00 01 2e 00 73 6c 00 02 d0 1f ##x###.#sl#### 00 0c 00 02 2e 2e 00 72 00 03 78 27 00 10 00 05 ####..#r##x’#### 66 69 6c 65 31 00 78 64 00 03 78 29 00 10 00 05 file1#xd##x)#### 66 69 6c 65 32 00 00 2f file2##/ p:0, fileno:227366, reclen:12, namlen:1, name:. p:12, fileno:184351, reclen:12, namlen:2, name:.. p:24, fileno:227367, reclen:16, namlen:5, name:file1 p:40, fileno:227369, reclen:16, namlen:5, name:file2 %
----------------------------------------------------------------------
ディレクトリを読むためのライブラリ関数として、次のようなものがある。
---------------------------------------------------------------------- #include <dirent.h> DIR *opendir(const char *dirname); struct dirent *readdir(DIR *dirp); long int telldir(DIR *dirp); void seekdir(DIR *dirp, long int loc); void rewinddir(DIR *dirp); int closedir(DIR *dirp); ----------------------------------------------------------------------このライブラリ関数は、システム・コールと比較して移植性が高い。
opendir(3) を用いて、次のような動きをするプログラムを作りなさい。
次のような長いファイル名が与えられたとする。
これに対して、次のようにディレクトリとファイルのinode番号(fileno)を表 示するプログラムを作りなさい。/usr/local/bin/tcsh
この時、chdir(2) を使って、探すディレクトリを "." で参照できるようにし なさい。ディレクトリの内容の読み込みには、getdirentries(2) か、 opendir(3) を使いなさい。2 / 2 usr 27650 local 29966 bin 29970 tcsh
---------------------------------------------------------------------- 1: /* 2: dir-mkdir.c -- ディレクトリを作成するプログラム 3: /usr/local/LECTURES/syspro-1997-shinjo/dir/dir-mkdir.c 4: $Header: dir-mkdir.c,v 1.1 97/05/12 21:29:36 yas Exp $ 5: Start: 1997/05/12 21:26:10 6: */ 7: 8: #include <stdio.h> 9: #include <sys/stat.h> 10: 11: main( argc,argv ) 12: int argc ; 13: char *argv[] ; 14: { 15: if( argc != 2 ) 16: { 17: fprintf( stderr,"Usage:%% %s dirname \n",argv[0] ); 18: exit( 1 ); 19: } 20: if( mkdir( argv[1],0777 ) == -1 ) 21: { 22: perror( argv[1] ); 23: } 24: } ----------------------------------------------------------------------単純に man mkdir と打つと、mkdir(1) コマンドのマニュアルが表示される。 mkdir(2) システム・コールを見るには、次のように打つ。
% man 2 mkdir![]()
ディレクトリを stat(2) で調べると、リンク数が2以上になっている。 "." と 子ディレクトリの ".." の分だけ増えている。---------------------------------------------------------------------- % ls -li /usr/bin/{compress,uncompress,zcat}4146 -r-xr-xr-x 3 bin bin 24576 Nov 8 1993 /usr/bin/compress 4146 -r-xr-xr-x 3 bin bin 24576 Nov 8 1993 /usr/bin/uncompress 4146 -r-xr-xr-x 3 bin bin 24576 Nov 8 1993 /usr/bin/zcat %
----------------------------------------------------------------------
本名は、open(), creat(), mkdir() の時に作られる。それ以外に、link() シ ステム・コールで増やすことができる。リンクを減らすには、unlink() シス テム・コールを使う。リンクが1つしかないファイルに unlink() を行うと、 ファイルが削除される。
シンボリック・リンクは、別のファイルの名前(シンボル)を含んでいる特殊 なファイルである。シンボリック・リンクは、symlink() システム・コールで 作成することができる。
UNIXでは、ファイルを read() したり write() したりするたびに、ファイル を読み書きする位置(先頭からのバイト数)が移動する。この位置は、シーク・ ポインタやファイル・ポインタと呼ばれる。(FILE *とは違うので注意。)
ファイルを先頭から順にアクセスするのではなく、ランダムにアクセスしたい 時には、まず読み書きしたい場所にシーク・ポインタを移動させる。これを行 うのが、lseek() システム・コールである。
引数は、ファイル記述子、オフセット(バイト数)、移動方法である。移動方法 (whence)には、次の3種類のいずれかを指定する。off_t lseek(int fildes, off_t offset, int whence);
---------------------------------------------------------------------- 1: 2: /* 3: wtmp-last10.c -- /etc/wtmpの最後の10個を表示するプログラム 4: /usr/local/LECTURES/syspro-1997-shinjo/file/wtmp-last10.c 5: $Header: wtmp-last10.c,v 1.2 97/05/12 22:20:46 yas Exp $ 6: Start: 1995/03/06 09:01:09 7: */ 8: 9: #include <stdio.h> /* FILE, NULL, stderr */ 10: #include <sys/types.h> /* struct utmp */ 11: #include <utmp.h> /* struct utmp */ 12: #include <fcntl.h> /* open() */ 13: #include <unistd.h> /* lseek() */ 14: 15: main() 16: { 17: wtmp_last10_print_file(); 18: } 19: 20: wtmp_last10_print_file() 21: { 22: struct utmp entry; 23: int fd, i ; 24: off_t offset ; 25: 26: if( (fd = open( WTMP_FILE, O_RDONLY )) == -1 ) 27: { 28: perror( WTMP_FILE ); 29: exit( -1 ); 30: } 31: for( i=-1 ; i>-10; i-- ) 32: { 33: offset = sizeof(struct utmp)*i ; 34: 35: if( lseek( fd, offset, SEEK_END ) == -1 ) 36: { 37: perror("lseek"); 38: exit( -1 ); 39: } 40: if( read(fd, &entry, sizeof(entry)) != sizeof(entry) ) 41: { 42: perror("read"); 43: exit( -1 ); 44: } 45: utmp_print( &entry ); 46: } 47: close( fd ); 48: } 49: 50: utmp_print( u ) 51: register struct utmp *u ; 52: { 53: print_char_array( u->ut_user, sizeof(u->ut_user) ); 54: print_char_array( u->ut_id, sizeof(u->ut_id) ); 55: print_char_array( u->ut_line, sizeof(u->ut_line) ); 56: printf("%d ", u->ut_type ); 57: printf("%6d ", u->ut_pid ); 58: print_char_array( u->ut_host, sizeof(u->ut_host) ); 59: printf("%s",ctime(&u->ut_time) ); 60: } 61: 62: print_char_array( a,len ) 63: char a[]; 64: 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: } ----------------------------------------------------------------------実行例。
---------------------------------------------------------------------- % ./wtmp-last10yas s0 pty/ttys0 8 18905 Mon May 12 22:40:28 1997 yas s0 pty/ttys0 7 18905 orchid Mon May 12 22:39:13 1997 LOGIN s0 pty/ttys0 6 18905 orchid Mon May 12 22:39:12 1997 xxxx s0 pty/ttys0 8 18875 Mon May 12 20:33:21 1997 xxxx s0 pty/ttys0 7 18875 Mon May 12 20:31:24 1997 LOGIN s0 pty/ttys0 6 18875 Mon May 12 20:31:23 1997 xxxx s0 pty/ttys0 8 18848 Mon May 12 20:05:42 1997 xxxx s0 pty/ttys0 7 18848 Mon May 12 20:02:02 1997 LOGIN s0 pty/ttys0 6 18848 Mon May 12 20:02:01 1997 % last | head -10
yas pty/ttys0 Mon May 12 22:39 - 22:40 (00:01) xxxx pty/ttys0 Mon May 12 20:31 - 20:33 (00:01) xxxx pty/ttys0 Mon May 12 20:02 - 20:05 (00:03) xxxx pty/ttys0 Mon May 12 18:56 - 18:58 (00:01) e999999 console Mon May 12 18:41 - 19:11 (00:29) e999999 console Mon May 12 18:32 - 18:41 (00:08) i999999 pty/ttys0 Mon May 12 17:40 - 17:41 (00:01) e999999 console Mon May 12 17:26 - 18:32 (01:05) i999999 pty/ttys2 Mon May 12 16:42 - 17:37 (00:54) xxxx pty/ttys1 Mon May 12 16:26 - 18:02 (01:35) % who
yas pty/ttysb May 12 22:39 %
----------------------------------------------------------------------
ファイルの内容を操作する方法として、read(), write() とは全く違った方法 がある。それは、ファイルの内容をメモリにマップ(map, 張り付ける)し、 ファイルの内容を配列のようにして扱う方法である。
---------------------------------------------------------------------- 1: 2: /* 3: mmap-cat.c -- mmap(2) を使った cat プログラム 4: $Header: mmap-cat.c,v 1.1 97/05/12 22:56:02 yas Exp $ 5: /usr/local/LECTURES/syspro-1997-shinjo/file/wtmp-last10.c 6: Start: 1997/05/12 17:15:57 7: */ 8: 9: #include <stdio.h> /* stderr */ 10: #include <sys/types.h> /* open(),mmap() */ 11: #include <sys/stat.h> /* open() */ 12: #include <fcntl.h> /* open() */ 13: #include <sys/mman.h> /* mmap() */ 14: #include <unistd.h> /* sysconf() */ 15: 16: extern char *mmap_file(); 17: 18: main( argc,argv ) 19: int argc ; 20: char *argv[] ; 21: { 22: if( argc != 2 ) 23: { 24: fprintf( stderr,"Usage: %% %s filename\n",argv[0] ); 25: exit( 1 ); 26: } 27: mmap_cat( argv[1] ); 28: } 29: 30: #define min( x, y ) ((x)<=(y)?(x):(y)) 31: 32: mmap_cat( filename ) 33: char *filename ; 34: { 35: int pagesize,rem,wcount ; 36: size_t size,pos ; /* unsigned int */ 37: char *file_address ; 38: 39: file_address = mmap_file( filename,&size ); 40: if( ((int)file_address) == -1 ) 41: { 42: perror( filename ); 43: exit( 1 ); 44: } 45: pagesize = sysconf( _SC_PAGE_SIZE ); 46: for( pos = 0 ; pos<size ; pos += pagesize ) 47: { 48: rem = min( pagesize, size ); 49: if( (wcount = write( 1, &file_address[pos], rem )) != rem ) 50: { 51: perror("write"); 52: exit( 1 ); 53: } 54: } 55: } 56: 57: char *mmap_file( filename,filesizep ) 58: char *filename ; /* in */ 59: size_t *filesizep ; /* out */ 60: { 61: int fd ; 62: struct stat buf ; 63: size_t size,p ; /* unsigned int */ 64: off_t off ; /* long */ 65: caddr_t addr ; /* char * */ 66: if( (fd = open( filename,O_RDONLY )) == -1 ) 67: return( (char *)-1 ); 68: fstat( fd, &buf ); 69: size = buf.st_size ; 70: addr = 0 ; off = 0 ; 71: addr = mmap( addr, size, PROT_READ, MAP_PRIVATE, fd, off ); 72: if( (int)addr == -1 ) 73: { 74: close( fd ); 75: return( (char *)-1 ); 76: } 77: close( fd ); 78: if( filesizep ) 79: *filesizep = size ; 80: return( addr ); 81: } ----------------------------------------------------------------------実行例。
mmap() の方が速いのは、なぜか。---------------------------------------------------------------------- % ls -l /hp-ux-rwxr-xr-x 1 root other 2297856 Jun 16 1995 /hp-ux %
% time ./mmap-cat /hp-ux > /dev/null
0.03u 0.06s 0:00.09 100.0% % time ./mmap-cat /hp-ux > /dev/null
0.01u 0.07s 0:00.00 0.0% % time ./mmap-cat /hp-ux > /dev/null
0.04u 0.04s 0:00.08 100.0% % time ./mmap-cat /hp-ux > /dev/null
0.01u 0.07s 0:00.08 100.0% % time cat /hp-ux > /dev/null
0.05u 0.33s 0:02.33 16.3% % time cat /hp-ux > /dev/null
0.02u 0.19s 0:00.33 63.6% % time cat /hp-ux > /dev/null
0.04u 0.18s 0:00.22 100.0% %
----------------------------------------------------------------------
練習問題(1),(2),(3),(9) の中から1つ以上選んでやりなさい。 問題を難しい方に変更してもよい。
---------------------------------------------------------------------- % mnews----------------------------------------------------------------------
---------------------------------------------------------------------- キー 動き ---------------------------------------------------------------------- q 抜ける ? ヘルプの表示(表示の終了はq) ----------------------------------------------------------------------
---------------------------------------------------------------------- キー 動き | キー 動き ---------------------------------------------------------------------- Q 終了 | SPACE,i,→ 入る(参照) ? ヘルプ | o,q,← 抜ける(復帰) j,^N, ↓ 次の行へ移動 | k,^P, ↑ 前の行へ移動 m 電子メールを出す| a 記事を出す < 最初の記事へ | > 最後の記事へ ----------------------------------------------------------------------
---------------------------------------------------------------------- キー 動き | キー 動き ---------------------------------------------------------------------- SPACE 1ページ進める | b 1ページ戻す j 1行進める | k 1行戻す q ページャの終了 | ? ヘルプの表示 n 次の未読記事へ | p 前の記事へ r,R メールで返事 | f,F フォローアップ ----------------------------------------------------------------------