即使我在父进程中使用 sleep() ,我的子进程也是最后执行的

Even when I use sleep() in parent process my child process is executed last

有人可以解释为什么即使我让父进程休眠,父进程总是在子进程中的 while 循环开始之前就完全完成。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char **argv) {
    int i = 10000, pid = fork();
    if (pid == 0) {
        while(i > 50) {
            if(i%100==0) {
                sleep(20);
            }
            printf("Child: %d\n", i);
            i--;
        }
    } else {
        while(i < 15000) {
            if(i%50==0) {
                sleep(50);
            }
            printf("Parent: %d\n", i);
            i++;
        }
    }
    exit(EXIT_SUCCESS);
}

输出如下所示:

Parent: ..
Parent: ..
Parent: ..

直到Parent完成,然后Child也一样。

会不会是我在单核上测试的原因CPU?如果我有一个多核设置,结果会改变吗? sleep(50) 绝对有效——因为脚本完成需要很长时间——为什么 CPU 不切换进程?是否存在例如在 while 循环中进程具有 "more" 或对 CPU 的独占权的情况?

感谢您的帮助。 :)

Are there situations like for example during a while loop where the process has "more" or exclusive rights to the CPU?

好吧,这还没有定义,但这太疯狂了。

我无法重现你的问题。将睡眠时间减少到 25 秒(这样我就不必永远等待),child 首先在这里得到解锁,正如人们所期望的那样. (Debian 8,Linux AMD64 上的 3.16.1-ck1 [BFS 调度程序,非标准])

我会说您的调度程序表现得很奇怪,可能 被破坏了。但话虽这么说,依赖 any 调度程序的特定行为绝不是一个好主意。总是假设它是坏的和疯狂的——如果你的代码允许一个特定的执行顺序,那么是一个足够疯狂的调度程序来选择它。(*)

因此,使用同步原语(semaphoresmutexes 例如有 shared 版本用于不同的进程——你也可以只使用 pipes 在某些情况下)每当您需要依赖某些同步时。

编辑:添加两个用于同步进程的示例。

第一个版本 (ab) 使用 pipes:

#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char **argv) {
    int i = 10000;
    int parent_done[2];
    int child_done[2];
    char dummy[1] = { 0 };
    int pid;

    pipe(parent_done);
    pipe(child_done);

    /* stdio buffering would lead to intermingled output */
    setvbuf(stdout, 0, _IONBF, 0);

    pid = fork();

    if (pid == 0) {
        close(parent_done[1]);
        close(child_done[0]);

        while(i > 50) {
            if(i%100==0) {
                if (i < 10000) write(child_done[1], dummy, 1);
                read(parent_done[0], dummy, 1);
            }
            printf("Child: %d\n", i);
            i--;
        }
    } else {
        close(parent_done[0]);
        close(child_done[1]);

        while(i < 15000) {
            if(i%50==0) {
                write(parent_done[1], dummy, 1);
                read(child_done[0], dummy, 1);
            }
            printf("Parent: %d\n", i);
            i++;
        }
    }
    exit(EXIT_SUCCESS);
}

然后使用 POSIX 信号量(恕我直言,这更干净,因为信号量 意味着 用于同步):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <unistd.h>

struct semaphores
{
    sem_t child_done;
    sem_t parent_done;
};

int main(int argc, char **argv) {
    int i = 10000;
    int pid;

    /* map shared memory for the semaphores */
    struct semaphores *sems = mmap(0, sizeof(*sems), PROT_READ|PROT_WRITE,
            MAP_SHARED|MAP_ANONYMOUS, -1, 0);

    /* initialize both semaphores as "shared" and with an initial count
     * of 0 */
    sem_init(&sems->parent_done, 1, 0);
    sem_init(&sems->child_done, 1, 0);

    /* stdio buffering would lead to intermingled output */
    setvbuf(stdout, 0, _IONBF, 0);

    pid = fork();

    if (pid == 0) {
        while(i > 50) {
            if(i%100==0) {
                if (i < 10000) sem_post(&sems->child_done);
                sem_wait(&sems->parent_done);
            }
            printf("Child: %d\n", i);
            i--;
        }
        sem_post(&sems->child_done);
    } else {
        while(i < 15000) {
            if(i%50==0) {
                sem_post(&sems->parent_done);
                sem_wait(&sems->child_done);
            }
            printf("Parent: %d\n", i);
            i++;
        }
        sem_post(&sems->parent_done);
    }
    exit(EXIT_SUCCESS);
}

Windows 对信号量有不同的 API,参见 Semaphore Objects on MSDN

(*) edit2 适合这里:在创建示例时,我注意到 stdio 缓冲妨碍了睡眠。因此,也许它甚至不是您的调度程序表现不佳,而只是 stdio 的实现,在 when 上具有 very 不可预测的行为以刷新缓冲区。当然,这只是胡乱猜测。您必须知道的是:C 中的所有 FILE 句柄都由 C 库的 stdio 部分缓冲。这包括预定义的 stdinstdoutstderr 句柄。结果是您在输出中看到的内容不一定反映不同线程或进程创建该输出的顺序。当然,除非您像我的示例片段中那样完全禁用缓冲。