システム・プログラム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
2 /
2 usr
27650 local
29966 bin
29970 tcsh
この時、chdir(2) を使って、探すディレクトリを "." で参照できるようにし
なさい。ディレクトリの内容の読み込みには、getdirentries(2) か、
opendir(3) を使いなさい。
----------------------------------------------------------------------
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![]()
----------------------------------------------------------------------
% 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
%
----------------------------------------------------------------------
ディレクトリを stat(2) で調べると、リンク数が2以上になっている。
"." と 子ディレクトリの ".." の分だけ増えている。
本名は、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 フォローアップ ----------------------------------------------------------------------