• 运行库


    运行库

    入口函数和程序初始化

    • 程序并非从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: 存储方式:0x0D(用CR表示)、0x0A(用LF表示)这两个字节

      • Linux/Unix:

      • Mac OS:

    • 调用轨迹:

      • fread

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

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

      • ->_read:换行符转换

      • ->ReadFile:Windows文件读取API

    本章小结

  • 相关阅读:
    ANSI C
    如何判断机器的endianness
    union的常见用法
    主流浏览器引擎
    用宏来求数组元素个数
    inode
    分区时"磁盘上没有足够的空间完成此操作"的解决方法
    删除OEM分区
    jquery加table布局 模仿实现FaceBook Dialog
    Container.DataItem使用
  • 原文地址:https://www.cnblogs.com/fr-ruiyang/p/14527922.html
Copyright © 2020-2023  润新知