系统调用与库函数

Linux下对文件操作有两种方式:系统调用(system call)和库函数调用(Library functions)。在《APUE》这本书中讲的绝大部分函数都是系统调用,而非库函数。

1,系统调用

1.1 概念

  • 在计算机中,系统调用(system call),又称为系统呼叫,指运行在使用者空间的程序向操作系统内核请求需要更高权限运行的服务。
  • 系统调用提供了用户程序与操作系统之间的接口。(即系统调用是用户程序与内核交互的接口)
  • 系统调用,我们可以理解是操作系统为用户提供的一系列操作的接口(API),这些接口提供了对系统硬件设备功能的操作。
  • 操作系统中的状态分管态(核心态)目态(用户态)
    大多数系统交互式操作需求在内核态执行。例如,设备I/O操作或者进程间通信。
    特殊指令:一类只能在核心态下运行的而不能在用户态下运行的特殊指令,不同操作系统之间的特殊指令有所差异,但是一般来说主要是和硬件相关的一些指令。
    用户程序只能在用户态运行,有时需要访问系统核心功能,这时通过系统调用接口使用系统调用。
    应用程序有时需要一些危险的,权限很高的指令,如果把这些权限放心地交给用户程序是非常危险的。(比如一个进程可能修改另一个进程的内存区,导致其不能正常运行),但是又不能完全不给这些权限。于是就有了系统调用,危险的指令被包装成系统调用,用户只能调用不能自己运行那些危险的指令。
    另外,计算机硬件资源是有限的,为了更好地管理这些资源,所有资源都由操作系统控制,进程只能向操作系统请求这些资源。
    操作系统是这些资源的唯一入口,这个入口就是系统调用。

在这里插入图片描述

1.2 系统调用举例

可以举个例子,我们最熟悉的 hello world 程序会在屏幕上打印出信息,程序中调用了printf() 函数,而库函数 printf 是将需要打印的信息输出到屏幕这个硬件设备上。我们知道,对于所有硬件设备的操作都需要驱动程序,而驱动程序是由操作系统内核实现 的。这也就意味着我们的printf()函数最终需要调用Linux内核的相关函数来操作屏幕这个设备,而这些函数就是系统调用。事实 上,printf()库函数的实现里最终会调用系统调用 write() 。

另外,创建进程fork(),vfork(),open、read、sbrk、fork
都是系统调用。

2,库函数

库函数可以理解为是对系统调用的一层封装。系统调用作为内核提供给用户程序的接口,它的执行效率是比较高效而精简的, 但有时我们需要对获取的信息进行更复杂的处理,或更人性化的需要,我们把这些处理过程封装成一个函数再提供给程序员,更 方便于程序猿编码。譬如在接下来的学习过程中,我们会学习read(int fd, char *buf, int size)这个系统调用,这个函数是从某个 文件(由fd)标志中,读取最多不超过size个字节的数据并存放到buf中去,在这个系统调用中我们只能指定读N个字节的数据,而 对于想一次读取一行这样的需求则可以调用库函数fgets()来实现,这在《C Primer Plus》的文件/IO中讲到,当然在《C Primer Plus》里提到的所有函数都是库函数。
例如: 典型的C函数库printf、fopen、fread、 malloc

3,两者关系

库函数有可能包含有一个系统调用,有可能有好几个系统调用,对于不需要涉及内核的功能的库函数也不需要调用系统调用, 比如strcpy()、strstr()、strlen()这些函数等。 对于一个功能,我们有时既可以使用系统调用来实现,也可以使用库函数来实现也 没有强制的规定,根据大家的实际需求来选择。譬如对于文件的操作,我们可以使用open()、read()、write()、close()这些系统 调用实现,当然我们也可以使用fopen()、fread()、fwrite()、fgets()、fclose()这些库函数来实现,只有对比了解他们各自的特 性我们针对不同的情况作出一个最优的选择。

库函数调用 系统调用
在所有的ANSI C编译器中,C库函数都是相同的 各个操作系统的系统调用是不同的,这导致程序不可移植
它调用函数库中的一段程序(或函数) 它调用系统内核的服务
与用户程序相联系 在内核地址空间执行
它的运行时间属于“用户时间” 它的运行时间属于“系统”时间
属于过程调用,调用开销较小 需要在用户控件和内核上下文环境间切换,开销较大
在C函数库libc中大约有300个函数 在UNIX中大约有90个系统调用
发布了17 篇原创文章 · 获赞 28 · 访问量 3846

猜你喜欢

转载自blog.csdn.net/weixin_46027505/article/details/104756929