[英]Why would I need threads for implementing a 'named pipe based on shared memory'?
在尝试实现Named pipe
(例如,使用相同共享内存的两个独立的不相关进程)时,我一直在阅读要使用pthread_atfork
和atexit
。
我完全同意互斥量和信号量的使用-使用它们,我们可以决定process A
何时读/写以及process B
何时读/写。
但是出于什么原因,我想为此使用pthread_atfork
和线程?
编辑:
一个不使用信号量的示例将耗资巨大:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/times.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <assert.h>
// Simple busy-wait loop to throw off our timing.
void busywait(void)
{
clock_t t1 = times(NULL);
while (times(NULL) - t1 < 2);
}
int main(int argc, char *argv[])
{
const char *message = "Hello World\n";
int n = strlen(message) / 2;
pid_t pid = fork();
int i0 = (pid == 0) ? 0 : n;
int i;
for (i = 0; i < n; i++) {
write(1, message + i0 + i, 1);
busywait();
}
}
没有人说过必须使用线程来实现命名管道。 但是您的库代码可以在线程化的项目中使用,因此您可以处理许多特殊情况。 您可能已经知道,SysV IPC对象(如共享内存段)在其使用计数降至0
时不会自动删除,除非已将其标记为销毁。 这意味着,如果程序使用您的代码创建管道,然后由于任何原因导致崩溃,则管道实施中的IPC对象很可能会保留下来,污染IPC名称空间,并消耗宝贵的系统资源。
您提到的两个函数pthread_atfork()
和atexit()
用于注册在某些情况发生时执行的回调。 每当进程以正常方式终止时(例如,通过调用exit(3)
或从main()
返回exit(3)
atexit()
注册要执行的代码。 这使您可以发现未显式关闭管道的情况,并进行必要的清理。
除了在不关闭管道的情况下退出进程之外,该程序还可能会派生自己。 这也是一种特殊情况,您必须相应地处理。 pthread_atfork()
应该在进行分叉时注册要在各个点调用的三个回调。
您可能还应该处理某些OS信号,这些信号可能最终未被捕获,从而可能在执行适当的清理之前终止程序。
如您所见,编写库比编写程序要复杂得多。 在编写程序时,您可以控制(几乎)所有用例。 当您编写库时,可能会在许多不同的场景中使用它,并且您应该考虑所有内容并为所有内容做好准备。 您应该考虑正确的用法和不正确的用法。 您应该考虑清理之类的问题,以及如果未正确使用您的库可能会残留哪些系统资源。 等等等等...
大致来说,类似管道的结构应位于共享内存中(例如,通过shm_open
获得),并包含以下成员:
读取功能基本上应该获取互斥体,然后检查是否有可读取的数据,如果没有,请等待条件变量并循环检查数据。 找到数据后,如果读取使得有可能在缓冲区中容纳更多数据以唤醒可能正在等待的写入器,则需要向条件变量发送信号。 将读取的数据复制到调用方提供的缓冲区中,然后解锁互斥锁。
写功能基本上应该获取互斥体,然后检查缓冲区中是否还有剩余空间要写。 如果没有,它应该等待条件变量并循环检查可用空间。 找到空间后,它应该将数据复制到缓冲区中,向条件变量发出信号以唤醒可能正在等待数据的任何读取器,然后解锁互斥锁。
互斥量和条件变量都需要使用进程共享属性创建,您将使用“管道”在进程之间进行通信(而不仅仅是同一进程中的线程)。 您可能还需要考虑是否要支持多个读取器/写入器以及条件变量是否需要单信号或广播信号语义。 有很多方法可以优化行为,但是上面的概述应该为您提供一个一般的起点。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.