Linux从用户层到内核层系列 - GNU系列之你所不知道的printf
来源:互联网 发布:数学建模算法与程序 编辑:程序博客网 时间:2024/06/11 20:40
题记:本系列文章的目的是抛开书本从源代码和使用的角度分析Linux内核和相关源代码,byhankswang和你一起玩转linux开发
轻松搞定TCP/IP协议栈,原创文章欢迎交流, byhankswang@gmail.com
欢迎加入到CHLK - Linux开发交流群 QQ:327084515 讨论Linux开发相关问题
原创文章,转载请标明出处。
GNU系列之你所不知道的printf
上篇文章介绍了GNU的libc并引出了几个问题(《Linux从用户层到内核层系列 - GNU系列之glibc介绍》,URL:http://blog.csdn.net/byhankswang/article/details/9319099),本篇文章将从实例的角度分析并解决之前的问题。这边文章立志把printf的问题从用户层到库函数再到内核调用这个脉络彻底讲明白。
首先从我们的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);
}
- Linux从用户层到内核层系列 - GNU系列之你所不知道的printf
- Linux从用户层到内核层系列 - GNU系列之glibc介绍
- Linux从用户层到内核层系列 - 进程管理系列1: 进程之子承父业
- Linux从用户层到内核层系列 - 开源项目之Libxml2
- Linux从用户层到内核层系列 - TCP/IP协议栈部分系列7: 基础知识之 - Linux内核源码目录与内核编译选项
- Linux从用户层到内核层系列 - Kernel compiling: Questions & Note
- Linux从用户层到内核层系列 - TCP/IP协议栈部分系列1: linux内核协议栈协议的注册
- Linux从用户层到内核层系列 - TCP/IP协议栈部分系列10:linux内核协议栈中对于socket相关API的实现
- Linux从用户层到内核层系列 - TCP/IP协议栈部分系列11: 再话Linux系统调用
- Linux从用户层到内核层系列 - TCP/IP协议栈部分系列8: 基础知识之 - Linux环境变量使用Tip
- Linux从用户层到内核层系列 - TCP/IP协议栈部分系列5:内核定时器的定义与使用及STP定时器
- Linux从用户层到内核层系列 - TCP/IP协议栈部分系列9: 基础知识之 - 操作系统中常用存储介质详解
- Linux从用户层到内核层系列 - TCP/IP协议栈部分系列2: 协议栈各种协议的初始化
- Linux从用户层到内核层系列 - TCP/IP协议栈部分系列3: bridge(网桥)FDB表中MAC地址的更新
- 你所不知道的传输层
- 你所不知道的传输层
- 你所不知道的传输层
- 你所不知道的传输层
- Pig实战
- Edit Control 控件属性 添加消息 [大三TJB_708]
- 21-定义枚举的构造函数、方法和字段
- 关于 size_t , uintptr_t , intptr_t, int
- WebForm请求模型与MVC请求模型 图
- Linux从用户层到内核层系列 - GNU系列之你所不知道的printf
- idea 使用手册
- git 获取android source
- 表长(lua)
- 字符串逆序(lua实现)
- 职场晋升有玄机,老板红人最得意
- wash card(java 实现)
- vmware tools ,/mnt/hgfs 下没有共享
- lua取整