2012-04-26 2 views
1

내 프로그램은 다음과 같다 :대화 형 쉘 정지 파이프

//... init fd[2] as pipe ... 
if (child==0){ 
     close(fd[1]); 
     dup2(fd[0], 0); 
     execlp("/bin/sh","sh",NULL); 
} else { 
     close(fd[0]); 
     char *line; int nbytes=100; int bytes=0; 
     line=(char*) malloc(nbytes+1); 
     while ((bytes = getline((char **)&line,&nbytes,stdin))!= -1){ 
      write(fd[1],line, bytes); 
     } 
} 

이 실행 OK, I 대화 형 쉘, 첫 번째 명령을 실행 한 후 내 프로그램 중지를 강제로 exec("/bin/sh","sh","-i",NULL)exec("/bin/sh","sh",NULL)를 교체하려고하지만 경우.

저는 파이프가 처음이에요. 이유를 이해하고 상호 작용하는 쉘 작업을 도와주세요. 나는 또한 코드를 읽고 파이프를 전달하는 것이 약간 이상하다고 생각합니다. 동일한 행동을 달성하는 방법?

+1

dup2 다음에 fd [0]을 닫고 쓰기가 끝나면 fd [1]을 닫으십시오. –

답변

1

어린이가 dup2() 인 경우 close(fd[0]);이어야합니다. "/bin/sh"과 같은 절대 또는 상대 경로를 제공하는 경우 execlp()을 사용할 필요가 없습니다. 그것은 단지 베어 파일 이름 (프로그램 이름)에 대한 PATH 기반 검색을 수행합니다. getline()으로 전화를 걸면 불필요합니다. 가능한 한 그러한 캐스트를 피하십시오. 실패 할 경우를 대비하여 이후에 적어도 exit(1);을 포함해야합니다. 진단 메시지 역시 좋은 생각입니다. 자식에 EOF를 나타내려면 부모의 루프 뒤에 close(fd[1]);을 입력해야합니다. (한 번만, malloc()에서 오류를 반환하지 않으면 문제가되지 않으며 포인터가 NULL을 포함하는 포인터의 주소를 getline() 함수에 전달하면 메모리를 할당하려고 시도합니다 . 메인 프로그램이 메모리를 할당하지 않을 경우 자체 물론, getline() 또한 메모리를 할당하는 데 실패 할 가능성이 높다)

그 변화

가 발생할 :. 이것은 엄격한에 따라 불만없이 컴파일

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

int main(void) 
{ 
    int fd[2]; 
    pid_t child; 

    if (pipe(fd) != 0) 
     perror("pipe"); 
    else if ((child = fork()) < 0) 
     perror("fork"); 
    else if (child == 0) 
    { 
     close(fd[1]); 
     dup2(fd[0], 0); 
     close(fd[0]); 
     execl("/bin/sh", "sh", NULL); 
     perror("oops"); 
     exit(1); 
    } 
    else 
    { 
     close(fd[0]); 
     size_t nbytes = 100; 
     int bytes = 0; 
     char *line = (char*)malloc(nbytes+1); 
     while ((bytes = getline(&line, &nbytes, stdin)) != -1) 
     { 
      write(fd[1], line, bytes); 
     } 
     close(fd[1]); 
    } 
    return(0); 
} 

컴파일 플래그 :

gcc -O3 -g -std=c99 -Wall -Wextra xf.c -o xf 

위의 코드 (-i 옵션없이 sh을 호출하면) (Mac OS X 10.7.3)을 실행하면 문제가 거의 해소됩니다. 명령을 입력하면 쉘이 명령을 실행할 수 있습니다. 'exit'를 입력하면 쉘이 종료되지만 새 명령을 입력 할 때까지 작성한 프로그램 (xf)이 종료되지 않습니다. 이제 리더기가없는 파이프에 쓰는 SIGPIPE 신호 때문에 종료됩니다. 표준 입력이 터미널이 아니기 때문에이 셸에서 프롬프트가 표시되지 않습니다 (파이프).

-i 옵션을 사용하여 하위 셸을 실행하면 셸이 터미널을 담당하는 작업 제어 셸 사이에 싸움이있는 것 같습니다. 내가 그것을 실행하면, 내가 얻을 :

$ ps -f 
    UID PID PPID C STIME TTY   TIME CMD 
    503 381 372 0 Wed08PM ttys001 0:00.07 -sh 
    503 21908 381 0 9:32PM ttys001 0:00.01 sh 
$ ./xf 
sh-3.2$ 

[1]+ Stopped(SIGTTIN)  ./xf 
$ 
$ ps -f 
    UID PID PPID C STIME TTY   TIME CMD 
    503 381 372 0 Wed08PM ttys001 0:00.07 -sh 
    503 21908 381 0 9:32PM ttys001 0:00.01 sh 
    503 22000 21908 0 9:36PM ttys001 0:00.00 ./xf 
    503 22001 22000 0 9:36PM ttys001 0:00.00 sh -i 
$ ls 
awk.data   osfile-keep.c  pthread-2.c  send.c    xf 
const-stuff.c  perl.data   pthread-3.c  so.8854855.sql  xf.c 
fifocircle.c  piped-merge-sort.c quine.c   strandsort.c  xf.dSYM 
madump.c   powa.c    recv.c    unwrap.c   xxx.sql 
makefile   pthread-1.c  regress.c   vap.c    yyy.sql 
$ jobs 
[1]+ Stopped(SIGTTIN)  ./xf 
$ fg %1 
./xf 
exit 
$ 

(초기 -sh 내 터미널 윈도우의 로그인 쉘입니다, 나는 서브 쉘을 sh를 실행했습니다, 나는 프롬프트를 설정했습니다. PS1='$ ' 프롬프트를 구분하십시오.

AFAICT, sh-3.2$ 프롬프트는 sh -i 쉘에서옵니다. 부모 셸이 입력을 읽고있는 것처럼 보였고 매우 문명화되지 않은 백그라운드에 xf 프로그램을 버렸습니다. ps -f 출력에 ps 명령이 표시되지 않습니다. 이는 불편합니다. 나는 ls 명령을 ps 목록에 한 번에 표시하려고했으나 sh -i이 아닌 xf이 아닌 원래 쉘의 하위 항목이었습니다.xf을 포 그라운드로 가져 오면 즉시 종료됩니다 (아마도 EOF를 나타내는 표준 입력에서 0 바이트를 읽으므로 getline()이 -1을 반환하고 모든 것이 상점에 저장됩니다.) exitsh -i에서 온 것입니다. 왜냐하면 쉘 대신에 xf이 터미널을 제어하기 때문에 명령을받지 못했기 때문입니다. 그 일은 왜 그렇게 심하게 일어 났는지 모르겠습니다. 그런 일이 일어나지 않는 이유는 모르겠지만 그렇게 느껴지지는 않습니다.

+0

많은 조언을 해주셔서 감사합니다!이 문제는이 복잡한 문제라고 상상할 수 없습니다. btw, 당신이 파이프 (fd) 파이프를 초기화하는 것을 잊었을 것 같아요? – w00d

+0

아! 내 테스트 코드는 내가 추가 할 때까지 가장 특이하게 행동했다. 나는 그 때 대답을 잊어 버렸다! 코드는'write()'시스템 콜을 너무 잘못 체크해야합니다 (문제가 더 빨리 나타 났을 것입니다). –