Operating Systems. Exercise 02

I. プロセス制御のシステムコール(準備課題, 配点無し)
演習問題を解くために必要なプロセス制御のためのC言語インターフェースを説明します。
配点はありませんので、 プロセス生成やシステムコールAPI群について知っている人はここを飛ばして、下のIIに進んでください。

1. forkによる子プロセス作成

プロセスは子プロセスを生成することが出来ます。
C言語では子プロセスの生成にforkシステムコールを使います。
以下の特徴を確かめ、いろいろ実験してください。

1. 子プロセスは, 親プロセスの変数とプログラムを複製したものが実行される。


int main() {

  int n = 0;

  int nPid = fork();
  //fork以前に定義されたデータを複製し
  //このプログラムを親プロセス子プロセス両方で同じように実行する

  ++n;

  //子プロセスと親プロセスで"1" と2回出力される
  printf("%d\n", n);

}
	

2. 親プロセスと子プロセスは同じグループIDを持つが, 別々のプロセスIDを持つ


int main() {

    int nPid = fork();

    //子と親でグループID(GID)は同じだが, 別々のプロセスID(PID)が出力される
    printf("GID: %d, PID : %d\n", getgid(), getpid());

}
	

3. 子プロセスがforkしようとすると失敗し0を返す(孫プロセスは作れない)


int main() {

    int nPid = fork();

    //forkの返り値が0なら子プロセスなので、親プロセスと子プロセスで別の処理をすることができます
    if (!nPid) {

        printf("これは子プロセスで PIDは%d\n", getpid());

        //子プロセスはここで終了させます 子プロセスの処理終了後に必ず呼んでください
        exit(0);
    }
    else {

        printf("これは親プロセスで PIDは%d, 子プロセスを持ち PIDは%d\n", getpid(), nPid);

        //親は子プロセスの終了を待ちます(子プロセスがゾンビプロセス化しないように) 
        //子プロセスの処理待ちをするために どこかで必ず呼んでください
        wait(0);
    }

}
	

4. 親と子で変数は別々の領域に取られる


int main() {

    int n = 0;

    int nPid = fork();

    //整数"n"が親と子で同じものであれば, 片方で"1"もう一方で"2"と
    //出力されるはずだが, 両者とも"1"と出力される

    if (!nPid) {
        //子プロセスの処理
        ++n;
        printf("%p %d\n", &n, n);
        exit(0);
    }
    else {
        //親プロセスの処理
        wait(0);    //子プロセスの実行終了を待つ
        ++n;
        printf("%p %d\n", &n, n);
    }
  
}
	

5. 親と子で変数は別々の領域に取られるが, プログラム中で開いたファイル入出力先は共通する


int main() {

  FILE *fpOut = fopen("temp.txt", "w");

  int nPid = fork();

  if (!nPid) {
    fprintf(fpOut, "write from child\n");
    exit(0);
  }
  else {
    wait(0);
    fprintf(fpOut, "write from parent\n");
  }

  fclose(fpOut);

  //--- 作成されたtemp.txtの中身 ---
  // write from child
  // write from parent
}
	

2. システムコール

C言語ではプログラム内からシステムコール"system"や"execve"を使って、 別のコマンドを他のプロセスとして実行することが出来ます。
以下の特徴を確かめ、いろいろ実験してください。

systemは実行したいコマンド文字列を引数にとり、シェルに渡します。
つまり、ユーザがターミナルからコマンドを実行するのと同じようにOSに要求が届くことになります。


int main() {

    //ls -lが実行される
    system("ls -l");

}
	

execveはシェルを介さずコマンドを実行するので、自分でコマンドの絶対パスを指定する必要があります。
さらに、execveを呼び出した以降のプログラムは実行されません。
以下の3つを引数として取ります。

  1. 実行するコマンドへの絶対パス(~などはシェルが解釈してくれて置き換えている記号なので無効です)
  2. 実行するコマンドに渡す引数文字列配列
  3. 環境文字列配列

int main(int argc, char **argv, char **envv) {

    char *lsArg[3] = {"ls", "-l"};

    //ls -lが実行される
    execve("/bin/ls", lsArg, envv);

    //execveが終わるとプログラムは終了するので ここまで来ない
    printf("can not reach here\n");
}
	


II. プロセス制御(配点100%)

課題1

課題: 子プロセスを生成し、子プロセスと親プロセスのプロセスID(PID)とグループID(GID)を
表示するプログラムを書いてください。 (テンプレート)
ただし、親プロセスは子プロセスの終了を待ってから終えるようなプログラムを書いてください。

: プログラムの実行形式のファイル名を "process1"としたとき

コマンド


    % ./process1
	
結果

Parent PID(GID) : 6753(1000)
Child PID(GID) : 6754(1000)
	


課題2

導入: "banner"コマンドは引数として与えられた文字列を、"#"で表現し出力するコマンドです。

コマンド


    % ~nisidate/bin/banner Aizu
	
結果

   #
  # #       #    ######  #    #
 #   #      #        #   #    #
#     #     #       #    #    #
#######     #      #     #    #
#     #     #     #      #    #
#     #     #    ######   ####
	

課題: 子プロセスを生成し、子プロセスから"banner"コマンドを実行するプログラムを作成してください。
ただし、プログラムに与えられたコマンドライン引数を、"banner"に引き渡すようにして、
さらに終了前に自分と子プロセスのIDを表示するようにしてください。 (テンプレート)

なお、execveを使うときはbannerコマンドへの絶対パスを使う必要があります。
"ls -l ~nisidate/bin/banner"などでパスを調べてください

: プログラムの実行形式のファイル名を "process2"とし, 引数を"Aizu"としたとき

コマンド


    % ./process2 Aizu
	
結果

Parent PID(GID) : 6753(1000)
Child PID(GID) : 6754(1000)
   #
  # #       #    ######  #    #
 #   #      #        #   #    #
#     #     #       #    #    #
#######     #      #     #    #
#     #     #     #      #    #
#     #     #    ######   ####