这里回顾第5章,本章介绍了插叙:进程API。

书籍介绍:

学习资料:

参考资料:

第5章 插叙:进程API

内容回顾

关键概念

  • fork系统调用
  • wait系统调用
  • exec系统调用

作业(编码)

1

不改变$x$时值不变:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
    int x = 100;
    printf("origin x value is %d\n", x);

    int rc = fork();
    if (rc < 0) {
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
        // 子进程
        // x = 50;
        printf("x value in child process is %d, pid is %d\n", x, getpid());
    } else {
        // 父进程
        // x = 20;
        printf("x value in parent process is %d, pid is %d\n", x, getpid());
    }

    return 0;
}

运行:

╰─○ make p1 && ./p1
make: 'p1' is up to date.
origin x value is 100
x value in parent process is 100, pid is 471
x value in child process is 100, pid is 472

改变$x$时,子进程和父进程的值不同:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
    int x = 100;
    printf("origin x value is %d\n", x);

    int rc = fork();
    if (rc < 0) {
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
        // 子进程
        x = 50;
        printf("x value in child process is %d, pid is %d\n", x, getpid());
    } else {
        // 父进程
        x = 20;
        printf("x value in parent process is %d, pid is %d\n", x, getpid());
    }

    return 0;
}

运行:

╰─○ make p1 && ./p1
gcc -o p1 p1.c -Wall
origin x value is 100
x value in parent process is 20, pid is 528
x value in child process is 50, pid is 529

2

代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main() {
    int fd = open("p2.output", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);
    if (fd < 0) {
        fprintf(stderr, "open fail");
        exit(1);
    }
    
    int rc = fork();
    if (rc < 0) {
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
        // 子进程
        printf("fd in child process is %d, pid is %d\n", fd, getpid());
        for (int i = 0; i < 1000; i++) {
            write(fd, "child\n", 6);
        }
    } else {
        // 父进程
        printf("fd in parent process is %d, pid is %d\n", fd, getpid());
        for (int i = 0; i < 1000; i++) {
            write(fd, "parent\n", 7);
        }
    }
}

父子进程都可以访问fd,父进程和子进程会交替写入,即不会等待对方写入完成才进行自己的写入:

╰─○ make p2 && ./p2
gcc -o p2 p2.c -Wall
fd in parent process is 3, pid is 705
fd in child process is 3, pid is 706

p2.output:

parent
child
parent
child
parent
......

3

参考资料:

需要通过内存映射在不使用wait的条件达到目标:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>

int main() {
    int fd = open("p3.output", O_RDWR|O_CREAT|O_TRUNC, S_IRWXU);
    if (fd < 0) {
        fprintf(stderr, "open fail");
        exit(1);
    }
    // 使其可以自动删除
    unlink("p3.output");
    // 改变文件大小
    ftruncate(fd, 10);
    // 映射
    int *p = (int *)mmap(NULL, 10, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    if (p == MAP_FAILED) {
        fprintf(stderr, "mmap error");
        exit(1);
    }
    *p = 1;

    int rc = fork();
    if (rc < 0) {
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
        // 子进程
        printf("hello\n");
        *p = -1;
    } else {
        // 父进程等待p改变
        while ((*p) > 0) {

        }
        printf("goodbye\n");
    }

    return 0;
}

运行:

╰─○ make p3 && ./p3
gcc -o p3 p3.c -Wall
hello
goodbye

4

参考资料:

代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main() {
    int rc = fork();
    if (rc < 0) {
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
        // 子进程
        printf("child pid is %d\n", getpid());
        char *myargs[2];
        myargs[0] = strdup("/bin/ls");
        myargs[1] = NULL;
        // execv(myargs[0], myargs);
        // execvp(myargs[0], myargs);
        // execl(myargs[0], myargs[0], NULL);
        // execle(myargs[0], myargs[0], NULL, NULL);
        execlp(myargs[0], myargs[0], NULL);
    } else {
        // 父进程
        printf("parent pid is %d\n", getpid());
    }

    return 0;
}

运行:

╰─○ make p4 && ./p4
gcc -o p4 p4.c -Wall
parent pid is 956
child pid is 957
Makefile  p1  p1.c  p2  p2.c  p2.output  p3  p3.c  p4  p4.c  p5  p5.c  p6  p6.c  p7  p7.c  p7.output  p8  p8.c 

之所以有这么多变种,推测是为了完成不同情形下的需求。

5

代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main() {
    int rc = fork();
    if (rc < 0) {
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
        // 子进程
        printf("child process's pid is %d\n", getpid());
        int wc = wait(NULL);
        printf("(child process): return value of wait is %d\n", wc);
    } else {
        // 父进程
        int wc = wait(NULL);
        printf("parent process's pid is %d\n", getpid());
        printf("(parent process): return value of wait is %d\n", wc);
    }
}

运行:

╰─○ make p5 && ./p5
gcc -o p5 p5.c -Wall
child process's pid is 1176
(child process): return value of wait is -1
parent process's pid is 1175
(parent process): return value of wait is 1176

子进程返回-1,因为子进程没有子进程;父进程返回子进程pid。

6

需要传入pid参数:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main() {
    int rc = fork();
    if (rc < 0) {
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
        // 子进程
        printf("child process's pid is %d\n", getpid());
        int wc = waitpid(rc, NULL, WUNTRACED);
        printf("(child process): return value of wait is %d\n", wc);
    } else {
        // 父进程
        // 等待直到rc终止
        int wc = waitpid(rc, NULL, WUNTRACED);
        printf("parent process's pid is %d\n", getpid());
        printf("(parent process): return value of wait is %d\n", wc);
    }
}

运行:

╰─○ make p6 && ./p6 
gcc -o p6 p6.c -Wall
child process's pid is 1297
(child process): return value of wait is -1
parent process's pid is 1296
(parent process): return value of wait is 1297

7

没有输出,因为关闭了STDOUT_FILENO,但打开文件会得到结果。

代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/wait.h>

int main() {
    close(STDOUT_FILENO); 
    printf("hello, world\n");

    int fd = open("p7.output", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);
    if (fd < 0) {
        fprintf(stderr, "open fail");
        exit(1);
    }
    printf("hello, world\n");
    
    return 0;
}

运行:

╰─○ ./p7 

查看p7.out:

hello, world
hello, world

8

参考资料:

思路:

关闭子进程的写端,只读;关闭父进程的读端,只写。

代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/wait.h>

int main() {
    // I/O
    int fd[2];
    // message
    char *message = "test message\n";
    // 缓冲区
    char buf[1024];

    int result = pipe(fd);
    if (result < 0) {
        printf("pipe failed\n");
        exit(1);
    }

    int rc = fork();

    if (rc < 0) {
        fprintf(stderr, "fork failed\n");
        exit(1);
    } else if (rc == 0) {
        // 子进程
        // 关闭写端
        close(fd[1]);
        printf("child process pid is %d\n", getpid());
        // 读取父进程内容
        int len = read(fd[0], buf, sizeof(buf));
        if (len == 0) {
            printf("don't get message from parent\n");
        } else {
            // 输出结果
            write(STDOUT_FILENO, buf, len);
        }
        // 关闭读端
        close(fd[0]);
    } else {
        // 父进程
        // 关闭读端
        close(fd[0]);
        printf("parent process pid is %d\n", getpid());
        write(fd[1], message, strlen(message));
        // 关闭写端
        close(fd[1]);
    }
    
    return 0;
}

运行:

╰─○ make p8 && ./p8
gcc -o p8 p8.c -Wall
parent process pid is 1592
child process pid is 1593
test message