第五章 系统调用

一、与内核通信

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

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

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

二、API、POSIX和C库

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

  一个API定义了一组应用程序使用的编程接口。因为API实际上不需要和系统调用对应(API可以在各种不同的操作系统上实现),一个API可以实现成一个系统调用,也可以通过调用多个系统调用来实现,也可以完全不用。

  POSIX、API、C库及系统调用关系如下:

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

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

三、系统调用

  要访问系统调用(在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()。

  1、系统调用号

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

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

  2、系统调用的性能

  Linux系统调用比其他许多操作系统执行得要快:Linux很短的上下文切换时间,系统调用处理程序和每个系统调用本身也都非常简洁。

四、系统调用处理程序

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

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

  • 找到指定的系统调用

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

  • 参数传递

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

五、系统调用的实现

  1、实现系统调用

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

  2、参数验证

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

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

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

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

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

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

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

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

    4)copy_to_user()和copy_from_user()都可能引起阻塞造成进程休眠。

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

六、系统调用上下文

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

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

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

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

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

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

  3、采用系统调用作为实现方式

优点有:

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

缺点是:

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

替代方法:

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

七、总结

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

《linux内核设计与实现》第五章的更多相关文章

  1. Linux内核设计与实现 第五章

    1. 什么是系统调用 系统调用就是用户程序和硬件设备之间的桥梁. 用户程序在需要的时候,通过系统调用来使用硬件设备. 系统调用的存在意义: 1)用户程序通过系统调用来使用硬件,而不用关心具体的硬件设备 ...

  2. linux及安全《Linux内核设计与实现》第一章——20135227黄晓妍

    <linux内核设计与实现>第一章 第一章Linux内核简介: 1.3操作系统和内核简介 操作系统:系统包含了操作系统和所有运行在它之上的应用程序.操作系统是指整个在系统中负责完成最基本功 ...

  3. 《linux内核设计与实现》第一章

    第一章Linux内核简介 一.unix 1.Unix的历史 Unix是现存操作系统中最强大和最优秀的系统. ——1969年由Ken Thompson和Dernis Ritchie的灵感点亮的产物. — ...

  4. Linux内核设计与实现 第十七章

    1. 设备类型 linux中主要由3种类型的设备,分别是: 设备类型 代表设备 特点 访问方式 块设备 硬盘,光盘 随机访问设备中的内容 一般都是把设备挂载为文件系统后再访问 字符设备 键盘,打印机 ...

  5. linux及安全《Linux内核设计与实现》第二章——20135227黄晓妍

    第二章:从内核出发 2.1获取源代码 2.1.1使用git Git:内核开发者们用来管理Linux内核源代码的控制系统. 我们使用git来下载和管理Linux源代码. 2.1.2安装内核源代码(如果使 ...

  6. Linux内核设计与实现 第三章

    1. 进程和线程 进程和线程是程序运行时状态,是动态变化的,进程和线程的管理操作都是由内核来实现的. Linux中的进程于Windows相比是很轻量级的,而且不严格区分进程和线程,线程不过是一种特殊的 ...

  7. 《linux内核设计与实现》第二章

    第二章 从内核出发 一.获取内核源码 1.使用Git(linux创造的系统) 使用git来获取最新提交到linux版本树的一个副本: $ git clone git://git.kernel.org/ ...

  8. Linux内核设计与实现 第四章

    1. 什么是调度 现在的操作系统都是多任务的,为了能让更多的任务能同时在系统上更好的运行,需要一个管理程序来管理计算机上同时运行的各个任务(也就是进程). 这个管理程序就是调度程序,功能: 决定哪些进 ...

  9. Linux内核设计与实现第五周读书笔记

    第十八章 调试 18.1准备开始 需要的只是: 一个确定的bug.大部分bug通常都不是行为可靠而且定义明确的. 一个藏匿bug的内核版本. 相关的内核代码的知识和运气. 18.2内核中的bug 内核 ...

  10. 【读书笔记】Linux内核设计与实现(第一章&第二章)

    http://pan.baidu.com/s/1hqYAZNQ OneNote做的笔记没法儿带着格式一起导进来.所以上传到百度云,麻烦老师下载一下了. 下次不再用OneNote.

随机推荐

  1. fedora更新

    先换源再更新,否则等的太久,如果已经开始了直接ctrl+c取消 # dnf update

  2. 浅谈tidb事务与MySQL事务之间的区别

    MySQL是我们日常生活中常见的数据库,他的innodb存储引擎尤为常见,在事务方面使用的是扁平事务,即要么都执行,要么都回滚.而tidb数据库则使用的是分布式事务.两者都能保证数据的高一致性,但是在 ...

  3. 5.3Python函数(三)

    目录 目录 前言 (一)装饰器 ==1.简单的装饰器== ==2.修饰带参数函数的装饰器== ==3.修饰带返回值函数的装饰器== ==4.自身带参数的装饰器== (二)迭代器 (三)生成器 ==1. ...

  4. (4)top详解 (每周一个linux命令系列)

    (4)top详解 (每周一个linux命令系列) linux命令 top详解 引言:今天的命令是用来看cpu信息的top top 我们先看man top top - display Linux pro ...

  5. nginx反向代理与Real-IP和X-Forwarded-For.txt

    本文作者张开涛.为保障<亿级流量网站架构核心技术>一书内容的连续性,有些需要读者了解的内容,或者书的补充和引申内容,会通过二维码嵌入的方式引导读者阅读学习.大家可以关注作者公众号“开涛的博 ...

  6. shell脚本之获取CPU使用率

    今天一个同事要测试OJ项目,同时我这边也需要知道他在压测过程中, CPU的使用率怎么样,虽说可以用top实时查看,但是进程太多了,我不需要获取那么多信息,我仅仅只要知道当前压测过程中CPU实时的使用率 ...

  7. JavaScript高级程序设计学习(四)之引用类型

    在javascript中也是有引用类型的,java同样如此. javascript常见也比较常用的引用类型就熟Object和Array. 一个对象和一个数组,这个在前后端分离开发中也用的最多.比如aj ...

  8. shell编程之转义和引用

    shell中有两类字符,一类是普通字符,在Shell中除了本身的字面意思外没有其他特殊意义,即普通纯文本:另一类即元字符,是Shell的保留字符,在Shell中有着特殊的含义. 一.转义 转义是指使用 ...

  9. How enable deprecated functions in Qt5

    问题:Qt5中如何启用以 QT_DEPRECATED_SINCE标识的已经废弃/过时的方法? I want to port a Qt4 program to Qt5 and some function ...

  10. Html5 标签四 (列表)

    1.有序列表 2.无序列表 3.自定义列表 <!DOCTYPE html> <html lang="en"> <head> <meta c ...