系统调用:是操作系统提供的一组接口,用于运行在用户态的进程与硬件设备(如CPU、磁盘、打印机等)之间的交互,即应用程序与硬件之间设置的接口层设备。 可以说它是操作系统留给用户程序的接口。 我们再谈谈吧。 Linux内核是单内核,结构紧凑,执行速度快。 各个模块之间存在直接的调用关系。 纵观整个Linux系统,从上到下依次是用户进程->Linux内核->硬件。 系统调用接口位于Linux内核中。 如果再细分一点的话,整个Linux系统从上到下可以是:用户进程->系统调用接口->linux内核子系统->硬件,也就是说Linux内核包括两部分:系统调用接口和内核子系统; 或者从下到上可以是:物理硬件->操作系统内核->操作系统服务->应用程序,其中操作系统起着“承上启下”的关键作用。 管理物理硬件并为操作系统服务和应用程序提供接口。 这里的接口就是系统调用。
一般来说,为了考虑实现的难度和管理的方便,操作系统只提供少量的系统调用。 这些系统调用一般都是用C和汇编混合实现的,其接口是用C定义的,而具体的实现是汇编,这样的优点是执行效率高,极大的方便了上层的调用。
库函数:顾名思义,函数是放在库中的。 就是把一些常用的函数编译出来,放到一个文件中,供别人使用。 别人使用时,只需在文件名后面加上#即可。 一般是放在lib文件中。 一般指编译器提供的可以在C源程序中调用的函数。 它可以分为两类,一类是C语言标准规定的库函数,另一类是编译器特定的库函数。 (由于版权原因,库函数的源码一般是看不到的,但是可以在头文件中看到它的外部接口)
Libc是一个C标准库,里面存储了一些基本函数。 这些基本功能是标准化的,并且这些功能通常直接用汇编实现。
库函数一般可以分为两类,一类是操作系统提供的,一类是第三方提供的。 由于系统提供的这些库函数对系统调用进行了封装或组合,可以实现更多的功能。 此类库函数可以实现一些对于内核来说相对复杂的操作。 例如,read()函数可以根据参数直接读取文件。 程序员不需要关心硬盘上的哪个磁道、文件在哪个扇区、加载到内存中的哪个位置等隐藏操作,这些操作自然还包括系统调用。 至于第三方库,其实和系统库是一样的,只不过不太可能直接使用系统调用。 而是利用系统提供的API接口来实现功能(API接口是开放的)。 Libc库中的一些函数的功能是借助系统调用来实现的。 例如,实现最终会调用write等系统调用; 而另一些则不使用系统调用,例如,等等。
系统调用实时地为用户提供直接、纯粹的高级服务。 如果我们想要更人性化,有更适合特定情况的功能,那么我们用户就必须自己定义它们。 因此衍生出了库函数,对一些系统调用进行了包装,将系统调用进行了抽象,方便了用户级的调用。 系统调用和库函数的执行效果很相似(当然库函数会更符合要求),但是系统调用运行在内核态; 库函数由用户调用并在用户模式下运行。
系统调用是为了方便使用操作系统接口,而库函数是为了方便人们编程。
从实现者的角度来看,系统调用和库函数之间存在重要区别,但从用户的角度来看,这种区别并不是很重要。 两者都是为应用程序提供服务,但是我们应该明白,如果我们愿意的话,我们可以替换库函数,但一般我们不能替换系统服务。
以内存分配函数为例。 有多种方法可以进行内存分配及其相关的垃圾收集操作(最佳适应、首次适应等),并且没有一种技术最适合所有程序。 处理内存分配的 Unix 系统调用是 sbrk(2),它不是通用内存管理器。 它将进程地址空间增加或减少指定的字节数。 如何管理该地址空间取决于进程。 内存分配函数 (3) 实现特定类型的分配。 如果我们不喜欢它的运行方式,我们可以定义自己的函数,并且很可能它仍然会调用 sbrk 系统调用。 事实上,有很多软件包实现了自己的内存分配算法,但仍然使用 sbrk 系统调用。 可见两者职责不同,相互分离。 内核中的系统调用需要为进程分配另一块空间,而库函数则管理这块空间。
另一个说明系统调用和库函数之间区别的例子是Unix提供了一个用于确定当前时间和日期的接口。 某些操作系统提供一个返回时间的系统调用和另一个返回日期的系统调用。 任何特殊处理,例如正常时间和夏令时之间的切换,都由系统核心处理或需要人工干预。 Unix 则不同。 它仅提供一个系统调用,返回自公元 197 年 1 月 1 日午夜(国际标准时间)以来的秒数。 该值的任何解释(例如使用本地时区将其转换为人类可读的时间和日期)都留给用户进程。 在标准 C 库中,提供了几个例程来处理大多数情况。 这些库函数处理各种细节,例如各种夏令时算法。