【发布时间】:2010-10-07 13:52:09
【问题描述】:
用popen打开一个进程的管道后,有没有办法杀死已经启动的进程? (使用pclose 不是我想要的,因为那会等待进程完成,但我需要杀死它。)
【问题讨论】:
标签: c multithreading kill popen
用popen打开一个进程的管道后,有没有办法杀死已经启动的进程? (使用pclose 不是我想要的,因为那会等待进程完成,但我需要杀死它。)
【问题讨论】:
标签: c multithreading kill popen
显而易见的方法是 system("pkill process_name");
如果您有多个进程实例在运行,显然这是有问题的。
【讨论】:
popen 实际上并不启动线程,而是分叉一个进程。当我查看定义时,似乎没有一种简单的方法可以获取该进程的 PID 并杀死它。可能有一些困难的方法,比如检查进程树,但我想你最好使用 pipe、fork 和 exec 函数来模仿 popen 的行为。然后你可以使用你从 fork() 得到的 PID 来杀死子进程。
【讨论】:
不要使用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() 是您想要的,但我的发行版似乎没有这种方法。
【讨论】:
execl() 失败,它会调用exit()。这将导致调用父进程注册的任何退出处理程序,并将从父进程复制的缓冲流数据刷新到父进程拥有的流中。如果从 C++ 进程调用 popen2() 函数,从父进程复制的静态析构函数也将被调用。代码应该调用_exit() 而不是exit() 来绕过父进程的exit() 处理程序。
实际上,如果进程正在执行 I/O(它应该是,否则为什么要使用 popen 而不是 system(3)?),那么 pclose 应该在下一次使用 SIGPIPE 来处理它尝试读取或写入,它应该很好地摔倒:-)
【讨论】:
我在 python 中做了类似的事情,你可以杀死一个子进程和任何为此分叉的子进程。它实际上类似于 slacy 的解决方案。 http://www.pixelbeat.org/libs/subProcess.py
【讨论】:
这是 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};
【讨论】:
我遵循 @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);
}
【讨论】:
我只是将 (bash) 脚本放在第一行:
echo PID $$
然后我阅读了 pid,并用它来做一个: 杀死(pid,9)
【讨论】: