0

C でシェルを作成しようとしていますが、問題が発生しています。シェルは、標準入力からテキストを読み取って解析するたびに、毎回ユーザーにプロンプ​​トを表示して、ループで動作することになっています。次に、引数がトークンに分割され、各トークンが引数ベクトルに配置されます。次に、コードは子をフォークし、引数ベクトルをパラメーターとして使用してコマンドを実行します。次に、コードは子が終了するのを待ち、子に関する統計 (ランタイムなど) を出力します。問題は、プログラムを実行するたびに ls /home と入力すると (たとえば)、ホーム ディレクトリが一覧表示されず、現在のディレクトリが一覧表示されることです。さらに、新しいディレクトリを追加しようとすると、コードに変数を追加すると、コードは機能しなくなります。これを解決する方法はありますか?

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <time.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <string.h>

#define TRUE 1
#define BUFFERSIZE 129

int main (int argc, char* argv[]){

    int status;
    int who = RUSAGE_CHILDREN;
    struct rusage usage;
    struct rusage before_usage;
    struct timeval start, end;
    while(TRUE){
    printf("==>:  ");
    char input_string[BUFFERSIZE];
    memset(input_string, '\0', BUFFERSIZE);
    fgets(input_string, BUFFERSIZE-1, stdin);

        int i = 0;
        char* input_arguments[32]; //Pointers to what the commands will be
        char* token; //The specific piece of the input "ls" or "/home"
    char* program;
    int run_exec = 1; //To say whether execvp will run


    token = strtok(input_string, " ");
    char* prog = malloc(strlen(token)+1);
    memset(prog, '\0', strlen(token));
    strncpy(prog, token, strlen(token));
    program = prog;
    printf("PROGRAM: %s\n", prog);
    token = strtok(NULL, " ");


    if(strncmp(program, "exit", 4) ==0)
        exit(0);

        while(token != NULL && i < 32){

        printf("TOKEN: %s \n", token);
        char* tmp = malloc(strlen(token)+1);
        memset(tmp, '\0', strlen(token)+1);
        strncpy(tmp, token, strlen(token));
        input_arguments[i]=tmp;
        printf("INPUT: %s \n", input_arguments[i]);

        if(strcmp(program, "cd") == 0){
            chdir(input_arguments[0]);
        run_exec = 0;
        printf("EXEC: %d\n", run_exec);
    }

    printf("%d\n", i);
    i++;
    size_t tmp2 = 50;
    printf("%s\n", getcwd(tmp, tmp2));

    token = strtok(NULL, " ");

    printf("IN LOOP\n");
}
input_arguments[i] = NULL;
printf("AFTER LOOP\n");

if(fork() != 0){
    int start_time = gettimeofday(&start, NULL);
    if(run_exec == 1){
        getrusage(who, &before_usage);
            waitpid(-1, &status, 0);
            int end_time = gettimeofday(&end, NULL);
            double wall_time_passed = (end.tv_sec -start.tv_sec)*1000
                + (end.tv_usec - start.tv_usec)/1000;
            getrusage(who, &usage);
            double user_time = (usage.ru_utime.tv_sec*1000 +
                usage.ru_utime.tv_usec/1000);
            double system_time = (usage.ru_stime.tv_sec*1000 
                + usage.ru_stime.tv_usec/1000);

//long page_faults = 0;
//long soft_faults = 0;
//long invol = 0;
//long vol = 0;
            printf("Number of Page Faults: %ld \n", usage.ru_majflt -before_usage.ru_majflt);
        //printf("soft_faults: %lu\n", soft_faults);
        printf("Number of Page Reclaims: %ld \n",                                usage.ru_minflt - before_usage.ru_minflt);
        //printf("soft_faults: %lu\n", soft_faults);
        printf("Number of times preempted involuntarily: %ld \n",                usage.ru_nivcsw - before_usage.ru_nivcsw);
        printf("Number of times preempted Voluntarily: %ld \n",                     usage.ru_nvcsw - before_usage.ru_nivcsw);
            printf("User Time: %f \n", user_time);
            printf("System Time: %f \n", system_time);
            printf("Wall-Time: %f \n", (wall_time_passed));
    }
}

else{
    if(run_exec == 1){
    printf("RUNNING EXEC: %s, %s \n", program, *input_arguments); 
    printf("EXEC: \n", execvp(program, input_arguments));
    }

}
}

return 0;

}

4

2 に答える 2

1

ここでいくつかのこと:

  1. プログラムでEnterキーを押すたびに、子にまたがり、最終的にwaitpidになります。それは望ましいことですか。
  2. char* tmp = malloc(strlen(token)+1);
    memset(tmp, '\0', strlen(token)+1);
    strncpy(tmp, token, strlen(token));
    

    主にstrdupと呼ばれます。

  3. ところで、実際に strlen(token) を使用してが返された場合、token[strlen(token)] はすでに '\0' です。これは strlen が停止方法を知っているためです。したがって、上記の memset は不要です (その長さで strncpy を実行します)。

  4. あなたの主な問題は、あなたがexecvp man pageを正しく理解していないことです。次のように述べています。

    この呼び出しの結果として C 言語プログラムが実行される場合、次のように C 言語関数呼び出しとして記述します。

    int main (int argc, char *argv[]);

    後で、それは続けて次のように述べています。

    arg0,... で表される引数は、ヌル終了文字列へのポインタです。これらの文字列は、新しいプロセス イメージで使用できる引数リストを構成します。リストは null ポインターで終了します。引数 arg0 は、exec 関数の 1 つによって開始されるプロセスに関連付けられたファイル名を指す必要があります。

    それは明らかにあなたが見逃したポイントです。argv パラメーターの最初の引数は、path 引数として渡される値と同じ (または許容される変化形) でなければなりません。

さらに、通常、シェルは PATH 変数自体を探索し、execve 呼び出しを介してプロセス イメージを置き換えます (最後の e は、通常、シェルでは環境変数を変更できるためです)。ファイルがあった実際のパスを *path として渡します。 PATH 内にあり、ユーザーが入力した argv[0] 引数です。

それについて、POSIX標準は次のように述べています

厳密に準拠した POSIX アプリケーションの要件では、最初の引数として渡される値は、開始されるプロセスに関連付けられたファイル名であると述べられています。[...] 渡されたファイル名が実際のファイル名ではない場合があります [...]

したがって、次のような実行可能ファイルがある場合:

#include <stdio.h>
int main(int argc, char *argv[]) {
  printf("I was called as %s\n", argv[0]); return 0;
}

としてコンパイルできますtestが、にシンボリックリンクしanother_testます。

として実行するとtestI was called as test表示されますが、another_test として実行すると表示されません。プログラムは、どのように呼び出されたかを知る必要がある場合があるため、これは理にかなっています。たとえば、busybox では、ユーザーが何を望んでいるのかを知るために、それが発生する必要があります。

しかし、あなたが投稿したコードは、いくつかを追加input_arguments[0] = programして「int i」を1で開始するだけではありません。上記のものも当てはまります。車輪を再発明しないでください。strdupはすでに存在し、malloc + memsetの代わりに使用する必要があります+strncpy. 3 つの関数呼び出しが少なくなり、コードが何をするかという概念がより凝縮されています。

また、ループが終了するたびに子が生成されます。プログラムが生成されなくても (プログラムが存在しなかった可能性があります)、問題ありません。チェック後にフォークしないのはなぜrun_execですか?生成した特定のスレッド (親で fork() が返す pid_t) を待たないのはなぜですか?

イメージを生成できない場合、子プロセスを強制終了しないのはなぜですか? 現在、子プロセスが execvp 呼び出しで失敗した場合、子プロセスがさらにコマンドを要求し、強制終了されると、親プロセスが統計を出力してジョブを続行します。それはおそらく意図したものではなく、直感に反するものです。

その上、気づいたかどうかはわかりませんが、stdin で EOF をチェックしていないため、インタラクティブではなくファイルからフィードしている場合 (またはユーザーが EOF を入力した場合 [Ctrl.+D] ]))。おそらく、終了アクションも EOF にバインドする必要があります。そうしないと、スクリプト ファイルの「消費」が完了しません。

于 2013-09-10T22:41:44.103 に答える