操作系统为用户运行的进程与硬件设备进行交互提供了一组接口。在应用程序和硬件之间设置这样一个接口具有很多优点,首先这使得编程更加容易,把用户从学习硬件设备的第即编程特性中解放出来。其次极大的提高了系统的安全性,内核在要满足某个请求之前就可以在接口级检查这种请求的正确性。最后,更重要的是,这些接口使得程序更具有可移植性,因为只要不同的操作系统所提供的这一组接口相同,那么在这些操作系统之上就可以正确的编译和执行相同的程序。这组接口就是所谓的系统调用。
1.系统调用与应用编程接口,系统命令,以及内核函数之间的关系
程序员或管理系统并非直接与系统调用直接打交道,在实际使用中程序员调用的是应用编程接口API,而管理员时用的是系统命令。
(1)系统调用与API
Linux的应用编程接口(API)遵循了在UNIX世界种最流行的应用编程接口标准——POSIX标准。POSIX标准是针对API而不是针对系统调用的,判断一个系统是否与POSIX兼容,要看它是否提供了一组合适的应用编程接口,而不管对应的函数是如何实现的。事实上一些非UNIX系统被认为是与POSIX兼容的,是因为他们在用户态的库函数中提供了传统UNIX能提供的所有服务。
应用编程接口其实是一个函数定义,这些函数说明了如何获得一个给定的服务,而系统调则是通过软中断向内核发出一个明确的请求。
API主要的目的是提供应用程序开发人员以访问一组例程的能力,而又无需访问源代码或理解内部工作机制的细节,API是一种接口故而是一种抽象。
从编程者看来,API和系统调用没什么区别,二者都是关注函数名,参数类型及返回代码的含义。然而,从设计者的观点看,系统调用实现是在内核之间完成的,而用户态的函数是在函数库实现的。
(2)系统调用与系统命令
系统命令相对应用编程接口更高一层,每个系统命令都是一个可执行程序,比如常用的系统命令ls等,这些命令的实现调用了系统调用。linux的系统命令多数位于/bin和/sbin目录下。如果通过strace命令查看他们所调用的系统调用。
(3)系统调用与内核函数
内核函数与普通函数在形式上并没用什么区别,只不过前者在内核实现,因此需要满足一些内核编程的要求。系统调用是用户进程进入内核的接口层。它本身并非内核函数,但是他是由内核函数实现的,进入内核后,不同的系统调用会找到各自对应的内核函数,这些内核函数被称为系统调用的“服务例程”,比如系统调用getpid()实际调用的的服务历程为sys_getpid(),或者说系统调用getpid()是服务例程sys_getpid()的“封装例程”。
直接调用内核函数:
ID1 = syscall(SYS_getpid);
printf("syscall(SYS_getpid) = %ld\n", ID1);
调用系统调用:
ID2 = getpid();
printf("getpid() = %ld\n", ID2);
内核编程相比用户编程由一些特点,简单的讲内核程序一般不能引用C库函数,缺少内存保护措施,堆栈有限(调用嵌套不能过多),而且由于调度关系,必须考虑内核执行路径的连续性,不能有长睡眠行为。