如何在C语言中生成另一个进程?

38

如何使用C运行外部程序并传递命令行参数?如果必须使用操作系统API,请提供Windows,Mac和Linux的解决方案。


2
我认为你需要重写你的问题 - 打开一个外部进程并不完全等同于生成另一个进程...它们有区别。 - Jason Bunting
8个回答

29

这真的取决于你想要做什么,因为它:

  1. 与操作系统有关
  2. 不太清楚你想做什么。

尽管如此,我会尝试为您提供一些信息来帮助您做出决定。
在UNIX上,fork()会从调用fork的位置创建一个进程的克隆。也就是说,如果我有以下进程:

#include <unistd.h>
#include <stdio.h>

int main()
{
    printf( "hi 2 u\n" );
    int mypid = fork();

    if( 0 == mypid )
        printf( "lol child\n" );
    else
        printf( "lol parent\n" );

    return( 0 );
}

输出将如下所示:

hi 2 u
笑死孩子
笑死父母

当你使用 fork() 函数时,子进程返回的进程ID为0,而父进程返回的进程ID是子进程的进程ID。注意,“hi2u”只会由父进程打印一次。

execve() 及其相关函数几乎总是和 fork() 一起使用。 execve() 和类似的函数将当前的栈帧覆盖为您传递给它的应用程序名称。 execve() 几乎总是与 fork() 一起使用,其中您会 fork 一个子进程,如果您是父进程,则继续进行必要的操作,如果您是子进程,则执行一个新进程。 execve() 通常也与 waitpid() 一起使用 -- waitpid 接收一个子进程的 PID,直到子进程终止并将子进程的退出状态返回给您。

有了这些信息,您应该能够编写一个非常基本的 shell;一个接受命令行上的进程名称并运行您告诉它运行的进程的 shell。当然,shell 做的不仅仅是这些,还有输入输出的管道等,但是您应该能够使用 fork()execve()waitpid() 完成基本操作。

注意:此内容仅适用于*nix系统!在Windows上将无法使用。

希望这对您有所帮助。


19

如果你想执行更加复杂的操作,比如读取外部程序的输出,那么你可能更好地使用popen系统调用。例如,要以编程方式访问目录列表(这是一个有点愚蠢但作为例子很有用),可以编写以下内容:

#include <stdio.h>

int main()
{
  int entry = 1;
  char line[200];
  FILE* output = popen("/usr/bin/ls -1 /usr/man", "r");
  while ( fgets(line, 199, output) )
  {
    printf("%5d: %s", entry++, line);
  }
}

生成这样的输出

1: cat1
2: cat1b
3: cat1c
4: cat1f
5: cat1m
6: cat1s
...

1
如果 !feof(output),则不要执行 pclose(output)(参考自微软的示例)。返回 -1;否则执行 pclose(output) 并返回 0。 - 7vujy0f0hy
popen不是系统调用,而是popen(3),只是一个C函数。 - attempt0

13
#include <stdlib.h>

int main()
{
    system("echo HAI");

    return 0;
}

7
永远不要使用system。它远非多线程安全,会在最重要的地方给你带来很大的麻烦。例如,信号处理错误是一些最讨厌的问题之一,“system”就充斥着这些问题。 - Lothar

12

我要强烈警告大家,不要使用system,而且在编写库时100%永远不要使用system。它是30年前设计的,当时多线程还未被称为 Unix 这个玩具操作系统所知。即使今天几乎所有程序都支持多线程,它仍然不可用。

请使用popen或进行fork + execvp操作,其他方法会导致信号处理难以找到的问题、环境处理代码崩溃等问题。使用“system”纯粹是一种邪恶行为,最受欢迎和评价最高的答案竟然在推广使用“system”,这真是一种耻辱。在工作场所推广可卡因会更健康。


真遗憾,你会认为Unix/Linux开发者早就使其支持多线程了。 - Cheetaiean
这是不可能做到线程安全的,就像你无法使“gets”内存安全一样。 - Lothar

5
在UNIX上,如果你想让生成的进程与产生它的进程分离运行,我认为你基本上需要对其进行分叉:例如,如果你不希望在退出产生进程时终止生成的进程。
这里有一个页面详细解释Fork、System、Exec之间的微妙差异。
如果你在Win、Mac和Linux上工作,我可以推荐Qt框架及其QProcess对象,但我不知道这是否适合你。最大的优势是你将能够在Windows、Linux和Mac上编译相同的代码:
 QString program = "./yourspawnedprogram";
 QProcess * spawnedProcess = new QProcess(parent);
 spawnedProcess->start(program);
 // or spawnedProcess->startDetached(program);

此外,您甚至可以从母进程中杀死子进程,并通过流与其保持通信。


2
一种解决方案是在stdlib.h中定义的system函数。
int system(const char *string);

system api example


1
如果你需要检查/读取/解析外部命令的输出,我建议使用popen()而不是system()。

1

说到平台相关的编程技巧,在Windows上使用CreateProcess,在Posix(Linux、Mac)上使用fork+execvp。但是system()应该可以满足您的基本需求,并且是标准库的一部分。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接