[유 닉 스 / 리 눅 스 프로 그래 밍 실천] 셸 은 프로그램 을 어떻게 실행 합 니까 - 명령 해석 기 를 작성 합 니 다 sh

1. 셸 은 어떻게 프로그램 을 실행 합 니까?
셸 은 아래 순환 으로 구성 되 어 있 습 니 다:
while (!end_of_input)
    getcommand
    execute command
    wait for command to finish

우 리 는 실제로 셸 을 사용 할 수 있다.
jiange@jiange-Inspiron:~/cpp$ ls
override  override.cpp  test  test1.cpp  test.cpp  test.o  test.sh
jiange@jiange-Inspiron:~/cpp$ ps
  PID TTY          TIME CMD
 2457 pts/12   00:00:00 bash
 3780 pts/12   00:00:00 ps

상기 과정 은:
sh 는 ls 를 읽 고 프로 세 스 ls 를 새로 만 들 고 실행 합 니 다. 이때 sh 는 ls 가 종료 되 기 를 기다 리 고 ls 가 종료 되면 sh 로 돌아 가 sh 는 ps 를 계속 읽 습 니 다.
따라서 sh 를 만 들 려 면 프로그램 을 실행 하 는 방법, 생 성, 프로 세 스 종료, 그리고 프로 세 스 가 다른 프로 세 스 의 끝 을 기다 리 는 방법 을 알 아야 합 니 다.
2. 프로그램 을 어떻게 실행 합 니까?
답:
execvp(progname, arglist)

그것 의 운영 절차:
  • 프로그램 이 execvp 를 호출 합 니 다.
  • 디스크 에서 프로그램 을 불 러 옵 니 다.
  • 커 널 은 arglist 를 프로 세 스 로 복사 합 니 다.
  • 커 널 호출 main (argc, argv).

  • ls 를 실행 하 는 예:
    /* exec1.c - shows how easy it is for a program to run a program
     */
    
    main()
    {
        char    *arglist[3];
    
        arglist[0] = "ls";
        arglist[1] = "-l";
        arglist[2] = 0 ;
        printf("* * * About to exec ls -l
    "
    ); execvp( "ls" , arglist ); printf("* * * ls is done. bye
    "
    ); }

    실행 결과:
    $ ./exec1 
    * * * About to exec ls -l
        40
    -rwxrwxr-x 1 jiange jiange 7370 12   7 22:59 exec1
    -rw-rw-r-- 1 jiange jiange  262 10  14  2007 exec1.c
    -rw-rw-r-- 1 jiange jiange  406 10  14  2007 forkdemo1.c
    -rw-rw-r-- 1 jiange jiange  315 10  14  2007 forkdemo2.c
    -rw-rw-r-- 1 jiange jiange  503 10  14  2007 forkdemo3.c
    -rw-rw-r-- 1 jiange jiange 1395 10  14  2007 psh1.c
    -rw-rw-r-- 1 jiange jiange 1766 10  14  2007 psh2.c
    -rw-rw-r-- 1 jiange jiange  784 10  14  2007 waitdemo1.c
    -rw-rw-r-- 1 jiange jiange 1077 10  14  2007 waitdemo2.c

    모든 것 이 정상적으로 작 동 하 는 것 같 지만, 왜 printf ("* * ls is done. bye");이 한 마디 가 실행 되 지 않 았 는데?!
    이 는 execvp 의 역할 을 이해 해 야 합 니 다. execvp 는 현재 프로 세 스 의 코드 와 데 이 터 를 디스크 에서 불 러 옵 니 다. 따라서 execvp 를 실행 한 후에 프로 세 스 exec 1 이 완전히 교체 되 어 후속 인쇄 작업 을 수행 하지 않 습 니 다.
    2. 프롬프트 가 있 는 셸
    이 버 전 은 사용자 에 게 프로그램 이름과 인 자 를 입력 하고 실행 하 라 고 알려 줍 니 다.
    /* prompting shell version 1 * Prompts for the command and its arguments. * Builds the argument vector for the call to execvp. * Uses execvp(), and never returns. */
    
    #include <stdio.h>
    #include <signal.h>
    #include <string.h>
    
    #define MAXARGS 20 /* cmdline args */
    #define ARGLEN 100 /* token length */
    
    int main()
    {
        char    *arglist[MAXARGS+1];        /* an array of ptrs */
        int numargs;            /* index into array */
        char    argbuf[ARGLEN];         /* read stuff here */
        char    *makestring();          /* malloc etc */
    
        numargs = 0;
        while ( numargs < MAXARGS )
        {                   
            printf("Arg[%d]? ", numargs);
            if ( fgets(argbuf, ARGLEN, stdin) && *argbuf != '
    '
    ) arglist[numargs++] = makestring(argbuf); else { if ( numargs > 0 ){ /* any args? */ arglist[numargs]=NULL; /* close list */ execute( arglist ); /* do it */ numargs = 0; /* and reset */ } } } return 0; } int execute( char *arglist[] ) /* * use execvp to do it */ { execvp(arglist[0], arglist); /* do it */ perror("execvp failed"); exit(1); } char * makestring( char *buf ) /* * trim off newline and create storage for the string */ { char *cp, *malloc(); buf[strlen(buf)-1] = '\0'; /* trim newline */ cp = malloc( strlen(buf)+1 ); /* get memory */ if ( cp == NULL ){ /* or die */ fprintf(stderr,"no memory
    "
    ); exit(1); } strcpy(cp, buf); /* copy chars */ return cp; /* return ptr */ }

    문자열 매개 변 수 를 동적 으로 할당 하기 위해 makestring 함 수 를 사용 합 니 다.
    위의 프로그램 을 실행 하면 우 리 는 파 라 메 터 를 전달 할 수 있다.
    그러나 비교적 심각 한 결함: ls 를 실행 한 후에 전체 프로그램 이 종료 되 었 습 니 다. 우 리 는 ls 를 실행 한 후에 우리 의 셸 로 돌아 가 셸 은 명령 입력 을 계속 받 고 실행 할 수 있 기 를 기대 합 니 다.
    처음에 우 리 는 sh 가 ls 를 읽 고 프로 세 스 ls 를 새로 만 들 고 실행 할 것 이 라 고 말 했 습 니 다. 이때 sh 는 ls 가 종료 되 기 를 기다 리 고 ls 가 종료 되면 sh 로 돌아 갑 니 다.
    즉, sh 는 ls 명령 을 직접 실행 하지 않 고 프로 세 스 를 새로 만 들 었 습 니 다. 그러면 문제 가 생 겼 습 니 다. 어떻게 새로운 프로 세 스 를 만 듭 니까?
    정 답: fork () 사용 하기;
    하위 프로 세 스 를 만 들 기 위해 fork 를 사용 할 수 있 습 니 다. 그리고 새로운 프로 세 스 는 execvp 를 사용 하여 사용자 명령 을 수행 할 수 있 습 니 다.
    그렇다면 부모 프로 세 스 sh 는 하위 프로 세 스 ls 의 끝 을 어떻게 기다 리 나 요?
    정 답: pid = wait (& status) 사용 하기;
    wait 는 하위 프로 세 스 가 끝 날 때 까지 프로 세 스 를 호출 하 는 것 을 막 고 wait 가 하위 프로 세 스 가 끝 날 때 exit 의 값 (status 를 통 해) 을 전달 하고 하위 프로 세 스 의 PID 를 되 돌려 줍 니 다.
    3. 사용자 명령 을 계속 받 을 수 있 는 셸
    이상 의 분석 을 통 해 우 리 는 새로운 셸 을 설계 할 수 있다.
    명령 가 져 오기 - > fork 로 새 프로 세 스 만 들 기 - > 부모 프로 세 스 wait - > 하위 프로 세 스 exec 새 프로그램 실행 - > 새 프로그램 main 에서 실행 - > 새 프로그램 exit - > 부모 프로 세 스 가 하위 프로 세 스 상 태 를 가 져 옵 니 다 - > 명령 가 져 오기
    /** prompting shell version 2 ** ** Solves the `one-shot' problem of version 1 ** Uses execvp(), but fork()s first so that the ** shell waits around to perform another command ** New problem: shell catches signals. Run vi, press ^c. **/
    
    #include <stdio.h>
    #include <signal.h>
    
    #define MAXARGS 20 /* cmdline args */
    #define ARGLEN 100 /* token length */
    
    main()
    {
        char    *arglist[MAXARGS+1];        /* an array of ptrs */
        int numargs;            /* index into array */
        char    argbuf[ARGLEN];         /* read stuff here */
        char    *makestring();          /* malloc etc */
    
        numargs = 0;
        while ( numargs < MAXARGS )
        {                   
            printf("Arg[%d]? ", numargs);
            if ( fgets(argbuf, ARGLEN, stdin) && *argbuf != '
    '
    ) arglist[numargs++] = makestring(argbuf); else { if ( numargs > 0 ){ /* any args? */ arglist[numargs]=NULL; /* close list */ execute( arglist ); /* do it */ numargs = 0; /* and reset */ } } } return 0; } execute( char *arglist[] ) /* * use fork and execvp and wait to do it */ { int pid,exitstatus; /* of child */ pid = fork(); /* make new process */ switch( pid ){ case -1: perror("fork failed"); exit(1); case 0: execvp(arglist[0], arglist); /* do it */ perror("execvp failed"); exit(1); default: while( wait(&exitstatus) != pid ) ; printf("child exited with status %d,%d
    "
    , exitstatus>>8, exitstatus&0377); } } char *makestring( char *buf ) /* * trim off newline and create storage for the string */ { char *cp, *malloc(); buf[strlen(buf)-1] = '\0'; /* trim newline */ cp = malloc( strlen(buf)+1 ); /* get memory */ if ( cp == NULL ){ /* or die */ fprintf(stderr,"no memory
    "
    ); exit(1); } strcpy(cp, buf); /* copy chars */ return cp; /* return ptr */ }

    형식 제어 에 있어 서, 예 를 들 어 한 줄 에 인 자 를 입력 하 는 방법, exit 로 셸 을 종료 하 는 방법 등 은 여기에서 우 리 는 토론 하지 않 습 니 다.
    실행 할 때, 우 리 는 심각 한 오 류 를 발견 했다.
    만약 우리 가 키 프로 세 스 를 실행 하고 있다 면, 이때 우 리 는 Ctrl - C 키 를 입력 합 니 다. 우 리 는 하위 프로 세 스 가 종료 되 어야 하 며, 셸 은 계속 실행 되 어야 합 니 다. 그러나 사실은 셸 도 종료 되 었 습 니 다. 키보드 신호 SIGINT 가 모든 연결 프로 세 스 를 보 냈 기 때 문 입 니 다!
    하위 프로 세 스 가 실 행 될 때 셸 은 신 호 를 중단 했다 고 해서 종료 되 지 않 습 니 다. 하위 프로 세 스 가 실 행 될 경우 셸 은 SIGINT 신호 에 대한 응답 을 ignore 로 설정 하고 하위 프로 세 스 가 끝 난 후에 기본 방식 으로 설정 합 니 다.이렇게 하면 셸 은 하위 프로 세 스 가 실 행 될 때 중단 신 호 를 차단 할 수 있 습 니 다.

    좋은 웹페이지 즐겨찾기