查看: 1043|回復: 0
打印 上一主題 下一主題

飛凌嵌入式干貨分享 - printf的歸宿-數據打印到哪兒了

[復制鏈接]
跳轉到指定樓層
樓主
發(fā)表于 2021-3-6 16:47:33 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
關(guān)鍵詞: 飛凌嵌入式
關(guān)于printfprintf是一個(gè)接口,跟UNIX標準IO的write系統調用類(lèi)似,但是更像C庫的fwrite,因為同系列的函數中還有一個(gè)fprintf(至于同系列其它的函數,請自行man)。printf和fwrite的區別在于兩點(diǎn):
1.它可以格式化輸出,如果用fwrite,它接受的是一個(gè)固定的buffer,你不得不在調fwrite之前先使用sprintf之類(lèi)的函數格式化buffer;
2.它免除了你的fopen-fwrite-fclose這個(gè)序列的調用,因為它直接將格式化的內容寫(xiě)入UNIX進(jìn)程自然打開(kāi)的1號文件描述符,即標準輸出。
既然printf寫(xiě)入了標準輸出,那么接下來(lái)就要定義什么是標準輸出。在早期UNIX年代,人們在終端或者偽終端操作機器,那時(shí)的輸入基本都是鍵盤(pán),磁帶更古老的東西,而輸出就是一個(gè)計算結果,需要展示出來(lái)給人看的那種,一般為終端屏幕,也可以是一條紙帶,那么程序怎么知道輸入和輸出到底是什么呢?這就需要程序明確指定。UNIX的“一切皆文件”思想以及“分離抽象”思想徹底改變了這一切。
UNIX定義了抽象文件描述符0,1,2分別為標準輸入,標準輸出,標準錯誤輸出。至于它們到底對應什么設備,你可以在程序初始化的時(shí)候顯式重定向到任意設備,也可以在外部shell做類(lèi)似的重定向,這樣就把指明設備這件事從程序分離了出來(lái)。
我為什么不統一說(shuō)一下fwrite調用對程序性能的影響呢?因為該調用之前你必須執行fopen,而fopen的一個(gè)參數明確表示了你希望寫(xiě)入的對象是什么,這就不會(huì )帶來(lái)異議,畢竟如果你非要在性能測試的時(shí)候寫(xiě)cf卡,那也是你愿意。printf就不同了,它對效率的影響取決于標準輸出是什么以及你是如何重定向標準輸出的,所謂的標準輸出并不是真實(shí)的設備,它只是一個(gè)抽象層,具體如何解釋標準輸出,還要依靠外部。
數據都去哪兒了我以下面這個(gè)超級小的程序來(lái)說(shuō)明printf的時(shí)候,數據都去哪了:
#include
#include
int main(int argc, char **argv)
{       int i = 0;        
int c = atoi(argv[1]);
        for(; i < c; i++) {               
printf("############  %d\n", i);
}        return 0;
}
我先給出結果:
1.在/dev/tty1上直接執行time ./test 1000
... #########  995
#########  996
#########  997
#########  998
#########  999
real    0m0.414s
user    0m0.003s
sys     0m0.411s

2.在/dev/tty1上執行time ./test 1000 >/dev/tty2
real    0m0.007s
user    0m0.003s
sys     0m0.007s
3.在SecureCRT上執行time ./test 1000
...
#########  997
#########  998
#########  999
real    0m0.010s
user    0m0.002s
sys     0m0.003s

在SecureCRT上執行time ./test 100000 >/dev/tty1,此時(shí)不切換tty
...
等了幾秒,無(wú)結果,于是在鍵盤(pán)按下Alt-F2,切換到第二個(gè)tty,馬上顯示出了結果:
real    0m4.276s
user    0m0.066s
sys     0m4.204s
5.在tty1上執行time ./test 100000 >/dev/tty2:
real    0m0.499s
user    0m0.081s
sys     0m0.410s
6.在tty1上執行time ./test 100000 >/dev/null
real    0m0.030s
user    0m0.028s
sys     0m0.001s
通過(guò)以上的結果數據,我們可以得到以下的結論:
a.對于tty終端而言,如果當前終端不是寫(xiě)入的終端,那么開(kāi)銷(xiāo)主要在內核態(tài),且開(kāi)銷(xiāo)不是很大;
b.對于tty終端而言,如果當前終端是寫(xiě)入的終端,那么開(kāi)銷(xiāo)主要在內核態(tài),且開(kāi)銷(xiāo)很大;
c.對于不管是tty還是遠程的pty終端,寫(xiě)入/dev/null的開(kāi)銷(xiāo)主要在用戶(hù)態(tài),開(kāi)銷(xiāo)不大;
d.對于pty遠程終端(/dev/pts/X),不管寫(xiě)入的是不是當前的pty終端,開(kāi)銷(xiāo)主要在內核態(tài),且開(kāi)銷(xiāo)不是很大
e.對應上面的結果和結論,下面給出一幅圖解,詳細解釋一下printf冰山下面的秘密:


線(xiàn)路規程串口舉例:

簡(jiǎn)易圖如下:


我想上圖已經(jīng)很清楚了,如果不懂什么叫行規程(也叫線(xiàn)路規程)的話(huà),請閱讀《UNIX環(huán)境高級編程》的終端和偽終端章節,簡(jiǎn)單來(lái)說(shuō),它就是一個(gè)中間層,用來(lái)適配VFS接口和底層的具體驅動(dòng),比如解釋和處理控制字符等。從上面的圖中,我們可以看出,主要的開(kāi)銷(xiāo)幾乎都集中在底層,而底層卻偏偏是我們不能控制或者很難控制的。之所以上面的測試例子中ssh登錄的終端對test性能的測試效果良好,但是那是因為網(wǎng)絡(luò )環(huán)境好,你在一個(gè)64kbps相隔5k公里的線(xiàn)路上試一下。
小小的printf下面竟然藏著(zhù)如此多的內容,并且很可能就是它成了你的程序的性能瓶頸,因為最底層的影響因素往往是不可控的。那么是不是就是意味著(zhù)我要建議大家從來(lái)不用printf打印呢?或者說(shuō)干脆就不要用標準輸出呢?并不是這樣。但是為何不把打印這種事交給本機的另一個(gè)進(jìn)程呢?事實(shí)上,幾乎所有的需要記錄日志的系統都是這么做的,而syslog則迎合了這個(gè)思想。這種思想的背后就是“用可控制的一次IPC替換不可控制冰山之下的茫茫深海
關(guān)于日志記錄日志記錄一直都是“薛定諤貓”式的東西,因為日志記錄作為一段代碼,它已經(jīng)是程序的一部分,不可能獨立地觀(guān)察程序的行為,如果說(shuō)用鏡像系統的話(huà),那么這種行為就是被動(dòng)的,你不得不鏡像每一條指令,以發(fā)現一些關(guān)鍵的信息,要想主動(dòng)記錄關(guān)鍵事件,必須用日志系統。打印日志可以方便信息獲取和審計,但是代價(jià)有時(shí)也是高昂的:
1.你要設計一套日志回滾系統,防止存儲空間被撐爆;
2.你要讓日志記錄盡快完成,不能降低關(guān)鍵路徑的性能;
3.你要反復調試代碼,確保日志記錄的緩沖區不會(huì )溢出;
4.為了讓日志更短,語(yǔ)言能力不好的人組織的日志就像電報一樣難以理解。
...
我認為,日志記錄應該遵循以下的原則:
1.除非必須要把事件發(fā)生的時(shí)間記錄下來(lái),否則就用計數器代替日志記錄,一系列的事件映射成一系列的計數器,由用戶(hù)決定什么時(shí)候查看事件發(fā)生了。事實(shí)上,Linux的網(wǎng)絡(luò )子系統就是用的這種方式,所有的/proc/net/netstat就是這個(gè)查看接口。
2.一定要有一個(gè)日志級別控制選項,用戶(hù)可以決定是否記錄日志,以及記錄的日志詳細到什么程度。
tty層接口
驅動(dòng)代碼摘自:
lichee/linux-3.10/drivers/tty/serial/sunxi-uart.c
接收數據
static unsigned int sw_uart_handle_rx(struct sw_uart_port *sw_uport, unsigned int lsr)
{
….
tty_flip_buffer_push(&sw_uport->port.state->port);

}
tty_termios_baud_rate(termios)
tty_termios_encode_baud_rate(termios, baud, baud);
發(fā)送數據

百度搜索:飛凌嵌入式  了解更多行業(yè)動(dòng)態(tài)
www.forlinx.com
您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

關(guān)于我們  -  服務(wù)條款  -  使用指南  -  站點(diǎn)地圖  -  友情鏈接  -  聯(lián)系我們
電子工程網(wǎng) © 版權所有   京ICP備16069177號 | 京公網(wǎng)安備11010502021702
快速回復 返回頂部 返回列表
午夜高清国产拍精品福利|亚洲色精品88色婷婷七月丁香|91久久精品无码一区|99久久国语露脸精品|动漫卡通亚洲综合专区48页