系统调用、POSIX、C库、系统命令和内核函数
(1)系统调用和POSIX。
系统调用虽然是内核和用户应用程序之间的沟通桥梁,是用户应用程序访问内核的入口点,但通常情况下,应用程序是通过操作系统提供的应用编程接口(API)而不是直接通过系统调用来编程。 操作系统API的主要作用是把操作系统的功能完全展示出来,提供给应用程序,基于该操作系统,与文件、内存、时钟、网络、图形、各种外设等互操作的能力。此外,操作系统API通常还提供许多工具类的功能,比如操纵字符串、各种数据类型、时间日期等。 在UNIX世界里,最通用的操作系统API基于POSIX(Portable Operating System Interface of UNIX,可移植操作系统接口)标准。 POSIX是一套操作系统接口的标准,POSIX标准定义了”POSIX兼容”的操作系统所必须提供的服务,提供了根据POSIX而定义的API 函数。这些API函数和系统调用之间有着直接的关系,一个API函数可以由一个系统调用实现,也可以通过调用多个系统调用来实现,还可以完全不使用任何系 统调用。
(2)系统调用和C库。
操作系统API通常都以C库的方式提供,Linux也是如此。C库提供了POSIX的绝大部分API,同时,内核提供的每个系统调用在C库中都具有相应的封装函数。系统调用与其C库封装函数的名称常常相同,比如,read系统调用在C库中的封装函数即为read函数。 C库中的系统调用封装函数在最终调用到相应系统调用之前,往往不做多少额外的工作。不过,某些情况下会有些例外,比如对于两个相关的系统调用truncate和truncate64,C库中的封装函数truncate函数即需要决定它们中的哪个应该最终被调用。 系统调用和C库函数之间并不是一一对应的关系。可能几个不同的函数会调用到同一个系统调用,比如malloc函数和 free函数都是通过brk系统调用来扩大或缩小进程的堆栈,execl、execlp、execle、execv、execvp和execve函数都是 通过execve系统调用来执行一个可执行文件。 也有可能一个函数调用多个系统调用。更有些函数并不依赖于任何系统调用,比如strcpy函数(复制字符串)和atoi函数(转换ASCII为整数),因为它们并不需要向内核请求任何服务。实际上,从用户的角度看,系统调用和C库之间的区别并不重要,他们只需通过C库函数完成所需功能。相反,从内核的角度看,需要考虑的则是提供哪些针对确定目的的系统调用,并不需要关注它们如何被使用。
(3)系统调用与系统命令。
系统命令位于C库的更上层,是利用C库实现的可执行程序,比如最为常用的ls、cd等命令。 strace工具可以跟踪命令的执行,使用希望跟踪的命令为参数,并显示出该命令执行过程中所使用到的所有系统调用。
(4)系统调用和内核函数。
内核函数与C库函数的区别仅仅是内核函数在内核实现,因此必须遵守内核编程的规则。 系统调用最终必须具有明确的操作。用户应用程序通过系统调用进入内核后,会执行各个系统调用对应的内核函数,即系统调用服务例程,比如系统调用getpid的服务例程是内核函数sys_getpid。