在C中,通过首先调用fork()
创建一个新进程(最终子进程),然后执行子进程的exec*()
函数族之一,可以获得最大数量的选项。原始进程和新进程将同时运行,因此您可以通过管道或套接字对交换(读取和/或写入数据)。最后,使用例如waitpid()
在一个循环中等待新进程退出,并“收获”其退出状态。例如:
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
int main(void)
{
pid_t child, p;
int status;
/*
* Prepare pipes et cetera first.
*/
/* Fork to create the subprocess. */
child = fork();
if (child == (pid_t)-1) {
/* Cannot fork(); usually out of resources (user limits).
* see errno for details. With <string.h>, you can use
* strerror(errno) to obtain the error string itself. */
return 1;
} else
if (!child) {
/* This is the child process itself.
* Do whatever cleanup is necessary, then
* execute the subprocess command. */
execlp("/bin/ls", "ls", "-lA", NULL);
/* This is only reached if the exec failed;
* again, see errno for reason.
* Always have the child process exit! */
return 127;
}
/* This is only run by the parent process
* (because the child always exits within the
* else if body above).
*
* The subprocess PID is 'child'.
*/
/* Wait for the child process to exit. */
do {
status = 0;
p = waitpid(child, &status, 0);
if (p == (pid_t)-1 && errno != EINTR)
break; /* Error */
} while (p != child);
if (p != child) {
/* Child process was lost.
* If (p == (pid_t)-1), errno describes the error.
*/
} else
if (WIFEXITED(status)) {
/* Child process exited with WEXITSTATUS(status) status.
* A status of 0 (or EXIT_SUCCESS) means success,
* no errors occurred. Nonzero usually means an error,
* but codes vary from binary to binary.
*/
} else
if (WIFSIGNALED(status)) {
/* Child process died from WTERMSIG(status) signal.
* If you include <string.h>, you can use
* strsignal(WTERMSIG(status))
* to obtain the name (string) of the terminating signal.
*/
} else {
/* Child process died from unknown causes.
*/
}
/* All done. */
return 0;
}
就个人而言,我更喜欢使用socketpair()
创建经由pipe()
创建我控制过程和管之间的Unix域流或数据报套接字如果子流程仅仅是一些随机二进制来运行。在所有情况下,您都可以使用dup2()
函数,用套接字或管道替换标准输入(STDIN_FILENO
描述符),标准输出(STDOUT_FILENO
描述符)和标准错误(STDERR_FILENO
描述符)。如果需要,您甚至可以从父级访问/proc/[child]/
下的伪文件以观察子进程的状态。
取决于您需要如何与子流程进行通信 - 从/到文件的输入/输出?内存中的字符串?动态分配输出缓冲区 - 有很多变体。当需要精确控制和/或全双工(包括读取和写入)和/或异步通信时,通常会使用与上述类似的代码。
您可以在您最喜爱的搜索引擎中搜索"linux" "fork" "exec",以获得不同质量的示例。
如果你想要一个简单的解决方案,你只需要捕获命令的输出(不供给输入命令,或者提供从文件输入),你可以使用的
一些变种
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
int main(void)
{
FILE *sub;
pid_t subpid;
int status;
sub = popen("setsid /bin/sh -c 'echo $$ ; exec command args' </dev/null", "rb");
if (!sub) {
/* popen() failed. */
return 1;
}
/* Read the first line from sub. It contains the PID for the command. */
{
char buffer[32], *line, dummy;
int value;
line = fgets(buffer, sizeof buffer, sub);
if (!line) {
/* setsid failed, or non-POSIXy system (Windows?) */
pclose(sub);
return 1;
}
if (sscanf(line, "%d%c", &value, &dummy) != 1 || value < 2) {
/* Internal bug, or extra output? */
pclose(sub);
return 1;
}
/* subpid is also the session ID and the process group ID,
* because it is the session leader. */
subpid = value;
}
/* Read from sub using standard I/O, to capture command output. */
/* After no more output to read from sub, reap the subprocess. */
errno = 0;
do {
status = pclose(sub);
} while (status == -1 && errno == EINTR);
if (status) {
/* Problem: sub exited with nonzero exit status 'status',
* or if status == -1, some other error occurred. */
} else {
/* Sub exited with success (zero exit status). */
}
/* Done. */
return 0;
}
在Linux中,popen()
使用/bin/sh
壳(按POSIX.1规范),我们可以使用setsid
命令行实用程序来创建新的会话。在该命令中,echo $$
是一个sh
命令,它输出外壳PID,exec CMD...
用该命令替换外壳;因此即使在命令执行之前,我们也可以获得命令的PID。
看到这个答案http://stackoverflow.com/questions/15274016/why-am-i-forking-more-than-5-times-here/15274727#15274727 – OregonTrail
@OregonTrail:但你不主动新的外部过程还是你?我是C的初学者。 – pistal
fork是生成进程的基础机制。它复制当前进程。 exec是你想要的,它复制当前进程然后用一个新的二进制可执行文件替换它 – OregonTrail