Pipe

為什麼程序退出後 exec 列印它的輸出?

  • April 25, 2021

我正在編寫一個程序來將一個命令傳遞給另一個命令。輸入將來自命令行:

$ ./a.out ls '|' wc
c2 PID 6804
c1 PID 6803
PARENT PID 6802
$       2       2      17

為什麼提示返回後輸出列印。有什麼辦法可以防止這種情況發生嗎?

這是我寫的程式碼:

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main(int argc, char * argv[])
{
   if(argc <= 1 )
   {
       printf("ERROR: No arguments passed\n");
       printf("USAGE: ./pipe <command 1> | <command 2>\n");
       return 1;
   }

   char * cmd1[50];
   char * cmd2[50];
   int cmd1_arg = 0;
   int cmd2_arg = 0;
   int pipe_num = 0;
   
   for(int cla = 1; cla<argc; cla++)
   {
       if( !strcmp(argv[cla],"|") )        
           pipe_num++;
       else if(pipe_num == 0)        
           cmd1[cmd1_arg++] = argv[cla];
       else if(pipe_num == 1)
           cmd2[cmd2_arg++] = argv[cla];
   }

   cmd1[cmd1_arg] = (char *)NULL;
   cmd2[cmd2_arg] = (char *)NULL;    

   if(pipe_num != 1)
   {
       printf("ERROR: Insufficient arguments passed\n");
       printf("USAGE: ./pipe <command 1> | <command 2>\n");
       return 1;
   }

   int pipe_fd[2];
   pipe(pipe_fd);   

   pid_t pid = fork();
   if(pid == -1)
   {
       perror("FORK FAILED");
       return 1;
   }    

   if(pid != 0)
   {          
       pid_t cmd_pid = fork();
       if(cmd_pid == -1)
       {
           perror("FORK FAILED");
           return 1;
       }

       if(cmd_pid != 0)
       {   
           waitpid(pid,NULL,0);                     
           waitpid(cmd_pid,NULL,WNOHANG);            
           printf("PARENT PID %d\n",getpid());            
       }

       if(cmd_pid == 0)
       {
           printf("c2 PID %d\n",getpid());
           close(pipe_fd[1]);
           int stdin_fd = dup(0);
           close(0);
           dup(pipe_fd[0]);            
           if(execvp(cmd2[0],cmd2) == -1 ) perror("CMD2 FAIL"); 
           close(0);
           dup(stdin_fd);
       }        
   }

   if(pid == 0)
   {
       printf("c1 PID %d\n",getpid());        
       close(pipe_fd[0]);        
       int stdout_fd = dup(1);        
       close(1);                
       int test = dup(pipe_fd[1]);
       if( execvp(cmd1[0],cmd1) == -1 ) perror("CMD1 FAIL");
       close(1);        
       dup(stdout_fd);
   }

   return 0;
}

你有:

   waitpid(cmd_pid,NULL,WNOHANG);

通過包含該WNOHANG選項,您是在告訴waitpid()如果程序尚未終止,則不要等待程序終止。

我的猜測是你添加了它,因為如果你不包含它,你的程序就會掛起。那是因為原始父級仍然有一個打開的文件描述符到管道的寫入端,所以讀取過程仍然被阻塞等待該管道上的輸入。

這是您的程序的修訂版本,它關閉了管道文件描述符,並且不使用WNOHANG.

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
   if (argc <= 1) {
       printf("ERROR: No arguments passed\n");
       printf("USAGE: ./pipe <command 1> | <command 2>\n");
       return 1;
   }

   char *cmd1[50];
   char *cmd2[50];
   int cmd1_arg = 0;
   int cmd2_arg = 0;
   int pipe_num = 0;

   for (int cla = 1; cla < argc; cla++) {
       if (!strcmp(argv[cla], "|")) {
           pipe_num++;
       } else if (pipe_num == 0) {
           cmd1[cmd1_arg++] = argv[cla];
       } else if (pipe_num == 1) {
           cmd2[cmd2_arg++] = argv[cla];
       }
   }

   cmd1[cmd1_arg] = NULL;
   cmd2[cmd2_arg] = NULL;

   if (pipe_num != 1) {
       printf("ERROR: Insufficient arguments passed\n");
       printf("USAGE: ./pipe <command 1> | <command 2>\n");
       return 1;
   }

   int pipe_fd[2];

   if (pipe(pipe_fd) < 0) {
       perror("pipe");
       return 1;
   }

   const pid_t pid = fork();

   if (pid < 0) {
       perror("fork");
       return 1;
   } else if (pid != 0) {
       const pid_t cmd_pid = fork();

       if (cmd_pid < 0) {
           perror("fork");
           return 1;
       } else if (cmd_pid != 0) {
           printf("PARENT PID %d\n", getpid());

           close(pipe_fd[0]);
           close(pipe_fd[1]);

           if (waitpid(pid, NULL, 0) < 0) {
               perror("waitpid");
           }

           if (waitpid(cmd_pid, NULL, 0) < 0) {
               perror("waitpid");
           }
       } else {
           printf("c2 PID %d\n", getpid());

           if (dup2(pipe_fd[0], STDIN_FILENO) < 0) {
               perror("dup2");
               return 1;
           }
           close(pipe_fd[0]);
           close(pipe_fd[1]);

           if (execvp(cmd2[0], cmd2) < 0) {
               perror("CMD2 FAIL");
               return 1;
           }
       }
   } else {
       printf("c1 PID %d\n", getpid());

       if (dup2(pipe_fd[1], STDOUT_FILENO) < 0) {
           perror("dup2");
           return 1;
       }
       close(pipe_fd[0]);
       close(pipe_fd[1]);

       if (execvp(cmd1[0], cmd1) < 0) {
           perror("CMD1 FAIL");
           return 1;
       }
   }

   return 0;
}

執行這個程序給了我:

./a.out echo 1 2 3 '|' wc
PARENT PID 20412
c1 PID 20413
c2 PID 20414
     1       3       6
$

引用自:https://unix.stackexchange.com/questions/646532