用fork()共享堆内存

问题描述 投票:8回答:5

我正在使用C实现一个数据库服务器,它将处理来自多个客户端的请求。为此,我使用fork()来处理各个客户端的连接。

服务器将数据存储在堆中,该数据包含一个指向动态分配记录的哈希表的根指针。记录是具有指向各种数据类型的指针的结构。我希望这些进程能够共享这些数据,以便当客户端对堆进行更改时,其他客户端将看到更改。

我已经知道fork()使用COW (Copy On Write),我的理解是,当子进程尝试修改内存中的数据时,它将复制父进程的堆(和堆栈)内存。

我发现我可以使用shm库来共享内存。

- 是否足以共享数据库的根指针或是否必须将所有已分配的内存分享?

- 如果孩子分配内存,父/其他孩子能够访问它吗?

- 如果一个孩子分配内存并且后来被杀死,分配的内存是否仍然留在堆上?

那么例如下面的代码是否是共享堆内存的有效方式(在shared_string中)?如果孩子使用类似的代码(即从//开始),其他孩子是否能够在孩子跑步和死亡之后读/写?

key_t key;
int shmid;

key = ftok("/tmp",'R');
shmid = shmget(key, 1024, 0644 | IPC_CREAT);

//start
char * string;
string = malloc(sizeof(char) * 10);

strcpy(string, "a string");

char * shared_string;

shared_string = shmat(shmid, string, 0);

strcpy(shared_string, string);
c++ c heap fork shared-memory
5个回答
3
投票

首先,fork完全不适合你想要实现的目标。即使你可以使它工作,这是一个可怕的黑客。总的来说,fork无论如何只适用于非常简单的程序,我甚至会说除了fork很快跟随exec之外不应该使用fork,但这不是重点。你真的应该使用线程。

有了这个说,在mmap之后,在父和子之间共享内存的唯一方法,就是shmat(或MAP_SHARED,但这更加狡猾)一个文件或匿名地图与forkfork之前。你不能在fork之后创建这样的新共享内存,因为无法保证它将在两者中的相同地址范围内映射。

只是不要使用http://redis.io/topics/persistence。它不适合这项工作。


3
投票

很抱歉在一个月后回答,但我不认为现有的答案给出了OP要求的内容。

我认为你基本上都想做Redis(以及其他人)所做的事情。他们在qazxsw poi中描述它(搜索“copy-on-write”)。

  • 线程打败了目的
  • 经典共享内存(shm,映射内存)也失败了目的

使用此方法的主要好处是避免锁定,这可能是一个很难做到的痛苦。

据我了解,使用COW的想法是:

  • 当你想写时,不要提前写
  • 子(重新)将数据写入磁盘,然后立即退出
  • 父母继续工作,并在孩子退出时检测(SIGCHLD)。如果在完成工作时父级最终对哈希进行更改,则内核将为受影响的块执行副本(正确的术语?)。 “脏标志”用于跟踪是否需要新的fork来执行新的写入。

需要注意的事项:

  • 确保只有一个优秀的孩子
  • 事务安全性:首先写入临时文件,然后将其移动,以便始终拥有完整的副本,如果移动不是原子的,则可以保留前一个。
  • 测试你是否会遇到其他资源重复的问题(文件描述符,c ++中的全局析构函数)

你可能也想在redis code上看看


1
投票

是否足以共享数据库的根指针或是否必须将所有已分配的内存作为共享?

不,因为每个进程都有自己的私有内存范围。 Copy-on-write是一种对用户空间透明的内核空间优化。

正如其他人所说,SHM或mmap'd文件是在不同进程之间共享内存的唯一方法。


1
投票

许多流行的HTTP服务器使用fork()来利用多个处理器,Nginx就是其中之一。

线程带来了一系列令我头疼的问题,除非绝对必要,否则我个人希望避免这样做,例如,您的程序永远不会免于多线程错误导致的崩溃(我对其他人的线程代码的体验)。

多处理允许您使用计算机上的所有处理器,而无需在执行线程之间隐式共享内存,默认情况下避免所有典型的多线程无限错误。

我喜欢晚上睡觉而没有得到那些凌晨2点的电话,知道我面向网络,高吞吐量的服务器不会让我崩溃,因为那天我没有看到数十个多线程陷阱中的一个。

在许多情况下,共享内存是无痛的,例如,如果共享内存中的数据是只读的。你不必担心锁等。


0
投票

如果你必须fork,共享内存似乎是'唯一'的选择。

实际上,我认为在你的场景中,线程更合适。

如果你不想成为多线程的。这是另一种选择,你只能使用单进程和单线程模式,如redis

使用这种模式,你不需要担心像lock这样的东西,如果你想扩展,只需设计一个路由策略,作为具有key哈希值的路由

© www.soinside.com 2019 - 2024. All rights reserved.