システム・プログラムI
                                       電子・情報工学系
                                       新城 靖
                                       <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
	http://www.hlla.is.tsukuba.ac.jp/~yas/coins/syspro1-1998/1998-05-26
あるいは、次のページから手繰っていくこともできます。
	http://www.hlla.is.tsukuba.ac.jp/~yas/coins/
	http://www.hlla.is.tsukuba.ac.jp/~yas/index-j.html
----------------------------------------------------------------------
   1:	/*
   2:	        env-print.c -- 環境変数を表示するプログラム
   3:	        ~yas/syspro1-1998/proc/env-print.c
   4:	        $Header: /home/lab2/OS/yas/syspro1-1998/proc/RCS/env-print.c,v 1.3 1998/05/25 14:47:51 yas Exp $
   5:	        Start: 1997/05/05 16:42:22
   6:	*/
   7:	extern char **environ ;
   8:	
   9:	main( int argc, char *argv[], char *envp[] )
  10:	{
  11:	    int i ;
  12:	        printf("envp == 0x%x\n",envp );
  13:	        printf("environ == 0x%x\n",environ );
  14:	        for( i=0 ; envp[i] ; i++ )
  15:	            printf("envp[%d]==0x%x, \"%s\"\n",i,envp[i],envp[i] );
  16:	}
----------------------------------------------------------------------
実行例。
---------------------------------------------------------------------- % ./env-printenvp == 0x7fff2f4c environ == 0x7fff2f4c envp[0]==0x7fff300c, "HOME=/home/lab2/OS/yas" envp[1]==0x7fff3023, "PATH=/home/lab2/OS/yas/bin:/usr/local/bin:/usr/local2/bin:/usr/local2/X11/bin:/usr/local/gnu/bin:/usr/local/X11/bin:/usr/local/tex/bin:/usr/java/bin:/usr/sbin:/usr/bsd:/sbin:/usr/bin:/usr/bin/X11:.:/usr/etc:/etc:/usr/freeware/bin" envp[2]==0x7fff3109, "LOGNAME=yas" envp[3]==0x7fff3115, "HZ=100" ... envp[24]==0x7fff32eb, "LESSCHARSET=japanese-euc" envp[25]==0x7fff3304, "IRCSERVER=130.158.87.200" envp[26]==0x7fff331d, "KCODE=euc" %
----------------------------------------------------------------------
UNIX では、新たにプロセスを作る時に、自分のコピーしか作れない(fork()シ ステム・コール)。元のプロセスを親プロセス、作られたプロセスを子プロセ スという。
現在のプログラムの実行はそのまま続けて、新しくプログラムを実行するには、 次のようにする。
----------------------------------------------------------------------
   1:	/*
   2:	        proc-create.c -- calプログラムよりプロセスを作る
   3:	        ~yas/syspro1/proc/proc-create.c
   4:	        $Header: /home/lab2/OS/yas/syspro1-1998/proc/RCS/proc-create.c,v 1.3 1998/05/25 14:58:24 yas Exp $
   5:	        Start: 1995/02/27 15:27:54
   6:	*/
   7:	
   8:	#include <unistd.h>     /* pid_t */
   9:	
  10:	extern char **environ;
  11:	
  12:	main()
  13:	{
  14:	   pid_t child_pid ;
  15:	        if( (child_pid=fork()) == 0 )
  16:	        {
  17:	            char *argv[4] ;
  18:	            printf("child: pid == %d, ppid == %d\n", getpid(), getppid() );
  19:	            argv[0] = "cal" ;
  20:	            argv[1] = "5" ;
  21:	            argv[2] = "1998" ;
  22:	            argv[3] = 0 ;
  23:	            execve( "/usr/bin/cal", argv, environ );
  24:	            perror( "execve" );
  25:	            /* exec に失敗したら exit() を忘れないこと */
  26:	            exit( 1 );
  27:	        }
  28:	        else if( child_pid > 0 )
  29:	        {
  30:	            printf("parent: pid == %d, ppid == %d, child_pid == %d\n",
  31:	                    getpid(), getppid(),child_pid );
  32:	        }
  33:	        else
  34:	        {
  35:	            perror("fork");
  36:	        }
  37:	}
----------------------------------------------------------------------
実行例。
execve() は、システム・コールである。これを使いやすくするために、次の ようなライブラリ関数が用意されている。---------------------------------------------------------------------- % ./proc-createchild: pid == 27405, ppid == 27404 parent: pid == 27404, ppid == 27288, child_pid == 27405 % 1998 年 5 月 日 月 火 水 木 金 土 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
%
% ./proc-create
child: pid == 27429, ppid == 27428 1998 年 5 月 日 月 火 水 木 金 土 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 parent: pid == 27428, ppid == 27288, child_pid == 27429 %
----------------------------------------------------------------------
     int execl (const char *path, const char *arg0, ..., const char *argn,
          (char *)0);
     int execv (const char *path, char *const *argv);
     int execle (const char *path, const char *arg0, ..., const char *argn,
          (char *0), const char *envp[]);
     int execve (const char *path, char *const *argv, char *const *envp);
     int execlp (const char *file, const char *arg0, ..., const char *argn,
          (char *)0);
     int execvp (const char *file, char *const *argv);
     int system(const char *string);
     FILE *popen(const char *command, const char *type);
     int pclose (FILE *stream);
この例では、echo のプロセスと、tr のプロセスは、パイプで接続されている。 パイプは、open() したファイルと同じようにread() したり write() したり することがでる。しかし実際には、ファイルではなく、プロセスとプロセスが データを交換する仕組(プロセス間通信の仕組)の1つである。---------------------------------------------------------------------- % echo "hello,world" | tr a-z A-ZHELLO,WORLD %
----------------------------------------------------------------------
パイプを作るには、pipe() システム・コールを使う。これで、パイプ が1本作られ、2つのファイル記述子(読込み用と書込み用)が返される。
pipe() システム・コールで作られたファイル記述子は、しばしば dup() シス テム・コールで、0, 1 に変えられる。dup() システム・コールは、記述子を コピーするものである。小さい数字から探していくので、たとえば close(0) の直後に dup(fd) すると、fd が 0 にコピーされる。
dup() よりも、dup2() の方が便利である。
----------------------------------------------------------------------
   1:	/*
   2:	        pipe-rw.c -- pipe() と dup() を使ったプログラム
   3:	        ~yas/syspro1/ipc/pipe-rw.c
   4:	        $Header: /home/lab2/OS/yas/syspro1/ipc/RCS/pipe-rw.c,v 1.5 1998/05/25 15:34:59 yas Exp $
   5:	        Start: 1997/05/26 20:43:29
   6:	*/
   7:	
   8:	#include <stdio.h>
   9:	#include <unistd.h>
  10:	
  11:	extern  void parent( int fildes[2] );
  12:	extern  void child( int fildes[2] );
  13:	
  14:	main()
  15:	{
  16:	    int fildes[2] ;
  17:	    pid_t pid ;
  18:	
  19:	        if( pipe(fildes) == -1)
  20:	        {
  21:	            perror("pipe");
  22:	            exit( 1 );
  23:	        }
  24:	        /* fildes[0] -- 読み込み用
  25:	         * fildes[1] -- 書き込み用
  26:	         */
  27:	        if( (pid=fork()) == 0 )
  28:	        {
  29:	            child( fildes );
  30:	        }
  31:	        else if( pid > 0 )
  32:	        {
  33:	            parent( fildes );
  34:	        }
  35:	        else
  36:	        {
  37:	            perror("fork");
  38:	            exit( 1 );
  39:	        }
  40:	}
  41:	
  42:	void parent( int fildes[2] )
  43:	{
  44:	    char *p ;
  45:	        close( fildes[0] );
  46:	        close( 1 );
  47:	        dup( fildes[1] );
  48:	        close( fildes[1] );
  49:	
  50:	        p = "hello,world\n" ;
  51:	        while( *p )
  52:	        {
  53:	            write( 1,p++,1 );
  54:	        }
  55:	}
  56:	
  57:	void child( int fildes[2] )
  58:	{
  59:	    char c ;
  60:	        close( fildes[1] );
  61:	        close( 0 );
  62:	        dup( fildes[0] );
  63:	        close( fildes[0] );
  64:	
  65:	        while( read(0,&c,1) >0 )
  66:	        {
  67:	            putchar( toupper(c) );
  68:	        }
  69:	}
----------------------------------------------------------------------
実行例。
---------------------------------------------------------------------- % ./pipe-rwHELLO,WORLD % ----------------------------------------------------------------------
proc-createを実行すると、上のように、親プロセスの出力と子
プロセスの出力が入り交じることがある。この理由を考えなさい。
この問題を解決しなさい。そのためには、wait() システム・コール (waitpid(),wait3(),wait4()など)を用いて、同期を行えばよい。すなわち、 子プロセスが表示する可能性がある間は、親プロセスを待ち状態にしなさい。
ヒント:使わないパイプのファイル記述子は、全部 close() すること。パイ プの書き込み側のファイル記述子が開いている間は、read() しても EOF (End Of File) にならない。
先にパイプを2つ作ってから2回 fork() してもよい。パイプを1つ作って fork() してから もう1つパイプを作って fork() するという方法でもよい。
ヒント:書き手がいないパイプは、全ての書込み側のファイル記述子を closeすると作れる。
ヒント:プロセスを fork() しなくても、パイプに書くことはできる。プロセ スが1個の状態で、バッファの大きさ以上のデータを書き込むと、プロセスが ブロックされる(先に進まなくなる)。
----------------------------------------------------------------------
     FILE *popen(const char *command, const char *type);
     int pclose (FILE *stream);
----------------------------------------------------------------------
これと同じような機能を持つ関数を定義しなさい。ただし、入出力は、ファイ
ル記述子のままでもよい。(ファイル記述子は、fdopen() 使えば、FILE * に
変えることができる。)