【问题标题】:How to make multiple `fork()`-ed processes comunicate using shared memory?如何使多个分叉进程使用共享内存进行通信?
【发布时间】:2010-10-10 06:33:10
【问题描述】:

我有一个有 5 个子进程的父进程。我想向每个子进程发送一个随机变量。每个孩子都会对变量进行平方并将其发送回父级,然后父级会将它们全部相加。

这甚至可能吗?我想不通...

编辑:此进程将使用共享内存。

【问题讨论】:

    标签: c fork shared-memory


    【解决方案1】:

    有很多方法可以做到这一点,都涉及某种形式的进程间通信。您选择哪一个将取决于许多因素,但其中一些是:

    • 共享内存。
    • 管道(popen 等)。
    • 套接字。

    一般来说,我可能会popen 在生成子代之前在父代中进行一些通信会话;父母会知道所有五个,但每个孩子可以配置为只使用一个。

    共享内存也是一种可能,尽管您可能必须为每个孩子设置几个值以确保通信顺利进行:

    • 用于存储变量和返回值的值。
    • 用于存储状态的值(0 = 开始,1 = 变量为子准备就绪,2 = 变量再次为父准备就绪)。

    在所有情况下,您都需要一种方法让孩子们只接受他们的价值观,而不是那些注定要给其他孩子的价值观。这可能就像向共享内存块添加一个值以存储孩子的 PID 一样简单。所有子节点都会扫描块中的每个元素,但只会处理状态为 1 且 PID 为其 PID 的元素。

    例如:

    • Main 为五个孩子创建共享内存。每个元素都有状态、PID 和值。
    • Main 将所有状态设置为“开始”。
    • Main 启动五个子节点,它们都附加到共享内存。
    • Main 存储其所有 PID。
    • 所有子进程都开始扫描共享内存以查找 state = "ready for child" 及其 PID。
    • Main 放入第一个元素(状态 =“准备好孩子”,PID = pid1,值 = 7)。
    • 主要放入第二个元素(状态 =“准备好孩子”,PID = pid5,值 = 9)。
    • 子 pid1 拾取第一个元素,将值更改为 49,将状态设置为“准备好为父”),返回监控。
    • 子 pid5 拾取第二个元素,将值更改为 81,将状态设置为“准备为父”),返回监控。
    • Main 获取 pid5 的响应,将该状态设置回“开始”。
    • Main 获取 pid1 的响应,将该状态设置回“开始”。

    这给出了并行度的度量,每个子进程持续监控共享内存的工作,Main 将工作放在那里并定期接收结果。

    【讨论】:

    • 您可能不需要进行扫描;子进程获得父进程数据的副本,因此(与 vfork() 示例不同)子进程可以查看一个计数器,告诉他们哪个条目是他们的。您可以使用随机值预加载内存;那么孩子们就知道数据已经准备好了。
    • 如果你只是 fork (在同一个可执行文件中使用父代码和子代码),但如果你 exec 引入子代码则不行:进程空间被覆盖(包括计数器)。不执行通常会在两个分叉之间共享很多危险的事情。不过,这是一种选择。
    【解决方案2】:

    最讨厌的方法是使用vfork(),让不同的孩子在退出之前踩踏内存的不同部分;然后父级只是将修改后的内存位相加。

    非常不推荐 - 但这是我遇到的唯一一个 vfork() 可能真正有用的案例。

    只是为了娱乐(我的)我把这个编码了:

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <time.h>
    #include <sys/wait.h>
    
    int main(void)
    {
        int i;
        int array[5];
        int square[5];
        long sum = 0;
    
        srand(time(0));
        for (i = 0; i < 5; i++)
        {
            array[i] = rand();
            if (vfork() == 0)
            {
                square[i] = array[i] * array[i];
                execl("/bin/true", "/bin/true", (char *)0);
            }
            else
                wait(0);
        }
    
        for (i = 0; i < 5; i++)
        {
            printf("in: %d; square: %d\n", array[i], square[i]);
            sum += square[i];
        }
        printf("Sum: %d\n", sum);
        return(0);
    }
    

    这行得通。以前使用“exit(0)”代替“execl()”的试用版不起作用;方阵全为零。示例输出(Solaris 10 上的 32 位编译,SPARC):

    in: 22209; square: 493239681
    in: 27082; square: 733434724
    in: 2558; square: 6543364
    in: 17465; square: 305026225
    in: 6610; square: 43692100
    Sum: 1581936094
    

    有时,总和会溢出 - 在处理上还有很大的改进空间。

    vfork()”的 Solaris 手册页说:

    与 fork() 函数不同,子进程借用 父级的内存和控制线程,直到调用 execve() 或退出(异常或通过调用 _exit()(参见 exit(2))。在此期间所做的任何修改 时间到子进程中内存的任何部分都反映 在从 vfork() 返回的父进程中。父母 进程在子进程使用其资源时暂停。

    这可能意味着我的代码中不需要“wait()”。 (但是,尝试简化代码似乎使其行为不确定。i 不会过早更改是相当重要的;wait() 确实确保了同步性。使用_exit() 而不是execl() 似乎也中断了不要使用vfork(),如果你重视自己的理智——或者如果你想为你的家庭作业打分。)

    【讨论】:

    • 有趣。顺便说一句,您也不需要pid_t pid。在 Mac OS X 上,我也得到不正确的结果。它打印了 2 个输出块,其中一些方块显然是错误的。在第二个输出块之后,该进程被挂起(但没有循环)。虽然没有尝试调试它。
    • @Craig S:是的 - 你说得对 pid。这是调试时实验的后遗症。我会删除它。从根本上说, vfork() 是邪恶的。我很高兴找到了它的潜在用途(但不好!)。
    • 是的,而且 vfork() 也不是很便携。看到它在行动很有趣。也许明天我会想出我自己的适用于 Mac OS X 的解决方案。我还想知道如果你调用 _exit(),而不是 exit(),而不是执行 /bin/true,会发生什么。
    • 我尝试了 _exit() 如前所述;它似乎无法正常工作。也就是说,我得到了奇怪的结果:in: 25671;广场:3 英寸:27310;方:-2147485992 中:14472;广场:4 英寸:1771;方:-2147485976 中:26575;平方:5 总和:-4294971956 (对不起,这一切都搞砸了,当然。_exit() 和 wait()。
    • 在 OS X 上,“true”在“/usr/bin”中,因此出现错误。 _exit(0) 也可以。
    【解决方案3】:

    the anti thread 之类的内容可能会让您更轻松一些,请参阅示例(尤其是 ns 查找程序)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-13
      • 2012-01-11
      • 2016-07-23
      • 1970-01-01
      相关资源
      最近更新 更多