質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

91.37%

  • C

    2524questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

自作シェルでシグナルハンドラを使ってpsコマンドを自作したい

受付中

回答 1

投稿 2017/11/20 02:58 ・編集 2017/11/20 10:56

  • 評価
  • クリップ 1
  • VIEW 94

ijuya_yika

score 14

前提・実現したいこと

C言語の自作シェル内でシグナルハンドラを使って自作psコマンド(現在動いているプロセスのIDを表示するという簡単な関数)を作成したい

発生している問題・エラーメッセージ

<コード訂正後>
removeProcessarr_pid配列のプロセスIDが子プロセス終了後-1に書き換えが行われない => 今まで行った全てのプロセスのプロセスIDが表示されてしまう

<コード訂正前>
(1)HELP1で子プロセスを配列に格納する際に親プロセスと同じプロセスIDが格納されてしまう
(2)HELP2でセグメンテーション違反が起こる
=>指摘を受けポインタを使いまわすのをやめる&int型ポインタの配列をint型配列に変更後し解決

該当のソースコード

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <errno.h>
#define S 1000

int arr_pid[S] = {}; /* ここにプロセスが呼ばれる度新しいプロセスIDを登録 */

void removeProcess(int signal){
    pid_t pid;
    while((pid = waitpid(-1, NULL, WNOHANG)) > 0){
        /* 子プロセスが終了したら子プロセスのPIDを-1に変える */
        for(int i=0; arr_pid[i] != 0; ++i){ /** HELP2 **/
            if(arr_pid[i] == signal){
                arr_pid[i] = -1;
            }
        }
    }
}

void printProcess(){
    int i;
    for(i=0; arr_pid[i] != 0; ++i){
        if(arr_pid[i] != -1){
            printf("PID: %d\n", arr_pid[i]);
        }
    }
}

int main(int argc, char *argv[]){
    char* token[S];
    char* eof;
    char* and;
    char usrin[S];
    int pid, status, count, num_p = 0;


    pid = getpid();
    arr_pid[num_p++]= pid; /* 親プロセスのプロセスIDを取得しarr_pidに入れる */
    signal(SIGCHLD, &removeProcess); /* 子プロセスが終了=>SIGCHLDシグナルが送られる度配列に入れたプロセスIDを-1に変更 */

    while(1){
        printf(">>");
        eof = fgets(usrin, sizeof(usrin), stdin);

        if(eof==NULL)
            exit(0);

        usrin[strlen(usrin)-1] = '\0';
        and = strchr(usrin, '&');

        token[0] = strtok(usrin, " ");
        for(count=1; count<S; ++count){
            token[count] = strtok(NULL, " ");
            if(token[count]==NULL)
                break;
        }

        /* 入力がmy_psなrプロセスを出力 */
        if((strcmp(token[0], "my_ps")==0)){
            printProcess();
        } else {
        /* my_ps 以外なら入力されたコマンドを実行 */
            pid = fork();


            if(pid==-1)
                exit(-1);
            else if(pid==0){
                if(execvp(token[0], token)<0){
                    printf("エラー : %s\n", strerror(errno));
                    exit(1);
                }
            } else {
                /* 子プロセスのプロセスIDを配列に格納 */
                arr_pid[num_p++] = pid;
                if(and==NULL)
                    wait(&status);

            }
        }
    }

    return 0;
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

+1

システムコールを用いて取得したいということでしょうか。
質問にOSの記載がないため参考になるかはわかりませんが、
Linux等であれば、/procを参照することでプロセスの情報を取得することもできます。

$ cat /proc/7163/status
Name:    ntpd
State:    S (sleeping)
Tgid:    7163
Ngid:    0
Pid:    7163
PPid:    1
TracerPid:    0
Uid:    108    108    108    108
Gid:    114    114    114    114
FDSize:    64
Groups:    114 
NStgid:    7163
NSpid:    7163
NSpgid:    7163
NSsid:    7163
VmPeak:      171336 kB
VmSize:      112092 kB
VmLck:           0 kB
VmPin:           0 kB
VmHWM:        5088 kB
VmRSS:         368 kB
VmData:       74456 kB
VmStk:         132 kB
VmExe:         684 kB
VmLib:        5812 kB
VmPTE:         104 kB
VmPMD:          12 kB
VmSwap:         308 kB
HugetlbPages:           0 kB
Threads:    1
SigQ:    0/63511
SigPnd:    0000000000000000
ShdPnd:    0000000000000000
SigBlk:    0000000000000000
SigIgn:    0000000000001000
SigCgt:    0000000180006a47
CapInh:    0000000000000000
CapPrm:    0000000002000400
CapEff:    0000000002000400
CapBnd:    0000003fffffffff
CapAmb:    0000000000000000
Seccomp:    0
Cpus_allowed:    f
Cpus_allowed_list:    0-3
Mems_allowed:    00000000,00000001
Mems_allowed_list:    0
voluntary_ctxt_switches:    5576843
nonvoluntary_ctxt_switches:    3409

投稿 2017/11/20 06:31

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/11/20 08:12

    質問を訂正しました。システムハンドラを使って子プロセスIDを管理するようにしたいです。

    キャンセル

  • 2017/11/20 08:18

    forkの結果としてpidを取得していますが、getpidの値で上書きしているように見えます。

    キャンセル

  • 2017/11/20 08:53

    コードを訂正したのですが頂いたコメントを上手く反映できているでしょうか。
    main直後のpid = getpid();でシェルのプロセスIDを取得し格納、pid=fork()後に親プロセスには子プロセスのプロセスIDが戻る為 pid = getpidとせずそのままarr_pid[num_p++] = &pid;としてみました。ただこれでも同じプロセスIDがarr_pid[0] arr_pid[1]に入ってしまいます

    キャンセル

  • 2017/11/20 09:16 編集

    pidはなぜ&で参照しているのでしょうか。
    ローカル変数をポインタ参照しているため、解放された後に使い回されているのでないかと推測します。
    大変危険なコードだと考えます。

    追記:
    そもそもこのコードだと使い回し等関係なく常に同じアドレスしか返りませんね。

    キャンセル

  • 2017/11/20 09:49 編集

    int* arr_pid[S] = {NULL}; ← int型のポインタの配列にプロセスIDを指したポインタを格納したかったので&で参照しています。

    ご指摘頂いたとおりポインタを使いまわしていた為、新たにpid_t temp = getpid(); arr_pid[num_p++] = &temp;と変更後、常に同じアドレスが格納されてしまう問題を解決できました。

    キャンセル

  • 2017/11/20 10:13 編集

    やりたいことは下記ではないですか?
    *arr_pid[num_p++] = pid;

    キャンセル

  • 2017/11/20 10:18

    *arr_pid[num_p++] = pid;だとセグメンテーション違反になります。
    初期化した時にarr_pidはNULLで埋まっているのが原因だと思います。

    キャンセル

  • 2017/11/20 10:21 編集

    int* arr_pid[S] = {};

    int arr_pid[S] = {};
    にすれば
    arr_pid[num_p++] = pid;
    とできるかと思います。

    キャンセル

  • 2017/11/20 10:30

    コメントありがとうございます。
    よくよく考えたらint型ポインタの配列にするメリットが1つも無かったのに今気づきました。
    int型のポインタへコードを修正しました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

ただいまの回答率

91.37%

関連した質問

同じタグがついた質問を見る

  • C

    2524questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。