【问题标题】:Synchronize two processes using two different states使用两种不同的状态同步两个进程
【发布时间】:2012-06-08 19:39:55
【问题描述】:

我正在尝试找出一种方法来同步两个共享数据的进程。

基本上我有两个使用共享内存链接的进程。我需要进程 A 在共享内存区域中设置一些数据,然后进程 B 读取该数据并对其进行操作。

我期待的事件顺序是:

  1. B 块等待数据可用信号
  2. A 写入数据
  3. A 信号数据可用
  4. B 读取数据
  5. B 块等待数据不可用信号
  6. A 信号数据不可用
  7. 一切回到起点。

换句话说,B 会阻塞,直到它收到“1”信号,获取数据,然后再次阻塞,直到该信号变为“0”。

我已经成功地使用纯共享内存来模拟它,但是要么我使用一个消耗 100% CPU 时间的 while 循环来阻塞,要么我使用一个带有 nanosleep 的 while 循环,它有时会错过一些信号。

我尝试过使用信号量,但我只能找到一种方法来等待一个零,而不是一个,并且尝试使用两个信号量只是没有用。我不认为信号量是要走的路。

会有很多进程都访问同一个共享内存区域,并且当共享内存被修改时需要通知所有进程。

它基本上是在尝试模拟硬件数据和控制总线,其中事件是边沿触发而不是电平触发。我感兴趣的是状态之间的转换,而不是状态本身。

那么,有什么想法或想法吗?

【问题讨论】:

  • 循环如何消耗 100% 的内存?
  • PS:您可能会考虑使用 pthreads 数据结构,在这种情况下是条件变量。 (是的,我知道您正在使用单独的进程,但是您可以将 condvar 扔到共享内存段中)
  • 哎呀......这应该是 100% 的 CPU 时间。
  • @H2CO3 信号会很好,但我无法预测哪些进程将访问共享内存。可能是多个进程。
  • 共享内存听起来像是问题的根源。为什么不使用更合适的 IPC 机制?

标签: c linux shared-memory


【解决方案1】:

如果涉及 2 个进程,您可以使用文件、共享内存甚至网络来传递标志或信号。但是如果进程比较多,修改内核可能会有一些合适的解决方案。您的问题中有一个共享内存,对吗?!现在信号是怎么传递的?!

【讨论】:

  • 我设法做的最好的事情是在共享内存中添加第二个字节,其中包含标志,充当“控制”行。进程通过在 while() 循环中重复轮询来等待该字节的变化。不过,这对处理器来说太密集了。添加延迟,即使使用 nanosleep,也意味着错过了一些更改。我仍然需要这个额外的字节来控制其他事情,但需要某种方式来通知它何时发生变化。它每秒可以更改数千次,并且需要多个进程知道这一点。
  • 进程 A 将一个信号放入您的第二个共享内存中,以便通知进程 B 新数据。当 B 没有全部读取它们时,进程 A 可以更改第二个共享内存及其标志吗?!正如你所说,进程 A 可以改变它,而现在,B 处于纳米睡眠状态,对吧?
  • 是的,A可以随时更改数据,无论B是否读取。实际上,进程 A 就像一个 CPU,只是在地址和数据总线上设置值。进程 B 就像一个 RAM 芯片,等待某些总线上的某些组合,然后再对它们做出反应。进程 A 不能等待 B 的确认,因为 B 可能不应该响应当前数据。响应的可能是 C,也可能是 Q,或者根本没有。我无法预测什么将负责或不负责处理共享内存中的数据。
  • 当我遇到这样的模式时,我的脑海中会出现一个队列。我知道这里的问题是不同的,但是如果进程 A 可以等待来自 B 的信号以读取标志以确保 A 确定已传递标志,那么似乎问题已解决,但如果进程数更高,则将是一个糟糕的解决方案。你也说进程 A 不等待任何东西。另一种情况可以看作是这样,进程A将标志放入队列中,进程B读取队列。他不适合这个问题,因为每当新标志放入第二个时,A都会更改数据...
  • 第二个共享内存。我们可以有一个数据队列吗?并且所有进程或在这种特殊情况下,进程 B 从该队列中删除。我的意思是有什么结构可以为你做到这一点?!如果您使用网络,例如 TCP 连接,则会创建一个内部队列。如果你想写在内核空间,你可以在那里有一个队列。
【解决方案2】:

Linux 有自己的eventfd(2) 工具,您可以将其合并到您的正常poll/select 循环中。您可以通过 UNIX 套接字将eventfd 文件描述符从一个进程传递到另一个进程,或者直接使用fork(2) 继承它。

编辑0:

在重新阅读问题后,我认为您的选择之一是信号和进程组:在同一进程组 (setpgid(2)) 下启动您的“侦听”进程,然后用 pid 的否定参数向它们发出信号987654323@ 或sigqueue(2)。同样,Linux 提供了signalfd(2) 用于轮询和避免慢信号蹦床。

【讨论】:

  • 如果有 15 个进程,其中任何一个都可能需要向其他 14 个进程表明共享内存值已更改,这将如何?
  • 我会使用像本地多播这样的一对多公告。管道适合一对一。抱歉错过了您关于“很多”的问题。
【解决方案3】:

在 linux 中,所有 POSIX 控制结构(互斥锁、条件、读写锁、信号量)都有一个选项,如果它们驻留在共享内存中,它们也可以在进程之间使用。对于您描述的经典互斥锁/条件对的过程,似乎很适合这项工作。查看这些结构的 ..._init 函数的手册页。

Linux 有其他适当的实用程序,例如“futex”,可以更有效地处理这个问题。但这些可能不是开始的正确工具。

【讨论】:

    【解决方案4】:

    1 个单读和单写
    1 个单读和单写 这可以使用信号量来实现。 在 posix semaphore api 中,您有 sem_wait() ,它会一直等到信号量计数的值为零,一旦使用来自其他进程的 sem_post 递增,等待将完成。

    在这种情况下,您必须使用 2 个信号量进行同步。

    进程 1(阅读器)
    sem_wait(sem1);
    .......
    sem_post(sem2);

    流程 2(作家)
    sem_wait(sem2);
    .......
    sem_post(sem1);

    这样就可以实现共享内存中的同步。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-26
      • 2012-07-02
      • 2015-10-24
      相关资源
      最近更新 更多