2012-04-26 46 views
1

我的程序如下:交互shell停止管

//... 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); 
     } 
} 

此運行正常,但是當我嘗試用exec("/bin/sh","sh","-i",NULL)更換exec("/bin/sh","sh",NULL)強制交互的shell,執行相應的命令後,我的程序停止。

我是新來的管道,所以請幫助我理解原因並製作交互式shell工作...我也覺得我的代碼讀取行並傳遞給子管道有點奇怪..有什麼更好的實現相同行爲的方式?

+1

嘗試關閉所有文件描述符,在dup2之後關閉fd [0],寫完後關閉fd [1] – 2012-04-27 04:38:46

回答

1

你應該在close(fd[0]);之後dup2()在孩子身上。如果您提供的絕對或相對路徑如"/bin/sh",則使用execlp()毫無意義;它只會對裸文件名(程序名)進行基於PATH的搜索。在電話getline()的演員應該是不必要的;儘可能避免此類演員。爲防萬一它失敗,你應該在execlp()之後包含至少exit(1);;診斷信息也是一個好主意。你應該在父母循環之後close(fd[1]);向孩子表明EOF。 (只有一次,沒有檢測到從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 

使用上面的代碼(在調用sh而沒有使用-i選項時)運行(在Mac OS X 10.7.3上)時,事情表現得相當理智。你可以鍵入命令,shell執行它們。你可以輸入'exit'並且shell退出,但是你寫的程序(我叫xf)不會退出,直到我輸入一個新的命令。然後在寫入現在無讀取器的管道時,由於SIGPIPE信號而退出。這個shell沒有提示,因爲它的標準輸入不是終端(它是一個管道)。

當子shell與-i選項一起運行時,在作業控制shell之間似乎有一個關於哪個shell負責終端的爭論。當我運行它,我得到:

$ 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是我的終端窗口登錄shell在這方面,我已經運行sh得到一個子shell,我已經設置提示。 PS1='$ '以使提示符與衆不同。)

AFAICT,sh-3.2$提示符來自sh -i shell。父shell似乎在閱讀輸入內容,並且已經將xf程序放到了背景中,這並不是很文明。 ps -f輸出不顯示ps命令,這是一個麻煩。我設法讓ls命令在一次運行中出現在ps列表中,它是原始shell的子項,而不是由xf運行的sh -i。當我將xf放到前臺時,它立即退出(大概它從標準輸入中讀取0字節,表示EOF,所以getline()返回-1,並且所有東西都關閉了,exit來自sh -i;它會迴應它。從來沒有得到任何輸入,因爲sh外殼採取了命令,而不是讓xf控制終端,這非常難以置信,我不知道爲什麼會發生這種情況,但我覺得它不應該發生這樣的事情。

+0

感謝您提供了很多提示!我無法想象這個問題是如此複雜。順便說一句,我想你忘了管(fd)來初始化管道? – w00d 2012-04-27 06:34:15

+0

啊!我的測試代碼表現得特別獨特,直到我添加它;然後我忘了更新答案!該代碼也應該錯誤地檢查'write()'系統調用(它會顯示出這個問題更快)。 – 2012-04-27 08:35:37