システムプログラム 練習問題 #4

練習問題(401)

printenv コマンドは,現在のプロセスの環境変数を表示してくれる. 同様の表示をしてくれるコマンドを,C 言語のプログラムとして記述せよ.

練習問題(402)

メモリマップを確かめるプログラムを Linux 上でコンパイルし,繰り返し実行したところ,以下のようにスタックのアドレスが毎回若干変化することがわかった. この現象が起きることを実際にプログラムを実行して確認しなさい.さらに,実行するごとにスタックの場所が変わる理由を予想した後,調査しなさい.

$ ./a.out[←]
environ:        0x7ffc47977e08
argv:           0x7ffc47977df8
stack:          0x7ffc47977d0f
bss:            0x6009d8
data:           0x6009a4
$ ./a.out[←]
environ:        0x7ffd5246bc08
argv:           0x7ffd5246bbf8
stack:          0x7ffd5246bb0f
bss:            0x6009d8
data:           0x6009a4
$ ./a.out[←]
environ:        0x7ffd3db44b48
argv:           0x7ffd3db44b38
stack:          0x7ffd3db44a4f
bss:            0x6009d8
data:           0x6009a4
$

練習問題(403)

以下のプログラムをコンパイル,実行すると Hello. が8回表示される. 8回表示される理由を説明しなさい.

     1  #include <stdio.h>
     2  #include <unistd.h>
     3  #include <sys/wait.h>
     4
     5  int main(void)
     6  {
     7          fork();
     8          fork();
     9          fork();
    10          printf("Hello.\n");
    11          wait(NULL);
    12          return 0;
    13  }

練習問題(404)

system は,与えられたコマンドを sh というシェルを起動して実行させるライブラリ関数である. 以下のように,コマンド行を文字列で渡すとそれを実行してくれる. リダイレクションやパイプやワイルドカードを意味する記号は system ではなく sh が解釈することに注意せよ.

     1  #include <stdio.h>
     2  #include <stdlib.h>
     3  #include <unistd.h>
     4
     5  int main(void)
     6  {
     7          system("ls *.c | wc");
     8          return 0;
     9  }

fork, execve(または相当のライブラリ関数),wait などを使用して,system 相当の機能を持つ関数 mysystem を作りなさい.

system のマニュアルページに記述されているシグナルについての仕様は無視してよい.

練習問題(405)

リダイレクションによって標準入力をファイルに切り替えるプログラムを変更し,標準出力への書き込みもファイルに対して行われるようにせよ. 書き込み先のファイルはプログラムの第2引数として受け取るものとする.

練習問題(406)

第1引数で与えられるコマンドの実行結果(exit status)に応じて,第2引数または第3引数で与えられるコマンドを実行するプログラム if-then-else を作りなさい.例えば

$ ./if-then-else "test 1 -eq 1" "echo yes" "echo no"[←]
yes
$ ./if-then-else "test 1 -eq 2" "echo yes" "echo no"[←]
no
$

となるようなプログラムである. fork, execve, wait などを使う練習のために,system 関数やシェルを使わずにプログラムを記述すること.

ダブルクオーテーションで囲った3つの文字列を与えるのではなく,コマンドの区切りとなる単語をあらかじめ決めておき,それによって区切りをプログラムに伝えるようにしても良い. 例えば以下は then, else を区切りとなる単語に決めたときのコマンド列である. execve を使用する場合には,if-then-else が受け取った文字列の配列の then, else に対応する要素を NULL で上書きすれば,その文字列の配列を execve にそのまま引数として渡せるので,以下の仕様のほうが,プログラムを作りやすいかもしれない.

$ ./if-then-else test 1 -eq 1 then echo yes else echo no

練習問題(407)

fork で子プロセスを作り,親プロセスと子プロセスが交互に実行を繰り返すプログラムを作りなさい.

練習問題(408)

練習問題(407)のプログラムを変更し,子プロセスの数をプログラムへの引数で与えられるようにしたプログラムを作りなさい.

練習問題(409)

3個のプロセスの標準入出力を2つのパイプで結び,それらのプロセスがデータをパイプでやりとりするプログラムを作りなさい.

先にパイプを2つ作ってから2回 fork する方法と,1つパイプを作り fork し,もう1つパイプを作りまた fork する方法があるが,どちらでも良い.

練習問題(410)

popen, pclose は,指定されたプログラムを実行するプロセスを生成し,そのプロセスと現在のプロセスをパイプにより接続し,そのプロセスからの入力またはそのプロセスへの出力に使えるファイルポインタを返すライブラリ関数である.

popen, pclose 相当の機能を持つ関数 mypopen, mypclose を作りなさい. 最初は,popenで実行するプログラムからの出力を受け取る機能だけを実装するところから始めるとよい.

練習問題(411)

パイプを使用するプログラムを,以下のように,親プロセスからの出力を子プロセスの入力にするように書き換えたところ,プログラムが終了しなくなってしまった. その理由を考え,プログラムが終了するようにプログラムを修正せよ.

     1  #include <stdio.h>
     2  #include <stdlib.h>
     3  #include <unistd.h>
     4  #include <sys/types.h>
     5  #include <sys/uio.h>
     6  #include <sys/wait.h>
     7
     8  int pipe_fd[2];
     9
    10  void do_parent(void)
    11  {
    12          char *p = "Hello, my kid.";
    13          int status;
    14
    15          printf("This is parent.\n");
    16
    17          close(pipe_fd[0]);
    18
    19          while (*p) {
    20                  if (write(pipe_fd[1], p, 1) < 0) {
    21                          perror("write");
    22                          exit(1);
    23                  }
    24                  p++;
    25          }
    26
    27          if (wait(&status) < 0) {
    28                  perror("wait");
    29                  exit(1);
    30          }
    31  }
    32
    33  void do_child(void)
    34  {
    35          char c;
    36          int count;
    37
    38          printf("This is child.\n");
    39
    40          close(pipe_fd[1]);
    41
    42          while ((count = read(pipe_fd[0], &c, 1)) > 0) {
    43                  putchar(c);
    44          }
    45          putchar('\n');
    46
    47          if (count < 0) {
    48                  perror("read");
    49                  exit(1);
    50          }
    51  }
    52
    53  int main(void)
    54  {
    55          int child;
    56
    57          if (pipe(pipe_fd) < 0) {
    58                  perror("pipe");
    59                  exit(1);
    60          }
    61
    62          if ((child = fork()) < 0) {
    63                  perror("fork");
    64                  exit(1);
    65          }
    66
    67          if (child)
    68                  do_parent();
    69          else
    70                  do_child();
    71
    72          return 0;
    73  }