《Linux内核设计与实现》——系统调用

一、与内核通信

 1、系统调用在用户空间进程和硬件设备之间添加了一个中间层。该层的作用如下:

   1)、为用户空间提供了一种硬件的抽象接口。

   2)、系统调用保证了系统的稳定和安全。

   3)、每个进程都运行在虚拟系统中,而在用户空间和系统的·其余部分提供这样一层公共接口,也出于这种考虑。


 2、在Linux中,系统调用是用户空间访问内核的惟一手段;除异常和陷入外,它们是内核唯一的合法入口。



二、API、POSIX和C库

 1、一般情况下,应用程序通过在用户空间实现的应用编程接口(API)而不是直接通过系统调用来编程。

   1)一个API定义了一组应用程序使用的编程接口。它可以实现成一个系统调用,也可以通过调用多个系统调用来实现,而完全不使用任何系统调用也不存在任何问题。


 2、在Unix系统中,最流行的应用程序编程接口是基于POSIX标准的。


 3、Linux的系统调用作为C库的一部分提供。C库实现了Unix系统主要API,包括标准C库函数和系统调用接口。


 4、应用编程与系统调用无关紧要,但内核只跟系统调用打交道;库函数及应用程序是怎么使用系统调用的,不是内核所关心的。



三、系统调用

 1、系统调用相关简介

   1)、系统调用通常需要定义零个、一个或几个参数(输入)而且可能产生一些副作用。

   2)、系统调用还会通过一个long类型的返回值来表示成功或错误。

       I、通常、但不是绝对,用一个负的返回值来表示错误。返回一个0值通常表示成功。

       II、系统调用会在出现错误的时候C库会把错误码写入errno全局变量中。

   3)、如何定义系统调用

      I、首先,必须在声明中使用asmlinkage限定词,这是一个编译指令,通知编译器仅从栈中提取该函数的参数,所有的系统调用都需要这个词。

      II、其次,函数返回龙。为了保证32位和64位系统的兼容,系统调用在用户空间和内核空间有不同的返回值类型,用户空间为int,内核空间为long。

      III、最后,系统调用应该被定义与sys_XX的形式。这是Linux种所有系统调用都应该遵守的命名规则。


 2、系统调用号

   1)、在Linux中,每个系统调用被赋予一个系统调用号。通过这个系统调用号可以关联系统调用。

   2)、系统调用好非常重要,一旦分配就不能再有任何变更,否则编译好的应用程序就会崩溃。

   3)、如果一个系统调用被删除,它所占用的系统调用号也不允许被回收利用,否则,以前编译过的代码会调用此系统调用,但事实上却调用另一个系统调用。Linux中有

             sys_ni_syscall(),它除了返回-ENOSYS外不做任何事,此错误号就是专门针对无效的系统调用而设的。

   4)、内核记录了系统调用表中的所有已注册过的系统调用的列表,存储在sys_call_tabe中。


 3、系统调用的性能

   1)、Linux系统调用比其他许多操作系统执行的要快。



四、系统调用处理函数

 1、应用程序通知内核的机制是靠软中断实现的:通过引发一个异常来促使系统切换到内核态去执行异常处理程序。此时的异常处理程序实际上就是系统调用处理程序。


 2、指定恰当的系统调用

   1)、仅仅陷入内核空间是不够的。必须把系统调用号一并传给内核。

   2)、在X86上,系统调用号是通过eax寄存器传递给内核的。在陷入内核之前,用户空间就把相应系统调用所对应的号放入eax中。

   3)、system_call函数通过将给定的系统调用号与NR_syscalls作比较来检查其有效性。如果它大于或等于NR_syscalls,该函数就返回-ENOSYS。否则,就执行相应的系统

             调用。

             call  *sys_call_table(  ,  %rax,  8或4);


 3、参数传递

   1)、除了系统调用号之外,大部分系统调用还相互咬一些外部参数的输入。再发生陷入的时候,应该把这些参数从用户空间中传给内核。

   2)、用寄存器传递系统调用。在X86系统上,ebx、ecx、edx、esi和edi按顺序存放前五个参数。留个或留个以上参数不常见。此外,应该用一个单独的寄存器存放指向所欲

             这些参数在用户空间地址的指针。



五、系统调用的实现

 1、实现系统调用

   1)、实现一个系统调用的第一步决定它的用途,不提倡采用多用途的系统调用。

   2)、系统调用的接口应该力求简洁,参数尽可能的少。系统调用的语义和行为非常关键,因为应用程序依赖它们,所以应该力求稳定,不做改动。

   3)、系统调用应该多为未来做考虑。

   4)、注意系统调用的一致性和健壮性,不但要考虑当前,还要考虑未来。


 2、参数验证

   1)、系统调用必须仔细检查它们所有的参数是否合法有效。最重要的一种检查就是检查用户提供的指针是否有效。

   2)、在接收一个用户空间的指针之前,内核必须保证:

      I、指针指向的内存区域属于用户空间。

      II、指针指向的内存区域在进程的地址空间里。

      III、如果是读,改内核应被标记为可读;如果是写,改内核应被标记为可写;如果是可执行,改内核应被标记为可执行。

   3)、内核提供了两个方法来完成必须的检查和内核空间与用户空间之间数据的来回拷贝。

      I、copy_to_user();

      II、copy_from_user();

      III、如果执行失败,这两个函数返回的都是没能完成拷贝的数据的字节数。如果成功,则返回0.当出现上述错误时,系统调用返回标准-EEAULT。

   4)、检查针对是否有合法权限。



六、系统调用上下文

 1、绑定一个系统调用的最后步骤

   1)、把系统调用注册成一个正式的系统调用:

      I、首先,在系统调用表的最后加入一个表项。

      II、对于所支持的各种体系结构,系统调用号都必须定义于<asm/unistd.h>中。

      III、系统调用必须被编译进内核映像(不能编译成模块)。


 2、从用户空间访问系统调用

   1)、用户程序通过包含标准头文件和C库连接,就可以使用系统调用。

   2)、Linux本身提供一个宏,用于直接对系统调用进行访问。


 3、不通过系统调用的方式实现的原因。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值