【问题标题】:Message queues : Bug with receiving消息队列:接收错误
【发布时间】:2013-12-06 14:33:43
【问题描述】:

我正在编写一个使用消息队列的软件。 我有一个问题:

主进程创建 16 个儿子(使用 fork),每个儿子为下一个儿子写一条消息。然后,他们正在等待接收他们的消息。 (儿子“0”向儿子“1”发送消息,...,儿子“15”向儿子“0”发送消息)。

它在大多数情况下运行良好,但有时会发生一些奇怪的事情......一个进程从未收到它的消息,尽管它是由相应的儿子发送的!我会说它在 10 次成功后发生一次。

我已经能够写出一段有 bug 的代码:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

struct buf
{
    long mtype;
    int data[32];
};

int main(int arc, char** argv)
{
    int son = 0;
    int pid = 0;
    struct buf msgbuf;

    key_t key;
    key = ftok(argv[0], 'O');

    int qid = msgget(key, IPC_CREAT | 0666);
    if(qid < 0)
    {
        printf("Error\n");
        return -1;
    }

    //Creates 16 sons
    for(int i = 0; i < 16; i++)
    {
        pid = i;
        son = fork();
        if(son == 0)
            break;
    }

    if(son == 0)
    {
        msgbuf.mtype = ((pid + 1) % 16) + 1;
        for(int i = 0; i < 32; i++)
            msgbuf.data[i] = pid;
        printf("Writing %d\n", ((pid + 1) % 16) + 1);
        msgsnd(qid, &msgbuf, 32 * sizeof(int), IPC_NOWAIT);
        printf("Waiting for %d\n", pid + 1);
        msgrcv(qid, &msgbuf, 32 * sizeof(int), pid + 1, 0);
        printf("Got %d\n", (int)msgbuf.mtype);
    }

    sleep(3);
    printf("----- END -----\n");

    msgctl(qid, IPC_RMID, NULL);

    return 0;
}

所以,预期的行为是这样的:

Writing 2
Writing 3
Waiting for 1
Waiting for 2
Got 2
Writing 4
Waiting for 3
Got 3
Writing 5
Waiting for 4
Got 4
Writing 6
Waiting for 5
Got 5
Writing 7
Waiting for 6
Got 6
Writing 8
Waiting for 7
Got 7
Writing 9
Waiting for 8
Got 8
Writing 10
Waiting for 9
Got 9
Writing 11
Waiting for 10
Got 10
Writing 12
Waiting for 11
Got 11
Writing 13
Waiting for 12
Got 12
Writing 14
Waiting for 13
Got 13
Writing 15
Waiting for 14
Got 14
Writing 16
Waiting for 15
Got 15
Writing 1
Waiting for 16
Got 16
Got 1
----- END -----
----- END -----
----- END -----
----- END -----
----- END -----
----- END -----
----- END -----
----- END -----
----- END -----
----- END -----
----- END -----
----- END -----
----- END -----
----- END -----
----- END -----
----- END -----
----- END -----

但有时,我有类似的东西:

Writing 2
Writing 3
Waiting for 1
Waiting for 2
Got 2
Writing 4
Waiting for 3
Got 3
Writing 5
Waiting for 4
Got 4
Writing 6
Waiting for 5
Got 5
Writing 7
Waiting for 6
Got 6
Writing 9
Waiting for 8
Writing 8
Waiting for 7
Got 7
Got 8
Writing 10
Waiting for 9
Got 9
Writing 11
Waiting for 10
Got 10
Writing 12
Waiting for 11
Got 11
Writing 13
Writing 14
Waiting for 12
Waiting for 13
Got 12
Writing 15
Waiting for 14
Got 14
Writing 16
Waiting for 15
Got 15
Writing 1
Waiting for 16
Got 16
Got 1
----- END -----
----- END -----
----- END -----
----- END -----
----- END -----
Got 14
----- END -----
----- END -----
----- END -----
----- END -----
----- END -----
----- END -----
----- END -----
----- END -----
----- END -----
----- END -----
----- END -----

如您所见,消息“14”从未收到,3 秒后,代码释放队列导致虚假“Got 14”。

在我的真实代码中,我使用信号量来确保程序只有在每个人都收到他的消息后才退出。这意味着发生了死锁。事实上,消息永远不会被接收,信号量永远不会“解锁”。所以这不是因为睡眠时间太短或类似的东西。这也不是因为我后来也删除了队列。

但不要忘记,大多数时候,这没关系!我不明白为什么有时儿子永远不会收到他的信息。

你能帮帮我吗?

【问题讨论】:

    标签: c fork message-queue


    【解决方案1】:

    我终于知道发生了什么。

    当我写入消息队列时,我执行“ msgsnd(qid, &msgbuf, 32 * sizeof(int), IPC_NOWAIT); ”,问题似乎与“IPC_NOWAIT”有关,似乎有时队列已满并且该消息并未实际写入(由于标志“IPC_NOWAIT”而被跳过)。

    没有这个标志,这没关系。

    【讨论】:

      【解决方案2】:

      首先,关于术语的友好迂腐吹毛求疵:分叉过程通常由更中性的“孩子”而不是“儿子”来指代。 :-)

      接下来,您是否有意在所有工作子进程退出前将其延迟 3 秒?因为这就是代码当前所做的。所有进程都必须在退出之前执行sleep(3)。在测试您的代码时,我将该块重写为:

      if (son > 0)
      {
          sleep(1);
          printf("main program exiting\n");
      }
      else
      {
          printf("(%d) ----- END -----\n", pid);
      }
      

      我认为您误解了第二个输出块中的结果。我推测您的输出可能存在一些计时/缓冲问题,当多个进程尝试同时写入标准输出时可能会发生这种情况。

      我能问一下您希望用这个消息队列完成什么吗?您似乎正在尝试使用队列来安排工作进程的装配线,而这些数据结构通常不是这样使用的。

      【讨论】:

        猜你喜欢
        • 2012-01-19
        • 1970-01-01
        • 2014-05-04
        • 2010-09-19
        • 2012-11-16
        • 2013-07-18
        • 2011-07-10
        • 2011-07-15
        • 2011-06-14
        相关资源
        最近更新 更多