之前一直对无缓冲、行缓冲、全缓冲不太感冒,
然后最近在《UNIX环境高级编程》上看到这样一个例子,感觉挺好的拿来给没看过的小伙伴们看看:
#include <unistd.h> #include <stdio.h> int globvar = 6; char buf[] = "a write to stdout\n"; int main() { int var; pid_t pid; var = 88; if (write(STDOUT_FILENO, buf, sizeof(buf) - 1) != sizeof(buf) - 1) ; printf("before fork\n"); if ((pid = fork()) < 0) ; else if (pid == 0) { globvar++; var++; } else { sleep(2); } printf("pid = %ld, glob = %d, var = %d\n", (long)getpid(), globvar, var); exit(0); }
好像蛮正常,但如果把输出重定向到一个文件,结果不一样了:
明明只有一行的 printf("before fork\n") 却被打印了两次。
首先,我们知道write()这种系统调用是无缓冲的,而标准I/O库提供了缓冲机制以尽可能减少使用read和write调用的次数:当流涉及一个终端时(不包含标准错误流),通常使用行缓冲;否则,默认为全缓冲。
那么原因很明了了,第一次 ./a.out 输出到终端,默认为行缓冲,字符串“before fork” 由于之后的 \n 被立刻输出了。
而 ./a.out > temp.out 输出端重定向到文件时,默认为全缓冲模式,printf("before fork\n") 后字符串“before fork\n”依然留在缓冲区等待输出(缓冲区溢出或程序退出),而I/O缓冲区是用malloc分配在进程的堆区的。
fork()执行后子进程获得了父进程数据空间、堆和栈的副本,当然也获得了缓冲区的一个副本;进程结束时,父进程与子进程的缓冲区数据先后被写到了temp.out中,得到两个“before fork”。
验证一下,我们把 printf("before fork\n") 里面的"\n"改成".",输出到标准输出:./a.out 看看:
由于没有了换行符,在行缓冲模式下,“before fork.”也被留在了缓冲区里,然后同上面一样被输出了两次。
当然我们也可以使用 setbuf 和 setvbuf 修改标准I/O的默认缓冲模式。
另外从该代码可以看出,在fork()之前打开的文件描述符是被父进程与子进程共享的,当然还有该文件的偏移量。
最后感谢和推荐一下牛客网,前段时间找工作时发现这个平台,在上面认识了不少大神和小伙伴,
然后网页底端有个“模拟终端”,简单的Linux学习用浏览器就可以,上面的shell界面就是在这个上面跑的,
再也不用默默地等虚拟机开机了(*^__^*) ~