2011-02-18 67 views
1

我试图通过使用mkfifo()创建一些FIFO管道来在操作系统shell项目中实现流水线。目前,该计划似乎在打开第一个写入FIFO后挂起。另外,在工作目录上执行ls时,FIFO会出现。我在这种情况下使用freopen()错误?程序挂起连接到FIFO(命名管道)

void execute_external() 
{ 
    int backgrounding = 0; 
    if (raw_command[strlen(raw_command)-1] == '&') 
    { 
     pmesg(2, "Backgrounding requested.\n"); 
     raw_command[strlen(raw_command)-1] = '\0'; 
     raw_command = realloc(raw_command, strlen(raw_command)-1); 
     backgrounding = 1; 
    } 

    int numCommands = 1; 
    char **commands; 
    commands = malloc(sizeof(char *)); 

    if(strstr(raw_command, "|") != NULL)   
    { 
     numCommands = separate_pipeline_commands(commands); 
    } 
    else 
    { 
     commands[0] = malloc(strlen(raw_command) * sizeof(char)); 
     commands[0] = raw_command; 
    } 

    int i; 
    char *fifo = malloc(MAXFIFOLEN); 
    for (i = 0; i < numCommands; i++) 
    { 
     char **parameters_array = malloc(strlen(commands[i]) * sizeof(char *)); 
     int num_params; 
     num_params = str_to_str_array(commands[i], parameters_array); 
     pmesg(2, "Command is %s.\n", commands[i]); 

     if (numCommands > 1) 
     { 
      int j; 
      for (j = 0; j < numCommands - 1; j++) 
      { 
       sprintf(fifo, "fifo%i", j); 
       mkfifo(fifo, S_IWUSR | S_IRUSR); 
      } 
     } 

     pid_t pid = fork(); 

     pmesg(2, "Process forked. ID = %i. \n", pid); 
     if (pid < 0) 
     { 
      fprintf(to_write_to, "Could not fork a process to complete the external command.\n"); 
      exit(EXIT_FAILURE); 
     } 
     int status; 
     if (pid == 0) // This is the child process 
     { 
      pmesg(1, "input: [%s] output: [%s] input: %d, output: %d backgrounding is %d\n",input_file_name, output_file_name, redirect_input, redirect_output, backgrounding); 
      if (numCommands > 1 && i == 0) // we may be pipelining and this is the first process 
      { 
       sprintf(fifo, "%s%i", "fifo", i); 
       printf("Initial output: %s.\n", fifo); 
       freopen(fifo, "w", stdout); 
       //~ unlink(fifo); 
      } 
      else if (numCommands > 1 && i !=0 && (i != numCommands-1)) // we are pipelining and this is not the first or last process 
      { 
       sprintf(fifo, "%s%i", "fifo", i-1); 
       printf("Input for process %i: %s.\n", i, fifo); 
       freopen(fifo, "r", stdin); 
       //~ unlink(fifo); 
       sprintf(fifo, "%s%i", "fifo", i+1); 
       printf("Output for process %i: %s.\n", i, fifo); 
       freopen(fifo, "w", stdout); 
       //~ unlink(fifo_2); 
      } 
      else if (numCommands != 1 &&i == numCommands) 
      { 
       char *fifo = malloc(strlen("fifo")+2); 
       sprintf(fifo, "%s%i", "fifo", i); 
       freopen(fifo, "r", stdin); 
       free(fifo);    
      } 
      if(redirect_output == 1) 
      { 
       freopen(output_file_name, "w", stdout); 
      } 
      if(redirect_input == 1) 
      { 
       freopen(input_file_name, "r", stdin); 
      } 
      if (backgrounding != 0) 
      { 
       freopen("/dev/null", "w", stdout); 
      } 
      pmesg(2, "This is the child process, running execlp.\n"); 
      pmesg(1, "Command: [%s]\n", parameters_array[0]); 
      if (execvp(parameters_array[0], parameters_array) < 0) 
      { 
       fprintf(to_write_to, "Could not execute the external command. errno: %i.\n", errno); 
       exit(EXIT_FAILURE); 
      } 
      else { pmesg(2, "Executed the child process.\n");} 
     } 
     else 
     { 
      if (backgrounding != 0) 
      { 
       enqueue(&process_queue, pid, clock(), 0, 0); 
       printQueue(process_queue); 
      } 
      if (backgrounding == 0) { while(wait(&status) != pid); }// Wait for the child to finish executing 
     } 
     pmesg(1, "The child has finished executing.\n"); 
     free(parameters_array); 
    } 
    free(commands); 
} 

回答

1

我已经放了一些评论在网上以低于你的代码,但XAder的总结是值得一读第一,帮助理解之前大局我的详细nitpicks。我建议以非常长的形式写出你为两条命令的流水线执行的确切代码,然后编写三条命令的流水线的代码,然后四条......然后然后将一个循环重复的代码。 (事实上​​,你只有一个fork()暗示给我,你做了两个命令的代码,但不是三个。:)

希望这会有所帮助。

/* if numCommands == 1 or 0, the rest is probably useless too 
    so this should guard the entire routine, not just the routine 
    that creates FIFOs */ 
if (numCommands > 1) 
{ 
    int j; 
    for (j = 0; j < numCommands - 1; j++) 
    { 
     char *fifo = malloc(strlen("fifo")+1); /* BUG #1 */ 
     sprintf(fifo, "%s%i", "fifo", j); /* BUG #2 */ 
     mkfifo(fifo, S_IWUSR | S_IRUSR); 
     free(fifo); /* messy */ 
    } 
} 

/* Bug #1 The malloc(strlen()+1) is only good for "fifo", doesn't 
    actually allocate space for your number. Don't forget the 
    ascii NUL character at the end of the string. Add another +1. */ 
/* Bug #2 works for numCommands < 10; with 10 commands, you'll need to +2 
    for two characters, and so on */ 
/* messy -- maybe just have a 'char fifo[MAXFIFOLEN];' allocation, 
    and set MAXFIFOLEN to 255 or 4096 or something. No need to allocate 
    and free a simple little buffer a dozen times. */ 

pid_t pid = fork(); 

/* You should probably place fork() in a for loop; one new process 
    per command. Another choice is to write this whole routine recursively 
    which might be adding too many moving parts into one program */ 

pmesg(2, "Process forked. ID = %i. \n", pid); 
/* Note that parent and child get different values of 'pid', so printing 
    the value of pid here may be very confusing */ 
int status; 
if (fork < 0) /* BUG #3 fork() is a function; should be pid */ 
{ 
    fprintf(to_write_to, "Could not fork a process to complete the external command.\n"); 
/* perror() is a wonderful routine! it helps users know _why_ something failed */ 
    exit(EXIT_FAILURE); 
} 

if (pid == 0) // This is the child process 
/* only ONE path below will ever execute; this is why your fork() 
    should be in a loop over numCommands, so each command's process 
    can get its own individual stdin and stdout hooked together */ 
{ 
    pmesg(1, "input: [%s] output: [%s] input: %d, output: %d backgrounding is %d\n",input_file_name, output_file_name, redirect_input, redirect_output, backgrounding); 
    if (numCommands > 1 && i == 0) // we may be pipelining and this is the first process 
    { 
     char *fifo = malloc(strlen("fifo")+2); 
     sprintf(fifo, "%s%i", "fifo", i); /* could just be sprintf(fifo, "fifo%i", i); */ 
     printf("Initial output: %s.\n", fifo); 
     freopen(fifo, "w", stdout); 
     //~ unlink(fifo); 
     free(fifo); 
    } 
    else if (numCommands > 1 && i !=0 && (i != numCommands-1)) // we are pipelining and this is not the first or last process 
    { 
     char *fifo = malloc(strlen("fifo")+2); 
     sprintf(fifo, "%s%i", "fifo", i-1); 
     printf("Input for process %i: %s.\n", i, fifo); 
     freopen(fifo, "r", stdin); 
     //~ unlink(fifo); 
     char *fifo_2 = malloc(strlen("fifo")+2); 
     sprintf(fifo_2, "%s%i", "fifo", i+1); 
     printf("Output for process %i: %s.\n", i, fifo); 
     freopen(fifo_2, "w", stdout); 
     //~ unlink(fifo_2); 
     free(fifo); 
     free(fifo_2); 
    } 
    else if (numCommands != 1 &&i == numCommands) 
    { 
     char *fifo = malloc(strlen("fifo")+2); 
     sprintf(fifo, "%s%i", "fifo", i); 
     freopen(fifo, "r", stdin); 
     free(fifo);    
    } 
+0

感谢您的详细说明。就子进程部分而言,这段代码实际上被称为循环。我遗漏了一些周围的代码,希望它能让事情变得更清楚......显然不是最好的决定。 – 2011-02-18 11:42:07

0

一般来说:

根据代码mkfifo名
    • 坏的内存分配有唯一的子进程的逻辑,它可以通过3的一个路径,只能走这么永远只有一个进程打开文件,根据手册页,它将被阻止,直到双方打开文件。
    • 我,numofcommands和逻辑都非常清楚 - 试试这个approach
  • +0

    您是否阅读过链接到的整个博客文章?作者讨论了他最近不喜欢伪代码方法。除此之外,感谢评论。 – 2011-03-03 04:52:28