0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

printf数据都去哪儿了

流川枫枫 来源:流川枫枫 作者:流川枫枫 2022-05-23 14:08 次阅读

关于printf

printf是一个接口,跟UNIX标准IO的write系统调用类似,但是更像C库的fwrite,因为同系列的函数中还有一个fprintf(至于同系列其它的函数,请自行man)。printf和fwrite的区别在于两点:

1.它可以格式化输出,如果用fwrite,它接受的是一个固定的buffer,你不得不在调fwrite之前先使用sprintf之类的函数格式化buffer;

2.它免除了你的fopen-fwrite-fclose这个序列的调用,因为它直接将格式化的内容写入UNIX进程自然打开的1号文件描述符,即标准输出。

既然printf写入了标准输出,那么接下来就要定义什么是标准输出。在早期UNIX年代,人们在终端或者伪终端操作机器,那时的输入基本都是键盘,磁带更古老的东西,而输出就是一个计算结果,需要展示出来给人看的那种,一般为终端屏幕,也可以是一条纸带,那么程序怎么知道输入和输出到底是什么呢?这就需要程序明确指定。UNIX的“一切皆文件”思想以及“分离抽象”思想彻底改变了这一切。

UNIX定义了抽象文件描述符0,1,2分别为标准输入,标准输出,标准错误输出。至于它们到底对应什么设备,你可以在程序初始化的时候显式重定向到任意设备,也可以在外部shell做类似的重定向,这样就把指明设备这件事从程序分离了出来。

我为什么不统一说一下fwrite调用对程序性能的影响呢?因为该调用之前你必须执行fopen,而fopen的一个参数明确表示了你希望写入的对象是什么,这就不会带来异议,毕竟如果你非要在性能测试的时候写CF卡,那也是你愿意。printf就不同了,它对效率的影响取决于标准输出是什么以及你是如何重定向标准输出的,所谓的标准输出并不是真实的设备,它只是一个抽象层,具体如何解释标准输出,还要依靠外部。

数据都去哪儿了

我以下面这个超级小的程序来说明printf的时候,数据都去哪了:
#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,此时不切换tty
...
等了几秒,无结果,于是在键盘按下Alt-F2,切换到第二个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

通过以上的结果数据,我们可以得到以下的结论:
a.对于tty终端而言,如果当前终端不是写入的终端,那么开销主要在内核态,且开销不是很大;
b.对于tty终端而言,如果当前终端是写入的终端,那么开销主要在内核态,且开销很大;
c.对于不管是tty还是远程的pty终端,写入/dev/null的开销主要在用户态,开销不大;
d.对于pty远程终端(/dev/pts/X),不管写入的是不是当前的pty终端,开销主要在内核态,且开销不是很大
e.对应上面的结果和结论,下面给出一幅图解,详细解释一下printf冰山下面的秘密:

test进程

线路规程串口举例:

线路规程串口举例

简易图如下:

简易图

我想上图已经很清楚了,如果不懂什么叫行规程(也叫线路规程)的话,请阅读《UNIX环境高级编程》的终端和伪终端章节,简单来说,它就是一个中间层,用来适配VFS接口和底层的具体驱动,比如解释和处理控制字符等。从上面的图中,我们可以看出,主要的开销几乎都集中在底层,而底层却偏偏是我们不能控制或者很难控制的。之所以上面的测试例子中ssh登录的终端对test性能的测试效果良好,但是那是因为网络环境好,你在一个64kbps相隔5k公里的线路上试一下。

小小的printf下面竟然藏着如此多的内容,并且很可能就是它成了你的程序的性能瓶颈,因为最底层的影响因素往往是不可控的。那么是不是就是意味着我要建议大家从来不用printf打印呢?或者说干脆就不要用标准输出呢?并不是这样。但是为何不把打印这种事交给本机的另一个进程呢?事实上,几乎所有的需要记录日志的系统都是这么做的,而syslog则迎合了这个思想。这种思想的背后就是“用可控制的一次IPC替换不可控制冰山之下的茫茫深海

关于日志记录

日志记录一直都是“薛定谔猫”式的东西,因为日志记录作为一段代码,它已经是程序的一部分,不可能独立地观察程序的行为,如果说用镜像系统的话,那么这种行为就是被动的,你不得不镜像每一条指令,以发现一些关键的信息,要想主动记录关键事件,必须用日志系统。打印日志可以方便信息获取和审计,但是代价有时也是高昂的:
1.你要设计一套日志回滚系统,防止存储空间被撑爆;
2.你要让日志记录尽快完成,不能降低关键路径的性能;
3.你要反复调试代码,确保日志记录的缓冲区不会溢出;
4.为了让日志更短,语言能力不好的人组织的日志就像电报一样难以理解。
...
我认为,日志记录应该遵循以下的原则:

1.除非必须要把事件发生的时间记录下来,否则就用计数器代替日志记录,一系列的事件映射成一系列的计数器,由用户决定什么时候查看事件发生了。事实上,Linux的网络子系统就是用的这种方式,所有的/proc/net/netstat就是这个查看接口。

2.一定要有一个日志级别控制选项,用户可以决定是否记录日志,以及记录的日志详细到什么程度。

tty层接口

驱动代码摘自:

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);

发送数据

发送数据
审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 接口
    +关注

    关注

    33

    文章

    8575

    浏览量

    151021
  • 数据
    +关注

    关注

    8

    文章

    7002

    浏览量

    88943
  • Printf
    +关注

    关注

    0

    文章

    83

    浏览量

    13650
收藏 人收藏

    评论

    相关推荐

    高手都去哪儿

    本人学习单片机数月,目前发现学习过程中的一个问题,用的教材是郭天祥老师的那本新概念。在这里就是想问问高手们,我是先按照书上历程做完还是朝着一个历程使劲的拓展,直到完全掌握在看下一个历程?
    发表于 03-12 14:29

    飞凌嵌入式干货分享 - printf的归宿-数据打印到哪儿

    标准输出是什么以及你是如何重定向标准输出的,所谓的标准输出并不是真实的设备,它只是一个抽象层,具体如何解释标准输出,还要依靠外部。数据都去哪儿我以下面这个超级小的程序来说明
    发表于 03-06 16:40

    淘汰的新能源汽车电池都去哪儿

    一辆指导价为30万的新能源汽车,其电池成本可能在10万元甚至以上。“一般来说,动力电池的容量低于80%就不能再用在新能源汽车上。”上海交大汽车工程研究院副院长殷承良此前在接受记者采访时表示。
    发表于 11-24 15:07 694次阅读

    SDK中大写的PRINTF和小写printf的区别

     讲一下SDK中大写的PRINTF和小写printf的区别。
    发表于 09-15 17:47 2次下载
    SDK中大写的<b class='flag-5'>PRINTF</b>和小写<b class='flag-5'>printf</b>的区别

    printf输出格式

    printf函数称为格式输出函数,其关键字最末一个字母f即为“格式”(format)之意。其功能是按用户指定的格式,把指定的数据显示到显示器屏幕上。printf函数调用的一般形式printf
    发表于 11-10 08:52 3.4w次阅读

    stm32串口通信用printf发送数据配置

    在STM32串口通信程序中使用printf发送数据,非常的方便。可在刚开始使用的时候总是遇到问题,常见的是硬件访真时无法进入main主函数,其实只要简单的配置一下就可以。下面就说一下使用pr
    发表于 11-25 09:08 4399次阅读

    基于STM32的printf串口数据输出

    该方法适用于 STM32 ,实现使用printf等标准C流函数输出数据的办法,极大的减少了输出 串口数据 时所需要做的数据处理。 实现原理
    发表于 06-21 07:51 2w次阅读
    基于STM32的<b class='flag-5'>printf</b>串口<b class='flag-5'>数据</b>输出

    程序员和开发者的时间都去

    对于那些不知道程序员/开发者的时间都去的人,本文可能会提供一些线索。我记录了这份日志不仅是为了看看时间都花费在哪,也是为了看看我都做了些什么,检视下自己是否偷懒。当回顾之后,我
    的头像 发表于 11-22 16:29 1444次阅读

    工业机器人需求自3月以来持续复苏,机器人都去哪儿

    ;服务机器人产量也实现单月70%以上的同比增速。 这些新生机器人都去哪儿?作为智能制造领域先进生产力的代表,机器人产业高速增长受哪些板块驱动,对中国制造业意味着什么? 机器人
    的头像 发表于 11-13 10:54 1883次阅读

    STM32中使用printf打印串口数据的实现原理及方法

    STM32中使用printf打印串口数据的实现原理 在C库中,printf()等输出流函数都是通过fputc()这个函数实现的,所以我们通过重映射的方式,修改这个函数的定义使它输出在STM32
    的头像 发表于 07-22 11:12 1.5w次阅读

    stm32 printf重定向

    stm32调试时,有时不太适合打断点的地方,还需要状态,那printf就很符合我们的要求。不多说,直接上修改方法:首先:添加printf的头文件 :#include “stdio.h”其次
    发表于 12-03 14:36 3次下载
    stm32 <b class='flag-5'>printf</b>重定向

    Keil下使用STlink重定向printf的配置

    Keil下使用STlink重定向printf的配置1. printf 重定向Keil默认下使用Micro LIB库,该库调用 fputs 实现 printf,所以需要重新定义fputs函数,以重定向
    发表于 12-27 18:43 18次下载
    Keil下使用STlink重定向<b class='flag-5'>printf</b>的配置

    stm32单片机串口使用printf及u3_printf

    无论是在51单片机还是在stm32,默认printf串口都是串口一。使用printf的时候头文件为&amp;quot;stdio.h&amp;quot;,但是一些
    发表于 12-27 19:24 1次下载
    stm32单片机串口使用<b class='flag-5'>printf</b>及u3_<b class='flag-5'>printf</b>

    通过串口利用printf函数输出数据

    一。printf函数格式printf函数具有强大的输出功能%表示格式化字符串输出目前printf支持以下格式的输出,例如:printf("%c",a);输出单个字符。
    发表于 12-28 19:11 11次下载
    通过串口利用<b class='flag-5'>printf</b>函数输出<b class='flag-5'>数据</b>

    stdio.h实现printf函数?

    我们平时包含的 stdio.h 头文件,里面是不是实现 printf 函数? 为什么会有这个疑问?因为每次使用 printf,就得包含 stdio.h ,这就导致很多同学误以为,stdio.h
    的头像 发表于 12-18 10:28 85次阅读