关于fork多进程中printf的问题

文章讨论了在C语言中,程序使用fork多进程时printf函数的行为差异,重点在于printf输出到stdout的行缓冲机制。程序1和2展示了不同情况下printf输出数量的变化,程序3和4解释了换行符对缓冲区的影响,以及程序5和6对比了带和不带换行符输出的星号数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于fork多进程中printf的问题

程序1

int main(int argc, char **argv)
{
    int remaining = 4;
    int child_pid;
    while (remaining > 0)
    {
        child_pid = fork();
        if (child_pid == 0) break;
        remaining--;
    }
    printf("P");
    return 0;
}

上面的程序将输出:PPPPP(5个P)

程序执行过程:

  1. remain = 4,

    父进程(P0):进入while循环,创建子进程(P1),remain--

    子进程(P1):执行语句if (child_pid == 0) break,退出循环,打印P

  2. remain = 3,

    父进程(P0):进入while循环,创建子进程(P2),remain--

    子进程(P2):执行语句if (child_pid == 0) break;,退出循环,打印P

  3. remain = 2,

    父进程(P0):进入while循环,创建子进程(P3),remain--

    子进程(P3):执行语句if (child_pid == 0) break;,退出循环,打印P

  4. remain = 1,

    父进程(P0):进入while循环,创建子进程(P4),remain--

    子进程(P4):执行语句if (child_pid == 0) break;,退出循环,打印P

  5. remain = 0,

    父进程(P0):退出while循环,打印P


程序2

int main(int argc, char **argv)
{
    int remaining = 4;
    int child_pid;
    while (remaining > 0)
    {
        child_pid = fork();
        remaining--;
    }
    printf("P");
    return 0;
}

上面的程序将输出:PPPPPPPPPPPPPPPP(16个P)

程序执行过程:

  1. remain = 4,

    父进程(P0):进入while循环,创建子进程(P1),

    子进程(P1):

    remain--

  2. remain = 3,

    父进程(P0):进入while循环,创建子进程(P2),

    子进程(P1):进入while循环,创建子进程(P3)

    子进程(P2),子进程(P3):

    remain--

  3. remain = 2,

    父进程(P0):进入while循环,创建子进程(P4),

    子进程(P1):进入while循环,创建子进程(P5),

    子进程(P2):进入while循环,创建子进程(P6),

    子进程(P3):进入while循环,创建子进程(P7),

    子进程(P4),子进程(P5),子进程(P6),子进程(P7):

    remain--

  4. remain = 1,

    父进程(P0):进入while循环,创建子进程(P8),

    子进程(P1):进入while循环,创建子进程(P9),

    子进程(P2):进入while循环,创建子进程(P10),

    子进程(P3):进入while循环,创建子进程(P11),

    子进程(P4):进入while循环,创建子进程(P12),

    子进程(P5):进入while循环,创建子进程(P13),

    子进程(P6):进入while循环,创建子进程(P14),

    子进程(P3):进入while循环,创建子进程(P15),

    子进程(P8),子进程(P9),子进程(P10),子进程(P11),子进程(P12),子进程(P13),子进程(P14),子进程(P15):

    remain--

  5. remain = 0,

    父进程(P0),子进程(P1),子进程(P2),子进程(P3),子进程(P4),子进程(P5),子进程(P6),子进程(P7),子进程(P8),子进程(P9),子进程(P10),子进程(P11),子进程(P12),子进程(P13),子进程(P14),子进程(P15):退出while循环,打印P


程序3

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

int main(int argc, char **argv)
{
    int remaining = 4;
    int child_pid;
    printf("P");				// printf在此处
    while (remaining > 0)
    {
        child_pid = fork();
        if (child_pid == 0) break;
        remaining--;
    }
    return 0;
}

程序4

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

int main(int argc, char **argv)
{
    int remaining = 4;
    int child_pid;
    printf("P\n");				// printf在此处,且在"P"后加上换行符\n
    while (remaining > 0)
    {
        child_pid = fork();
        if (child_pid == 0) break;
        remaining--;
    }
    return 0;
}

程序3输出:PPPPP(5个P)

程序4输出:P(1个P)

原因分析:

  1. printf是向标准输出(stdout)输出想要打印的东西,而默认的stdout是行缓存line buffering的,所以当打印的东西不够一行的时候(没有\n),这些信息是保存在stdoutbuffer中。

  2. fork()时,子进程会复制父进程的所有资源,做到和父进程一样,也包括了缓冲区。如果在fork()前刷新了缓冲区,也就是在printf中使用换行符\n,那么在fork()前,信息就会被打印出来,fork()之后父进程和子进程stdoutbuffer中都是空的了。

同理,如果注释掉if (child_pid == 0) break;,程序4将输出16个P。


程序5

int main()
{
    for (int i = 0; i < 3; i++)
    {
        fork();
        printf("*\n");
    }
    return 0;
}

程序6

int main()
{
    for (int i = 0; i < 3; i++)
    {
        fork();
        printf("*");
    }
    return 0;
}

程序5输出:(14个*

程序6输出:************************(24个*

原因分析:

请添加图片描述

  1. 程序5中每次printf都会刷新缓冲区,具体示意图如下:

请添加图片描述

  1. 程序6中的printf没有刷新缓冲区,fork时子进程会复制父进程的缓冲区

请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值