Linux从用户层到内核层系列 – GNU系列之你所不知道的printf

释放双眼,带上耳机,听听看~!

题记:本系列文章的目的是抛开书本从源代码和使用的角度分析Linux内核和相关源代码,byhankswang和你一起玩转linux开发

首先从我们的Hello World!说起:

#include <stdio.h>

int main()

{

    printf("Hello World!\n");

    return 0;

}

这段代码可能是大多数程序员或者学生的接触的首段代码,从老师的讲解到工作中的使用,甚至工作多年之后可能还有很多人不知道这段代码程序中printf到底是怎么运行的。

1.<stdio.h>头文件

在ubuntu12.04系统中 /usr/include/stdio.h 包含了上面代码中的头文件<stdio.h>, 其中对于printf的外部引用为:

359 /* Write formatted output to stdout.
360 
361    This function is a possible cancellation point and therefore not
362    marked with __THROW.  */
363 extern int printf (__const char *__restrict __format, …);

我们看到关于printf为外部引用,GCC在编译main.c文件的时候包含了头文件<stdio.h>,在运行这段代码的时候会调用libc.so库。

2.glibc对printf的实现

在glibc中printf的实现在源文件/stdio-common/printf.c中:

/* Write formatted output to stdout from the format string FORMAT.  */
/* VARARGS1 */
int
__printf (const char *format, …)
{
va_list arg;
int done;
va_start (arg, format);
done = vfprintf (stdout, format, arg);
va_end (arg);
return done;
}

然后很多同学就该问了,printf和__printf是什么关系?其实文件printf.c中这段代码还有一次重要的别名替换:

ldbl_strong_alias (__printf, printf);

正是ldbl_strong_alias把__printf替换成了我们熟知的printf,并放到了libc的符号表中:

#define ldbl_strong_alias(name, aliasname) strong_alias (name, aliasname)

#   define strong_alias(original, alias)
\
.globl C_SYMBOL_NAME (alias) ASM_LINE_SEP
\
.set C_SYMBOL_NAME (alias),C_SYMBOL_NAME (original) ASM_LINE_SEP
\
.globl C_SYMBOL_DOT_NAME (alias) ASM_LINE_SEP
\
.set C_SYMBOL_DOT_NAME (alias),C_SYMBOL_DOT_NAME (original)

至此,我们引用的头文件和在程序运行是动态加载的动态库libc.so就连贯起来了。

3.printf是如何支持可变参数的

在printf的源码实现中我们可以看到,使用了va_list arg变量,并调用了va_start和va_end宏定义,对于va_*不清楚的可以百度一下。

  va_start (arg, format);

  done = vfprintf (stdout, format, arg);

  va_end (arg);

所以对于printf中可变参数的支持原来是如此简单。

4.printf进阶问题

我们看到printf的实现是调用了vfprintf。对于格式化输出的函数包括:printf, fprintf, sprintf, snprintf, vprintf, vfprintf, vsprintf, vsnprintf。我们再研究一下vfprintf, vfprintf的源码如下:

__fortify_function int
vfprintf (FILE *__restrict __stream,

 const char *__restrict __fmt, _G_va_list __ap)
{
return __vfprintf_chk (__stream, __USE_FORTIFY_LEVEL – 1, __fmt, __ap);
}

其中__vfprintf_chk的实现代码是:

int attribute_hidden __vfprintf_chk (FILE *s, int flag, const char *fmt, va_list ap) {   return __nldbl___vfprintf_chk (s, flag, fmt, ap); }

给TA打赏
共{{data.count}}人
人已打赏
安全运维

WordPress网站专用docker容器环境带Waf

2020-7-18 20:04:44

安全运维

运维安全-Gitlab管理员权限安全思考

2021-9-19 9:16:14

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索