東京工業大学 工学部 情報工学科 プログラミング第三 演習2の回答例 (一部) 東京工業大学 大学院情報理工学研究科 小林隆志 野呂智哉 <[email protected]> 課題1 プログラミング第三のホームページから ex1.c をダウンロードし,ex1.c に変更を加える /* ex1.c */ void child() { printf("PID = %d, PPID = %d¥n", getpid(), getppid()); sleep(1); printf("Process %d terminated.¥n", getpid()); exit(0); } int main() { printf("PID = %d, PPID = %d¥n", getpid(), getppid()); /*子プロセスを生成し,次の child() を子プロセスで実⾏するよう変更*/ child(); /*ここで親プロセスが生成した子プロセスの終了を待つようにする*/ printf("Process %d terminated.¥n", getpid()); return 0; } 2 1 課題2-1 ● ● 未完成シェルプログラム ex2.c をダウンロード – ex2.c はコマンドの⼊⼒は受け付ける – コマンドの実⾏はできない – Ctrl-d(ctrl キーと d キーの同時押し)で終了する 以下を実装 ● ユーザが⼊⼒したコマンドを実⾏できるようにする ● “exit” が⼊⼒された場合はシェルプログラム⾃体が終了する (条件) ● forkとexecファミリで実装してください ● system()関数による実装は減点します。 3 課題2-2 • パイプ機能を加える – 例: ls | wc -l ls: 現在作業中のディレクトリにあるファイルの一覧を 標準出力に出力する wc -l : 標準入力中の行数を数える つまり,上記をパイプで接続すると,現在作業中の ディレクトリにあるファイルの数を数える – “comm1 | comm2” と一段のパイプが扱えれば OK – “|” の両側にスペースがあると仮定してよい 4 2 課題2-3 ※ 余力のある人向け さらに一般的なshellにある機能を加えて下さい. 例1) “comm1 | comm2 | ... | commN” という 多段のパイプを扱えるように拡張する 例2) cd コマンドを利⽤できるようにする 例3) ヒストリ機能*を利⽤できるようにする *ヒストリ機能: 過去に実⾏したコマンドを呼び出す機能.「↑」や「↓」キーで ヒストリの探索を⾏った経験があると思うが,例えば bash というシェルプログラムでは !! と⼊⼒すれば直前に実⾏したコマンドがもう一度実⾏される.また history と⼊⼒すればヒストリのリストが表⽰され, !50 とすればリスト中の50番のコマンドが実⾏されます. 5 提出状況と回答状況 第2回 E 45人,O 50人 問題1:全員 回答 問題2:2-1が未完成 E 3名,O 7名 2-2が未完成 E 24名,O 20名 問題3: 多段パイプ (5/16), cd (4/8), ヒストリ(3/8), リダイレクト(1/0) 6 3 実装例 問題1 int main() { printf("PID = %d, PPID = %d¥n", getpid(), getppid()); int status; pid_t pid = fork(); if (pid == -1) { perror("error in fork"); exit(1); } else if (pid == 0) { child(); } else { wait(&status); } printf("Process %d terminated.¥n", getpid()); return 0; } 7 実装例 問題2-1 If (argc > 0) { if (strcmp("exit", argv[0]) == 0) { printf("Goodbye!¥n"); exit(0); } 子プロセスの生成に失敗した場合 int status; pid_t pid = fork(); if (pid == -1) { execvpはパス検索を行うので, perror("Error in fork"); 第1引数にフルパスを指定する必要はない } else if (pid == 0) { execvp(argv[0], argv); perror("Failed"); exit(1); execvpが正常に実行されれば } else { コマンド終了時に子プロセスは自動的に終了するが, wait(&status); } execvpの実行に失敗した場合 } (入力したコマンドが存在しない場合) 子プロセスを明示的に終了させなければならない 8 4 実装例 問題2-2 int pos_pipe; for (pos_pipe = 0; pos_pipe < argc; pos_pipe++) { if (strcmp("|", argv[pos_pipe]) == 0) { argv[pos_pipe] = NULL; break; } パイプ位置検出,NULLにしておく } if (pos_pipe == argc) { /* パイプを含まない⼊⼒ (問題2-1) */ } else { int fd[2]; pipe(fd); 9 } パイプ作成 pid_t pid = fork(); if (pid == -1) { perror("Error in fork"); exit(1); 1つ目のコマンド } else if (pid == 0) { dup2(fd[1], 1); close(fd[1]); execvp(argv[0], argv); perror("Failed"); close(fd[0]); exit(1); } else { close(fd[1]); wait(&status); if (status != 0) {close(fd[0]); continue;} pid_t pid2 = fork(); if (pid2 == -1) { 子プロセスを2つ生成しているので, perror("Error in fork"); exit(1); waitは2回必要 } else if (pid2 == 0) { dup2(fd[0], 0); close(fd[0]); execvp(argv[pos_pipe+1], &argv[pos_pipe+1]); perror("Failed"); exit(1); } else {close(fd[0]); wait(&status);} } 2つ目のコマンド 実装例 問題2-3 (多段パイプ) 10 int fd[2], fd_backup; int i, command_pos = 0; for (i = 0; i < argc; i++) { if (strcmp("|", argv[i]) != 0) continue; argv[i] = NULL; if (command_pos > 0) { if ((fd_backup = dup(fd[0])) == -1) { 1つ前のコマンドの出力が perror("Error in dup"); exit(1); fd[0]に入っているので, } close(fd[0]); fd_backupにとっておく } pipe(fd); pid_t pid = fork(); if (pid == -1) { perror("Error in fork"); exit(1); } else if (pid > 0) { if (command_pos > 0) close(fd_backup); close(fd[1]); wait(&status); command_pos = i + 1; } else { if (command_pos > 0) { 1つ前のコマンドの出力を if (dup2(fd_backup, 0) == -1) { perror("Error in dup2"); exit(1); 標準入力に } close(fd_backup); 標準出力をfd[1]に } if (dup2(fd[1], 1) == -1) { perror("Error in dup2"); exit(1); argv[command_pos]から } argv[i]までのコマンドを実行 close(fd[1]); execvp(argv[command_pos], &argv[command_pos]); perror("Failed"); close(fd[0]);exit(1); } } (次ページに続く) 5 実装例 問題2-3 (多段パイプ) (つづき) 最後のコマンドを実行 argv[command_pos]から末尾まで if (command_pos > 0) { if ((fd_backup = dup(fd[0])) == -1) { perror("Error in dup"); exit(1); } close(fd[0]); } pid_t pid = fork(); if (pid == -1) { perror("Error in fork"); exit(1); } else if (pid > 0) { if (command_pos > 0) close(fd_backup); wait(&status); } else { if (command_pos > 0) { if (dup2(fd_backup, 0) == -1) { perror("Error in dup2"); exit(1); } close(fd_backup); } execvp(argv[command_pos], &argv[command_pos]); perror("Failed"); exit(1); } 11 実装例 問題2-3 (cd) if (strcmp(argv[0], "cd") == 0) { if (argc == 1) argv[1] = getenv("HOME"); chdir(argv[1]); } 引数がない場合はホームディレクトリ 12 6 実装例 問題2-3 (ヒストリ) char *history[1000]; int last = 0; 先に履歴を保存しておく場所を用意しておく if ('!' == argv[0][0]) { int index = last; if (strcmp("!!", argv[0]) != 0) { char *command = &argv[0][1]; index = atoi(command); } /* inputの内容をhistory[index]にして再実⾏ (省略) */ } else if (strcmp("history", argv[0]) == 0) { /* historyの内容を出⼒ (省略) */ } /* inputの内容を実⾏した後, inputをhistory[last]に追加,last++ (省略) */ 13 7
© Copyright 2024 ExpyDoc