运行库

运行库

入口函数和程序初始化

  • 程序并非从main函数开始,首先运行的代码是入口函数,负责准备好main函数执行所需要的环境,并且负责调用main函数。

  • GLIBC入口函数和MSVC CRT入口函数的实现略

  • I/O指代任何操作系统理解为文件的事务。在Linux里有文件描述符(File Descriptor),在Windows里由句柄(Handle)。句柄可以防止用户随意读写操作系统内核的文件对象。

  • IO初始化函数需要在用户空间中建立stdin、stdout、stderr机器对应的FILE结构,使得程序进入main之后可以直接使用printf、scanf等函数。

  • 堆初始化

C/C++运行库

  • C语言运行库的组成部分大部分是C语言标准库:

    • 标准输入输出(stdio.h)

    • 文件操作(stdio.h)

    • 字符操作(ctype.h)

    • 字符串操作(string.h)

    • 数学函数(math.h)

    • 资源管理(stdlib.h)

    • 格式转换(stdlib.h)

    • 时间/日期(time.h)

    • 断言(assert.h)

    • 各种类型上的常数(limits.h & float.h)

    • 变长参数(stdarg.h)

    • 非局部跳转(setjmp.h)

运行库与多线程

  • CRT的多线程困扰

    • 线程的访问权限

      可以访问进程内存里的所有数据,甚至包括其他线程的堆栈,但实际运用中线程也拥有自己的私有存储空间。

      • 栈:尽管并非完全无法被其他线程访问,但一般情况下仍然可以认为是私有的数据

      • 线程局部存储(Thread Local Storage):某些操作系统为线程单独提供的私有空间,但容量很有限

      • 寄存器:存放的数据是执行流的基本数据,为线程私有。

    • 多线程运行库

      • 对于C/C++标准库来说,线程相关的部分是不属于标准库的内容的,跟网络、图形图像一样,属于标准库之外的系统相关库。主流的CRT在设计时都会考虑:

        • 提供那些多线程操作的接口

        • 本身要能够在多线程的环境下正确运行

      • CRT不支持多线程的问题

        • errno:错误信息被其他线程覆盖

        • strtok:不同线程调用这个函数会把它内部的局部静态变量弄混乱

        • malloc/new与free/delete:堆分配/释放函数或关键字在不加锁的情况下是线程不安全的。

        • 异常处理:不同的线程抛出的异常会彼此冲突,从而造成信息丢失的情况。

        • printf/fprintf及其他IO函数:流输出函数同样是线程不安全的,因为它们共享了同一个控制台或文件输出。

        • 其他线程不安全函数:包括与信号相关的一些函数

  • CRT改进

    • 使用TLS

      • errno必须称为各个线程的私有成员。在glibc中,errno被定义为一个宏。函数_errno_location在单线程库版本中直接返回全局变量errno的地址;在多线程版本中,不同线程调用该函数返回的地址不同。
    • 加锁:在多线程版本的运行库中,线程不安全的函数内部都会自动地进行加锁。

    • 改进函数调用方式

      • 修改所有的线程不安全的函数的参数列表,改成某种线程安全的版本。比如,strtok_s()。

      • 最好的做法是不改变任何标准库函数的圆形,只是对标准库的实现进行一些改进,使得它能够在多线程环境下运行,做到向后兼容。

  • 线程局部存储(Thread Local Storage)实现

    如果要定义一个全局变量为LTS类型,值需要在定义前加上相应的关键字即可。对于GCC,这个关键字是__thread;对于MSVC,这个关键字是__declspec(thread)

    • Windows TLS的实现

      • 线程私有变量会放在PE文件的 .tls段。当线程启动时,会从堆中分配一块足够的内存,然后把tls段的内容复制过来,因此每个线程都有自己独立的副本。

      • tls表中保存了所有TLS变量的构造函数和析构函数的地址。TLS表本身位于PE文件的.rdata段

      • 每个线程都有一个线程环境块(TEB),保存线程的堆栈地址、线程ID等,其中有一个域是一个TLS数组,可以访问到TLS变量。

    • 显式LTS

      • 在Windows平台下,系统提供了TlsAlloc、TlsGetValue、TlsSetValue、TlsFree这个4个API

      • Linux对应的API是pthread_key_create、pthread_getspecific、pthread_setspecific、pthread_key_delete。

      • 相对于隐式的TLS变量,显式的TLS变量的使用十分麻烦,而且有诸多限制。并不推荐使用

C++全局构造和析构

不想看,太枯燥了

fread实现

  • 缓冲

    • 行缓冲

    • 全缓冲

  • 文本换行

    • Windows:\r\n 存储方式:0x0D(用CR表示)、0x0A(用LF表示)这两个字节

    • Linux/Unix:\n

    • Mac OS:\r

  • 调用轨迹:

    • fread

    • ->fread_s:增加缓冲移除保护,加锁

    • ->_fread_nolock_s:循环读取、缓冲

    • ->_read:换行符转换

    • ->ReadFile:Windows文件读取API

本章小结

posted @ 2021-03-13 10:57  睿阳  阅读(762)  评论(0编辑  收藏  举报