为什么我需要线程来实现“基于共享内存的命名管道”?

时间:2012-07-18 13:30:44

标签: c operating-system pipe shared-memory

在尝试实现Named pipe时(例如,使用相同共享内存的两个独立无关的进程),我继续阅读我将使用pthread_atfork和{ {1}}。

我完全同意使用互斥锁和信号量 - 使用它们我们可以决定atexit何时读/写以及process A何时读/写。

但是出于什么原因我想使用process B和线程呢?

修改

不使用信号量的例子会花费很多:

pthread_atfork

2 个答案:

答案 0 :(得分:1)

没有人说你必须使用线程来实现命名管道。但是您的库代码可以用于有线程的项目,因此您可以处理许多特殊情况。您可能知道SysV IPC对象(如共享内存段)在使用计数降至0时不会自动删除,除非它们已被标记为销毁。这意味着如果程序使用您的代码创建管道然后因任何原因而崩溃,那么管道实现中的IPC对象很可能会保留,污染IPC命名空间并消耗宝贵的系统资源。

您提到的两个函数pthread_atfork()atexit()用于注册在发生某些事情时执行的回调。只要进程以正常方式终止(例如,通过调用atexit()或从exit(3)返回),main()就会注册要执行的代码。这使您可以捕获管道未明确关闭的情况并进行必要的清理。

除了在不关闭管道的情况下退出进程外,程序也可以自行分叉。这也是您必须相应处理的特殊情况。 pthread_atfork()应该注册三个回调,以便在进行分叉时在各个点调用。

您还应该处理某些操作系统信号,否则这些信号可能会导致无法捕获,这可能会在执行正确的清理之前终止程序。

正如您所看到的,编写库比编写程序要复杂得多。在编写程序时,您可以控制(几乎)所有用例。当你在写一个图书馆时,它可能会用在许多不同的场景中,你应该考虑所有这些并为所有这些图书做好准备。您应该考虑正确的用法和不正确的用法。如果没有正确使用库,您应该考虑清理以及可能会延迟的系统资源。依此类推,等等......

答案 1 :(得分:0)

粗略地说,你的管道结构应该位于共享内存中(例如由shm_open获得)并包含以下成员:

  • 控制对结构的所有访问的一个互斥锁。
  • 用于发信号通知等待对等方的一个条件变量。
  • 一些固定大小的缓冲区。
  • 缓冲区有两个偏移量,用于当前读写位置。

读取函数基本上应该获取互斥锁,然后检查是否有可用于读取的数据,如果没有,则等待条件变量并循环重新检查数据。一旦找到数据,如果读取使得可以在缓冲区中容纳更多数据以唤醒潜在等待的写入器,则需要发出条件变量的信号。将读取的数据复制到调用者提供的缓冲区中,然后解锁互斥锁。

写入函数应该基本上获取互斥锁,然后检查缓冲区中是否留有空间来写入。如果没有,它应该等待条件变量并循环重新检查可用于写入的空间。一旦找到空间,它应该将数据复制到缓冲区,发出条件变量信号以唤醒可能正在等待数据的任何读取器,并解锁互斥锁。

互斥和条件变量都需要使用进程共享属性创建,您将使用“管道”进行进程之间的通信(而不仅仅是同一进程中的线程)。您可能还需要考虑是否要支持多个读取器/写入器以及是否需要条件变量的单信号或广播信号语义。有很多方法可以优化行为,但上面的大纲应该给你一个通用的起点。