perrynzhou

专注于系统组件研发

0%

fork之前的缓冲区问题

fork 系统调用

Description

  • fork() creates a new process by duplicating the calling process. The new process, referred to as the child, is an exact duplicate of the calling 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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    /*************************************************************************
    > File Name: fork0.c
    > Author:perrynzhou
    > Mail:perrynzhou@gmail.com
    > Created Time: Tue 11 Jun 2019 10:27:20 AM CST
    ************************************************************************/

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdbool.h>
    #include <ctype.h>
    #include <unistd.h>
    #include <getopt.h>
    void usage(char *s)
    {
    printf("%s -f {0 | 1}\n", s);
    printf(" 1 -- force flush all opened stream before fork\n");
    printf(" 0 -- not flush all opened stream before fork\n");
    }
    bool is_number(const char *s)
    {
    if (s == NULL)
    {
    return false;
    }
    while (*s != '\0')
    {
    if (isdigit(*s++) == 0)
    {
    return false;
    }
    }
    return true;
    }
    int main(int argc, char *argv[])
    {
    if (argc != 3)
    {
    usage(argv[0]);
    exit(0);
    }
    const char *cmd_parse_fmt = "f:";
    char ch;
    int force_flush_flag = 0;
    while ((ch = getopt(argc, argv, cmd_parse_fmt)) != -1)
    {
    switch (ch)
    {
    case 'f':
    if (!is_number(optarg))
    {
    force_flush_flag = -1;
    }
    else
    {
    force_flush_flag = atoi(optarg);
    }
    break;
    default:
    break;
    }
    }
    if (force_flush_flag > 1 || force_flush_flag < 0)
    {
    usage(argv[0]);
    exit(0);
    }
    printf("flush opened stream :%d\n", force_flush_flag);

    printf("[%ld]begin\n", getpid());
    pid_t pid;
    if (force_flush_flag == 1)
    {
    fflush(NULL);
    }
    if ((pid = fork()) < 0)
    {
    perror("fork");
    exit(0);
    }
    else if (pid == 0)
    {
    printf("[%ld] child process working\n", getpid());
    exit(0);
    }
    else
    {
    printf("[%ld] parent process working\n", getpid());
    }
    return 0;
    }

标准输出

  • 标准输出 红色标记的都是输出一个。

文件输出

  • 文件输出 f=1和f=0 结果完全不同,这是为什么?

标准输出和文件输出

  • 标准输入输出是行缓冲模式(\n,强制输出),在终端执行fork之前的printf,都是执行一次。
  • 文件输入输出是全缓冲模式(buffer到了指定的size,才会输出)
  • 当执行 test_fork -f 0重定向文件 时候,父进程执行fork之后,父子进程的地址空间是cow,没有任何改动的前提下,都是执行同一个地址空间。此时,父子进程执行的缓冲区都是一模一样,所以可以看到父子进程都会执行fork之前的printf.所以文件看到的是2个”flush opened stream :0”和”[92741]begin”.
  • 当f=1,重定向文件时候可以看到,父子进程仅仅执行一次 “flush opened stream :1”和”[92741]begin”.原因在于f=1的时候会在父进程fork之前执行fflush(NULL),此时执行这个函数目的在于强制flush所有的已经打开的stream.