【问题标题】:Messages behavior after fork()fork() 之后的消息行为
【发布时间】:2021-12-30 14:30:45
【问题描述】:

我正在通过消息学习进程间通信。

在下面的代码sn-p中,我申请fork(),在父子进程之间发送消息。

我期望“1 - 2 - 3 - 4”控制台输出。但是,我得到了“1 - 2”,在此之后,程序似乎在打印“3”之前永远卡在msgrcv 行上。谁能告诉代码有什么问题?

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define BUF_SIZE 16
#define MSG_KEY1 75
#define MSG_KEY2 76

struct msgform
{
    long     mtype;
    char     mbuf[BUF_SIZE];
    long     mind;
} msg;

struct msgping
{
    long     mtype;
    long     ping ;
} msgPing;

int main() {

    if(fork() == 0) {
    // child process
    int msgid1;
    int msgid2;
    msgid1 = msgget(MSG_KEY1, 0666 | IPC_CREAT);
    msgid2 = msgget(MSG_KEY2, 0666 | IPC_CREAT);
    msg.mtype = 1;
    msgPing.mtype = 1;

    printf("1 - started, sending msgPing\n");

    msgsnd(msgid1, &msgPing, sizeof(msgPing), 0);

    msgrcv(msgid2, &msg, sizeof(msg), 1, 0);

    printf("3 - msg received, sending msgPing\n");

    msgsnd(msgid1, &msgPing, sizeof(msgPing), 0);

    msgctl(msgid1, IPC_RMID, 0);
    msgctl(msgid2, IPC_RMID, 0);

    return 0;
    }

    //parent process
    sleep(1);
    int msgid1;
    int msgid2;
    msgid1 = msgget(MSG_KEY1, 0666 | IPC_CREAT);
    msgid2 = msgget(MSG_KEY2, 0666 | IPC_CREAT);

    msgrcv(msgid1, &msgPing, sizeof(msgPing), 1, 0);

    printf("2 - msgPing received, sending msg\n");

    msgsnd(msgid2, &msg, sizeof(msg), 0);

    msgrcv(msgid1, &msgPing, sizeof(msgPing), 1, 0);

    printf("4 - msgPing received, finished\n");

    return 0;
}

【问题讨论】:

    标签: c unix ipc message-queue


    【解决方案1】:

    问题在于这里的消息类型初始化:

    msg.mtype = 1;
    msgPing.mtype = 1;
    

    这些值只会在子进程中初始化。当父母尝试在msg 中发送消息时,mtype 尚未设置,因此孩子无休止地等待mtype 1 的消息,但始终没有到达。

    fork() 之前设置这两个值,或者将它们设置在父级和子级中,都可以做到这一点:

    $ ./msg
    1 - started, sending msgPing
    3 - msg received, sending msgPing
    2 - msgPing received, sending msg
    4 - msgPing received, finished
    

    大约一秒钟。

    【讨论】:

      【解决方案2】:

      您只在子级中设置了mtype,而在父级中没有

      此外,子级中的msgctl 调用与父级中的最终msgrcv 进行竞赛。也就是说,孩子可能会在父母有机会完成其最终的msgrcv 之前完成msgctl 调用。

      而且,我认为,孩子的 return 0; 应该是 exit(0); 以防止落入父代码。


      在下面的重构代码中,我使用了cpp 条件来表示旧/损坏与新/固定代码:

      #if 0
      // old code
      #else
      // new code
      #endif
      
      #if 1
      // new code
      #endif
      

      这是重构后的代码:

      #include <stdio.h>
      #include <stdlib.h>
      #include <unistd.h>
      #include <sys/ipc.h>
      #include <sys/msg.h>
      #if 1
      #include <errno.h>
      #include <string.h>
      #include <sys/wait.h>
      #endif
      
      #define BUF_SIZE 16
      #define MSG_KEY1 75
      #define MSG_KEY2 76
      
      struct msgform {
          long mtype;
          char mbuf[BUF_SIZE];
          long mind;
      } msg;
      
      struct msgping {
          long mtype;
          long ping;
      } msgPing;
      
      #define ONERR(_fd) \
          do { \
              if (_fd >= 0) \
                  break; \
              printf("msgget error " #_fd " at line %d cldpid=%d -- %s\n", \
                  __LINE__,cldpid,strerror(errno)); \
              exit(1); \
          } while (0)
      
      int
      main()
      {
      
      #if 0
          if (fork() == 0) {
      #else
          pid_t cldpid = fork();
          if (cldpid == 0) {
      #endif
              // child process
              int msgid1;
              int msgid2;
      
              msgid1 = msgget(MSG_KEY1, 0666 | IPC_CREAT);
              ONERR(msgid1);
              msgid2 = msgget(MSG_KEY2, 0666 | IPC_CREAT);
              ONERR(msgid2);
              msg.mtype = 1;
              msgPing.mtype = 1;
      
              printf("1 - started, sending msgPing\n");
      
              msgsnd(msgid1, &msgPing, sizeof(msgPing), 0);
      
              msgrcv(msgid2, &msg, sizeof(msg), 1, 0);
              printf("3 - msg received, sending msgPing\n");
      
              msgsnd(msgid1, &msgPing, sizeof(msgPing), 0);
      
      #if 0
              msgctl(msgid1, IPC_RMID, 0);
              msgctl(msgid2, IPC_RMID, 0);
      #endif
      
      #if 0
              return 0;
      #else
              exit(0);
      #endif
          }
      
          // parent process
          sleep(1);
          int msgid1;
          int msgid2;
      
          msgid1 = msgget(MSG_KEY1, 0666 | IPC_CREAT);
          ONERR(msgid1);
          msgid2 = msgget(MSG_KEY2, 0666 | IPC_CREAT);
          ONERR(msgid2);
      
      // NOTE/FIX: the parent needs to set this as well as the child
      #if 1
          msg.mtype = 1;
          msgPing.mtype = 1;
      #endif
      
          msgrcv(msgid1, &msgPing, sizeof(msgPing), 1, 0);
      
          printf("2 - msgPing received, sending msg\n");
      
          msgsnd(msgid2, &msg, sizeof(msg), 0);
      
          msgrcv(msgid1, &msgPing, sizeof(msgPing), 1, 0);
      
          printf("4 - msgPing received, finished\n");
      
      #if 1
          waitpid(cldpid,NULL,0);
      
          msgctl(msgid1, IPC_RMID, 0);
          msgctl(msgid2, IPC_RMID, 0);
      #endif
      
          return 0;
      }
      

      当我为实时、关键任务、生产代码完成msgsnd/msgrcv 后,我在两个方向都使用了一个单个 msgid,使用了一个不同 mtype 值(例如,mtype = 1 用于父级,mtype = 2 用于 ping)。

      这是一个进一步清理和简化的版本:

      #include <stdio.h>
      #include <stdlib.h>
      #include <unistd.h>
      #include <sys/ipc.h>
      #include <sys/msg.h>
      #if 1
      #include <sys/wait.h>
      #endif
      
      #define BUF_SIZE 16
      #define MSG_KEY1 75
      #define MSG_KEY2 76
      
      struct msgform {
          long mtype;
          char mbuf[BUF_SIZE];
          long mind;
      } msg;
      
      struct msgping {
          long mtype;
          long ping;
      } msgPing;
      
      enum {
          MSGTYPE = 1,
          MSGPING = 2,
      };
      
      int
      main(void)
      {
      
          int msgid = msgget(MSG_KEY1, 0666 | IPC_CREAT);
      
          pid_t cldpid = fork();
      
          msg.mtype = MSGTYPE;
          msgPing.mtype = MSGPING;
      
          if (cldpid == 0) {
              sleep(1);
      
              printf("1 - started, sending msgPing\n");
              msgsnd(msgid, &msgPing, sizeof(msgPing), 0);
      
              msgrcv(msgid, &msg, sizeof(msg), MSGTYPE, 0);
      
              printf("3 - msg received, sending msgPing\n");
              msgsnd(msgid, &msgPing, sizeof(msgPing), 0);
      
              exit(0);
          }
      
          // parent process
          sleep(1);
      
          msgrcv(msgid, &msgPing, sizeof(msgPing), MSGPING, 0);
          printf("2 - msgPing received, sending msg\n");
      
          msgsnd(msgid, &msg, sizeof(msg), 0);
      
          msgrcv(msgid, &msgPing, sizeof(msgPing), MSGPING, 0);
          printf("4 - msgPing received, finished\n");
      
          waitpid(cldpid,NULL,0);
          msgctl(msgid, IPC_RMID, 0);
      
          return 0;
      }
      

      【讨论】:

        猜你喜欢
        • 2011-01-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-01
        • 2018-12-13
        相关资源
        最近更新 更多