printf的异常后" fork()的" [英] printf anomaly after "fork()"

查看:105
本文介绍了printf的异常后" fork()的"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

操作系统:Linux,语言:纯C

OS: Linux, Language: pure C

我在一个特殊的情况下,在UNIX下学习通用C progpramming和C编程前进:D所以,我检测到的printf()函数的一个奇怪的(对我来说)的行为使用后叉()呼叫。让我们来看看简单的测试程序:

I'm moving forward in learning C progpramming in general, and C programming under UNIX in a special case :D So, I detected a strange (as for me) behaviour of the printf() function after using a fork() call. Let's take a look at simple test program:

#include <stdio.h>
#include <system.h>

int main()
{
    int pid;
    printf( "Hello, my pid is %d", getpid() );

    pid = fork();
    if( pid == 0 )
    {
            printf( "\nI was forked! :D" );
            sleep( 3 );
    }
    else
    {
            waitpid( pid, NULL, 0 );
            printf( "\n%d was forked!", pid );
    }
    return 0;
}

在此情况下,输出如下:

In this case the output looks like:

Hello, my pid is 1111
I was forked! :DHello, my pid is 1111
2222 was forked!

为什么第二个你好字符串中的孩子的输出发生?是的,这正是印有父母的开始,与父进程的PID。

Why the second "Hello" string occured in the child's output? Yes, it is exactly what the parent printed on it's start, with the parent's pid.

但是!如果我们把'\\ n字符每个字符串的结尾,我们得到了预期的输出:

But! If we place '\n' character in the end of each string we got the expected output:

#include <stdio.h>
#include <system.h>

int main()
{
    int pid;
    printf( "Hello, my pid is %d\n", getpid() ); // SIC!!

    pid = fork();
    if( pid == 0 )
    {
            printf( "I was forked! :D" ); //removed the '\n', no matter
            sleep( 3 );
    }
    else
    {
            waitpid( pid, NULL, 0 );
            printf( "\n%d was forked!", pid );
    }
    return 0;
}

和输出如下:

Hello, my pid is 1111
I was forked! :D
2222 was forked!

为什么会发生?它是......嗯......正确的行为?或者,它是一种对错误?

Why does it happen? Is it ... ummm ... correct behaviour? Or it's a kind of the 'bug'?

推荐答案

我注意到,&LT;&system.h中GT; 是一个非标准的头;我用它取代&LT; unistd.h中方式&gt; 和code编译干净

I note that <system.h> is a non-standard header; I replaced it with <unistd.h> and the code compiled cleanly.

当你的程序的输出是要一个终端(屏),则行缓冲。当你的程序的输出是进入管道,它是全缓冲。您可以控制​​由标准C函数缓冲模式 setvbuf用来() _IOFBF (全缓冲), _IOLBF (行缓冲)和 _IONBF (无缓冲)模式。

When the output of your program is going to a terminal (screen), it is line buffered. When the output of your program goes to a pipe, it is fully buffered. You can control the buffering mode by the Standard C function setvbuf() and the _IOFBF (full buffering), _IOLBF (line buffering) and _IONBF (no buffering) modes.

您可以通过管道程序,比方说,的输出修改后的程序证明这一点。即使在中的printf()字符串末尾的换行,你会看到双信息。如果你把它直接到终端,然后你会看到只是一个大量的信息。

You could demonstrate this in your revised program by piping the output of your program to, say, cat. Even with the newlines at the end of the printf() strings, you would see the double information. If you send it direct to the terminal, then you will see just the one lot of information.

这个故事的寓意是要小心调用 fflush(0); 清空所有I / O分叉之前缓冲区

The moral of the story is to be careful to call fflush(0); to empty all I/O buffers before forking.

行由行分析,要求(括号去掉等等 - 并通过标记编辑器删除前导空格):

Line-by-line analysis, as requested (braces etc removed - and leading spaces removed by markup editor):


  1. 的printf(你好,我pid是%D,GETPID());

  2. PID =叉();

  3. 如果(PID == 0)

  4. 的printf(\\ Ni被分叉!:D);

  5. 睡眠(3);

  6. 其他

  7. waitpid函数(PID,NULL,0);

  8. 的printf(\\ n%d个被叉!,PID);

  1. printf( "Hello, my pid is %d", getpid() );
  2. pid = fork();
  3. if( pid == 0 )
  4. printf( "\nI was forked! :D" );
  5. sleep( 3 );
  6. else
  7. waitpid( pid, NULL, 0 );
  8. printf( "\n%d was forked!", pid );

分析:


  1. 将你好,我pid是1234进入缓冲标准输出。因为在最后没有换行和输出的行缓冲模式下运行(或全缓冲模式),没有出现在终端上。

  2. 为我们提供了两个独立的过程,与正好在标准输出缓冲器相同的材料。

  3. 孩子有 PID == 0 和执行行4和5;家长对 PID 非零值(这两个过程之间的一些区别之一 - 返回值从 GETPID() getppid()是两个以上)。

  4. 添加一个换行符和我叉!:D孩子的输出缓冲区。输出的第一行显示在终端上;其余的是在缓冲器以来的输出行缓冲保持。

  5. 暂停一切为3秒。在此之后,孩子通过在主月底回归正常退出。在这一点上,在标准输出缓冲器中的剩余数据被刷新。这使得在一行的末尾输出位置,因为没有换行。

  6. 父来这里。

  7. 家长等待孩子完成死亡。

  8. 母公司增加了一个新行和1345是分叉的!到输出缓冲器。新行刷新你好消息的输出,被孩子所产生的不完整的行之后。

  1. Copies "Hello, my pid is 1234" into the buffer for standard output. Because there is no newline at the end and the output is running in line-buffered mode (or full-buffered mode), nothing appears on the terminal.
  2. Gives us two separate processes, with exactly the same material in the stdout buffer.
  3. The child has pid == 0 and executes lines 4 and 5; the parent has a non-zero value for pid (one of the few differences between the two processes - return values from getpid() and getppid() are two more).
  4. Adds a newline and "I was forked! :D" to the output buffer of the child. The first line of output appears on the terminal; the rest is held in the buffer since the output is line buffered.
  5. Everything halts for 3 seconds. After this, the child exits normally through the return at the end of main. At that point, the residual data in the stdout buffer is flushed. This leaves the output position at the end of a line since there is no newline.
  6. The parent comes here.
  7. The parent waits for the child to finish dying.
  8. The parent adds a newline and "1345 was forked!" to the output buffer. The newline flushes the 'Hello' message to the output, after the incomplete line generated by the child.

父现在通过在主月底回归正常退出,剩余的数据被刷新;因为仍然没有在最后一个换行符,光标位置是感叹号之后,并出现在同一行shell提示符。

The parent now exits normally through the return at the end of main, and the residual data is flushed; since there still isn't a newline at the end, the cursor position is after the exclamation mark, and the shell prompt appears on the same line.

我看到的是:

Osiris-2 JL: ./xx
Hello, my pid is 37290
I was forked! :DHello, my pid is 37290
37291 was forked!Osiris-2 JL: 
Osiris-2 JL: 

PID编号是不同的 - 但整体外观是明确的。添加新行到的printf()语句的结束(这很快成为标准的做法)改变输出了很多:

The PID numbers are different - but the overall appearance is clear. Adding newlines to the end of the printf() statements (which becomes standard practice very quickly) alters the output a lot:

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

int main()
{
    int pid;
    printf( "Hello, my pid is %d\n", getpid() );

    pid = fork();
    if( pid == 0 )
        printf( "I was forked! :D %d\n", getpid() );
    else
    {
        waitpid( pid, NULL, 0 );
        printf( "%d was forked!\n", pid );
    }
    return 0;
}

我现在得到:

Osiris-2 JL: ./xx
Hello, my pid is 37589
I was forked! :D 37590
37590 was forked!
Osiris-2 JL: ./xx | cat
Hello, my pid is 37594
I was forked! :D 37596
Hello, my pid is 37594
37596 was forked!
Osiris-2 JL:

注意,当输出到终端,这是行缓冲,因此叉()前的'你好'线出现,只是有一复制。当输出通过管道输送到,它完全缓冲的,所以没有出现在叉()无一不方法具有在缓冲器中的你好行被刷新。

Notice that when the output goes to the terminal, it is line-buffered, so the 'Hello' line appears before the fork() and there was just the one copy. When the output is piped to cat, it is fully-buffered, so nothing appears before the fork() and both processes have the 'Hello' line in the buffer to be flushed.

这篇关于printf的异常后&QUOT; fork()的&QUOT;的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆