操作系统导论-进程API

本文最后更新于:2022年3月23日 上午

第五章:进程API

  1. 子进程和父进程的变量x的值内容相互独立。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <fcntl.h>
    #include <sys/wait.h>

    int main(int argc, char *argv[])
    {
    int x = 100;
    int rc = fork();
    if (rc < 0)
    {
    printf("fork failed\n");
    exit(1);
    }
    else if (rc == 0)
    {
    // child process
    printf("Child: %d\n", x);
    // 子进程变量增加100
    x += 100;
    printf("Child + 100: %d\n", x);
    }
    else
    {
    // 父进程在子进程执行完毕后执行
    int wc = wait(NULL);
    // parent process
    printf("Parent: %d\n", x);
    // 父进程变量增加100
    x += 100;
    printf("Parent + 100: %d\n", x);
    }
    // 分别再次输入最后的值
    printf("Final Address: %d\n", x);
    return 0;
    }

    测试结果如下:

    1
    2
    3
    4
    5
    6
    Child: 100
    Child + 100: 200
    Final Address: 200
    Parent: 100
    Parent + 100: 200
    Final Address: 200
  2. 同时打开文件p4.output并且分别写入Child processParent Process,可以正常并发写入。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    #include <stdio.h>
    # include <stdlib.h>
    # include <unistd.h>
    # include <fcntl.h>

    int main()
    {
    // close stdout and open output file
    close(STDOUT_FILENO);
    open("./p4.output", O_CREAT | O_WRONLY | O_TRUNC, 0700);

    int rc = fork();
    if (rc < 0)
    {
    fprintf(stderr, "fork failed\n");
    exit(1);
    }
    else if (rc == 0)
    {
    printf("Child process\n");
    }
    else
    {
    printf("Parent process\n");
    }
    return 0;
    }

    结果(p4.output文件)

    1
    2
    Child process
    Parent process

    或(并发输出的先后顺序不同)

    1
    2
    Parent process
    Child process
  3. 题目要求的是在不适用wait()函数下实现子进程和父进程的先后,通过vfork()函数可以实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    # include <stdio.h>
    # include <stdlib.h>
    # include <unistd.h>
    # include <fcntl.h>

    int main()
    {
    // close stdout and open output file
    close(STDOUT_FILENO);
    open("./p4.output", O_CREAT | O_WRONLY | O_TRUNC, 0700);
    int rc = vfork();
    if (rc < 0)
    {
    fprintf(stderr, "fork failed\n");
    exit(1);
    }
    else if (rc == 0)
    {
    printf("Child process\n");
    }
    else
    {
    printf("Parent process\n");
    }
    return 0;
    }

    数据结果可以参考题目2的第一个情况。不过这里插入Stack Overflow上书原作者的话作为备注:

    Without calling wait() is hard, and not really the main point. What you did – learning about signals on your own – is a good sign, showing you will seek out deeper knowledge. Good for you!

    Later, you’ll be able to use a shared memory segment, and either condition variables or semaphores, to solve this problem.

  4. 针对不同的调用和变种,写了一篇详细的博客进行解析:区分不同exec()形式

  5. 在父进程使用wait(),等待子进程完成后,wait()会返回子进程的pid

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    int main()
    {
    int rc = fork();
    if (rc < 0)
    {
    cout << "fork failed" << endl;
    exit(1);
    }
    else if (rc == 0)
    {
    printf("I am child process %d\n", getpid());
    }
    else
    {
    int ws = wait(NULL);
    printf("I am parent process %d\n", getpid());
    printf("Return Value of wait() is %d\n", ws);
    }
    return 0;
    }

    运行结果

    1
    2
    3
    I am child process 4878
    I am parent process 4873
    Return Value of wait() is 4878

    在子进程中使用wait()则会返回-1,并且在失败后依旧会执行当前子进程

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    int main()
    {
    int rc = fork();
    if (rc < 0)
    {
    cout << "fork failed" << endl;
    exit(1);
    }
    else if (rc == 0)
    {
    int fake_ws = wait(NULL);
    printf("I am child process %d\n", getpid());
    printf("fake_ws = %d\n", fake_ws);
    printf("Test\n");
    }
    else
    {
    int ws = wait(NULL);
    printf("I am parent process %d\n", getpid());
    printf("Return Value of wait() is %d\n", ws);
    }
    return 0;
    }

    运行结果

    1
    2
    3
    4
    5
    I am child process 5011
    fake_ws = -1
    Test
    I am parent process 5006
    Return Value of wait() is 5011
  6. 针对waitpid()的使用,总结了笔记:waitpid()调用

  7. 子进程中关闭标准输出,父进程输出子进程和本身pid

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    int main()
    {
    int rc = fork();
    if (rc < 0)
    {
    printf("fork failed\n");
    exit(1);
    }
    else if (rc == 0)
    {
    close(STDOUT_FILENO);
    printf("Hello from child\n");
    }
    else
    {
    int ws = wait(NULL);
    printf("Hello from parent, child exited with status %d\n", ws);
    }
    return 0;
    }

    运行结果,子进程中的输出被关闭,只有父进程输出。

    1
    Hello from parent, child exited with status 1326
  8. 通过pipe()管道进行传输数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    int main()
    {
    int fd[2]; // 创建两个文件标示符
    int rc_pipe = pipe(fd); // 创建管道

    char msg[] = "Hello_World!"; // 用于输出的字符串
    char buff[100]; // 用于输出的缓冲区

    int rc_1 = fork(); // 创建第一个进程

    if (rc_1 == 0)
    {
    for (int i = 0; i < 5; i++)
    {
    printf("Child Process 1: %d\n", i);
    sleep(1);
    }
    close(fd[0]); // 为防止文件读写出现问题,关闭读取端,只允许写入
    dup2(fd[1], STDOUT_FILENO); // 将标准输出重定向到管道的写入端
    printf("%s", msg); // 输出字符串到标准输出,然后重定向至管道的写入端
    close(fd[1]); // 关闭管道的写入端
    exit(0); // 结束当前进程
    }

    int rc_2 = fork();

    if (rc_2 == 0)
    {
    // 即使通过sleep()让第二个进程等待,由于第一个进程的输入管道没有内容,所以第二个进程的scanf会等待至第一个进程将数据写入缓冲区
    for (int i = 0; i < 3; i++)
    {
    printf("Child Process 2: %d\n", i);
    sleep(1);
    }
    close(fd[1]); // 为防止文件读写出现问题,关闭写入端,只允许读取
    dup2(fd[0], STDIN_FILENO); // 将标准输入重定向到管道的读取端
    scanf("%s", buff); // 从标准输入读取字符串,然后重定向至管道的读取端
    close(fd[0]); // 关闭管道的读取端
    printf("%s", buff); // 输出由管道传输的字符串
    exit(0); // 结束当前进程
    }
    return 0;
    }

    输出结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    Child Process 1: 0
    Child Process 2: 0
    Child Process 2: 1
    Child Process 1: 1
    Child Process 2: 2
    Child Process 1: 2
    Child Process 1: 3 # 从这个时候开始,rc_2在等待rc_1通过管道传入"Hello World!"
    Child Process 1: 4
    Hello_World! # rc_2通过管道读取到了内容,进行输出