2013-09-16 2 views
3

나는이 질문을 머리 글자로 쓰고 싶다. 나는 C 언어에 익숙하지 않아서 끔찍한데, 그래서 나는 뻔뻔스러운 실수 나 나쁜 스타일에 대해 사전에 사과한다. 또한, 나는 당신이 내 코드를 표시하기 전에 문제를 소개하는 방법을 잘 모르겠어요, 그래서 여기있다 :프로그램이 이상하게 루프의 맨 위로 돌아 가기

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 


int main() 
{ 

    int MAX_INPUT_SIZE = 200; 
    volatile int running = 1; 
    while (running) 
    { 

     char input[MAX_INPUT_SIZE]; 
     char *tokens[100]; 
     const char *cmds[] = { "wait", "pwd", "cd", "exit" }; 
     char *cmdargs[100]; 

     printf("shell> "); 
     fgets(input, MAX_INPUT_SIZE, stdin); 

     // remove newline character at end 
     int nl = strlen(input) - 1; 
     if (input[nl] == '\n') 
     { 
      input[nl] = '\0'; 
     } 

     // tokenize input string, put each token into an array 
     char *space; 
     space = strtok(input, " "); 
     tokens[0] = space; 

     int i = 1; 
     while (space != NULL) 
     { 
      space = strtok(NULL, " "); 
      tokens[i] = space; 
      ++i; 
     } 

     // copy tokens after first one into string 
     int noargscheck; 
     if (tokens[1] != NULL) 
     { 
      noargscheck = 0; 
      strcpy((char *)cmdargs, tokens[1]); 
      for (i = 2; tokens[i] != NULL; i++) 
      { 
       strcat((char *)cmdargs, " "); 
       strcat((char *)cmdargs, tokens[i]); 
      } 
     } 
     else 
     { 
      noargscheck = 1; 
     } 

     // compare tokens[0] to list of internal commands 
     int isInternal = -1; 
     for (i = 0; i < 4; i++) 
     { 
      if (strcmp(tokens[0], cmds[i]) == 0) 
      { 
       isInternal = i; 
      } 
     } 


     // internal commands 
     char wd[200]; 
     if (isInternal != -1) 
     { 
      switch (isInternal) 
      { 
      case 0: 
       // wait 
       break; 
      case 1: 
       // pwd 
       if (getcwd(wd, sizeof(wd)) == NULL) 
       { 
        perror("getcwd() error!"); 
       } 
       else 
       { 
        printf("%s\n", wd); 
       } 
       break; 
      case 2: 
       // cd 
       if (noargscheck) 
       { 
        chdir("/home"); 
       } 
       else if (chdir((const char *)cmdargs) != 0) 
       { 
        perror("cd failed"); 
       } 
       break; 
      case 3: 
       // exit 
       exit(1); 
       break; 
      } 
     } 

     else 
     { 
      // external commands 

      pid_t child_pid; 
      switch (child_pid = fork()) 
      { 
      case -1: 
       perror("Fork failed"); 
       return 1; 
      case 0: 
       // child 
       printf("\nHERE\n"); // for debugging 
       execvp(tokens[0], cmdargs); 
       break; 
      } 
     } 
    } 
} 

내가 입력 echo hello world이 코드를 실행하면 프로그램은 성공적으로 두 번째의 case 0 경우 진입 다음과 같이 switch (child_pid=fork())하지만 예기치 않은 출력, 시작 스위치 문입니다 :

출력 :

shell> echo hello world (내 입력)

,691 (프롬프트에서 내 입력을 표시 한 줄 포함)

shell>

HERE

shell>가 (프로그램이 이제 다음 사용자 입력을위한 프롬프트에서 여기 대기)

내가 왜 추가를 알아낼 수 없습니다 (이것은 내가 이해하지 못하는 부분입니다) shell> 프롬프트가 인쇄 중입니다. 아무도 문제를 볼 수 있습니까?

EDIT : execvp의 첫 번째 매개 변수가 수정되었습니다. "echo" (내가 어리석기 때문에 거기에 있었다)에서 tokens[0]에 바뀌었다.

+3

* 왜 보이지 않는지 설명 할 수 있습니까? 즉, 코드가이 동작을 초래해서는 안된다고 생각하는 방식을 설명하십시오. –

+0

첫 번째'shell>'과'HERE' 사이에있는 여분의'shell>'이 인쇄되는 것을 원하지 않습니다. Dan은 문제는 내 자식 프로세스가 execvp에서 오류를보고하고 있음을 지적합니다.이 오류는 확인하지 않고 루프의 맨 위로 돌아갑니다. – flexcalibur6

답변

7

포크를하면 이제 그 시점에서 두 개의 프로세스가 있습니다. 귀하의 자녀는 HERE 메시지를 인쇄 한 다음 execvp으로 전화하십시오. execvp의 반환 값을 확인하지 않으므로 오류가 반환 될 수 있습니다. cmdargs은 벡터, 즉 널 포인터로 끝나는 문자열 포인터 배열이어야합니다. 대신 execvp 문자열을 전달하고 있습니다. 즉, char* []인데, 이는 cmdargs이지만 이전에 cmdargs을 잘못 처리 한 것입니다. 예 : strcpy((char*)cmdargs, tokens[1]);. 그러면 *cmdargs에 문자열이 배치됩니다.

char* cmdargs[] is a double pointer 
you treat cmdargs as a single pointer and feed it to strcpy 
cmdargs points to: 'H' 'e' 'l' 'l' 'o' '\0' 

그러나,이 execvp는 원하는 것을되지 않습니다 : 문자열은 8 비트입니다 아스키 NUL 다음에 0 개 이상의 비 - 제로 문자의 배열입니다. execvp는 더 같이 다소 보이는 벡터를 원하는 :

char* cmdargs[] is a double pointer 
cmdargs[0] is a single pointer 
cmdargs[0] points to: 'H' 'e' 'l' 'l' 'o' '\0' 
cmdargs[1] points to: 'w' 'o' 'r' 'l' 'd' '\0' 
cmdargs[2] is a null pointer, indicating that it is the end of the vector 

따라서, execvp는이 벡터의 끝을 찾을 수 없습니다 및 -1를 반환, 실패합니다. 이를 테스트하지 않으므로 부모 프로세스와 마찬가지로 하위 프로세스가 루프의 맨 위로 돌아가고 두 프로세스 모두 shell>을 인쇄합니다.

EDIT : 그런데, argv 벡터의 첫 번째 캐릭터 파일 이름이 실행되고 있어야한다 -이 경우 echo을하고 두 번째 및 세 번째 문자열을 제 2 '인자'이어야 - 여기 helloworld입니다. 이것은 여러분이 호출하는 프로그램에 공급되는 argv 벡터이며, 관례 상 벡터의 첫 번째 요소는 호출 된 프로그램의 이름입니다. 해당 협약을 무시하면 echo은 끔찍하게 혼란 스러울 것입니다.

+0

나는 본다. 포인터는 항상 저의 죽음이었습니다. 올바른 매개 변수를'execvp'로 보내기 위해'cmdargs'를 수정하는 방법은 무엇입니까? – flexcalibur6

0

cmdargs는 100 개의 문자열 포인터의 배열로 정의되어 있지만 단일 문자열에 대해 하나의 100 바이트 버퍼로 사용하는 것처럼 보입니다. 또한 토큰 [1]을 특별히 처리하는 이유도 알지 못합니다. 오직 토큰 [0]만이 특별합니다. 그것은 명령입니다. 나머지는 모두 인수입니다. 이 쉘 인

cmdargs[i] = NULL; 

, 당신은 또한 자식 프로세스를 기다리는 잊었 : 인수를 처리하면

while (cmdargs[i++] = strtok(NULL, " ")) 

의 루프 다음 ​​execvp는 대한 cmdargs에서 닫는 NULL 포인터()해야한다. 마지막 하위 프로세스가 끝나기 전에 사용자에게 입력을 요구합니다. 마지막 스위치 케이스는 다음과 같이 표시되어야합니다.

pid_t child_pid; 
switch (child_pid = fork()) 
{ 
case -1: 
    perror("Fork failed"); 
    return 1; 
case 0: 
    // child 
    printf("\nHERE\n"); // for debugging 
    execvp(tokens[0], cmdargs); 
    perror("Exec failed"); 
    exit(1); 
default: 
    // parent 
    int status; 
    wait(&status); 
    break; 
} 
+0

'perror ("Exec failed")'줄이 왜 거기에 있습니까? – flexcalibur6

+0

전체 자식 프로세스가 새 프로그램을 실행하기 시작함에 따라 execvp()가 정상적으로 반환되지 않습니다. 반환되는 경우 오류입니다 (예 : 실행할 프로그램을 찾을 수 없으므로 오류 메시지입니다. 하위 프로세스도 종료해야합니다. – SzG

+0

그럴 때마다 'perror'가 출력되지 않습니까? execvp가 작동하더라도? – flexcalibur6

관련 문제