2011-04-12 71 views
6

當我在Linux上有兩個不同的IPC消息隊列時,有時會從錯誤隊列中獲取消息。線程抓住其他線程來自隊列(Linux)的IPC消息

下面的玩具程序顯示問題,可以在不同的處理器上重複。

任何幫助非常感謝!

伯特

/* 
    To compile; 
    gcc MM.c -o mm -fno-stack-protector -pthread 

    We want Mickey to send a message to Minnie exclusively. 
    We want Donald to send a message to pluto exclusively. 

    Problem: Pluto intercepts Minnie's messages. 

    Listing gives: 

    $ ./mm 
    Mickey thread successfully started. 
    Minnie thread successfully started. 
    Pluto thread successfully started. 
    Donald thread successfully started. 
    Donald sent a message to Pluto. 
    Mickey sent a message to Minnie. 
    Pluto received: Sit, Pluto! 

    Minnie received: Hello, Minnie! 

    Mickey sent a message to Minnie. (100 times) 

    Pluto received: Hello, Minnie! 
*/ 
#include <sys/types.h> 
#include <sys/msg.h> 
#include <sys/ipc.h> 
#include <string.h> 
#include <stdio.h> 

pthread_t t1,t2,t3,t4; 

// Mickey send 
key_t ipcMickey; 
int mqMickeyid; 
char helloMickeymsg[] = {"Hello, Minnie!"}; 
struct { long type; char text[100]; } myMickeymsg; 

// Minnie get 
int mqMinnieid; 
struct { long type; char text[100]; } myMinniemsg; 

// Donald send 
key_t ipcDonald; 
int mqDonaldid; 
char helloDonaldmsg[] = {"Sit, Pluto!"}; 
struct { long type; char text[100]; } myDonaldmsg; 

// Pluto get 
int mqPlutoid; 
struct { long type; char text[100]; } myPlutomsg; 

static void * DONALDthreadFunc(void *arg) 
{ 
    printf("Donald thread successfully started.\n"); 
    char *s = (char *) arg; 

    while(1) 
    { 
     //send 
     memset(myDonaldmsg.text, 0, 100); 
     strncpy(myDonaldmsg.text, helloDonaldmsg, strlen(helloDonaldmsg)); 
     myDonaldmsg.type = 1; 
     msgsnd(mqDonaldid, &myDonaldmsg, sizeof(myDonaldmsg), 0); 
     printf("Donald sent a message to Pluto.\r\n"); 
     sleep(4); 
    } 

    /* just a formality */ 
    return (void *) strlen(s); 
} 

static void * PLUTOthreadFunc(void *arg) 
{ 
    printf("Pluto thread successfully started.\n"); 
    char *s = (char *) arg; 

    while(1) 
    { 
     //receive 
     mqPlutoid = msgget(ipcDonald, 0); 
     msgrcv(mqPlutoid, &myPlutomsg, sizeof(myPlutomsg), 0, 0); 
     printf("Pluto received: %s\r\n\r\n", myPlutomsg.text); 
     sleep(1); 
    } 

    /* just a formality */ 
    return (void *) strlen(s); 
} 

static void * MICKEYthreadFunc(void *arg) 
{ 
    printf("Mickey thread successfully started.\n"); 
    char *s = (char *) arg; 

    while(1) 
    { 
     //send 
     memset(myMickeymsg.text, 0, 100); 
     strncpy(myMickeymsg.text, helloMickeymsg, strlen(helloMickeymsg)); 
     myMickeymsg.type = 1; 
     msgsnd(mqMickeyid, &myMickeymsg, sizeof(myMickeymsg), 0); 
     printf("Mickey sent a message to Minnie.\r\n"); 
     usleep(10000); 
    } 

    /* just a formality */ 
    return (void *) strlen(s); 
} 

static void * MINNIEthreadFunc(void *arg) 
{ 
    printf("Minnie thread successfully started.\n"); 
    char *s = (char *) arg; 

    while(1) 
    { 
     //receive 
     mqMinnieid = msgget(ipcMickey, 0); 
     msgrcv(mqMinnieid, &myMinniemsg, sizeof(myMinniemsg), 0, 0); 
     printf("Minnie received: %s\r\n\r\n", myMinniemsg.text); 
     sleep(3); 
    } 

    return (void *) strlen(s); 
} 

int main (void) 
{ 
     ipcMickey = ftok("/tmp/mqmickey", 63); 
     mqMickeyid = msgget(ipcMickey, IPC_CREAT | 0666); 

    ipcDonald = ftok("/tmp/mqdonald", 69); 
     mqDonaldid = msgget(ipcDonald, IPC_CREAT | 0666); 

    pthread_create(&t1, NULL, MICKEYthreadFunc, "Mickey sends\n"); 
    pthread_create(&t2, NULL, MINNIEthreadFunc, "Minnie replies\n"); 
    pthread_create(&t3, NULL, DONALDthreadFunc, "Donald sends\n"); 
    pthread_create(&t4, NULL, PLUTOthreadFunc, "Pluto replies\n"); 

    while(1) 
    { 
     sleep(5); 
    } 
} 
+0

+1編譯小例子,將測試 – sehe 2011-04-12 20:58:44

回答

5

問題是你錯過了錯誤處理。

包括

ipcMickey = ftok("/tmp/mqmickey", 63); 
if (-1==ipcMickey) 
{ 
    perror("ipcMickey"); 
    exit(255); 
} 
ipcDonald = ftok("/tmp/mqdonald", 69); 
if (-1==ipcDonald) 
{ 
    perror("ipcDonald"); 
    exit(255); 
} 

,你很快就會發現

./mm 
ipcDonald: No such file or directory 

ftok返回-1在這種情況下。如果這兩個文件丟失,無論IPC鍵將是-1,這意味着所有流量將共享相同的端口:)

所以

touch /tmp/mqmickey /tmp/mqdonald 

修復它。有人統計(與usleep(random()%10000)替換睡覺):

gcc MM.c -o mm -O3 -fno-stack-protector -pthread 
time ./mm | { trap "" INT; sort | uniq -c | tee stats; } 
    16047 
     1 Donald sent a 
    8054 Donald sent a message to Pluto. 
     1 Donald thread successfully started. 
    8040 Mickey sent a message to Minnie. 
     1 Mickey thread successfully started. 
    8065 Minnie received: Hello, Minnie! 
     1 Minnie thread successfully started. 
    7982 Pluto received: Sit, Pluto! 
     1 Pluto thread successfully started. 


real 0m40.814s 
user 0m0.168s 
sys 0m0.092s 
+2

這也是不可能的,但遠之內有可能的是'ftok'返回兩個文件名稱相同的密鑰。它只是有點太少,以避免偶爾的共謀。 OP應該考慮查看缺少這個基本缺陷的POSIX消息隊列('mq_ *')。或者,您可以循環創建臨時文件,直到獲得唯一密鑰,然後與所有需要它們的線程/進程共享密鑰,而不是再次調用「ftok」。 – 2011-04-12 21:50:38

0

這裏被稍微修改程序的版本:

/* 
    To compile; 
    gcc MM.c -o mm -fno-stack-protector -pthread 

    We want Mickey to send a message to Minnie exclusively. 
    We want Donald to send a message to pluto exclusively. 

    Problem: Pluto intercepts Minnie's messages. 

    Listing gives: 

    $ ./mm 
    Mickey thread successfully started. 
    Minnie thread successfully started. 
    Pluto thread successfully started. 
    Donald thread successfully started. 
    Donald sent a message to Pluto. 
    Mickey sent a message to Minnie. 
    Pluto received: Sit, Pluto! 

    Minnie received: Hello, Minnie! 

    Mickey sent a message to Minnie. (100 times) 

    Pluto received: Hello, Minnie! 
*/ 
#include <sys/types.h> 
#include <sys/msg.h> 
#include <sys/ipc.h> 
#include <string.h> 
#include <stdio.h> 

pthread_t t1,t2,t3,t4; 

// Mickey send 
key_t ipcMickey; 
int mqMickeyid; 
char helloMickeymsg[] = {"Hello, Minnie!"}; 
struct { long type; char text[100]; } myMickeymsg; 

// Minnie get 
int mqMinnieid; 
struct { long type; char text[100]; } myMinniemsg; 

// Donald send 
key_t ipcDonald; 
int mqDonaldid; 
char helloDonaldmsg[] = {"Sit, Pluto!"}; 
struct { long type; char text[100]; } myDonaldmsg; 

// Pluto get 
int mqPlutoid; 
struct { long type; char text[100]; } myPlutomsg; 

static void * DONALDthreadFunc(void *arg) 
{ 
    printf("Donald thread successfully started.\n"); 
    char *s = (char *) arg; 

    while(1) 
    { 
     //send 
     memset(myDonaldmsg.text, 0, 100); 
     strncpy(myDonaldmsg.text, helloDonaldmsg, strlen(helloDonaldmsg)); 
     myDonaldmsg.type = 1; 
     msgsnd(mqDonaldid, &myDonaldmsg, sizeof(myDonaldmsg), 0); 
     printf("Donald sent a message to Pluto.\r\n"); 
     sleep(4); 
    } 

    /* just a formality */ 
    return (void *) strlen(s); 
} 

static void * PLUTOthreadFunc(void *arg) 
{ 
    printf("Pluto thread successfully started.\n"); 
    char *s = (char *) arg; 

    while(1) 
    { 
     //receive 
     mqPlutoid = msgget(ipcDonald, 0); 
     msgrcv(mqPlutoid, &myPlutomsg, sizeof(myPlutomsg), 0, 0); 
     printf("Pluto received: %s\r\nPluto uses MQ with id = %d\r\n", myPlutomsg.text,mqPlutoid); 
     sleep(1); 
    } 

    /* just a formality */ 
    return (void *) strlen(s); 
} 

static void * MICKEYthreadFunc(void *arg) 
{ 
    printf("Mickey thread successfully started.\n"); 
    char *s = (char *) arg; 

    while(1) 
    { 
     //send 
     memset(myMickeymsg.text, 0, 100); 
     strncpy(myMickeymsg.text, helloMickeymsg, strlen(helloMickeymsg)); 
     myMickeymsg.type = 1; 
     msgsnd(mqMickeyid, &myMickeymsg, sizeof(myMickeymsg), 0); 
     printf("Mickey sent a message to Minnie.\r\n"); 
     usleep(10000); 
    } 

    /* just a formality */ 
    return (void *) strlen(s); 
} 

static void * MINNIEthreadFunc(void *arg) 
{ 
    printf("Minnie thread successfully started.\n"); 
    char *s = (char *) arg; 

    while(1) 
    { 
     //receive 
     mqMinnieid = msgget(ipcMickey, 0); 
     msgrcv(mqMinnieid, &myMinniemsg, sizeof(myMinniemsg), 0, 0); 
     printf(" Minnie received: %s\r\nMinnie uses MQ with id = %d\r\n", myMinniemsg.text,mqMinnieid); 
     sleep(3); 
    } 

    return (void *) strlen(s); 
} 

int main (void) 
{ 
    ipcMickey = ftok("./mqmickey", 63); 
    mqMickeyid = msgget(ipcMickey, IPC_CREAT | 0666); 
    printf("mqMickeyid=%d\n",mqMickeyid); 

    ipcDonald = ftok("./mqdonald", 69); 
    mqDonaldid = msgget(ipcDonald, IPC_CREAT | 0666); 
    printf("mqDonaldid=%d\n",mqDonaldid); 

    pthread_create(&t1, NULL, MICKEYthreadFunc, "Mickey sends\n"); 
    pthread_create(&t2, NULL, MINNIEthreadFunc, "Minnie replies\n"); 
    pthread_create(&t3, NULL, DONALDthreadFunc, "Donald sends\n"); 
    pthread_create(&t4, NULL, PLUTOthreadFunc, "Pluto replies\n"); 

    while(1) 
    { 
     sleep(5); 
    } 
} 

我發現,由於某些原因,msgget失敗,都MQS都具有相同的ID,那麼它是一個線程首先踢的比賽。

隨着你的程序的這款改裝版,這樣做:

touch mqmickey 
touch mqdonald 

先創建文件。

你應該看到消息去正確的收件人。

2

非常感謝你sehe和VJo,

下面是節目的修改版本,考慮到所有的你的意見。

希望這對其他人有用。

伯特

/* 
    We want Mickey to send a message to Minnie exclusively. 
    We want Donald to send a message to pluto exclusively. 

    To compile: 
    gcc mm.c -o mm -fno-stack-protector -pthread 

    Requires: 
    syslog enabled and started. 

    To test: 
    time ./mm | { trap "" INT; sort | uniq -c | tee stats; } 

    Results: 
    1503373 Donald sent a message to Pluto. 
      1 Donald thread successfully started. 
    1423964 Mickey sent a message to Minnie. 
      1 Mickey thread successfully started. 
    1423958 Minnie received: Hello, Minnie! 
      1 Minnie thread successfully started. 
    1503333 Pluto received: Sit, Pluto! 
      1 Pluto thread successfully started. 

    real 0m17.133s 
    user 0m16.053s 
    sys 0m5.248s 

*/ 
#include <sys/types.h> 
#include <sys/msg.h> 
#include <sys/ipc.h> 
#include <string.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <syslog.h> 


#define MQMICKEY "/tmp/MQMickey" 
#define MQDONALD "/tmp/MQDonald" 

pthread_t t1,t2,t3,t4; 
FILE *fMickeypointer; 
FILE *fDonaldpointer; 

// Mickey send 
key_t ipcMickey; 
int mqMickeyid; 
char helloMickeymsg[] = {"Hello, Minnie!"}; 
struct { long type; char text[100]; } myMickeymsg; 

// Minnie get 
int mqMinnieid; 
struct { long type; char text[100]; } myMinniemsg; 

// Donald send 
key_t ipcDonald; 
int mqDonaldid; 
char helloDonaldmsg[] = {"Sit, Pluto!"}; 
struct { long type; char text[100]; } myDonaldmsg; 

// Pluto get 
int mqPlutoid; 
struct { long type; char text[100]; } myPlutomsg; 

static void * DONALDthreadFunc(void *arg) 
{ 
    printf("Donald thread successfully started.\n"); 
    char *s = (char *) arg; 

    while(1) 
    { 
     //send 
     memset(myDonaldmsg.text, 0, 100); 
     strncpy(myDonaldmsg.text, helloDonaldmsg, strlen(helloDonaldmsg)); 
     myDonaldmsg.type = 1; 
     msgsnd(mqDonaldid, &myDonaldmsg, sizeof(myDonaldmsg), 0); 
     printf("Donald sent a message to Pluto.\r\n"); 
    } 

    /* just a formality */ 
    return (void *) strlen(s); 
} 

static void * PLUTOthreadFunc(void *arg) 
{ 
    printf("Pluto thread successfully started.\n"); 
    char *s = (char *) arg; 

    while(1) 
    { 
     //receive 
     mqPlutoid = msgget(ipcDonald, 0); 
     msgrcv(mqPlutoid, &myPlutomsg, sizeof(myPlutomsg), 0, 0); 
     printf("Pluto received: %s\r\n", myPlutomsg.text); 
    } 

    /* just a formality */ 
    return (void *) strlen(s); 
} 

static void * MICKEYthreadFunc(void *arg) 
{ 
    printf("Mickey thread successfully started.\n"); 
    char *s = (char *) arg; 

    while(1) 
    { 
     //send 
     memset(myMickeymsg.text, 0, 100); 
     strncpy(myMickeymsg.text, helloMickeymsg, strlen(helloMickeymsg)); 
     myMickeymsg.type = 1; 
     msgsnd(mqMickeyid, &myMickeymsg, sizeof(myMickeymsg), 0); 
     printf("Mickey sent a message to Minnie.\r\n"); 
    } 

    /* just a formality */ 
    return (void *) strlen(s); 
} 

static void * MINNIEthreadFunc(void *arg) 
{ 
    printf("Minnie thread successfully started.\n"); 
    char *s = (char *) arg; 

    while(1) 
    { 
     //receive 
     mqMinnieid = msgget(ipcMickey, 0); 
     msgrcv(mqMinnieid, &myMinniemsg, sizeof(myMinniemsg), 0, 0); 
     printf(" Minnie received: %s\r\n", myMinniemsg.text); 
    } 

    return (void *) strlen(s); 
} 

int main (void) 
{ 
    /* 
     MUST create the target files first. 

     The return of ftok is -1 if file is missing. If BOTH files are missing, both ipc keys 
     will be -1, meaning all traffic shares the same port. Result: one queue will acquire 
     messages not intended for it! 
    */ 
    fMickeypointer = fopen(MQMICKEY, "a"); 
    if(fMickeypointer == NULL) 
    { 
     printf("Failed to create Mickey queue file.\n"); 

     /* system logging */ 
     setlogmask (LOG_UPTO (LOG_NOTICE)); 
     openlog ("MickeyDonald", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1); 
     syslog (LOG_NOTICE, "Failed to create Mickey queue file."); 
     closelog(); 
     exit(91); 
    } 

    fDonaldpointer = fopen(MQDONALD, "a"); 
    if(fDonaldpointer == NULL) 
    { 
     printf("Failed to create Donald queue file.\r\n"); 

     /* system logging */ 
     setlogmask (LOG_UPTO (LOG_NOTICE)); 
     openlog ("MickeyDonald", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1); 
     syslog (LOG_NOTICE, "Failed to create Donald queue file."); 
     closelog(); 
     exit(92); 
    } 

    ipcMickey = 0; 
    ipcDonald = 0; 

    /* make sure we assign DIFFERENT ipc values */ 
    while(ipcMickey == ipcDonald) 
    { 
     ipcMickey = ftok(MQMICKEY, 63); 
     if (-1==ipcMickey) 
     { 
      printf("ipcMickey does not exist.\r\n"); 

      /* system logging */ 
      setlogmask (LOG_UPTO (LOG_NOTICE)); 
      openlog ("MickeyDonald", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1); 
      syslog (LOG_NOTICE, "Failed to ftok Mickey file."); 
      closelog(); 
      exit(93); 
     } 

     ipcDonald = ftok(MQDONALD, 69); 
     if (-1==ipcDonald) 
     { 
      printf("ipcDonald does not exist.\r\n"); 

      /* system logging */ 
      setlogmask (LOG_UPTO (LOG_NOTICE)); 
      openlog ("MickeyDonald", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1); 
      syslog (LOG_NOTICE, "Failed to ftok Donald file."); 
      closelog(); 
      exit(94); 
     } 
    } 

    mqMickeyid = msgget(ipcMickey, IPC_CREAT | 0666); 
    mqDonaldid = msgget(ipcDonald, IPC_CREAT | 0666); 

    pthread_create(&t1, NULL, MICKEYthreadFunc, "Mickey sends\n"); 
    pthread_create(&t2, NULL, MINNIEthreadFunc, "Minnie replies\n"); 
    pthread_create(&t3, NULL, DONALDthreadFunc, "Donald sends\n"); 
    pthread_create(&t4, NULL, PLUTOthreadFunc, "Pluto replies\n"); 

    long exitcounter = 0; 
    while(exitcounter < 100000) 
    { 
     exitcounter +=1;   
     usleep(100); 
    } 
    exit(0); 
} 
+0

乾杯,很高興幫助:) – sehe 2011-04-13 14:48:55