システム・プログラム 電子・情報工学系 新城 靖 <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/syspro-2001/2001-05-14
あるいは、次のページから手繰っていくこともできます。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/
http://www.is.tsukuba.ac.jp/~yas/index-j.html
exec-date.c で execvp() は、execve() の間違 いである。environ の型も違っていた。ヘッダ・ファイルも include した方 がよい。
システムコールでのプロセスの扱い
---------------------------------------------------------------------- 1: /* 2: fork-hello.c -- 画面に文字列を表示するプログラム 3: ~yas/syspro/proc/fork-hello.c 4: $Header: /home/lab2/OS/yas/syspro/proc/RCS/fork-hello.c,v 1.1 2001/05/13 14:20:45 yas Exp $ 5: Start: 2001/05/13 23:19:01 6: */ 7: 8: main() 9: { 10: fork(); 11: fork(); 12: fork(); 13: printf("hello\n"); 14: } ----------------------------------------------------------------------
実行結果
---------------------------------------------------------------------- % ./fork-hellohello hello hello hello hello hello hello hello %
----------------------------------------------------------------------
---------------------------------------------------------------------- 1: /* 2: exec-date.c -- 実行中のプロセスのプログラムをdateプログラムに切替える 3: ~yas/syspro/proc/exec-date.c 4: $Header: /home/lab2/OS/yas/syspro-2001/proc/RCS/exec-date.c,v 1.2 2001/05/20 07:36:11 yas Exp $ 5: Start: 2001/05/13 23:25:49 6: */ 7: 8: #include <unistd.h> 9: 10: extern char *environ[] ; 11: 12: main() 13: { 14: char *argv[2] ; 15: argv[0] = "/bin/date" ; 16: argv[1] = 0 ; 17: execve( argv[0],argv,environ ); 18: execve( argv[0],argv,environ ); 19: execve( argv[0],argv,environ ); 20: execve( argv[0],argv,environ ); 21: } ----------------------------------------------------------------------
実行結果
一度、/bin/date が exec されたら、元のプログラムにもどることはない。 date は、仕事が終ると exit() する。---------------------------------------------------------------------- % ./exec-dateSun May 13 23:31:51 JST 2001 %
----------------------------------------------------------------------
UTMP_FILE
」
または、「WTMP_FILE
」という文字列を含む行を、ファイル
/usr/include/utmp.h
から探し、結果を表示している。
一般的には、次のようになる。---------------------------------------------------------------------- % egrep '[UW]TMP_FILE' /usr/include/utmp.h#define UTMP_FILE "/var/adm/utmp" #define WTMP_FILE "/var/adm/wtmp" %
----------------------------------------------------------------------
---------------------------------------------------------------------- % egrep pattern file1 file2 ...----------------------------------------------------------------------
pattern
としては、次のような正規表現(regular
expressions)が使える。
「*」、「$」、「[]」など、シェルが展開してしまう文字をパタンとして指定 する時には、シングル・クォート「''」で括る。
egrep は、機能が拡張され、アルゴリズムも改良されていて速い。
status には、exit() システムコールの引数で指定された値や kill() で殺さ れた時の状態が入る。wait( 0 ); または int status; wait( &status );
waitの引数の型は、int *だが、int 型に変数に「&」を付ける。 次のようなプログラムは、間違いである。
---------------------------------------------------------------------- main() { int *statptr ; .... if( fork() ) .... wait( statptr ); } ----------------------------------------------------------------------ポインタを使う時には、その変数になんらかの方法で確保されたメモリの番地 をセットしておく必要がある。次のいずれかが必要である。
インターネット上に落ちている例を拾ってきてもよいが、短くて動く形で整理 しておかないと、自分のものにはならない。さらに、インターネット上に落ち ているものの品質を見極める目が必要である。間違いが含まれていることが多々 ある。
現在の時刻を取得するには、time() システム・コール(System V系)や gettimeofday() システム・コール (BSD系)を使う。
その秒数からカレンダーに便利な形式(struct tm)に変換するものが、 localtime() ライブラリ関数である。struct tm は、次のようなフィールドが ある。
struct tm { int tm_sec; /* seconds after the minute - [0, 61] */ /* for leap seconds */ int tm_min; /* minutes after the hour - [0, 59] */ int tm_hour; /* hour since midnight - [0, 23] */ int tm_mday; /* day of the month - [1, 31] */ int tm_mon; /* months since January - [0, 11] */ int tm_year; /* years since 1900 */ int tm_wday; /* days since Sunday - [0, 6] */ int tm_yday; /* days since January 1 - [0, 365] */ int tm_isdst; /* flag for alternate daylight */ /* savings time */ };
ctime(), strftime() などを使うと、文字列に変換してくれる。
逆に、struct tm から time_t を作る関数が mktime() ライブラリ関数である。
localtime() や mktime() は、TZ環境変数(Time Zone)を見ている。閏秒など が混じると単純には計算できない。
構造体を読み書きするには、システムコール read(), write() または、高水 準(ライブラリ)の fread(), fwrite() を使う。
struct struct1 var1 ; FILE *fp ; ... fread(&var1, sizeof(struct1),1,fp ); または struct struct1 var1 ; int fd ; read(fd, &var1, sizeof(struct1) );
構造体をファイルに保存する時に注意することは、ポインタが(直接的には) 使えないことである。
さらにポータビリティ(別のOSやCPUのコンピュータでも使う)を考える 時には、 文字コード、 バイトオーダー、 浮動小数点の形式、構造体の穴に注意する。
Cコンパイラは、構造体に見えない要素(穴)を追加して、int や double を、 4バイト境界に合うようにしてしまうことがある。 (XDR ライブラリを使えば、自動的にこのあたりのことを変換してくれる。 XDR については、3学期の「分散システム」で述べる。)
---------------------------------------------------------------------- 1: 2: /* 3: utmp-print.c -- /var/adm/utmp の内容を表示するプログラム 4: ~yas/syspro/file/utmp-print.c 5: $Header: /home/lab2/OS/yas/syspro/file/RCS/utmp-print.c,v 1.5 2001/05/13 11:08:59 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 no) */ 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: } 73: 74: /* ------------------------ utmp-print.c ------------------------ */ 75: static char rcsid[] = 76: "$Header: /home/lab2/OS/yas/syspro/file/RCS/utmp-print.c,v 1.5 2001/05/13 11:08:59 yas Exp $" ; ----------------------------------------------------------------------
ut_time には、1970年1月1日0:00(GMT)からの秒数で時刻 が含まれている。
実行結果
ctime() は、文字列のの最後に改行 \n を含めてしまう。改行を含まないよう な文字列が必要な時には、localtime() を使うか、strftime() を使う。---------------------------------------------------------------------- % ./utmp-printsystem boot 0 2 Fri May 11 10:54:20 2001 run-level 2 0 1 Fri May 11 10:54:20 2001 lnsyscon link 34 8 Fri May 11 10:54:20 2001 rc2 s2 37 8 Fri May 11 10:55:44 2001 LOGIN t1 ttyd1 702 6 Fri May 11 10:55:44 2001 ken-ichi q0 ttyq0 7165 7 Sun May 13 20:28:56 2001 i961351 q1 ttyq1 3416 7 Fri May 11 20:09:51 2001 yas q2 ttyq2 7050 7 Sun May 13 20:04:46 2001 yas q3 ttyq3 7082 7 Sun May 13 20:05:37 2001 yamajyun q4 ttyq4 7196 8 Sun May 13 20:50:41 2001 % who
ken-ichi ttyq0 5月 13日 20時28分 i961351 ttyq1 5月 11日 20時09分 yas ttyq2 5月 13日 20時04分 yas ttyq3 5月 13日 20時05分 %
----------------------------------------------------------------------
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/syspro/file/wtmp-last10.c 5: $Header: /home/lab2/OS/yas/syspro/file/RCS/wtmp-last10.c,v 1.4 2001/05/13 13:22:24 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 ) <以下略> ----------------------------------------------------------------------実行例。
---------------------------------------------------------------------- % ./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/syspro/file/RCS/mmap-head.c,v 1.2 2001/05/13 13:46:35 yas Exp $ 5: ~yas/syspro/file/mmap-head.c 6: Start: 1998/05/12 01:06:15 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: 15: extern void mmap_head( char *name ); 16: extern char *mmap_file( char *name /* in */, size_t *filesizep /* out */); 17: 18: void main( int argc, char *argv[] ) 19: { 20: if( argc != 2 ) 21: { 22: fprintf( stderr,"Usage: %% %s filename\n",argv[0] ); 23: exit( 1 ); 24: } 25: mmap_head( argv[1] ); 26: } 27: 28: void mmap_head( char *name ) 29: { 30: size_t size,i ; /* unsigned int */ 31: char *file_address ; 32: int nlcount ; 33: 34: file_address = mmap_file( name,&size ); 35: if( ((int)file_address) == -1 ) 36: { 37: perror( name ); 38: exit( 1 ); 39: } 40: for( i = 0, nlcount=0 ; i<size ; i ++ ) 41: { 42: if( file_address[i] == '\n' ) 43: { 44: nlcount ++ ; 45: if( nlcount >= 10 ) 46: { 47: i++ ; 48: break; 49: } 50: } 51: } 52: write( 1, file_address, i ); 53: } 54: 55: char *mmap_file( char *name /* in */, size_t *filesizep /* out */) 56: { 57: int fd ; 58: struct stat buf ; 59: size_t size,p ; /* unsigned int */ 60: off_t off ; /* long */ 61: caddr_t addr ; /* char * */ 62: if( (fd = open( name,O_RDONLY )) == -1 ) 63: return( (char *)-1 ); 64: fstat( fd, &buf ); 65: size = buf.st_size ; 66: addr = 0 ; off = 0 ; 67: addr = mmap( addr, size, PROT_READ, MAP_PRIVATE, fd, off ); 68: if( (int)addr == -1 ) 69: { 70: close( fd ); 71: return( (char *)-1 ); 72: } 73: close( fd ); 74: if( filesizep ) 75: *filesizep = size ; 76: return( addr ); 77: } ----------------------------------------------------------------------実行例。
---------------------------------------------------------------------- % ./mmap-head /etc/passwdroot:sBcDxfGhijklmx: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 %
----------------------------------------------------------------------
この課題では、wtmp にある情報だけが表示できればよい。 wtmp ではなく wtmpx を使うと、ホスト名まで調べられる。
たとえば、引数として日付、時刻、ユーザ名 user1, user2 ... のユーザが、 その時間にログインしていたら、yes を返す。
ヒント:まず struct tm に日付と時間をセットして、 mktime() を使って time_t を得る。ログイン時間とログアウトの時間を wtmp ファイルで調べる。 指定されていた範囲にかかれば、yes と判定する。% present 2001/05/14 11:30 2001/05/14 13:00 user1 user2 ...user1 yes user2 no ... %
![]()
tail コマンドは、head コマンドとは反対に、ファイルの末尾を表示する ものである。