GLIBC源码——putchar

GLIBC源码——从我认为最简单的putchar开始

putchar放在putchar.c中,而putchar.c放在libio文件夹里

加上注释,一共只有36

#include "libioP.h"
#include "stdio.h"#undef putcharint
putchar (int c)
{int result;_IO_acquire_lock (stdout);result = _IO_putc_unlocked (c, stdout);_IO_release_lock (stdout);return result;
}#if defined weak_alias && !defined _IO_MTSAFE_IO
#undef putchar_unlocked
weak_alias (putchar, putchar_unlocked)
#endif

首先是putchar的定义,然后是一个编译宏:如果定义了weak_alias并且没有定义_IO_MTSFAE_IO,即线程安全的IO,则,putchar_unlocked就和putchar没有区别

我理解为,当没有定义线程安全的IO后,putchar所使用的锁就失效了,所以和putchar_unlocked功能上就一样了

所有带有_unlocked后缀的函数都没有对自己的锁,因此不是线程安全的


putchar的流程大致如下:申请锁,把真正的工作交给_IO_putc_unlocked函数,释放锁

其中,和IO锁有关的函数在libio/libioP.h里面定义

打开这个文件,查找对应的定义:

static inline void
__attribute__ ((__always_inline__))
_IO_acquire_lock_fct (FILE **p)
{FILE *fp = *p;if ((fp->_flags & _IO_USER_LOCK) == 0)_IO_funlockfile (fp);
}#if !defined _IO_MTSAFE_IO && IS_IN (libc)
# define _IO_acquire_lock(_fp)                                                \do {# define _IO_release_lock(_fp)                                                \} while (0)
#endif

让我比较震惊的是,不论在哪里都找不到_IO_release_lock有关的定义

很显然的是,_IO_acquire_lock也不是在这里定义的

进行查找后发现在sysdeps里面的文件里有定义,说明这个锁的实现和具体的架构有关

sysdeps/generic/stdio-lock.h里面,我找到了定义

#if defined _LIBC && IS_IN (libc)# ifdef __EXCEPTIONS
# define _IO_acquire_lock(_fp) \do {                                                                        \FILE *_IO_acquire_lock_file                                               \__attribute__((cleanup (_IO_acquire_lock_fct)))                       \= (_fp);                                                              \_IO_flockfile (_IO_acquire_lock_file);
# else
#  define _IO_acquire_lock(_fp) _IO_acquire_lock_needs_exceptions_enabled
# endif
# define _IO_release_lock(_fp) ; } while (0)#endif

这里的__EXCEPTIONS宏,我只在ChangeLog里面找到了一句话:[!__EXCEPTIONS]: Mangle local variable not_first_call.


Cleanup修饰一个变量在该变量作用域结束后, 自动调用一个指定的方法。

所谓作用域结束,包括大括号结束、return、goto、break、exception等各种情况。

https://blog.csdn.net/u013062716/article/details/52145692?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control


完整地把这一段连在一起:

do {FILE *_IO_acquire_lock_file __attribute__((cleanup(_IO_acquire_lock_fct))) = (_fp);_IO_flock_file(_IO_acquire_lock_file);// some code} while(0);

首先,声明了一个FILE*类型的智能指针,它在出作用域的时候,会自动调用_IO_acquire_lock_fct这个函数
但是在这个函数出作用域之前,会先调用_IO_flockfile这个宏

这个宏定义在htl/flockfile.c

void
__flockfile (FILE *stream)
{#ifdef SHARED__libc_ptf_call (_IO_flockfile, (stream), 0);
#endif
}
weak_alias (__flockfile, _IO_flockfile)
weak_alias (__flockfile, flockfile)

_IO_flockfile__flockfile的弱别名

它调用了这个宏:__libc_ptf_call,即pthread function call,它定义在libc-lockP.h

# define __libc_ptf_call(FUNC, ARGS, ELSE) \(__libc_pthread_functions_init ? PTHFCT_CALL (ptr_##FUNC, ARGS) : ELSE)

即这个宏会让__flockfile执行ptr__IO_flockfile这个函数

这个函数定义在pthread_function.h中,它是结构体pthread_functions的一员

htl/pt-initialize.c中,这个函数指针指向了这个函数:_cthreads_flockfile

该函数在sysdeps/htl/pthread-functions.h里面有一个声明,而定义的内容则放在htl/lockfile.c中:

void
_cthreads_flockfile (FILE *fp)
{_IO_lock_lock (*fp->_lock);
}

这个宏,_IO_lock_lock,在多个地方都有定义:

#define _IO_lock_lock(_name) \do {                                                                        \void *__self = THREAD_SELF;                                               \if ((_name).owner != __self)                                              \{                                                                       \lll_lock ((_name).lock, LLL_PRIVATE);                                 \(_name).owner = __self;                                               \}                                                                       \++(_name).cnt;                                                            \} while (0)

这里有一个lll_lock和一个THREAD_SELF
THREAD_SELF大概只是一个身份标记之类的宏,它的实现细节涉及到底层汇编

lll_locklll的意思就是,low level lock
它定义在sysdeps/nptl/lowlevellock.h
它才是真正的上锁的工具
它的底层实现使用了原子操作,具体细节因为体力优先,不再深究


现在让我们梳理一下路径:
putchar需要调用_IO_acquire_lock这个宏

这个宏在libio/libioP.h中有相关的一个函数,叫_IO_acquire_lock_fct,理解为_IO_acquire_lockfact函数

_IO_acquire_lock,在sysdeps/generic/stdio-lock.h里面,有一个定义,它声明了一个在出了定义域后就会自动调用_IO_acquire_lock_fct的变量,然后调用了一个函数_IO_flockfile

而这个_IO_flockfile其实是__flockfile的别名,它定义在sysdeps/htl/flockfile.c里,调用了一个函数,名为ptr__IO_flockfile,这个函数是sysdeps/htl/pthread-functions.h里面的一个结构体pthread_functions里面的一员

而这个变量的初始化放在htl/pt-initialize.c中,它指向_cthreads_flockfile,定义在htl/lockfile.c中,调用了_IO_lock_lock,这个宏使用了lll_lockTHREAD_SELF,它们几乎是最底层的操作,使用了内嵌汇编和原子操作等

_IO_acquire_lock_fct里面调用了_IO_funlockfile,它是__funlockfile的别名,定义在sysdeps/pthread/funlockfile.c中,调用了unlock


至此,整个文件加锁的流程都在我们面前展示出来了(我们只是简单地跟踪了调用的函数流程,并没有深究,很多细节都被我们忽略,只能知其然)

任何要加锁的函数都要调用_IO_acquire_lock_IO_release_lock这一对宏,其中_IO_release_lock基本上什么都没干,主要工作放在_IO_acquire_lock里面

这一对宏构建了一个像这样子的结构

do {IO_filelock// some codeIO_fileunlock
} while(0);

_IO_acquire_lock一个人差不多就实现了加锁和放锁的所有功能

加锁是怎么实现的呢?使用了_IO_flockfile这个函数,它会调用_cthreads_flockfile,而这个函数会调用两个底层的宏,通过原子操作和自旋锁来实现加锁的功能

而放锁的功能使用了GNU的扩展:__attribute__((cleanup)),只要变量出了定义域,就自动调用_IO_acquire_lock_fct函数,这个函数名字起得好像是要加锁一样,其实执行的是解锁的功能
为什么要用这个扩展呢?而不是直接放在_IO_release_lock这个宏里呢?
主要是因为,只要这个变量出了有效区域,不管是以什么形式出去的,都会失效
如果放在_IO_release_lock里面,则会因为执行到一般的时候出错了或者其他原因而退出

至于自旋锁具体是怎么用的,我觉得不适合今天的研究,毕竟今天的研究甚至没有进入正题:研究putchar是怎么输出的


int
putchar (int c)
{int result;_IO_acquire_lock (stdout);result = _IO_putc_unlocked (c, stdout);_IO_release_lock (stdout);return result;
}

如上,主要工作其实是_IO_putc_unlocked这个宏在做

这个宏定义在libio/libio.h

#define _IO_putc_unlocked(_ch, _fp) __putc_unlocked_body (_ch, _fp)

__putc_unlocked_body定义在libio/bits/types/strcut_FILE.h

#define __putc_unlocked_body(_ch, _fp)                                  \(__glibc_unlikely ((_fp)->_IO_write_ptr >= (_fp)->_IO_write_end)      \? __overflow (_fp, (unsigned char) (_ch))                            \: (unsigned char) (*(_fp)->_IO_write_ptr++ = (_ch)))

首先判断是否越界,如果没有越界,则将_IO_write_ptr赋值并后移一位,于是文件内容就被修改了

如果越界,则调用__overflow
它的声明:

libio/stdio.h:extern int __overflow (FILE *, int);

它定义在libio/genops.c

int
__overflow (FILE *f, int ch)
{/* This is a single-byte stream.  */if (f->_mode == 0)_IO_fwide (f, -1);return _IO_OVERFLOW (f, ch);
}

具体的实现过程,因为太复杂了,而且体力有限,没有办法继续深究

总结

这次探索给我带来的收获有:

  1. 学习了__attribute__((cleanup))这个扩展
  2. 进一步熟悉了怎么在浩大的源码里找到自己需要的东西
  3. 感受到了源码的巧妙的构思,学习到了一些奇妙的方法

GLIBC源码——putchar相关推荐

  1. 【glibc源码分析】--strcpy.c 字符串复制

    strcpy是常用的字符串复制函数,经常在面试中考到.该文件位于glibc源码的string目录中. 在线资源路径: http://www.oschina.net/code/explore/glibc ...

  2. Linux中C语言标准库glibc源码下载

    在这篇文章理清gcc.libc.glibc.libc++.libstdc++的关系,我们大概理解了libc,glibc之间的一些关系. 下面我们就开了解一些Linux中C语言标准库glibc源码. 在 ...

  3. 【精选】uboot/linux/qt/busybox/opengl/yaffs/lzop/glibc源码下载地址

    随着Linux.Android等开源平台和开源项目的推广和应用,它们所带来的开源思维也更加深入的普及到更多领域.现在,非常多的项目都推崇开源分享了(当然,商业盈利模式的应用仍然是要收费的,此处不讨论这 ...

  4. glibc源码编译安装

    文章目录 1.检查已有版本: 2.下载源码: 3.编译源码: 4.再次检查版本: 1.检查已有版本: strings /lib64/libc.so.6 |grep GLIBC_ 2.下载源码: 官方链 ...

  5. glibc源码分析之utime系列函数

    glibc中关于utime的函数有utime,utimes.它们都是系统调用的封装函数. utime函数由脚本生成.生成的.S文件的内容为: #define SYSCALL_NAME utime #d ...

  6. 源码解析glibc中的pclose与fclose函数

    文章目录 pclose 和 fclose 的阻塞问题 测试代码 问题 pclose与fclose的关系 fclose函数的定义 pclose函数的定义 总结 fclose中的block 有嫌疑的地方 ...

  7. glibc-2.23学习笔记(一)—— malloc部分源码分析

    glibc-2.23学习笔记(一)-- malloc部分源码分析 搭建Glibc源码调试环境 1.下载并解压glibc源码 2.配置gdb 3.编译测试程序 第一次调用 源码分析 __libc_mal ...

  8. Linux线程同步(三)---互斥锁源码分析

    先给自己打个广告,本人的微信公众号:嵌入式Linux江湖,主要关注嵌入式软件开发,股票基金定投,足球等等,希望大家多多关注,有问题可以直接留言给我,一定尽心尽力回答大家的问题. 一 源码分析 1.li ...

  9. socket 源码解析之创建

    数据结构 /*** struct socket - general BSD socket* @state: socket state (%SS_CONNECTED, etc)* @flags: soc ...

最新文章

  1. 行为模式之Observer模式
  2. jsp文件通常用common_29.jsp-动态生活之用Commons-FileUpload组件控制文件上传
  3. 二叉树最大深度:给定一个二叉树,找出其最大深度。 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
  4. 在软件开发中应用80:20原则
  5. ▲数据结构 【2012】五2 C++版
  6. 生活与工作原则-Ray Dalio
  7. SCM提升供应链管理效率
  8. 等保测评--网络安全等级保护定级指南
  9. 机房短信通(SMS Alert System)
  10. 解决Vue中的对象属性无法实现动态响应
  11. java 倒计时_Java倒计时实现的三种简单方式
  12. 解决本地图片上传正常,服务端上传图片报500
  13. 数论(继续补充)(gcd + lcm + qpow + prime+qmul)
  14. Metasploit 渗透测试01-背景和功能介绍
  15. 数据报告 | 2017年中国汽车后市场app研究报告
  16. Scala编程(一)
  17. 局域网中资源共享(FTP)
  18. css中div超出自动换行
  19. 计算机管理员权限无法粘贴,win10, windows10 复制文件需要管理员权限 才能复制的解决方法...
  20. 【鸿蒙操作系统刷机教程】你知道安卓(小米,红米,一加,OPPO,vivo,荣耀,魅族手机)怎么刷华为鸿蒙操作系统(HarmonyOS)刷机包吗?

热门文章

  1. 不同VLAN间是如何通信的?
  2. 52句《职来职往》经典语句,震惊大学生!
  3. 默克公司扩大美国生命科学产能,合计投资4000万欧元
  4. 一个字节可以用多少位的十六进制表示
  5. 加密解密(字符串处理)
  6. 辛巴巴巴鲁比啦音乐计算机版,抖音辛巴巴巴鲁比拉是什么歌 辛巴巴巴鲁比啦完整歌曲歌词...
  7. Process的waitFor死锁问题及解决办法
  8. STL LIST使用简介
  9. 身着OpenParty紧身服上台领金牌,并现身于滨江中路珠江旁:)
  10. 怎样给自己的网站加一个网址,然后让别人访问呢?