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

2023-05-16

题记:本系列文章的目的是抛开书本从源代码和使用的角度分析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);
}







本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux从用户层到内核层系列 - GNU系列之你所不知道的printf 的相关文章

  • Selenium:下拉框操作

    下拉框 1 在Web网页中经常会遇到下拉框 下拉列表 比如说分页 每页显示的条数 语言的切换等功能 很多时候经常都是以下拉框的形式展现 2 下拉列表是网页中一种最节省页面空间的选择方式 默认状态下只显示一个选项 只有单击下拉按钮后才能看到全
  • Selenium:上传、下载文件

    Selenium 上传 下载文件 1 上传文件是比较常见的Web功能之一 比如 常见的上传头像功能 2 对于Web页面的上传功能实现一般有以下两种方式 nbsp nbsp nbsp 普通上传 普通的附件上传是将本地文件的路径作为作为一个值放
  • Python:类的__dict__属性

    Python中类的 dict 属性 1 Python中一切皆对象 nbsp nbsp nbsp 不管是内置的数据类型 内置函数 内置类 第三方类 还是自定义类都叫做对象 nbsp nbsp nbsp 一个对象就会有自己的方法或属性 只是说有
  • Python:处理XML文件汇总

    处理XML文件汇总 1 前面介绍了使用Python中的ElementTree库来解析 创建XML文件 nbsp nbsp nbsp 如果对这个库不了解的可以先去看看这个库 nbsp nbsp nbsp 地址 https mouse blog
  • Python:json数据与Python对象之间的转换

    json数据与Python对象之间的转换 1 前面介绍了在Python中如何处理json数据 主要使用了以下几个方法 nbsp nbsp nbsp dumps 将Python对象转换为JSON格式的字符串 nbsp nbsp nbsp du
  • 用结构体指针访问结构体中的结构体指针(结构体指针的嵌套)

    结构体中的结构体指针是使用 一 问题背景二 代码三 说明 一 问题背景 日常工作中没有编程的机会 xff0c 所以只看得懂代码 xff0c 现在需要重新写一段代码 xff0c 实现固定格式存储数据 xff0c 需要使用到结构体和结构体指针
  • MOOC —— Python语言基础与应用 by 北京大学 第五章 计算和控制流(一)

    第五章 计算和控制流 xff08 一 xff09 23 自动计算过程24 控制流程25 条件分支语句26 条件循环27 迭代循环28 上机练习 基本的计算程序测验题 23 自动计算过程 简单介绍了 冯 诺依曼结构计算机 xff0c 五大部件
  • 关于sort函数中的cmp参数的笔记(C++)

    C 43 43 中sort函数是经常会用到的一个函数 xff0c 主要的功能是排序 xff0c 可以给数组 xff0c 或者链表list 向量排序 sort first pointer first pointer 43 n cmp 在三个参
  • 16进制转10进制方法(初级)

    目标 xff1a 输入16进制字符串 xff0c 输出对应10进制数字 以下以一个题目给出方法 xff1b span class token comment span span class token comment coding in 2
  • 图像格式RGB与BGR互转

    在opencv中 xff0c 读取后图像的格式为BGR的 与其它的读取图像方式不同 xff0c 需要进行转换 基于python的切片特性 xff0c 可以快速进行转换 BGR to RGB opencv 转 常规img im 61 im 1
  • 深度学习框架Pytorch——学习笔记(九) 基础知识补充

    由于基础知识不牢固 xff0c 又学习了一下其他入门学习资料 pytorch中文手册 https github com zergtant pytorch handbook 入门基础补充 相比之前学过的东西 xff0c 一些常用的东西做了下补
  • 深度学习-CV基础知识笔记

    CNN卷积神经网络相关 分类 xff0c backbone LeNet 卷积神经网络的里程碑 由2个卷积层 2个池化层和2个全连接层组成 xff0c 卷积核大小是5 5 xff0c 池化层大小为2 2 xff0c 最后通过softmax输出
  • 目标检测基础算法框架代表

    目前的目标检测可以分为 单阶段 one stage xff0c 双阶段 two stage xff0c anchor free 三个方向 还有一个最近杀的比较疯的Transformer 还属于探索阶段 目前前沿检测框架是基础算法框架的衍生
  • Django 学习笔记 (二)dwebsocket

    dwebsocket 一次连接 xff0c 就可以直接发送 xff0c 不需要多次请求 安装 通过pip安装有问题 可直接下载git版 xff0c 将websocket文件复制到项目中 使用代码解释 django中 服务端 独立url数据通
  • 深度学习框架Pytorch——学习笔记(一)基础认识和实践

    深度学习框架Pytorch 学习笔记 xff08 一 xff09 记录在学习过程中 xff0c 学到的东西和遇到的问题 1 什么是Pytorch xff0c 为什么选择Pytroch xff1f pytorch是深度学习框架 xff0c 是
  • 深度学习框架Pytorch——学习笔记(四)逻辑回归

    深度学习框架Pytorch 学习笔记 xff08 四 逻辑回归 在上一个线性回归的基础上进行实现逻辑回归 逻辑回归中用到了sigmoid函数 可以理解为将结果值转为概率问题 0 1 之间 xff0c 逻辑回归实际上就是一个二分类了 将结果分
  • 深度学习框架Pytorch——学习笔记(六)PyTorch实现L1,L2正则化以及Dropout

    深度学习框架Pytorch 学习笔记 xff08 六 PyTorch实现L1 xff0c L2正则化以及Dropout 什么是过拟合 xff1f 过拟合的表现就是在训练过程中 xff0c loss越来越低 xff0c 但在测试集上测试发现效
  • debian源配置

    说明 xff1a 在没有接入网络的情况下 xff0c 采用最小话方式安装debian xff0c 默认是没有配置apt get源 没有安装远程管理服务SSH的 xff0c 这个时候是无法使用apt get install 在线安装软件的 x
  • 深度学习框架Pytorch——学习笔记(七)常用损失函数和神经网络优化方法,激活函数简述

    深度学习框架Pytorch 学习笔记 七 常用损失函数和神经网络优化方法 xff0c 激活函数简述 优化方法参考地址 https www leiphone com news 201706 e0PuNeEzaXWsMPZX html 网络的可
  • Ubuntu更新报错: 无法下载 http://cn.mirrors.ustc.edu.cn/ubuntu/dists/bionic/InRelease 连接失败BUNTU

    Ubuntu执行apt get update报错 xff1a 解决方案 xff1a xff08 1 xff09 这是因为ubuntu的服务器在国外 xff0c 使用国内的软件源在软件下载速度方面相对比较快 输入 xff1a 以下命令 cd

随机推荐