Linux 库函数与系统调用的关系与区别
上周总结了《C 标准库的基础 IO》,其实这些功能函数通过「系统调用」也能实现相应功能。这次文章并不是要详细介绍各系统调用接口的使用方法,而是要深入理解「库函数」与「系统」调用之间的关系和区别。
一、系统调用
系统调用,我们可以理解是操作系统为用户提供的一系列操作的接口(API),这些接口提供了对系统硬件设备功能的操作。这么说可能会比较抽象,举个例子,我们最熟悉的 hello world 程序会在屏幕上打印出信息。程序中调用了 printf() 函数,而库函数 printf 本质上是调用了系统调用 write() 函数,实现了终端信息的打印功能。
二、库函数
库函数可以理解为是对系统调用的一层封装。系统调用作为内核提供给用户程序的接口,它的执行效率是比较高效而精简的,但有时我们需要对获取的信息进行更复杂的处理,或更人性化的需要,我们把这些处理过程封装成一个函数再提供给程序员,更方便于程序猿编码。
库函数有可能包含有一个系统调用,有可能有好几个系统调用,当然也有可能没有系统调用,比如有些操作不需要涉及内核的功能。可以参考下图来理解库函数与系统调用的关系。
三、系统调用意义
- 避免了用户直接对底层硬件进行编程。比如最简单的
hello world程序是将信息打印到终端,终端对系统来说是硬件资源,如果没有系统调用,用户程序需要自己编写终端设备的驱动,以及控制终端如何显示的代码。 - 隐藏背后的技术细节。比如读写文件,如果使用了系统调用,用户程序无须关心数据在磁盘的哪个磁道和扇区,以及数据要加载到内存什么位置。
- 保证系统的安全性和稳定性。要知道用户程序是不能直接操作内核地址空间的,比如一个刚出道的程序猿,让他直接去访问内核底层的数据,那么内核系统的安全性就无法保证。而系统调用的功能是由内核来实现,用户只需要调用接口,无需关心细节,也避免了系统的安全隐患。
- 方便程序的移植性。如果针对一个系统资源的功能操作比如 write(),大家都按照自己思路去实现这个功能,那么我们写出来的程序的移植性就会非常差。
总而言之,我们只需要把系统调用当作一个接口,而这个接口能实现我们的一个功能,既方便又安全。
四、库函数 vs 系统调用
参考了《C 专家编程》书籍中的附录 A.4,书中关于两者区别的回答是这样的,函数库调用是语言或应用程序的一部分,而系统调用是操作系统的一部分。
- 所有 C 函数库是相同的,而各个操作系统的系统调用是不同的。
- 函数库调用是调用函数库中的一个程序,而系统调用是调用系统内核的服务。
- 函数库调用是与用户程序相联系,而系统调用是操作系统的一个进入点
- 函数库调用是在用户地址空间执行,而系统调用是在内核地址空间执行
- 函数库调用的运行时间属于「用户」时间,而系统调用的运行时间属于「系统」时间
- 函数库调用属于过程调用,开销较小,而系统调用需要切换到内核上下文环境然后切换回来,开销较大
- 在C函数库libc中大约 300 个程序,在 UNIX 中大约有 90 个系统调用
- 函数库典型的 C 函数:system, fprintf, malloc,而典型的系统调用:chdir, fork, write, brk
据书中记载,库函数调用大概花费时间为半微妙,而系统调用所需要的时间大约是库函数调用的 70 倍(35微秒),因为系统调用会有内核上下文切换的开销。纯粹从性能上考虑,你应该尽可能地减少系统调用的数量,但是,你必须记住许多 C 函数库中的程序通过系统调用来实现功能。
五、正确理解库函数高效于系统调用
首先解释,上述说明的库函数性能远高于系统调用的前提是,库函数种没有使用系统调用。再来解释下某些包含系统调用的库函数,然而其性能确实也要高于系统调用。比如上篇文章中关于文件 IO 函数 fread、fwrite、fputc、fgetc 等,这些函数通常情况下性能确实比系统调用高,原因在于这些库函数使用了缓冲区,减少了系统调用的次数,因而显得性能比较高。
六、系统调用是如何运行的
上述内容基本说清楚了库函数与系统调用的概念以及它们之间的关系,下面我们来理解系统调用到底是如何运行的。
当一个进程正在运行,遇到读写文件操作,会发生一个中断,中断后系统会把当前用户进程的一些寄存器信息保存在内核堆栈中,接着去处理中断服务程序,这里是要去执行系统调用,Linux 中通过执行 int $0x80 来执行系统调用的中断,但内核实现了很多系统调用,这时需要传递「系统调用号」来指明需要哪个系统调用。
为了更清楚的说明系统调用的过程,我们这里参考网上的一段代码来实现系统调用:
int main()
{
time_t tt;
struct tm *t;
asm volatile (
"mov $0,%%ebx\n\t"
"mov $0xd,%%eax\n\t"
"int $0x80\n\t"
"mov %%eax,%0\n\t"
: "=m" (tt)
);
t = localtime(&tt);
printf("Time: %d-%02d-%02d %02d:%02d:%02d\n",
t->tm_year + 1900,
t->tm_mon + 1, t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec);
}
[linuxblogs@host ~]$ gcc a.c -oa && ./a
Time: 2018-05-06 03:23:46
首先通过 mov $0xd %%eax 来将系统调用放入 %eax 寄存器中,time() 的系统调用号是 13,然后执行 int $0x80 系统就会去执行 time() 这个系统调用了。其实代码中的汇编部分就是实现 time() 系统调用的功能,汇编代码不懂没关系(我也不太懂),这里主要是为了说清楚系统调用的整个过程。
欢迎关注公众号: 「linuxblogs」
Linux 库函数与系统调用的关系与区别的更多相关文章
- Linux 编程中的API函数和系统调用的关系【转】
转自:http://blog.chinaunix.net/uid-25968088-id-3426027.html 原文地址:Linux 编程中的API函数和系统调用的关系 作者:up哥小号 API: ...
- linux入门--Linux和UNIX的关系及区别
UNIX 与 Linux 之间的关系是一个很有意思的话题.在目前主流的服务器端操作系统中,UNIX 诞生于 20 世纪 60 年代末,Windows 诞生于 20 世纪 80 年代中期,Linux 诞 ...
- 1.3 Linux和UNIX的关系及区别(详解版)
UNIX 与 Linux 之间的关系是一个很有意思的话题.在目前主流的服务器端操作系统中,UNIX 诞生于 20 世纪 60 年代末,Windows 诞生于 20 世纪 80 年代中期,Linux 诞 ...
- java语言:Linux与JVM的内存关系分
在一些物理内存为8g的服务器上,主要运行一个Java服务,系统内存分配如下:Java服务的JVM堆大小设置为6g,一个监控进程占用大约 600m,Linux自身使用大约800m.从表面上,物理内存应该 ...
- 转: 关于Linux与JVM的内存关系分析
转自: http://tech.meituan.com/linux-jvm-memory.html Linux与JVM的内存关系分析 葛吒2014-08-29 10:00 引言 在一些物理内存为8g的 ...
- 系统调用与API的区别
整理自系统调用与API的区别 1.为什么用户程序不能直接访问系统内核模式提供的服务? 答:在linux中,将程序的运行空间分为内核与用户空间(内核态和用户态),在逻辑上它们之间是相互隔离的,因此用户程 ...
- Linux与JVM的内存关系分析(转)
引言 在一些物理内存为8g的服务器上,主要运行一个Java服务,系统内存分配如下:Java服务的JVM堆大小设置为6g,一个监控进程占用大约600m,Linux自身使用大约800m.从表面上,物理内存 ...
- Linux库函数制作(静态库、动态库)
Linux库函数制作(静态库.动态库) 静态库与动态库 链接方式 链接分为两种:静态链接.动态链接 静态链接: 由链接器在链接时将库的内容加入到可执行程序中 静态链接的特点是: 优点: 对运行环境的依 ...
- linux中fork, source和exec的区别
转:linux中fork, source和exec的区别 shell的命令可以分为内部命令和外部命令. 内部命令是由特殊的文件格式.def实现的,如cd,ls等.而外部命令是通过系统调用或独立程序实现 ...
随机推荐
- React Native入门教程2 -- 基本组件使用及样式
在上一篇文章中,我们学会了如何搭建React Native的环境(React Native入门教程(笔记) 1 – 开发环境搭建),不知道你们是否搭建好了呢,如果还没有,那么快动起小手,来体验RN带给 ...
- 一种公认提供toString的方法_JAVA核心技术卷轴Ⅰ
从JAVA核心技术卷轴Ⅰ:基础知识中整理得到. import java.lang.reflect.AccessibleObject; import java.lang.reflect.Array; i ...
- Chipmunk碰撞形状:cpShape
目前有3种碰撞类型: 圆(Circles):最快并且最简单的碰撞形状 线段(Line segment):主要用于静态形状.可以表示斜线(Can be beveled in order to give ...
- 【算法导论】最小生成树之Kruskal法
在图论中,树是指无回路存在的连通图.一个连通图的生成树是指包含了所有顶点的树.如果把生成树的边的权值总和作为生成树的权,那么权值最小的生成树就称为最小生成树.因为最小生成树在实际中有很多应用,所以我们 ...
- 如何运行 rpcz python example
试着运行 rpcz-python 的 example.过程记录如下.假设protobuf-py已经按照protobuf的安装说明安装了.发现 protobuf-2.5.0版的python包是pytho ...
- of这个变态
英式口语还能听懂,一到美式,连读,爆破,就让人疯掉. 尤其big bang theory, of就是个变态,其读法有,英[əv, əv, v, f] 美[əv, ɑv,əv].但大部分都是/əv/. ...
- git remote
在git里,服务器上的仓库在本地称之为remote. 直接clone一个仓库: $: git clone git@search.ued.taobao.net:projects/search.git 另 ...
- java工具类(四)之实现日期任意跳转
Java实现日期任意跳转 项目开发过程中,需要进行订单提醒日期的设置,主要包括设置每月指定的日期或者指定的天数,代码如下: public static String DateOperation(Str ...
- 修改量更新API
/* Update a Modifier header of type 'PRO' (Promotion) */ l_MODIFIER_LIST_rec.active_flag := 'N'; l_M ...
- Android高级控件(二)——SurfaceView实现GIF动画架包,播放GIF动画,自己实现功能的初体现
Android高级控件(二)--SurfaceView实现GIF动画架包,播放GIF动画,自己实现功能的初体现 写这个的原因呢,也是因为项目中用到了gif动画,虽然网上有很多的架包可以实现,不过我们还 ...