n管道生产EOF

时间:2014-09-29 17:14:35

标签: c shell operating-system

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>

#include <string.h>

void tokenizer(char* input, char** output) { //My tokenizer
  char* input_dup = strdup(input);
  output[0] = strtok(input_dup, " ");
  int i = 1;
  while ((output[i] = strtok(NULL, " ")) != NULL) {
   i++; 
  }
}

void run_command(char** args, int* fd) { //no pipe
  pid_t pid = fork();

  if (pid < 0) {
   printf("Forking failed...\n"); 
  }
  else if (pid == 0) {
    close(fd[0]);
    if (fd[1] != 1)
      dup2(fd[1], 1);
    execvp(args[0], args);
    printf("Command failed...\n");
    exit(1);
  }
  else {
    close(fd[1]);
    wait(pid);
    char buff[1];

    while (read(fd[0], buff, 1) > 0) {
      if (buff[0] == EOF || buff[0] == '\0') {
    printf("Caught something, returning out...");
    return;
      }
      else {
    printf("%c", buff[0]);
      }      
    }
  }
}

//pipeline function
void run_pipe(char** args, int* fd) {
    pid_t pid = fork();

    if (pid < 0) {
   printf("Forking failed...\n"); 
  }
  else if (pid == 0) {
    if (fd[1] != 1) {
      dup2(fd[1], 1);
    }
    execvp(args[0], args);
    printf("Command failed...\n");
    exit(1);
  }
  else {
    close(fd[1]);
    if (fd[0] != 0) {
     dup2(fd[0], 0);
    }
    wait(pid);
  }         
}

int main(int argc, char** argv) {
  printf ("Starting myshell (mysh) \n..\n..\n");

  while (1) {
    char cwd[1024];

    printf ("mysh :: %s -> ", getcwd(cwd, sizeof(cwd)));

    char ch[1024];
    memset(ch, 0, 1023); //for cleanup
    char c = 0;
    int i = 0;

    while (c != '\n') {
      c = getchar();
      if (c == EOF) {
    printf ("EOF Received, exiting...\n");
    return 0;
     }
      if (c != '\n')
     ch[i] = c;
      i++;
    }

    if (ch[0] != '\0') {

      char* tokens[128];
      tokenizer(ch, tokens);

      //first check for keywords
      if (strcmp(tokens[0], "cd") == 0) {
    if (chdir(tokens[1]) < 0) {
      printf("ERROR: Directory %s does not exist\n", tokens[1]);
    }
      }
      else if (strcmp(tokens[0], "exit") == 0) {
      printf("Leaving shell...\n");
      return 0;
      }
      else {
    char* commands[50];
    memset(commands, 0, sizeof(commands));
    int j = 0;
    int k = 0;

    int fd[2];

    //try something different...

    while (tokens[j] != NULL) {
     if (strcmp(tokens[j], "|") == 0) {
       commands[k] = NULL;
       pipe(fd);
       run_pipe(commands, fd);
       j++;
       k = 0;
     }
     //more cases here
     else { //nothing special
       commands[k] = tokens[j];
       j++;
       k++;
     }
    }
    commands[k] = NULL;
    pipe(fd);
    run_command(commands, fd);
      }
    }
  }
}

以上代码用于模拟shell。它处理单个命令并正确处理流水线操作(即ps | sort | wc返回正确的输出)但是当流水线操作完成时,它返回一个EOF,它被循环中的条件用getchar()捕获。如果我试图忽略这个EOF就会出现段错误。我在某个地方打开管道并且stdin被淹了吗?任何帮助表示赞赏。

2 个答案:

答案 0 :(得分:1)

编译修复

您需要添加#include <sys/wait.h>,然后修复对wait()的调用。我用过(两次):

int status;
int corpse = wait(&status);
printf("PID %d status 0x%.4X\n", corpse, status);

可以说,这应该是一个寻找特定PID的循环,或者你应该使用waitpid()代替。在调试shell时,您希望了解退出的每个PID及其状态。

我跑了`ps | wc'并得到:

Starting myshell (mysh) 
..
..
mysh :: /usr/local/google/home/jleffler/soq -> ps | wc
PID 25960 status 0x0000
PID 25961 status 0x0000
      4      16     117
mysh :: /usr/local/google/home/jleffler/soq -> EOF Received, exiting...

如果你的意思是“代码应该继续而不是获得EOF”,那么还有一些工作要做。


切线问题

我注意到这一行:

if (buff[0] == EOF || buff[0] == '\0')

buff[0]中的字符来自read()来电。永远不会有意义上的EOF; EOF与每个字符都不同(因此getchar()返回int)。这后来变得很重要:

char c = 0;
while (c != '\n')
{
    c = getchar();
    if (c == EOF)

由于cchar,因此您无法将其与EOF进行可靠的比较。您必须将getchar()的结果存储在int

我还不相信这些是造成麻烦的原因,但你必须小心。


可能原因

我认为麻烦在于父代码中的run_pipe()(修改后):

    else
    {
        close(fd[1]);
        if (fd[0] != 0)
        {
            dup2(fd[0], 0);
        }
        int status;
        int corpse = wait(&status);
        printf("PID %d status 0x%.4X\n", corpse, status);
    }

fd[0] != 0条件将始终为真(非常不可能是错误的),因此您将shell的输入更改为从fd[0]读取。你应该检讨一下;这意味着您正在读取从管道读取端到子节点的标准输入。那很糟;你丢失了原来的输入!

您的代码似乎也让父母等待孩子死亡,然后读取管道并回显标准输出。这不是一个好主意;最好让孩子(管道中的最后一个孩子)直接写入标准输出。这有两个原因:

  1. 孩子可能写的数据多于管道中的数据,所以它会阻止等待某些东西读取它的输出,但是读者将被阻止等待孩子死亡,所以你会遇到死锁

  2. 它减慢了速度,孩子的输出可能会被缓冲而不是及时出现在终端上。

  3. 我对如何处理三部分管道持怀疑态度。在运行三者的中间过程之前,您需要创建两个管道;我在你的代码中没有看到它。

答案 1 :(得分:0)

我能解决这个问题。它可能不是正确的方法,但我保存了stdin的副本,并在流水线完成时使用dup2重置它。

int in_bak = dup(0);

//stuff

dup2(in_bak, 0);
close(in_bak);