操作系统:Linux,语言:纯C
我将继续学习一般的C编程,在特殊情况下将学习UNIX下的C编程。
printf()使用fork()调用后,我检测到该函数的奇怪行为(对我而言)。
printf()
fork()
码
#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; }
输出量
Hello, my pid is 1111 I was forked! :DHello, my pid is 1111 2222 was forked!
为什么第二个“ Hello”字符串出现在孩子的输出中?
是的,这恰恰是父母在开始时打印的内容,并带有父母的pid。
pid
但!如果我们\n在每个字符串的末尾放置一个字符,则会得到预期的输出:
\n
#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!
为什么会发生?这是正确的行为还是错误?
我注意到这<system.h>是一个非标准头文件;我将其替换为<unistd.h>干净地编译的代码。
<system.h>
<unistd.h>
当程序的输出进入终端(屏幕)时,它是行缓冲的。当程序的输出进入管道时,将被完全缓冲。您可以通过标准C函数setvbuf()以及_IOFBF(完全缓冲),_IOLBF(行缓冲)和_IONBF(无缓冲)模式来控制缓冲模式。
setvbuf()
_IOFBF
_IOLBF
_IONBF
您可以通过将程序的输出传递到,在修改后的程序中进行演示cat。即使printf()字符串末尾有换行符,您也会看到重复信息。如果直接将其发送到终端,则只会看到很多信息。
cat
这个故事的寓意是要小心fflush(0);在分叉之前调用所有I / O缓冲区。
fflush(0);
按要求进行逐行分析(大括号等已删除-标记编辑器已删除了前导空格):
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 );
分析:
pid == 0
getpid()
getppid()
父级现在通常通过main的结尾处的返回退出,并且清除了剩余数据;由于末尾仍然没有换行符,因此光标位置在感叹号之后,并且shell提示出现在同一行上。
我看到的是:
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()语句的末尾添加换行符(这很快成为标准做法)会极大地改变输出:
#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:
请注意,当输出到达终端时,它是行缓冲的,因此“ Hello”行出现在之前,fork()并且只有一个副本。当输出通过管道传递到时cat,它已被完全缓冲,因此在fork()和两个进程的要刷新的缓冲区中都有“ Hello”行之前,什么都没有出现。