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等.而外部命令是通过系统调用或独立程序实现 ...
随机推荐
- (NO.00004)iOS实现打砖块游戏(九):游戏中小球与反弹棒的碰撞
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 前一篇博文介绍了物理对象中小球与砖块的碰撞处理,在这一篇中我们再 ...
- java设计模式---合成模式2
合成模式属于对象的结构模式,有时又叫做"部分--整体"模式.合成模式将对象组织到树结构中,可以用来描述整体与部分的关系.合成模式可以使客户端将单纯元素与复合元素同等看待. 合成模式 ...
- Tomcat中定制阀门
我们说管道机制给我们带来了更好的扩展性,Tomcat中在扩展性方面具体如何体现,这便是本节讨论的内容.从上节了解到基础阀门是必须执行的,假如你需要一个额外的逻辑处理阀门,可以添加一个非基础阀门. 我的 ...
- VB.NET版机房收费系统---报表
报表,即报告情况的表格,简单的说:报表就是用表格.图表等格式来动态显示数据,可以用公式表示为:"报表 = 多样的格式 + 动态的数据". 在没有计算机以前,人们利用纸和笔来记录数据 ...
- Linux进程-进程的创建
今天学习了Linux的进程创建的基本原理,是基于0.11版本核心的.下面对其作一下简单的总结. 一.Linux进程在内存中的相关资源 很容易理解,Linux进程的创建过程就是内存中进程相关资源产生 ...
- Linux进程实践(5) --守护进程
概述 守护进程是在需要在后台长期运行不受终端控制的进程,通常情况下守护进程在系统启动时自动运行,在服务器关闭的时候自动关闭:守护进程的名称通常以d结尾,比如sshd.xinetd.crond.atd等 ...
- 介绍几个好用的android自定义控件
首先看效果图, 看下这两个界面,第一个中用到了一个自定义的FlowRadioGroup,支持复合子控件,自定义布局: 第二个界面中看到了输入的数字 自动4位分割了吧:也用到了自定义的DivisionE ...
- git使用详解
1. Git概念 1.1. Git库中由三部分组成 Git 仓库就是那个.git 目录,其中存放的是我们所提交的文档索引内容,Git 可基于文档索引内容对其所管理的文档进行内容追踪,从而实现文档的版本 ...
- 《java入门第一季》StringBuffer类小案例
/* * 把数组拼接成一个字符串 */ public class StringBufferTest2 { public static void main(String[] args) { // 定义一 ...
- Cocos2D物理碰撞不按预期工作的排查工作
如果该碰撞的节点不碰撞或反过来不该碰的碰撞了,你可以检查一下几个方面: 1.对应2个节点的分类和掩码必须匹配.如果它们应该碰撞则一个节点的分类应该在另一个节点的掩码中,反之亦然. 2.注意空的分类和掩 ...