2013-10-19 144 views
3

我的問題是關於初始化使用shm_open()mmap()獲得存儲器。我在幾個地方都看到一個共同的建議是呼籲shm_open()與標誌O_CREAT|O_EXCL:如果成功那麼我們是共享內存的第一個用戶,並可以初始化它,否則我們不是第一,共享內存已經被初始化另一個過程。POSIX共享存儲器初始化

但是,根據我對shm_open的瞭解以及我在Linux上所做的測試,這樣做不起作用:即使在共享內存對象的最後一個用戶擁有共享內存對象後,共享內存對象仍留在系統中未映射並關閉。這就要求shm_openO_CREAT|O_EXCL一個簡單的測試程序,然後關閉描述符和退出,將在第一次運行成功,但仍然會失敗的第二次運行,即使沒有其他人使用當時的共享內存。

在我看來,(至少在我測試的系統上)shm_open的行爲與open()的行爲幾乎完全相同:如果我修改我的簡單測試程序來向共享內存寫入內容(通過獲得的指針由mmap)並退出,然後共享內存對象將保持其內容持久(我可以運行另一個簡單的程序來回讀我以前寫的數據)。

所以是關於使用shm_openO_CREAT|O_EXCL只是錯了,還是我失去了一些東西的建議嗎?

我知道,共享內存對象可以用shm_unlink()被刪除,但似乎只會造成更多的問題:

  1. 如果一個進程調用shm_unlink()前去世,然後我們又回到了所描述的問題以上。

  2. 如果一個進程調用shm_unlink()而其他一些進程仍然映射到同一個共享內存中,這些其他進程仍然照常繼續使用它。現在,如果另一個進程來並調用shm_open()具有相同的名稱,並指定O_CREAT,它實際上將具有相同的名稱,這是完全無關的舊共享內存的其他進程仍在使用對象創建新的共享內存對象成功。現在我們有一個進程試圖通過共享內存與其他進程通信,並且完全不知道它正在使用錯誤的通道。

我習慣了其中共享內存對象只存在只要至少一個手柄處於打開位置,以它的Windows語義,所以這Posix的東西是非常混亂。

+1

它看起來像你沒有設置你的shm段刪除,當你完成它。因此,它需要你的過程。您可以在終端中使用'ipcs'命令來查看系統中當前存在哪種_IPC_(信號量,shm段等)。 – Rerito

+1

但我如何設置刪除?是否有一些標誌傳遞給shm_open()?我在manpage上找不到任何東西。 –

+0

@Rerito:關於評論評論的任何想法? – alk

回答

1

由於您使用的O_EXCL標誌,我會假設你有一組圍繞一個主站(段的創建者)收集的過程。

然後,你的主進程將創建使用調用共享內存段shm_open

shmid = shm_open("/insert/name/here", O_CREAT|O_EXCL, 0644); 
if (-1 == shmid) { 
    printf("Oops ..\n"); 
} 

這裏,奴隸們就可以使用該段。由於主設備要創建段,因此不需要在從設備調用中使用O_CREAT標誌。如果在段尚未創建或已被銷燬時執行從設備調用,則只需處理可能的錯誤。

當您的任何流程都完成了該分段時,應該調用shm_unlink()。在這種體系結構中,主設備通常會提供從設備。當它無話可說時,它就會關閉。那麼奴隸就有責任優雅地處理相應的錯誤。

如您所述,如果一個進程在之前死亡程序調用shm_unlink過程,那麼該段將在此後繼續存在。爲了在某​​些情況下避免出現這種情況,您可以定義自己的信號處理程序,以便在收到諸如SIGINT的信號時執行操作。無論如何,如果SIGKILL已發送到您的流程,您將無法覆蓋這些混亂情況。

編輯: 更具體地說,當不必要時使用O_CREAT | O_EXCL是錯誤的。通過上面的小例子,您可以看到主設備需要創建段,因此需要這些標誌。另一方面,任何從屬進程都不必創建它。因此,您絕對禁止在相關調用中使用O_CREAT

現在,如果另一個進程在段已被創建時調用shm_open(..., O_CREAT, ...),它將只檢索與該段相關的文件描述符。因此,這將是右聲道(如果它有權利這樣做,看到mode參數)

+0

感謝您的回覆。然而,就我而言,這個過程並不知道它是一個「主」還是「奴隸」。這裏的全部問題是找出它是哪一個。基本上,一旦進程映射到共享內存,它就需要回答「我是第一次?」這個問題。如果是,則調用初始化例程。因爲一個進程可能會被終止(如果是SIGKILL,我們不能攔截它),但我們不能假設文件系統中共享內存段的存在意味着什麼。 –

+0

我有一個解決方案,使用文件鎖來確保正確的共享內存初始化(它實際上使用2個文件鎖來避免某些競爭條件)。然而有一些疑慮,這又與異常進程終止有關。我在這裏發佈了相關問題:http://stackoverflow.com/questions/19674214/resource-cleanup-on-abnormal-process-termination –

+0

@YevgeniyP通常,信號量與共享內存一起使用以確保正確使用。但是,再一次,如果你的進程集中的一些被殺死(使用SIGKILL),你無法阻止它的良好行爲。無論如何,我現在找不到任何解決方案... – Rerito

0

你可以做到以下幾點: INT測試= shmget的(關鍵的key_t,大小,0);把它放在每個過程的明星身上。這裏的零標誌嘗試打開一個現有的共享內存,如果它的未創建的測試將等於-1,所以你可以在這個語句後檢查一下,如果test -1去創建一個共享內存,否則你只是得到一個ID到一個現有的共享內存.....我希望這可以幫助