【问题标题】:Concurrent Processes并发进程
【发布时间】:2026-02-01 13:35:01
【问题描述】:

我希望在 C 程序中运行多个并发进程。程序将从用户那里获取参数,然后将每个参数作为子进程执行。我认为这意味着我需要做的就是确保每次都由原始父进程执行 fork(),然后每个生成的子进程将同时运行,而不是顺序运行。

我这样想对吗?谁能让我知道我该怎么做?

【问题讨论】:

  • 您的示例程序中没有对fork() 的任何调用。这是故意的吗?
  • 否定的,只是忘了包含它。
  • 如果 wait() 在循环内,它将按顺序执行参数。我认为您想要做的是在循环启动所有进程后等待。
  • @Ronny,如果父级不等到每个子级完成,则 2 个或更多命令的输出可能会在标准输出上混淆。
  • 如果您将多个线程/进程中的内容输出到一个通用设备 - 控制台,例如,你必须同步,是的。

标签: c concurrency parent-child


【解决方案1】:

请原谅我在我之前的回答中转移了手头的事情(通过建议使用线程)。由于我要在这里走向一个全新的方向,我觉得有必要将其添加为单独的答案。

短版:

请在您的程序中进行以下更改:

1. length = argc;               // in place of length = sizeof(argv);
2. execl(argv[i],argv[i],0);    // in place of execvp(argv[i],0);
3. #include <unistd.h>          // if you haven't already

加长版:

(1) 通过变量length,我假设您想获取参数的总数。 argvpointer-to-char-pointer,因此只是一个内存地址。如果您在程序中打印出长度,您会注意到它始终为 4(或系统中内存地址的大小)。

所以这个:

length = sizeof(argv);

应该是这样的:

length = argc;

argc 保存执行进程时传递的参数总数。例如,

./a.out /bin/ps /bin/ls

给出:argc = 3(而不是 2,这是一个非常常见的陷阱)

(2) 程序的另一个问题是execvp 调用。

execvp 的原型如下:

int execvp(const char *file, char *const argv[]);

其中,argv 是传递给新命令的参数列表,与您自己程序中的 argv 非常相似。

您在程序中使用的是:

execvp(argv[i],0);

假设i=1argv[1] = "/bin/ls"。 该命令的作用是查找 /bin/ls 可执行文件并将 NULL 指针 (0) 传递给它。这可能会导致以下运行时错误:

A NULL argv[0] was passed through an exec system call.

参考 exec 手册页,

第一个参数,按照惯例, 应该指向文件名 与文件相关联 执行。

虽然不必再次传递文件名,但您当然不应该传递 NULL 指针。由于您不想传递任何参数,我建议您改用以下execl 调用:

execl(argv[i],argv[i],0);

请记住,所有此类调用最终都会转换为execve(),然后执行,最终使它们等效。

我鼓励您使用man 阅读有关exec 系列函数的更多信息。

【讨论】:

  • 非常感谢大家,这些都是我自己应该学会的小东西:\我现在已经完成了这个程序,并将编辑上面的代码以匹配最终版本。如果您发现任何其他错误,请随时告诉我:P
【解决方案2】:

由于您在循环中调用 wait(),您将分叉一个子进程 然后分叉过程将等待它完成 它分叉下一个。你需要分叉所有的孩子 如果您希望它们并行执行,则在等待之前处理,

编辑:您计算的长度无效。 sizeof argv 返回大小 指向 char 的指针。这段代码

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

int main(int argc, char *argv[])
{
    int pid, i, length;

    length = argc;

    for(i = 1; i < length; i++)
    {
        printf("Argv[%d]: %s\n", i, argv[i]);  //error checking
        pid = fork();

        if(pid < 0)
        {
           printf("Fork failed.\n");
        }//end if
        else if(pid == 0)
        {
           execvp(argv[i], 0);
        }//end else if
        else
        {
           printf("Parent process (%d)\n", getpid());
        }//end if-else

    }//end for
    wait();
}//end main

对我来说似乎工作正常:

datan:~/src/c> ./a.out /bin/ps /bin/ps /bin/ps
Argv[1]: /bin/ps
Parent process (12748)
Argv[2]: /bin/ps
Parent process (12748)
Argv[3]: /bin/ps
Parent process (12748)
  PID TTY          TIME CMD
 6615 pts/5    00:00:00 bash
  PID TTY          TIME CMD
 6615 pts/5    00:00:00 bash
12627 pts/5    00:00:01 emacs
12748 pts/5    00:00:00 a.out
12749 pts/5    00:00:00 ps
12750 pts/5    00:00:00 ps
12751 pts/5    00:00:00 ps
datan:~/src/c> 12627 pts/5    00:00:01 emacs
12749 pts/5    00:00:00 ps
12750 pts/5    00:00:00 ps
  PID TTY          TIME CMD
 6615 pts/5    00:00:00 bash
12627 pts/5    00:00:01 emacs
12749 pts/5    00:00:00 ps

(尽管您可能应该等待所有孩子,而不是像这段代码那样只等待任何孩子)。

【讨论】:

  • 一开始我在循环外等待,但将其移出并不会改变任何东西,只是它需要用户输入才能恢复提示。
  • 我确信并行执行的 ls & ps 命令不会产生任何好处,它会导致控制台上的输出混乱。
  • 另外,sizeof argv 返回指向字符指针的大小,而不是指向字符的指针。
【解决方案3】:

您的推理似乎是正确的。父进程将派生与参数相同数量的子进程。

使用线程进行并发处理不是更高效吗?

【讨论】:

  • 可能是这样,但我在线程方面的经验比我在 fork 方面的经验还要少:)
  • 还不错.. 有时间看看吧 ;)
【解决方案4】:

我相信这会如你所愿。不过……

fork() 不会真正在单个程序中运行多个进程。它在不同进程中运行同一程序的多个副本。如果程序之间没有明显的重叠,您不妨为每个程序编写单独的main()s。

【讨论】: