2013-02-12 103 views
0

對於我正在開發的應用程序(在Linux下,但我試圖保持可移植性),我需要切換到共享內存以跨不同進程(以及進程內的線程)共享數據。有一個父進程產生不同的孩子fork exec和mmap問題

我需要例如讓每個進程能夠使用命名的信號量遞增共享計數器。

在這種情況下,一切都很好:

#include <sys/mman.h> 
#include <sys/wait.h> 
#include <semaphore.h> 
#include <fcntl.h> 
#include <iostream> 
#include <stdlib.h> 
#include <string.h> 
using namespace std; 

#define SEM_NAME "/mysem" 
#define SM_NAME "tmp_sm.txt" 

int main(){ 
    int fd, nloop, counter_reset; 
    int *smo; 
    sem_t *mutex; 

    nloop = 100; 
    counter_reset = 1000; 

    if (fork() == 0) { 
     /* child */ 
     /* create, initialize, and unlink semaphore */ 
     mutex = sem_open(SEM_NAME, O_CREAT, 0777, 1); 
     //sem_unlink(SEM_NAME); 
     /* open file, initialize to 0, map into memory */ 
     fd = open(SM_NAME, O_RDWR | O_CREAT); 
     smo = (int *) mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 
     close(fd); 
     /* INCREMENT */ 
     for (int i = 0; i < nloop; i++) { 
      sem_wait(mutex); 
      cout << "child: " << (*smo)++ << endl; 
      if(*smo>=counter_reset){ 
       (*smo)=0; 
      } 
      sem_post(mutex); 
     } 
     exit(0); 
    } 
    /* parent */ 
    /* create, initialize, and unlink semaphore */ 
    mutex = sem_open(SEM_NAME, O_CREAT, 0777, 1); 
    sem_unlink(SEM_NAME); 
    /* open file, initialize to 0, map into memory */ 
    fd = open(SM_NAME, O_RDWR | O_CREAT); 
    smo = (int *) mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 
    close(fd); 
    /* INCREMENT */ 
    for (int i = 0; i < nloop; i++) { 
     sem_wait(mutex); 
     cout << "parent: " << (*smo)++ << endl; 
     if(*smo>=counter_reset){ 
      (*smo)=0; 
     } 
     sem_post(mutex); 
    } 
    exit(0); 
} 

到目前爲止好:兩個信號燈和共享計數器都ok(在內存地址相同)和增量和復位做工精細。

程序僅僅通過將子代碼源代碼移動到由exec調用的新源文件而失敗。共享內存和命名信號地址不同,因此增量失敗。

有什麼建議嗎?我使用命名信號量和命名共享內存(使用文件)嘗試獲取相同的指針值。


UPDATE:

由勒夫Pileborg請求,這是在 「服務器側」 改進遵守上述原代碼:

... 
if (fork() == 0) { 
    /* child */ 
    /*spawn child by execl*/ 
    char cmd[] = "/path_to_bin/client"; 
    execl(cmd, cmd, (char *)0); 
    cerr << "error while istantiating new process" << endl; 
    exit(EXIT_FAILURE); 
} 
... 

這是 「客戶機」 的源代碼:

#include <sys/mman.h> 
#include <sys/wait.h> 
#include <semaphore.h> 
#include <fcntl.h> 
#include <iostream> 
#include <stdlib.h> 
using namespace std; 

#define SEM_NAME "/mysem" 
#define SM_NAME "tmp_ssm.txt" 

int main(){ 
    int nloop, counter_reset; 
    int *smo; 
    sem_t *mutex; 
    /* create, initialize, and unlink semaphore */ 
    mutex = sem_open(SEM_NAME, O_CREAT, 0777, 1); 
    //sem_unlink(SEM_NAME); 
    /* open file, initialize to 0, map into memory */ 
    int fd = open(SM_NAME, O_RDWR | O_CREAT); 
    smo = (int *) mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 
    close(fd); 
    nloop=100; 
    counter_reset=1000; 
    /* INCREMENT */ 
    for (int i = 0; i < nloop; i++) { 
     sem_wait(mutex); 
     cout << "child: " << (*smo)++ << endl; 
     if(*smo>=counter_reset){ 
      (*smo)=0; 
     } 
     sem_post(mutex); 
    } 
    exit(0); 
} 

執行此代碼會導致進程阻塞(死鎖)並等待無限時間。看着他們tipically發現地址:

father semaphore: 0x7f2fe1813000 
child semahpore: 0x7f0c4c793000 
father shared memory: 0x7f2fe1811000 
child shared memory: 0x7ffd175cb000 

去除「sem_post」和「sem_wait」一切都很好,但我需要相互排阻而遞增......

+0

你不應該在實際的地址上看起來很難,因爲這是不同的過程,所以這些將會有所不同。另外,我不希望在父進程中調用sem_unlink?另外,由於'mmap'返回-1,你應該檢查錯誤代碼('errno',或者用'perror'打印出來)。 – 2013-02-12 13:40:58

+0

哦,順便說一句,你在調用'shm_open'時設置了錯誤的模式。它應該是一個文件模式,就像在調用'sem_open'時一樣。 – 2013-02-12 13:44:27

+0

@JoachimPileborg:沒有sem_unlink沒有更多的叫...我沒有修改原始代碼的追蹤變更,但我現在要添加評論...我要檢查你告訴我的一切... – schu 2013-02-12 13:44:43

回答

2

不要斷開鏈接的信號。它實際上消除了信號量。

sem_unlink手冊頁面:

sem_unlink()去除命名信號量通過名稱提及。信號名稱立即被刪除。一旦信號量開放的所有其他進程關閉,信號就會被銷燬。

這意味着,一旦您在父進程中創建了信號量,就立即將其刪除。子進程將無法找到信號量,而是創建一個新的信號量。

+0

mmmm謝謝...這可能來自程序的先前版本,其中信號量和共享內存是在fork之前創建的。修復它! – schu 2013-02-12 11:27:29

+0

但無論如何它不能解決問題... – schu 2013-02-12 12:45:49

+0

@schu您可能應該在那裏製作您的非工作程序,而不是縮小到最基本的要領,而不是在問題中包含您的工作程序。 – 2013-02-12 12:48:59