C运行库、C标准库、操作系统API与系统调用的关系

所有的代码/函数的运行都要归结到机器级指令上来

自己编写的hello world小程序,编译成为了.exe文件,是机器级指令

调用的stdio.h中声明的prinf函数,实际上在msvcrt.dll/libc.so运行库中,是机器级指令

调用的unistd.h中声明的write函数,其对应的运行库在哪里呢?其实也在glibc中。

 

头文件.h与库文件.a/.so/.lib/.dll

  • 当我们#include<stdio.h>时,实际上是把这个头文件中的所有代码在我们的源文件中展开了而已,头文件中实际上没有任何函数实现(没有函数,当然更没有main())只有各种宏、函数的声明和全局变量的extern声明而已!如果是C++的话就是类的声明(包括类里面的成员和方法的声明)、函数原型、宏等
  • 头文件中声明函数、宏、全局变量等,具体的实现在哪里呢?如果是自己的函数,则实现在.c/.cpp源文件中,如果是别人提供的接口,则实现在.so或者.a文件中。
  • 那么printf()函数在stdio.h中只是被声明而已,对应的.a或者.so在哪里呢?具体的实现是C运行库的一部分,不同的平台实现不同,Windows下在msvcrt.dll中实现,(本机上这个文件在C:\Windows\System32和C:\Windows\SystemWoW64中),Linux下在lib.so中实现
  • 但是,我们只是在源代码中包含了.h文件而已,真正的.so文件是如何被链接的呢?事实上gcc在编译时会默认链接一些库,如:
    • linux-gate.so 负责内核调用的库
    • libc.so 也就是glibc,C运行时库
    • ld-linux.so 加载libc和其他库

 

C运行库

是C语言的概念,与操作系统无关;例如<stdio.h>,<cmath>是运行库(的头文件),其函数的具体实现实际上在libc.so/msvcrt.dll文件中, 这就是C运行时库。只有当我们的电脑上安装了开发环境才会有stdio.h文件(在编译器目录下),但是C运行时库是系统自带的,因为操作系统本身的运行都时时依赖msvc.dll(操作系统本来就是用C语言编写的啊)

C运行时库虽然是给C语言编程序用的,但其本身可以是汇编语言编写的,也就是源代码是汇编语言。

我们自己也可以编写自己的动态库,如创建"my_math.h"/"my_math.cpp"工程, 并将其编译为.so文件,然后新的程序包含头文件就可以使用其中的函数了。显而易见有好处:不必重复编译从而节省时间、源代码不公开从而保密等等!

 

C标准库

C标准定义了C语言的实现,由两部分组成,C的语法(编译器),C语言标准库。要在一个平台上实现C标准,要实现C编译器与C标准库。  

C标准库说白了就是一组标准头文件,每个头文件中包含函数、变量、类型声明和宏定义。这些声明的具体实现在库文件中,我们也可以按照C标准库头文件编写自己的标准库,然后编译成.so文件。其实C标准库及其扩展有很多种,标准只是理论,各种平台上的实现只需要与标准一致即可。

Linux平台下最广泛使用的C运行时库是glibc,包含了C标准库和系统函数。最基本、最常用的C标准库函数和系统函数在libc.so库文件中,几乎所有C程序的运行都依赖于libc.so,有些做数学计算的C程序依赖于libm.so,以后我们还会看到多线程的C程序依赖于libpthread.so

 

操作系统API与系统调用

API是操作系统为开发者提供的,因为仅仅依靠C标准库我们无法实现很多功能,如IO,访问内核代码, 打开文件、创建进程等。这些功能需要系统调用实现。API为C标准库提供接口,实例化的C标准库(如glibc)是在操作系统API上加入独特的算法封装成标准接口的库。

glibc中不仅提供了C标准库libc的实现,还提供了系统的API. 其实和libc一样也是一系列的库函数,其头文件有<unistd.h>等

gilbc是库,包含了C标准库和操作系统API,但为什么也称之为“系统调用”呢?

实际上系统调用可以分为两类:

1. 操作系统服务例程(系统调用函数,狭义/真正的系统调用,在内核中,普通进程无法访问

2. 系统调用封装函数(glibc封装函数,系统调用API函数)

两者几乎是一一对应的:open函数是API,对应系统调用函数sys_open,通过软中断实现调用内核的系统服务例程。

Linux中每个系统调用都有相应的系统调用号作为唯一的标识,内核维护一张系统调用表,sys_call_table,表中的元素是系统调用函数的起始地址,而系统调用号就是系统调用在调用表的偏移量。

总之,应用程序到C标准库到API到系统调用,(几乎)是一个高层到底层的过程。(Linux平台下APP可以不用libc而直接使用API)

最后,请思考,如何不使用C语言标准库(不#include<stdio.h>等),不用printf()函数而实现打印字符串到终端?

参考资料

https://www.zhihu.com/question/46763480

https://blog.csdn.net/qq_40732350/article/details/81937915

https://wenku.baidu.com/view/6cf2e96a760bf78a6529647d27284b73f3423639.html

posted @ 2019-08-01 20:19  LiaoQian1996  阅读(1237)  评论(0编辑  收藏  举报