天天看點

printf 緩沖區原理 (你想知道的C語言 1.4)

Q: 緩沖區究竟是什麼概念?

A: 緩沖: 顧名思義, 表示可以酌情商量有回旋餘地. 可以想象一種隻能強制遵守且說一不二的情形,一種可以協商開發此消彼長的模式.

    區: 了解成可以協助存儲/處理的實體位置,一般了解成記憶體。

    計算機科學的"緩沖區"概念可以說是到處都有, 這種模式的作用至少有兩種:

    1 協助存儲和内部處理;

    2 盡可能提高潛在的效率.

        就像是數學課代表統一把作業本交到辦公室要比每個學生都跑一遍辦公室更高效.    

Q: printf的緩沖區究竟在哪裡?

A: printf隸屬libc範疇, libc屬于使用者态, printf的緩沖區也可以了解成使用者态配置設定的一段記憶體空間. 可以想象在每次調用printf的時候,内部都會根據配置設定的緩沖區狀态進行判斷, 如果沒有必要真實輸出,就先暫存,否則就按要求輸出.

    如下是libc FILE結構體buffer的位置:

printf 緩沖區原理 (你想知道的C語言 1.4)
printf 緩沖區原理 (你想知道的C語言 1.4)

  對于緩沖區的操作,大體就是對于一塊連續的memory寫操作,每次都會記錄position, 當position到達緩沖區最大或者有換行标志,就會強制重新整理.

  緩沖區執行個體: printf 内部原理和實作 (你想知道的C語言 1.2)

  Apple libc源代碼:  https://opensource.apple.com/source/Libc/Libc-1272.250.1/   printf.c

Q: 既然緩沖區是使用者态, 我們如何dump緩沖區的資料?

A: 我們先嘗試利用通過stdout找到對應FILE資料結構buffer指針,就可以dump具體資料了.

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

void dump_stdout_buffer()
{
	FILE *fp;
	unsigned char *buf;
	int size;

	fp = stdout;
	buf = fp->_bf._base;
	size = fp->_bf._size;

	write(1, "stdout buffer:", strlen("stdout buffer:"));
	write(1, buf, size);
	write(1, "\n", 1);
}

int main(int argc, char *argv[])
{
	printf("cat");
	dump_stdout_buffer();
	printf("dog");
	dump_stdout_buffer();
	printf("\n");

	return 0;
}
           

代碼: https://github.com/cxsjabc/basic/blob/dev/c/_topics/printf/dump_buffer.c

執行結果:

printf 緩沖區原理 (你想知道的C語言 1.4)

一開始, printf("cat")會把cat放入緩沖區, 後面放入dog, 最後catdog會一起輸出.

當我們遇到printf沒有輸出的時候,可以從緩沖區角度考慮.

Q: printf緩沖區大小是多大?

A: 我們先不看libc實際設定的多少,采用江湖一直流傳的手動構造緩沖區重新整理的做法來反推緩沖區大小.

     https://blog.csdn.net/skyflying2012/article/details/10044035

     中心思想是不斷打出一個字元, 每次間隔毫秒級别, 當第一次輸出即緩沖區已經滿了,此時記錄輸出的字元個數.

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

void delay_ms(int miliseconds)
{
	int i;
	while(miliseconds) {
		for(i = 0; i < 100000; ++i) ;
		--miliseconds;
	}
}

void check_stdout_buf_size()
{
	while(1) {
		printf("a");
		delay_ms(1);
	}
}

int main(int argc, char *argv[])
{
	check_stdout_buf_size();

	return 0;
}
           

當螢幕第一次刷出一堆a之後停住的時候,按Ctrl + C終止程式,然後記錄a的個數.

 實驗得到的資料是: 4096.

 我們再用代碼測試4096個字元輸出将發生緩沖區滿.

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

void test_stdout_buf_size()
{
	int i;

	for(i = 0; i < 4096; ++i)
		printf("a");

	sleep(3);
	printf("b");
	sleep(10);
}

int main(int argc, char *argv[])
{
	test_stdout_buf_size();

	return 0;
}
           

實驗結果表明: 在4096個字元a輸出後,間隔13秒左右才會輸出剩下的b, 即證緩沖區大小為4096!

作者:     陳曦
環境:     MacOS 10.14.5
         Apple LLVM version 10.0.1 (clang-1001.0.46.4)
         Target: x86_64-apple-darwin18.6.0
 
轉載請注明出處