Skip to content

primes.c

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

TIP

函数原型

void sieve(int left_pipe_read_end);

int main(int argc, char *argv[])
{

TIP

创建第一个管道

    int p[2];
    if (pipe(p) < 0) {
        fprintf(2, "pipe failed\n");
        exit(1);
    }

    int pid = fork();

    if (pid < 0) {
        fprintf(2, "fork failed\n");
        exit(1);
    }

    if (pid == 0) {

TIP

子进程:启动筛选过程 关闭第一个管道的写端,因为它只需要从中读取

        close(p[1]);
        sieve(p[0]);
    } else {

TIP

父进程:初始数据生产者 关闭第一个管道的读端,因为它只需要向其中写入

        close(p[0]);

TIP

将数字 2 到 280 写入管道

        for (int i = 2; i <= 280; ++i) {
            if (write(p[1], &i, sizeof(i)) != sizeof(i)) {
                fprintf(2, "write error\n");
                exit(1);
            }
        }

TIP

写完后关闭写端,这样下游进程才能读到EOF

        close(p[1]);

TIP

等待子进程结束

        wait(0);
    }

    exit(0);
}

/**
 * @brief 递归的筛选函数
 * @param left_pipe_read_end 从左邻居进程接收数据的管道读端
 */
void sieve(int left_pipe_read_end) {
    int prime, num;
    

TIP

  1. 从左边管道读取第一个数,这个数必然是素数
    if (read(left_pipe_read_end, &prime, sizeof(prime)) <= 0) {

TIP

如果管道为空或已关闭,说明没有更多数字了,结束进程

        close(left_pipe_read_end);
        exit(0);
    }

TIP

  1. 打印这个素数
    printf("prime %d\n", prime);

TIP

  1. 创建一个新的管道,用于连接到右邻居
    int right_pipe[2];
    if (pipe(right_pipe) < 0) {
        fprintf(2, "pipe failed\n");
        exit(1);
    }

    int pid = fork();

    if (pid < 0) {
        fprintf(2, "fork failed\n");
        exit(1);
    }

    if (pid == 0) {

TIP

子进程(右邻居) a. 关闭它不需要的管道端口

        close(right_pipe[1]);
        close(left_pipe_read_end);

TIP

b. 递归调用sieve,处理下一阶段的筛选

        sieve(right_pipe[0]);
    } else {

TIP

当前进程(作为父进程) a. 关闭它不需要的管道端口

        close(right_pipe[0]);

TIP

b. 从左管道持续读取数字

        while (read(left_pipe_read_end, &num, sizeof(num)) > 0) {

TIP

c. 如果数字不是当前素数的倍数,就通过右管道传给下一个进程

            if (num % prime != 0) {
                if (write(right_pipe[1], &num, sizeof(num)) != sizeof(num)) {
                    fprintf(2, "write error\n");
                    exit(1);
                }
            }
        }

TIP

d. 所有数字都处理完毕,关闭剩下的管道端口

        close(left_pipe_read_end);
        close(right_pipe[1]);

TIP

e. 等待自己的子进程结束,以确保整个链条正确终止

        wait(0);
    }
}