2012-04-26 64 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