C/C++ 运行库

C/C++ 运行库


       任何一个C 程序,它的背后都有一套庞大的代码来进行支撑,以使得该程序能够正常运行。这套代码至少包括入口函数,及其所依赖的函数所构成的函数集合。另外包括各种标准库函数的实现。
       这样的一个代码集合称之为运行时库(Runtime Library),C 语言的运行时库,称为 C 运行库(CRT)。
       C 运行库大致包含的功能:

  • 启动与退出函数
  • 由C 语言标准规定的C 语言标准库所拥有的函数实现。
  • I/O 功能的封装与实现。
  • 堆的封装和实现
  • 语言中一些特殊功能的实现。
  • 实现调试功能的代码。

C语言标准库

C语言的标准库非常轻量,仅仅包含了数学函数、字符/字符串处理,I/O 等基本方面。

  • 标准输入输出—-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
  1. 变长参数宏

    GCC:
    #define printf(args...) fprintf(stdout,##args)

    MSVC:
    #define printf(...) fprintf(stdout,__VA_ARGS__);

  2. 非局部跳转
           使用非局部跳转,可以实现从一个函数体内向另一个事先登记过的函数体内跳转,而不必担心堆栈混乱。

#include<setjmp.h>
jmp_buf b;
void f()
{
    longjmp(b,1);
}
int main()
{
    if(setjmp(b))
    {
        printf("World!\n");
    }
    else
    {
        printf("Hello ");
        f();
    }
    return 0;
}

       运行库是平台相关的,C 语言的运行库从某种程度上说是C 语言的程序和不同操作系统平台之间的抽象层,将不同的操作系统API 抽象成了相同的库函数。但是C 运行库的功能毕竟有限,比如创建线程等操作,不同的操作系统完全不同。我们不得不通过其它的方法,绕过C 语言运行库直接调用操作系统API 或使用其它的库。Linux 和 Windows 平台下两个主要C 语言的运行库分别为glibc 和 MSVCRT。像线程操作这样的功能,虽然不是标准C 语言运行库的一部分,也现在两个库中,因此glibc 和 MSVCRT 事实上是标准C 语言运行库的超集。

  • glibc 组成
           主要包含两部分:1.头文件,比如stdio.h、stdlib.h等。往往位于/usr/include。2.库的二进制文件部分,分为静态和动态两个版本。动态:/lib/libc.so.6 。非静态:/usr/lib/libc.a。另外还有辅助程序运行的运行库:/usr/lib/crtl.o、/usr/lib/crti.o 和 /usr/lib/crtn.o。这几个文件虽然小,但都是程序运行的最关键的文件。

  • MSVC CRT
    命名规则如下:
    libc [p] [mt] [d] .lib
      p 表示 C++ 标准库
      mt 表示支持多线程
      d 表示调试版本

常见的MSVC CRT 版本

文件名相关的DLL属性编译器选项预编译宏
libcmt.lib多线程,静态链接/MT_MT
msvcrt.libmsvcr80.dll多线程、动态链接/MD_MT、_DLL
libcmtd.lib多线程、静态链接、调试/MTd_DEBUG、_MT
msvcrtd.libmsvcr90d.dll多线程、动态链接、调试/MDd_DEBUG、_MT、_DLL
msvcmrt.libmsvcm90.dll托管/本地混合代码/clr
msvcurt.libmsvcm90.dll纯托管代码/clr:pure

当我们在程序里包含某个C++ 标准库的头文件时,MSVC 编译器认为该源代码文件是一个C++ 源代码程序,会在编译时根据编译选项,在目标文件的”.drectve”段相应的C++ 标准库连接相应的C++ 标准库信息。

C++ CRT

文件名相关的DLL属性编译器选项预编译宏
LIBCPMT.LIB多线程,静态链接/MT_MT
MSVCPRT.LIBMSVCP90.dll多线程、动态链接/MD_MT、_DLL
LIBCPMTD.LIB多线程、静态链接、调试/MTd_DEBUG、_MT
MSVCPRTD.LIBMSGCP90D.DLL多线程、动态链接、调试/MDd_DEBUG、_MT、_DLL

运行库与多线程

线程私有线程之间共享
局部变量 函数的参数 TLS数据全局变量 堆上的数据 函数里的静态变量 程序代码 文件资源

多线程运行库主要包含两方面的支持:

  • 提供多线程操作的接口,如创建线程、退出线程、设置线程优先级等函数
  • 运行库本身能够在多线程环境下正确的执行

CRT 为支持多线程所做的改进

  • 使用TLS
    TLS 即线程局部存储,多线程环境下,设置错误代码时将值设置到TLS 中,以免引起混乱。

  • 加锁
    线程不安全的函数内部自动加锁,包括malloc printf 等。

  • 改进函数调用方式
    比如strtok 函数内部实现使用了一个char* 类型的静态局部变量,新版本将添加一个char* 指针参数,替代原来的静态局部变量的功能,但同时,使用此函数的源代码需要进行相应的修改。

C++ 全局构造与析构

  • glibc 全局构造与析构
    “.init” 和 “.finit”段,这两个段中的代码最终会被拼成两个函数_init() 和 _finit(),这两个函数会先于/后于 main 函数执行。
    调用流程如下:
    _start->__libc_start_main->__libc_csu_init->_init->__do_global_ctors_aux。
void __do_global_ctors_aux(void)
{
    /* call constructor functions. */
    unsigned long nptrs = (unsigned long) __CTOR_LIST__(0);
    unsigned i;
    for(i = nptrs; i >= 1; i--)
        __CTOR_LIST__[i]();
}

_CTOR_LIST_数组的第一个元素为元素的个数,下面的对应个数的函数即各个模块的全局构造函数。

析构函数的实现是通过__exa_atexit()在exit()函数中注册进程退出回调函数来实现析构。按照构造的逆序注册对应的析构函数。

  • MSVCRT 全局构造与析构
    mainCRTStartup->_initterm
typedef void (__cdecl * _PVFV)();
static void __cdecl _initterm(_PVFV* pfbegin,_PVFV* pfend)
{
    while ( pfbegin < pfend)
    {
        if(*pfbegin != NULL)
            (**pfbegin)();
        ++ pfbegin;
    }
}

析构函数
同Glibc,通过atexit() 实现全局析构。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值