我正在使用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);
答案 0 :(得分:3)
首先,fork
完全不适合您要实现的目标。即使你可以使它工作,这是一个可怕的黑客。一般来说,fork
仅适用于非常简单的程序,我甚至会说fork
永远不应该被使用,除非很快跟上exec
,但是除了指向这里。你真的应该使用线程。
话虽如此,在fork
之后在父级和子级之间共享内存以及同一指针在两者中都有效的唯一方法是mmap
(或{{1}在shmat
之前MAP_SHARED
的{{1}}文件或匿名地图,但这是更加狡猾的。您无法在fork
之后创建这样的新共享内存,因为无法保证它将在两者中的相同地址范围内映射。
请勿使用fork
。它不适合这项工作。
答案 1 :(得分:3)
很抱歉一个月后回答,但我不认为现有的答案给出了OP的要求。
我认为你基本上是想做Redis所做的事情(也可能是其他人)。 他们在http://redis.io/topics/persistence中描述它(搜索“copy-on-write”)。
使用此方法的主要好处是避免锁定,这可能是一个很难做到的事情。
据我所知,使用COW的想法是:
需要注意的事项:
您可能想要redis code以及
答案 2 :(得分:1)
分享数据库的根指针还是必须将所有已分配的内存分享?
不,因为每个进程都有自己的私有内存范围。 Copy-on-write是一种对用户空间透明的内核空间优化。
正如其他人所说,SHM或mmap'd文件是在不同进程之间共享内存的唯一方法。
答案 3 :(得分:1)
许多流行的HTTP服务器使用fork()来利用多个处理器,Nginx就是其中之一。
线程带来了一系列令我头疼的问题,我个人希望除非绝对必要,否则你的程序将永远不会出现由多线程错误(我与其他人的线程代码的体验)引起的崩溃。
多处理允许您使用计算机上的所有处理器,而无需在执行线程之间隐式共享内存,默认情况下避免所有典型的多线程无限错误。
我喜欢晚上睡觉而没有得到那些凌晨2点的电话,因为我知道面向网络,高吞吐量的服务器不会让我崩溃,因为那天我没有看到数十个多线程陷阱中的一个。
在许多情况下,共享内存是无痛的,例如,如果共享内存中的数据是只读的。你不必担心锁等。
答案 4 :(得分:0)
如果你必须fork
,共享内存似乎是'唯一'的选择。
实际上,我认为在你的场景中,线程更合适。
如果您不想成为多线程。这是另一种选择,你只能使用一个过程&单线程模式,如redis
使用此模式,您无需担心lock
之类的内容,如果您想扩展,只需将路由策略设计为哈希值为key