在打开一个进程的管道后popen
,有没有办法杀死已经启动的进程?(使用pclose
不是我想要的,因为那会等待进程完成,但我需要杀死它。)
8 回答
不要使用popen()
,编写自己的包装器来做你想做的事。
很简单fork()
,然后用 & 替换
& stdin
,然后调用你的孩子。stdout
dup2()
exec()
这样,您的父母将拥有确切的孩子 PID,您可以使用
kill()
它。
谷歌搜索“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() 是你想要的,但我的发行版似乎没有这种方法。
这是 popen2 的改进版本(感谢 Sergey L.)。slacy 发布的版本并没有返回popen2中创建的进程的PID,而是分配给sh
.
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};
popen 实际上并不启动线程,而是分叉一个进程。当我查看定义时,似乎没有一种简单的方法可以获取该进程的 PID 并杀死它。可能有一些困难的方法,比如检查进程树,但我想你最好使用 pipe、fork 和 exec 函数来模仿 popen 的行为。然后您可以使用从 fork() 获得的 PID 来终止子进程。
实际上,如果进程正在执行 I/O(它应该是,否则为什么popen
而不是system
(3)?),那么pclose
应该SIGPIPE
在下次尝试读取或写入时用 a 重击它,它应该很好地倒下:-)
我遵循@slacy 创建函数popen2 的想法。
但是当子进程死亡时发现问题,仍然读取文件描述符的父进程outfp
没有从函数返回。
这可以通过在父进程上添加关闭的未使用管道来解决。
close(p_stdin[READ]);
close(p_stdout[WRITE]);
我们可以通过使用 bash 作为 shell 来获得新进程的正确 pid。
execl("/bin/bash", "bash", "-c", command, NULL);
当子进程死亡时,函数的调用者popen2
应该通过 call 收集子进程的状态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);
}
显而易见的方法是 system("pkill process_name");
显然,如果您有多个正在运行的进程实例,这是有问题的。
我在 python 中做了类似的事情,您可以在其中杀死一个子进程以及为此派生的任何子进程。它实际上类似于 slacy 的解决方案。 http://www.pixelbeat.org/libs/subProcess.py
我只是在第一行放入(bash)脚本:
echo PID $$
然后我读了 pid,并用它来做一个:kill(pid,9)