子进程似乎在 while 循环中陷入睡眠

child process seems to get stuck in sleep in a while loop

我有一个 C 程序,它在循环中的某个点分叉一个子进程。子进程等待父进程完成它的工作(一些数值计算)。如果出现问题,父进程将中止,子进程应从分叉时的状态继续,并在进行一些修改后重试计算。否则,父进程保持运行ning,子进程应该被杀死。

父子进程之间的通信是通过内存映射文件进行的,只有1个字节作为表示父进程状态的字符。

内存映射是这样完成的

    char child_flag[]="W";
    
    fp1 = fopen( "child_interface.dat","wb");
    // the interface file has two bytes, but only one is meaningful to the program
    fwrite(child_flag, 1, sizeof(child_flag), fp1); 
    fclose(fp1);
    printf("child_interface.dat created\n");
    
    if(mmap_child_flag() ==0) {
        printf("memory map of parent-child interface successful.\n");
        fflush(stdout);
    }

子进程中的等待循环是这样的

child_pid = fork();                     
if (child_pid ==0) { /* child process, wait for parent process to finish*/

    mmap_child_flag();

    while(child_file[0]=='W' ){  //Child waits
        usleep(100000);
    }
    if(child_file[0]=='R'){ // run child process (as a new parent process)
        child_file[0]='W';
        goto label2;
    }
    if(child_file[0]=='K'){ //Kill child process
        exit(0);
    }
}

问题是子进程似乎卡在了 sleep while 循环中,即使父进程已将状态设置为 'K'(已在内存映射的文件中检查)。这段代码已经 运行 在几台基于 linux 的超级计算机上,并且行为似乎非常不一致。在某些平台上,它可以 运行 顺利,但在其他平台上,它会不断地卡在 while 循环中。有时,如果我在 usleep 调用后的 while 循环中添加一些语句,它就可以 运行 就好了。

但是,我不确定 sleep while 循环是否是这个问题的根本原因。我的猜测是因为这个进程除了检查内存中的一个字节外几乎无事可做,所以系统让它一直休眠并以某种方式“忘记”让它检查内存。 Linux系统能发生这种事吗?

这是执行实际映射的函数

/* Memory map for parent-child processes interface */
int mmap_child_flag()
{
    int fd_child;    
    struct stat st_child; 
    
    // open files
    if ((fd_child = open("child_interface.dat", O_RDWR)) == -1){
        perror("open child_interface.dat");
        exit(1);
    }
    // stat
    if (stat("child_interface.dat", &st_child) == -1){
        perror("stat of child_interface.dat");
        exit(1);
    }
    // map, child_file is global char array
    child_file = mmap(0, st_child.st_size, PROT_WRITE, MAP_SHARED, fd_child, 0);
    if (child_file == (char *)(-1)) {
        perror("mmap child_interface.dat");
        exit(1);
    }
    return 0;
}

共享内存区域有利于共享大量数据,但不利于进程间通信。原因是您无法收到有关某些内容已更改的通知,如果共享内存的其他用户死亡,您也不会收到通知。

要在两个进程之间进行通信,请使用pipe() if you need to create a one-way communication channel, or if you need bidirectional communication, use socketpair(). You can use poll()等待对方发送一些数据。如果另一端的进程终止,您也会收到通知。

您使用的是这样的循环:

while(child_file[0]=='W' ){  //Child waits
    usleep(100000);
}

这很糟糕,因为您平均浪费了 50 毫秒的时间,而这些时间本可以用来做一些有用的事情。除此之外,还有一个问题是编译器和 CPU 有时会改变写入内存的顺序。如果 child_file 中的数据多于开头的标志,那么这可能是个问题,除非您使用 atomics 或明确的障碍。

The problem is that the child process seems to get stuck in the sleep while loop, even when the parent process has set the status to 'K' (checked in the file that is memory mapped).

您的程序有几处奇怪的地方,其中之一就是您为此任务使用了共享内存。请参阅下文以获得更好的方法。

当前方法存在问题

但是,就目前的问题而言,您遇到了同步问题。映射内存的内容正在 child 进程范围之外更改,但您没有理由怀疑可能是这种情况。因此,编译器可以假定,如果等待循环条件在第一次求值时得到满足,那么它也会在每次后续求值时得到满足。

对于更复杂的交互,您可能需要设置一个 process-shared 互斥锁或类似的东西来保护对共享内存的访问,但为此,声明 child_file 可能就足够了作为指向 volatile char.

的指针

更好的方法

您希望 child 等待来自 parent 的一个或 two-byte 指令。您目前通过轮询共享内存段的内容来执行此操作,但正如您发现的那样,设置和使用起来很复杂。使用管道将所需信息从 parent 传送到 child:

会容易得多
  • 设置:声明一个数组。调用 pipe().
  • child 使用: child 对管道执行阻塞 read()
  • parent 使用:write() 准备好后将消息发送到管道,然后关闭它。或者干脆关闭它。

请注意,管道本身会提供足够的同步,并且不需要等待循环。另请注意,child 可以检测到 parent 没有发送任何消息就死掉的情况,您的共享内存方法不支持这种情况。