システム・プログラムI 電子・情報工学系 新城 靖 <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/syspro1-1997/1997-05-27
あるいは、次のページから手繰っていくこともできます。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/
http://www.hlla.is.tsukuba.ac.jp/~yas/index-j.html
---------------------------------------------------------------------- 1: /* 2: passwd-get-uid.c -- UID から /etc/passwd のエントリを引く 3: /usr/local/LECTURES/syspro-1997-shinjo/proc/passwd-get-uid.c 4: $Header: passwd-get-uid.c,v 1.2 97/05/26 14:52:56 yas Exp $ 5: Start: 1997/05/26 14:38:47 6: */ 7: 8: #include <pwd.h> /* getpwuid() */ 9: #include <stdio.h> /* NULL, stderr */ 10: 11: main() 12: { 13: passwd_print_uid( getuid() ); 14: } 15: 16: passwd_print_uid( uid ) 17: uid_t uid ; 18: { 19: struct passwd *pw ; 20: printf("uid == %d \n",uid ); 21: pw = getpwuid( uid ); 22: if( pw == NULL ) 23: { 24: fprintf(stderr,"no passwd entry\n"); 25: exit( 1 ); 26: } 27: passwd_print( pw ); 28: } 29: 30: passwd_print( pw ) 31: struct passwd *pw ; 32: { 33: printf("pw_name: %s\n",pw->pw_name ); 34: printf("pw_passwd: %s\n",pw->pw_passwd ); 35: printf("pw_uid: %d\n",pw->pw_uid ); 36: printf("pw_gid: %d\n",pw->pw_gid ); 37: printf("pw_gecos: %s\n",pw->pw_gecos ); 38: printf("pw_dir: %s\n",pw->pw_dir ); 39: printf("pw_shell: %s\n",pw->pw_shell ); 40: } ----------------------------------------------------------------------実行例。
その他の passwd 構造体(/etc/passwd)をアクセスする関数。---------------------------------------------------------------------- % ./passwd-get-uiduid == 1231 pw_name: yas pw_passwd: 8QGOq0kY9R21k pw_uid: 1231 pw_gid: 40 pw_gecos: Yasushi SHINJO,[os],5163 pw_dir: /home/lab2/OS/yas pw_shell: /usr/local/bin/tcsh %
----------------------------------------------------------------------
---------------------------------------------------------------------- struct passwd *getpwnam(const char *name); struct passwd *getpwuid(uid_t uid); struct passwd *getpwent(void); void setpwent(void); void endpwent(void); struct passwd *fgetpwent(FILE *f); ----------------------------------------------------------------------UID ではなく、GID 操作するには、getcrent() ライブラリ関数などを使う。
Set UID とは、そのプログラムを実行する時に、そのプログラムが保存されて いるファイルのユーザ(所有者)の権限で実行するための仕組みである。ファ イルの stat(2) の st_uid が、そのプロセスの (実効) uid となる。これは、 stat(2) のst_mode で S_ISUID ビットが 1 の時にの動きである。
プロセスの UID には、次の2種類がある。
Set-UID と同様に、Set-GID という考え方もある。
SetUID のコマンドは、ls -l で見ると、user の x が s になっている。---------------------------------------------------------------------- % ls -l /bin/su-r-sr-xr-x 1 root bin 147456 Dec 2 1993 /bin/su %
----------------------------------------------------------------------
普通の割込み(ハードウェアによる割込み)は、オペレーティング・システム のカーネルにおいて利用されています。
ソフトウェア割込みとは、本来はオペレーティング・システムのカーネルしか 使えない割込みの機能を、ソフトウェアにより実現して、一般の利用者プログ ラム(プロセス)でも使えるようにしたものです。
UNIXでは、ソフトウェア割込みの機能は、シグナル(signal)という名前で実現 されています。シグナルとは、本来はプロセス間通信の一種で、ある事象が起 きたことを他のプロセスに知らせることです。ここで伝わるのは、ある事象が 起きたかどうかだけで、引数などを付けることはできません。UNIXでは、プロ セス間でシグナルにより通信をする他に、キーボードからシグナルを送ること もできます。これは、「ソフトウェア割込み」として、プロセス1つひとつに 割込みボタンが付いているようなものです。また、プログラムの中で例外 (exception)が起きた時にも、ハードウェアの割込みと同様に、ソフトウェ ア割込みが生じます。これも、他のシグナルと同じように受け取ることができ ます。
UNIXのソフトウェア割込み(シグナル)を使うには、次のようなことが必要で す。
ソフトウェア割り込みは、1つのプログラムの中に制御の流れが1つしかない ようなプログラムの時に有効な方法である。最近のマルチスレッドのプログラ ムでは、シグナルの意味が不明確である。
---------------------------------------------------------------------- 1: /* 2: signal-int.c -- SIGINT を3回受け付けて終了するプログラム。 3: /usr/local/LECTURES/syspro-1997-shinjo/proc/signal-int.c 4: $Header: signal-int.c,v 1.1 97/05/26 19:51:22 yas Exp $ 5: Start: 1997/05/26 18:38:38 6: */ 7: 8: #include <stdio.h> 9: #include <signal.h> 10: 11: int sigint_count = 3 ; 12: void sigint_handler(); 13: 14: main() 15: { 16: signal( SIGINT, &sigint_handler ); 17: while( 1 ) 18: { 19: printf("main(): sigint_count == %d\n", sigint_count ); 20: pause(); 21: } 22: } 23: 24: void sigint_handler() 25: { 26: printf("sigint_handler(): sigint_count == %d\n", sigint_count ); 27: if( -- sigint_count <= 0 ) 28: { 29: exit( 1 ); 30: } 31: signal( SIGINT, &sigint_handler ); /* System V */ 32: } ----------------------------------------------------------------------実行例。
stty で intr に相当するキーを3回押す。---------------------------------------------------------------------- % stty -aspeed 9600 baud; line = 0; susp = ^Z; dsusp = ^Y rows = 48; columns = 80 intr = ^C; quit = ^\; erase = ^H; kill = ^U; swtch = ^@ eof = ^D; eol
; min = 4; time = 255; stop = ^S; start = ^Q parenb -parodd cs8 -cstopb hupcl cread -clocal -loblk -crts -ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl -iuclc ixon -ixany -ixoff -rtsxoff -ctsxon -ienqak isig icanon iexten -xcase echo echoe echok -echonl -noflsh opost -olcuc onlcr -ocrnl -onocr -onlret -ofill -ofdel -tostop % ./signal-int main(): sigint_count == 3 sigint_handler(): sigint_count == 3 main(): sigint_count == 2 sigint_handler(): sigint_count == 2 main(): sigint_count == 1 sigint_handler(): sigint_count == 1 %
----------------------------------------------------------------------
関数間でも有効な goto がある。
---------------------------------------------------------------------- 1: /* 2: setjmp-longjmp.c -- setjmp() と longjmp() のテスト 3: /usr/local/LECTURES/syspro-1997-shinjo/proc/setjmp-longjmp.c 4: $Header: setjmp-longjmp.c,v 1.1 97/05/26 20:33:22 yas Exp $ 5: Start: 1997/05/26 20:26:27 6: */ 7: 8: #include <setjmp.h> 9: 10: jmp_buf main_env ; 11: 12: main() 13: { 14: int x ; 15: if( (x=setjmp(main_env)) == 0 ) 16: { 17: printf("setjmp(), first time\n"); 18: f(); 19: } 20: else 21: { 22: printf("return from longjmp(), %d \n",x ); 23: } 24: } 25: 26: f() 27: { 28: printf("f() called.\n"); 29: g(); 30: printf("f() return.\n"); 31: } 32: 33: g() 34: { 35: printf("g() called.\n"); 36: h(); 37: printf("h() return.\n"); 38: } 39: 40: h() 41: { 42: printf("h() called.\n"); 43: longjmp( main_env,10 ); 44: printf("h() return.\n"); 45: } ----------------------------------------------------------------------実行例。
---------------------------------------------------------------------- % ./setjmp-longjmpsetjmp(), first time f() called. g() called. h() called. return from longjmp(), 10 %
----------------------------------------------------------------------
この例では、echo のプロセスと、tr のプロセスは、パイプで接続されていま す。パイプは、open() したファイルと同じようにread() したり write() し たりすることができます。しかし実際には、ファイルではなく、プロセスとプ ロセスが通信(プロセス間通信)の1つです。---------------------------------------------------------------------- % echo "hello,world" | tr a-z A-ZHELLO,WORLD %
----------------------------------------------------------------------
パイプを作るには、pipe() システム・コールを使います。これで、パイプの 読み込み用のファイル記述子と書き込み用のファイル記述子が返される。
pipe() システム・コールで作られたファイル記述子は、しばしば dup() シス テム・コールで、0, 1 に変えられる。dup() システム・コールは、記述子を コピーするものである。小さい数字から探していくので、たとえば close(0) の直後に dup(fd) すると、fd が 0 にコピーされる。dup() よりも、dup2() の方が便利である。
---------------------------------------------------------------------- 1: /* 2: pipe-rw.c -- pipe() と dup() を使ったプログラム 3: /usr/local/LECTURES/syspro-1997-shinjo/proc/pipe-rw.c 4: $Header: pipe-rw.c,v 1.1 97/05/26 21:00:50 yas Exp $ 5: Start: 1997/05/26 20:43:29 6: */ 7: 8: #include <stdio.h> 9: #include <unistd.h> 10: 11: main() 12: { 13: int fildes[2] ; 14: pid_t pid ; 15: 16: if( pipe(fildes) == -1) 17: { 18: perror("pipe"); 19: exit( 1 ); 20: } 21: /* fildes[0] -- 読み込み用 22: * fildes[1] -- 書き込み用 23: */ 24: if( (pid=fork()) == 0 ) 25: { 26: child( fildes ); 27: } 28: else if( pid > 0 ) 29: { 30: parent( fildes ); 31: } 32: else 33: { 34: perror("fork"); 35: exit( 1 ); 36: } 37: } 38: 39: parent( fildes ) 40: int fildes[2] ; 41: { 42: char *p ; 43: close( fildes[0] ); 44: close( 1 ); 45: dup( fildes[1] ); 46: close( fildes[1] ); 47: 48: p = "hello,world\n" ; 49: while( *p ) 50: { 51: putchar( *p++ ); 52: } 53: } 54: 55: child( fildes ) 56: int fildes[2] ; 57: { 58: int c ; 59: close( fildes[1] ); 60: close( 0 ); 61: dup( fildes[0] ); 62: close( fildes[0] ); 63: 64: while( (c=getchar()) != EOF ) 65: { 66: putchar( toupper(c) ); 67: } 68: } ----------------------------------------------------------------------実行例。
---------------------------------------------------------------------- % ./pipe-rwHELLO,WORLD %
----------------------------------------------------------------------
---------------------------------------------------------------------- FILE *popen(const char *command, const char *type); int pclose (FILE *stream); ----------------------------------------------------------------------これと同じような機能を持つ関数を定義しなさい。ただし、入出力は、ファイ ル記述子のままでもよい。(ファイル記述子は、fdopen() 使えば、FILE * に 変えることができる。)
---------------------------------------------------------------------- 1: /* 2: socketpair-rw.c -- socketpair() による双方向通信 3: /usr/local/LECTURES/syspro-1997-shinjo/ipc/socketpair-rw.c 4: $Header: socketpair-rw.c,v 1.1 97/05/26 21:51:59 yas Exp $ 5: Start: 1997/05/26 21:29:34 6: */ 7: 8: #include <stdio.h> 9: #include <unistd.h> /* fork() */ 10: #include <sys/types.h> /* socketpair() */ 11: #include <sys/socket.h> /* socketpair() */ 12: 13: main() 14: { 15: int fildes[2] ; 16: pid_t pid ; 17: 18: if( socketpair(AF_UNIX,SOCK_STREAM,0,fildes) == -1) 19: { 20: perror("socketpair"); 21: exit( 1 ); 22: } 23: /* fildes[0] -- 読み込み用 24: * fildes[1] -- 書き込み用 25: */ 26: if( (pid=fork()) == 0 ) 27: { 28: close( fildes[1] ); 29: child( fildes[0] ); 30: } 31: else if( pid > 0 ) 32: { 33: close( fildes[0] ); 34: parent( fildes[1] ); 35: } 36: else 37: { 38: perror("fork"); 39: exit( 1 ); 40: } 41: } 42: 43: parent( fd ) 44: int fd ; 45: { 46: char *p,c,ret ; 47: p = "hello,world\n" ; 48: while( c = *p++ ) 49: { 50: if( write( fd, &c, 1 ) != 1 ) 51: perror("write"); 52: if( read( fd, &ret, 1 ) != 1 ) 53: perror("read"); 54: putchar( ret ); 55: } 56: close( fd ); 57: } 58: 59: child( fd ) 60: int fd ; 61: { 62: char c ; 63: while( read(fd,&c,1) == 1 ) 64: { 65: c = toupper( c ); 66: if( write( fd,&c,1 ) != 1 ) 67: perror("read"); 68: } 69: close( fd ); 70: } ----------------------------------------------------------------------実行例。
---------------------------------------------------------------------- % ./socketpair-rwHELLO,WORLD %
----------------------------------------------------------------------