2012-04-01 72 views
8

我正在處理C中的數據庫服務器,該服務器將處理來自多個客戶端的請求。爲了做到這一點,我使用fork()來處理單個客戶端的連接。使用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); 
+2

如果您想在同一程序的各個部分之間共享內存,使用*線程*更爲常見。但是,您必須非常小心如何使用鎖等同步訪問共享數據結構。 – alberge 2012-04-01 03:13:56

+1

好吧,您需要爲所有想要共享的內容使用共享內存。 – 2012-04-01 03:19:45

+0

只有shm可以共享,如果你分配新的內存,它也必須在shm上,沒有捷徑。 – pizza 2012-04-01 03:25:47

回答

3

首先,fork是完全不適合你想要實現的。即使你可以使它工作,這是一個可怕的黑客。一般來說,fork只適用於非常簡單的程序,我甚至可以說fork不應該被使用,除非快速跟隨exec,但這不在這裏。你真的應該使用線程。

隨着中說,有多數民衆贊成fork後,父母和孩子之間共享內存的唯一途徑,並在同一個三分球都是有效的,是mmap(或shmat,但是這是一個很大fuglier)的文件或fork之前的MAP_SHARED的匿名地圖。您不能在fork之後創建像這樣的新共享內存,因爲無法保證它們將映射到兩者中的相同地址範圍。

只是不要使用fork。這不適合這項工作。

+0

同意。 Fork()創建一個單獨的,大部分相同的實例。線程是需要的。或者asio,這可能很複雜,但Boost在提供它方面做得相當不錯。 – 2012-04-01 04:47:46

0

如果你必須你fork,共享內存似乎是'唯一'的選擇。

其實我覺得在你的場景中線程更合適。

如果你不想成爲多線程的。這裏是另一個選擇,你只能使用一個進程&一個線程模式,像redis

在此模式下,你並不需要的東西擔心像lock,如果你想向規模化,只是設計一個路由策略,因爲散列值爲key

1

是否足以共享數據庫的根指針,還是必須將所有分配的內存共享?

不,因爲每個進程都有自己的私有內存範圍。寫時複製是對用戶空間透明的內核空間優化。

正如其他人所說,SHM或mmap'd文件是在不同進程之間共享內存的唯一方式。

2

對不起回答一個月後,但我不認爲現有的答案給了OP要求的。

我認爲你基本上是想要做什麼由Redis(可能是其他人)完成。 他們在http://redis.io/topics/persistence(搜索「copy-on-write」)中描述它。

  • 線程打敗目的
  • 經典共享存儲器(SHM,映射存儲器)也失敗的目的

使用這種方法的主要好處是鎖定的避免,它可以是一個痛苦得到正確。

據我瞭解它使用COW的想法是:

  • 叉,當你想要寫,不能提前
  • 孩子(重新)的數據立即寫入磁盤,然後退出
  • 父母繼續工作,並在孩子退出時檢測(SIGCHLD)。 如果在完成其工作的同時父進程對散列進行了更改,則內核 將爲受影響的塊執行副本(右側術語?)。
    「髒標誌」用於跟蹤是否需要新叉以執行新寫入。

事情需要提防:

  • 確保只有一個優秀的孩子
  • 交易安全:寫入到一個臨時文件,然後再移動過來,讓你始終擁有完整副本,也許保持以前的這一舉措並非原子。
  • 測試你是否有與該得到重複了其他資源的問題(文件描述符,在C全局析構函數++)

您可能需要採取甘德在redis code以及

+0

臨時文件並非嚴格必要:您可以使用管道將由子項發起的更改傳回給父項。通過這種方法,每個孩子都可以獲得整個數據的一致原子視圖,而主人不需要任何鎖定來確保一致性。這樣,你甚至可以同時擁有多個孩子,每個孩子都有自己不變的,一致的數據視圖,還有一個主人根據孩子們在分娩新孩子之間的指示更新數據。 – cmaster 2014-11-19 06:39:04

+0

@cmaster臨時文件不是作爲鎖,線程之間的同步或類似的東西。如果是在文件系統中的原子性。你可以在寫入中途崩潰並且有一個不好的文件。如果你完成你的寫作,然後重命名(原子)。 – nhed 2014-11-21 12:29:30

+0

我從來沒有這樣想過。我試圖指出的是,分叉可以被看作是內存中所有數據的廉價檢查點。一個過程可以自由地分析數據,而主人已經忙於應用從另一個孩子傳回的變化。在這種設置中,您不需要將任何數據寫入磁盤進行通信。這種設置的重點不在於堅持數據,而是爲了確保防彈,併發訪問內存數據庫。 – cmaster 2014-11-21 16:53:45

1

許多流行的HTTP服務器使用fork()利用多個處理器,Nginx就是其中之一。我的個人喜歡避免的一整套麻煩,除非絕對必要,比如說,你的程序永遠不會因多線程錯誤(我與其他人的線程代碼的經驗)而導致的崩潰無關。

多處理可讓您使用機器上的所有處理器,而無需在執行線程之間隱式共享內存,默認情況下可避免所有典型的多線程無盡的錯誤。

我喜歡在晚上睡覺而沒有得到那些凌晨2點的電話,知道我的網絡面臨高吞吐量的服務器不會崩潰在我身上,因爲我沒有看到當天的數十個多線程陷阱之一。

在很多情況下,共享內存是無痛的,比如共享內存中的數據是隻讀的。您不必擔心鎖等問題。