《linux内核设计与实现》第五章

第五章 系统调用linux

1、与内核通讯编程

  系统调用在用户空间进程和硬件设备之间添加了一个中间层。做用安全

  • 为用户空间提供了一种硬件的抽象接口。
  • 系统调用保证了系统的稳定和安全。
  • 每一个进程都运行在虚拟系统中,而在用户空间和系统的其他部分提供这样一层公共接口,也是出于这种考虑。

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

2、API、POSIX和C库性能

  通常状况下,应用程序经过在用户空间实现的应用编程接口(API)而不是直接经过系统调用来编程。spa

  一个API定义了一组应用程序使用的编程接口。由于API实际上不须要和系统调用对应(API能够在各类不一样的操做系统上实现),一个API能够实现成一个系统调用,也能够经过调用多个系统调用来实现,也能够彻底不用。操作系统

  POSIX、API、C库及系统调用关系以下:翻译

  在Unix世界中最流行的应用编程接口是给予POSIX标准的设计

  C库实现了大部分的POSIX标准API.指针

3、系统调用

  要访问系统调用(在Linux中常称做syscall),一般经过C库中定义的函数调用来进行。

  的返回值表示错误,0值一般表示成功。系统调用在出现错误时C库会把错误码写入errno全局变量,经过调用perror()库函数,能够把该变量翻译成用户能够理解的错误字符串。

例如:获取进程ID号的系统调用getpid()

 asmlinkage long sys_getpid(void)   {

    return current->tgid;

}

  • asmlinkage限定词是编译器指令,通知编译器仅从堆栈中提取函数的参数(全部的系统调用都须要这个限定词);
  • 内核返回long,用户空间返回int,是为了保证32位/64位系统兼容;
  • get_pid在内核被定义为sys_getpid(),linux中全部系统调用都是如此定义的,bar()被定义为sys_bar()。

  一、系统调用号

    Linux中,每一个系统调用号被赋予一个惟一的系统调用号,进程不会说起系统调用名称,而是用系统调用号来关联具体的系统调用。系统调用号一旦分配就不能再改变;若是一个系统调用被删除,它所占用的系统调用号也不允

  许被回收利用。Linux有一个“未实现”系统调用sys_ni_syscall(),来补缺已经删除的调用号。

  二、系统调用的性能

  Linux系统调用比其余许多操做系统执行得要快:Linux很短的上下文切换时间,系统调用处理程序和每一个系统调用自己也都很是简洁。

4、系统调用处理程序

  用户空间的程序没法直接执行内核代码,经过软中断的机制通知内核须要执行系统调用。经过软中断引起一个异常,促使系统切换到内核态,执行异常处理程序代码;这个异常处理程序就是系统调用处理程序system_call()。

x86系统上软中断是中断号128,经过int $0x80指令触发该中断system_call()。

  • 找到指定的系统调用

  在x86上,系统调用号是经过eax寄存器传递给内核的。

  • 参数传递

  在x86-32系统上,ebx,ecx,edx,esi和edi依次存放前五个参数,若须要六个以上参数,用单独寄存器指向这些参数在用户空间地址的指针。经过eax存放返回值。

5、系统调用的实现

  一、实现系统调用

  • 决定用途,每一个系统调用功能应该单一明确,不提倡多用途系统调用;
  • 系统调用参数,返回值和错误码都要明确;
  • 设计接口时尽可能为未来多作考虑,不要对机器的字节长度和字节序作假设。

  二、参数验证

  • 检查全部的参数是否合法有效,最重要的一种检查就是检查用户提供的指针是否有效。
  • 在接收一个用户空间的指针以前,内核必须保证:

    1)指向用户空间内存的指针,内核不能直接访问;

    2)指针指向的内存在用户进程空间里,内核不能读其余进程空间;

    3)内存不能绕过访问限制:可读内存标记为可读,可写标记为可写,可执行标记为可执行。

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

    1)为了向用户空间写入数据内核提供了copy_to_user();

    2)为了从用户空间读取数据内核提供了copy_from_user()。

    3)都是把第二个参数指定位置数据传送到第一个参数指定位置,长度由第三个参数决定。若是执行失败,两者都返回未完成拷贝的数据的字节数,成功返回0。

    4)copy_to_user()和copy_from_user()均可能引发阻塞形成进程休眠。

  • 最后一项检查针对是否有合法权限。

 6、系统调用上下文

  内核在执行系统调用的时候处于进程上下文,urrent指针指向引起系统调用的那个进程。

  在进程上下文中,内核能够休眠,能够被抢占。因此系统调用必须是可重入的。

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

  •  在系统调用表中加入表项;
  • 系统调用号定义于<asm/unistd.h>中;
  • 编译进内核映像,放入kernel/下的相关文件。

  二、从用户空间访问系统调用:

  • 系统调用靠C库支持。
  • Linux自己提供了一组宏,用于直接对系统调用进行访问;这些宏是_syscalln(),n的范围是0-6,表明须要传递给系统调用的参数个数。

  三、采用系统调用做为实现方式

优势有:

  • 系统调用建立容易,且使用方便;
  • Linux系统调用高性能显而易见。

缺点是:

  • 须要一个系统调用号,这个须要官方分配;
  • 系统调用被加入稳定内核固化后,接口不能改变;
  • 须要将系统调用分别分配到各类体系结构去;
  • 在脚本中不容易调用系统调用,也不能从文件系统直接访问系统调用;
  • 在主内核树以外很难维护;
  • 若是只进行简单信息交换,系统调用大材小用了。

替代方法:

  • 实现一个设备节点,并对此实现read()和write(),ioctl()来进行操做;
  • 像信号量这样的某些接口,能够用文件描述符来表示;
  • 把增长的信息做为一个文件放在sysfs的合适位置。

 7、总结

      这一章讲了系统调用的知识,Linux尽可能使系统调用简洁,事实上Linux已是一个相对稳定而且功能已经较为完善的操做系统。本章的内容与视频上的知识相辅相成,有助于咱们对系统调用的理解,咱们了解了系统调用是什么,以及他们与API和库函数的关系。还有实现系统调用的过程,和参数验证,最后还总结了采用系统调用做为实现方式的利弊和代替方法。有助于咱们理解和掌握。

相关文章
相关标签/搜索