0

Unix コマンド環境で C シェルを作成しています。これまでのところ、すべてが正常に機能していますが、次のコマンドを実行すると、コードは正しく実行されますが、セグ フォールトで終了します。

1. ls | ls | ls
2. ls -al | ls -al > output.txt

結論: セグメンテーション違反は、「XXXX ここから始まる」と「XXXX ここから終わる」の間の行から発生していると思います。また、間違っているかもしれませんが、最初の while ループの getchar テストと関係があると思います。このコードをコンパイルして実行すると、「ls -al | ls -al > output.txt」と入力するとすべてが実行され、その後に shhh> AAAAA が出力され、その後に seg fault が表示されることがわかります。誰かが私の最初の観察を確認または否定できますか?

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <fcntl.h>

main()
{
   char *path, *argv[20], buf[80], n, *p;
   int m, status, inword, continu;
   int inFlag, outFlag, bgFlag, k, count, j, pipes, pid, aCount, dirCount,
      hCount;
   int r_tube[2], l_tube[2];
   char *hist;
   char *h1[20] = { 0 };
   aCount = 0;
   dirCount = 0;

   while (1)
   {
      inword = 0;
      p = buf;
      m = 0;
      continu = 0;
      inFlag = outFlag = bgFlag = k = count = j = pipes = pid = 0;
      hCount = 0;
      int loc[20] = { 0 };
      char currD[50];

      printf("\nshhh> ");

      while ((n = getchar()) != '\n' || continu)   //XXXXXXXXX Starts here
      {
         if (n == ' ')
         {
            if (inword)
            {
               inword = 0;
               *p++ = 0;
            }
         }
         else if (n == '\n')
            continu = 0;
         else if (n == '\\' && !inword)
            continu = 1;
         else
         {
            if (!inword)
            {
               inword = 1;
               argv[m++] = p;
               *p++ = n;
            }
            else
               *p++ = n;
         }
      }                                    //XXXXXXXX Ends here

      *p++ = 0;
      argv[m] = 0;

      getcwd(currD, 50);

      while (argv[k] != 0)
      {                         //stores all entered commands into history array
         h1[aCount] = strdup(argv[k]);
         aCount++;
         k++;
      }
      k = 0;

      if ((strcmp(argv[0], "history") == 0) ||
         (strcmp(argv[0], "History") == 0))
      {                         //tests to see if history is asked for
         printf("History: \n");
         for (k = 0; k < aCount; k++)
            printf("%s\n", h1[k]);
      }


      if (strcmp(argv[0], "cd") == 0)
      {                         //tests to see if CD is needed
         if (strcmp(argv[1], "..") == 0)
            chdir(currD);
         else
         {
            while (argv[dirCount] != 0)
            {
               getcwd(currD, 50);
               chdir(argv[dirCount]);
               dirCount++;
            }
         }
      }

      char *outFile = NULL;
      char *inFile = NULL;
      loc[0] = 0;

      while (argv[count] != 0)
      {  //while loop sets the flags for input redirection, 
         // output redirection, background operator, and piping
         if (strcmp(argv[count], "<") == 0)
         {
            inFile = strdup(argv[count + 1]);
            argv[count] = argv[count + 1] = 0;
            inFlag = 1;
         }
         else if (strcmp(argv[count], ">") == 0)
         {
            outFile = strdup(argv[count + 1]);
            argv[count] = argv[count + 1] = 0;
            outFlag = 1;
         }
         else if (strcmp(argv[count], "&") == 0)
         {
            argv[count] = 0;
            bgFlag = 1;
         }
         else if (strcmp(argv[count], "|") == 0)
         {
            argv[count] = 0;
            loc[pipes + 1] = count + 1;
            pipes++;
            printf("LOC0: %d ", loc[0]);
            printf("LOC1: %d ", loc[1]);
            printf("LOC2: %d ", loc[2]);
            printf("DONE WITH IF\n");
         }
         else
            loc[count] = count;

         count++;
      }

      for (k = 0; k <= pipes; k++)
      {                         //actual execution of commands
         printf("IN FOR\n");
         if (k < pipes)
         {
            pipe(r_tube);
            j++;
         }

         pid = fork();          //fork child every time to exec

         if (pid > 0)
         {
            if (j > 0)
            {
               close(l_tube[0]);
               close(l_tube[1]);
            }
            l_tube[0] = r_tube[0];
            l_tube[1] = r_tube[1];
         }
         else if (pid == 0)
         {
            if ((k == 0) && (inFlag == 1))
            {
               int n = open(inFile, O_RDONLY | O_CREAT);
               if (n == -1)
               {
                  printf("Couldn't open inFile!\n");
                  exit(1);
               }
               close(0);
               dup(n);
               close(n);
            }
            else if ((k == pipes) && (outFlag == 1))
            {
               int out = open(outFile, O_WRONLY | O_CREAT, 0666);
               if (out < 0)
               {
                  printf("Could'nt open outFile!\n");
                  exit(1);
               }
               close(1);
               dup(out);
               close(out);
            }
            printf("K: %d ", k);
            printf("PIPES: %d ", pipes);
            printf("PID: %d\n", pid);
            execvp(argv[loc[k]], &argv[loc[k]]);
         }
         printf("ONE\n");
         if (bgFlag == 0)
            wait(NULL);
      }
      printf("TWO\n");
      if (strcmp(argv[0], "quit") == 0)
         exit(0);               //tests for exit/quit to end program
      if (strcmp(argv[0], "exit") == 0)
         exit(0);
      printf("THREE\n");
      for (k = 0; k < 20; k++)  //reset all of argv to NULL
         argv[k] = 0;
      printf("FOUR\n");

      wait(&status);
   }
}
4

1 に答える 1

1

セグメンテーション違反に対処する最も簡単な方法は、カーネルによって生成されたコアダンプに保存されたスタック トレースを分析することです。ほとんどのディストリビューションではコアダンプの保存が無効になっているため、有効にする方法があります

ulimit -c unlimited

proc ファイル システムには、corudump の生成を構成するためのいくつかのファイルがあります。最も重要なのはcore pattern /proc/sys/kernel/core_patternで、コアダンプ名とオプションで後処理コマンドを指定します。

プロセスがセグメンテーション違反シグナルを受信すると、カーネルはその状態をコアダンプ ファイルに保存します。これは後でdgbで検査できます。

gdb -c core ./binary_which_produced_the_core
bt

これにより、セグメンテーション違反が発生したときにプログラムが行っていたことのバック トレースが出力されます。

たとえば、プログラムの場合

Program terminated with signal 11, Segmentation fault.
#0  main () at main.c:58
58                 *p++ = n;
(gdb) bt
#0  main () at main.c:58
(gdb) 

不足しているヘッダーを追加する必要があったため、行番号は +2 です。

また、-Wall -Wextraコンパイラ オプションを使用してすべての警告を報告し、常に修正してください。たとえば、初期化されていない l_tube 配列があります。

于 2012-10-27T06:21:49.027 に答える