【问题标题】:kill a process started with popen杀死一个以 popen 开始的进程
【发布时间】:2010-10-07 13:52:09
【问题描述】:

popen打开一个进程的管道后,有没有办法杀死已经启动的进程? (使用pclose 不是我想要的,因为那会等待进程完成,但我需要杀死它。)

【问题讨论】:

    标签: c multithreading kill popen


    【解决方案1】:

    显而易见的方法是 system("pkill process_name");

    如果您有多个进程实例在运行,显然这是有问题的。

    【讨论】:

    • 这适用于多个实例正在运行并且需要以精确实例为目标的情况。
    【解决方案2】:

    popen 实际上并不启动线程,而是分叉一个进程。当我查看定义时,似乎没有一种简单的方法可以获取该进程的 PID 并杀死它。可能有一些困难的方法,比如检查进程树,但我想你最好使用 pipe、fork 和 exec 函数来模仿 popen 的行为。然后你可以使用你从 fork() 得到的 PID 来杀死子进程。

    【讨论】:

      【解决方案3】:

      不要使用popen(),编写你自己的包装器来做你想做的事。

      fork() 相当简单,然后替换stdin & stdout 通过使用dup2(),然后对您的孩子调用exec()

      这样,您的父母将拥有确切的孩子 PID,您可以使用 kill() 那个。

      Google 搜索“popen2() implementation”以获取一些示例代码 如何实现popen() 正在做的事情。只有十几行 长。取自dzone.com我们可以看到 一个看起来像这样的例子:

      #define READ 0
      #define WRITE 1
      
      pid_t
      popen2(const char *command, int *infp, int *outfp)
      {
          int p_stdin[2], p_stdout[2];
          pid_t pid;
      
          if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0)
              return -1;
      
          pid = fork();
      
          if (pid < 0)
              return pid;
          else if (pid == 0)
          {
              close(p_stdin[WRITE]);
              dup2(p_stdin[READ], READ);
              close(p_stdout[READ]);
              dup2(p_stdout[WRITE], WRITE);
      
              execl("/bin/sh", "sh", "-c", command, NULL);
              perror("execl");
              exit(1);
          }
      
          if (infp == NULL)
              close(p_stdin[WRITE]);
          else
              *infp = p_stdin[WRITE];
      
          if (outfp == NULL)
              close(p_stdout[READ]);
          else
              *outfp = p_stdout[READ];
      
          return pid;
      }
      

      注意:似乎 popen2() 是您想要的,但我的发行版似乎没有这种方法。

      【讨论】:

      • 这并不能解决fork的内存重复问题。出于这个原因,我们可能不想调用 fork
      • @phooji python跟什么有什么关系?
      • @MahmoudAl-Qudsi:哎呀。我一定一直在看太多的 python 问题。 [先前与 python 相关的评论已编辑]
      • 以上需要"close(p_stdin[READ]); close(p_stdout[WRITE]);"避免两个不需要的打开文件描述符挥之不去(这会阻止诸如 fgets() 之类的调用检测到分叉的孩子已经退出)。
      • 这个答案中的代码有一个严重的问题:如果execl() 失败,它会调用exit()。这将导致调用父进程注册的任何退出处理程序,并将从父进程复制的缓冲流数据刷新到父进程拥有的流中。如果从 C++ 进程调用 popen2() 函数,从父进程复制的静态析构函数也将被调用。代码应该调用_exit() 而不是exit() 来绕过父进程的exit() 处理程序。
      【解决方案4】:

      实际上,如果进程正在执行 I/O(它应该是,否则为什么要使用 popen 而不是 system(3)?),那么 pclose 应该在下一次使用 SIGPIPE 来处理它尝试读取或写入,它应该很好地摔倒:-)

      【讨论】:

        【解决方案5】:

        我在 python 中做了类似的事情,你可以杀死一个子进程和任何为此分叉的子进程。它实际上类似于 slacy 的解决方案。 http://www.pixelbeat.org/libs/subProcess.py

        【讨论】:

          【解决方案6】:

          这是 popen2 的改进版本(感谢 Sergey L.)。 slacy发布的版本并没有返回popen2中创建的进程的PID,而是分配给sh的PID。

          pid_t popen2(const char **command, int *infp, int *outfp)
          {
              int p_stdin[2], p_stdout[2];
              pid_t pid;
          
              if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0)
                  return -1;
          
              pid = fork();
          
              if (pid < 0)
                  return pid;
              else if (pid == 0)
              {
                  close(p_stdin[WRITE]);
                  dup2(p_stdin[READ], READ);
                  close(p_stdout[READ]);
                  dup2(p_stdout[WRITE], WRITE);
          
                  execvp(*command, command);
                  perror("execvp");
                  exit(1);
              }
          
              if (infp == NULL)
                  close(p_stdin[WRITE]);
              else
                  *infp = p_stdin[WRITE];
          
              if (outfp == NULL)
                  close(p_stdout[READ]);
              else
                  *outfp = p_stdout[READ];
          
              return pid;
          }
          

          新版本将被调用

          char *command[] = {"program", "arg1", "arg2", ..., NULL};
          

          【讨论】:

          • 我没有看到 pid 部分有什么不同;你能更新差异部分的cmets吗?
          【解决方案7】:

          我遵循 @slacy 创建函数 popen2 的想法。
          但是当子进程死亡时发现问题,仍然读取文件描述符outfp的父进程没有从函数返回。

          这可以通过在父进程上添加关闭未使用管道来解决。

          close(p_stdin[READ]);
          close(p_stdout[WRITE]);
          

          我们可以通过使用 bash 作为 shell 来获得新进程的正确 pid。

          execl("/bin/bash", "bash", "-c", command, NULL);
          

          当子进程死亡时,函数popen2的调用者应该通过调用pclose2来收集子进程的状态。如果我们不收集该状态子进程在它终止时可能是僵尸进程。

          这是经过测试并按预期工作的完整代码。

          #define READ 0
          #define WRITE 1
          
          pid_t
          popen2(const char *command, int *infp, int *outfp)
          {
              int p_stdin[2], p_stdout[2];
              pid_t pid;
          
              if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0)
                  return -1;
          
              pid = fork();
          
              if (pid < 0)
                  return pid;
              else if (pid == 0)
              {
                  dup2(p_stdin[READ], STDIN_FILENO);
                  dup2(p_stdout[WRITE], STDOUT_FILENO);
          
               //close unuse descriptors on child process.
               close(p_stdin[READ]);
               close(p_stdin[WRITE]);
               close(p_stdout[READ]);
               close(p_stdout[WRITE]);
          
                  //can change to any exec* function family.
                  execl("/bin/bash", "bash", "-c", command, NULL);
                  perror("execl");
                  exit(1);
              }
          
              // close unused descriptors on parent process.
              close(p_stdin[READ]);
              close(p_stdout[WRITE]);
          
              if (infp == NULL)
                  close(p_stdin[WRITE]);
              else
                  *infp = p_stdin[WRITE];
          
              if (outfp == NULL)
                  close(p_stdout[READ]);
              else
                  *outfp = p_stdout[READ];
          
              return pid;
          }
          
          int
          pclose2(pid_t pid) {
              int internal_stat;
              waitpid(pid, &internal_stat, 0);
              return WEXITSTATUS(internal_stat);
          }
          

          【讨论】:

            【解决方案8】:

            我只是将 (bash) 脚本放在第一行:

            echo PID $$
            

            然后我阅读了 pid,并用它来做一个: 杀死(pid,9)

            【讨论】:

            • popen 一开始就很俗气,这是解决俗气问题的一个不错的俗气解决方案。
            猜你喜欢
            • 2011-06-11
            • 2014-03-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-07-28
            • 1970-01-01
            相关资源
            最近更新 更多