システム・プログラム
電子・情報工学系
新城 靖
<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 コマンドとは反対に、ファイルの末尾を表示する ものである。