from unix系統編程 p91 練習4-22
下面的程序會產生什麼樣的輸出呢?
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("This is my output.") ;
fork() ;
return 0 ;
}
輸出如下所示:
This is my output.This is my output.
(似乎很不正常!該程序中,一個printf語句輸出之後,用fork函數創建子進程,然後父子進程一起返回。
怎麼會輸出兩個printf語句呢??)
其實,
由於緩衝的原因,printf的輸出可能會寫入stdout所對應的緩衝區中,而不會被寫入實際的輸出設備。由於這個緩衝區是用戶空間的一部分,所有它會被fork複製。父進程和子進程每次終止時,從main的返回都會將緩衝區作爲清理的一部分清空。
而下面的程序會產生什麼樣的輸出呢?
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("This is my output.\n") ; /*注意,原字符串後多了一個 \n */
fork() ;
return 0 ;
}
輸出如下顯示:
This is my output.
標準輸出的緩衝通常是行緩衝,這意味着當新行包含新行的時候,緩衝區會被清空,所以會在fork之前被清空,只輸出一行輸入。
磁盤文件是完全緩衝的(fully buffered),程序調用fprintf時,實際上沒有把消息寫入磁盤,而是將這些字節寫人如FILE結構的一個緩衝區中,可用fflush調用強制寫出緩衝的任何內容。
與終端有關的文件是行緩衝(line buffered)的。
標準錯誤(strerror)默認情況下是不緩衝的。
附件1:
在Unix系統中,緩衝方式存在三種,分別是:
1,全緩衝
2,行緩衝
3,無緩衝
在學習APUE這本書時,程序8-1中,就很好的體現了全緩衝和行緩衝的區別,代碼如下:
- #include<stdio.h>
- #include<unistd.h>
- int glob=6;
- char buf[]="a write ro stdout/n";
- int main()
- {
- int var;
- pid_t pid;
- printf("a write to stdout/n");
- fflush(NULL);
- if((pid=fork())<0)
- {
- printf("fork error");
- }
- else
- {
- if(pid==0)
- {
- glob++;
- var++;
- }
- else
- {
- sleep(2);
- }
- }
- printf("pid=%d,glob=%d,var=%d/n",getpid(),glob,var);
- exit(0);
- }
編譯成功後,我這裏生成的二進制文件默認爲a.out
運行:./a.out
可以看到結果如下:
- a write to stdout
- pid=6587,glob=7,var=134514042
- pid=6586,glob=6,var=134514041
運行./a.out > temp.out
結果如下:
- a write to stdout
- pid=6591,glob=7,var=134514042
- a write to stdout
- pid=6590,glob=6,var=134514041
分析原因:
在./a.out輸出中,標註輸出是STDOUT_FILENO,是交互式的終端,所以系統採用的緩衝方式行緩衝,所以在printf函數中,輸出後,立即刷新緩衝區,而在./a.out > temp.out命令中,輸出流定向到了temp.out文件中,所以採用的輸出方式爲全緩衝方式,所以會兩次輸出
a write to stdout
可以將程序中fflush(NULL)加入,則只會輸出一次
a write to stdout
因爲flush即時刷新了緩衝區。
附件2:
使用緩衝區的目的:儘量減少使用read/write的調用
分類:
1. Fully buffered means that I/O takes place only when the buffer is fully, the process explicitly calls fflush, or the process terminates by calling exit. A common
size for the standard I/O buffer is 8192 bytes;
2. Line buffered means that I/O takes place when a newline is encountered, when the process calls fflush, or when the process terminates by calling exit.
3. Unbuffered means that I/O take place each time a standard I/O output function is called.
Most unix implementations of the standard I/O libarary use the following rules.
1. Standard error is always unbuffered. 能夠快速顯示錯誤
2. Standard input and standard output are fully buffered, unless they refer to a terminal device, in which case, they are line buffered.
3. All other streams are fully buffered unless they refer to a terminal device,
in which case, they are line buffered.
使用setbuf()和setvbuf()可以更改緩衝的類型:
setbuf(FILE *fp, char *buf) buf用malloc()開闢,當buf爲NULL時,緩衝區爲不帶緩衝
setvbuf(FILE *fp,char *buf, int buf_mode, int size)
buf_mde有三種:_IOFBF 0 全緩衝, _IOLBF 1 行緩衝, _IONBF 2 不帶緩衝
在任何時刻,可以使用fflush強制刷新一個數據流,此函數使該流所有未寫的數據都被傳遞至內核。
由於fflush(stdin)的移植性不好,gcc不支持,故可以用以下代碼清空stdin緩衝區:
while ((c = getchar()) != '\n' && c != EOF);