第五章 系统调用

在现代操作系统中,内核提供了进程与内核进行交互的一组接口。有如下作用:

  • 让应用程序受限的访问硬件设备
  • 提供了创新进程并与已有进程进行通信的机制
  • 提供了申请操作系统其它资源的能力
  • 保证系统稳定可靠,避免应用程序恣意妄行

一、与内核通信

1、用户空间进程和硬件设备之间通过系统调用来交互,其主要作用有三个。

  • 为用户空间提供了硬件的抽象接口。
  • 保证了系统的稳定和安全。
  • 实现多任务和虚拟内存。保证良好的稳定性和安全性。

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

二、API、POSIX和C库

1、应用程序通过在用户空间实现的应用编程接口(API)而非直接通过系统调用来编程。

2、POSIX是应用编程接口的一个国际标准,C库提供了POSIX的绝大部分API。

  • POSIX-Unix世界中最流行的应用编程接口是给予POSIX标准的。
  • Linux的系统调用作为C库的一部分提供。
  • C库实现了Unix系统的主要API,包括标准C库函数和系统调用接口,即POSIXdM大部分API。
  • Unix的接口设计——“提供机制而不是策略”

三、系统调用

1、要访问系统调用(在Linux 中常称作syscall),通常通过C库中定义的函数调用来进行。

2、系统调用在出现错误的时候C 库会把错误码写入errno 全局变量。通过调用perror()库函数,可以把该变量翻译成用户可以理解的错误字符串。

3、系统调用最终具有一种明确的操作,例如getpid() 系统调用

SYSCALL_DEFINEO(getpid)
{
return task_tgid_vnr(current); //returns current- >tgid
}

4、在Linux 中,每个系统调用被赋予一个系统调用号。这样,通过这个独一无二的号就可以关联系统调用。

5、系统调用号相当重要, 一旦分配就不能再有任何变更,否则编译好的应用程序就会崩捕。

6、Linux 有一个“未实现”系统调用sys_ni_syscall(),它除了返回-ENOSYS 外不做任何其他工作,这个错提号就是专门针对无效的系统调用而设的。

7、Linux 系统调用比其他许多操作系统执行得要快。Linux 很短的上下文切换时间是一个重要原因,迸出内核都被优化得简洁高效。另外一个原因是系统调用处理程序和每个系统调用本身也都非常简洁。

四、系统调用处理程序

  • 用户空间不能直接执行内核代码,如果进程可以直接在内核的地址空间上读写的话,系统的安全性和稳定性将不复存在。
  • 通知内核的机制是靠软中断实现的。
  • 在x86 系统上预定义的软中断是中断号128,通过int $0x80 指令触发该中断。这条指令会触发一个异常导致系统切换到内核态并执行第128 号异常处理程序,而该程序正是系统调用处理程序。这个处理程序名字起得很贴切,叫system_call()。
  • 指令sysenter,比int中断指令更快更专业。

1、指定恰当的系统调用

  • 在x86 上,系统调用号是通过eax 寄存器传递给内核的。在陷入内核之前,用户空间就把相应系统调用所对应的号放入eax中。
  • system_ call()函数通过将给定的系统调用号与NR_ syscalls 做比较来检查其有效性。如果它大于或者等于NR_syscalls,该函数就返回-ENOSYS 。否则,就执行相应的系统调用:

call *sys_call_table ( %rax, 8)

  • 由于系统调用表中的表项是以64 位(8 字节)类型存放的,所以内核需要将给定的系统调用号乘以4,然后用所得的结果在该表中查询其位置。
  • 除了系统调用号以外,大部分系统调用都还需要一些外部的参数输入。
  • 在x86-32 系统上, ebx, ecx、edx、esi 和edi 按照顺序存放前五个参数。需要六个或六个以上参数的情况不多见,此时, 应该用一个单拙的寄存器存放指向所有这些参在用户空间地址的指针。

五、系统调用的实现

1、Linux中不提倡采用多用途的系统调用

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

  • 指针指向的内存区城属于用户空间。进程决不能哄骗内核去读内核空间的数据
  • 指针指向的内存区域在进程的地址空间里。进程决不能哄骗内核去读其他进程的数据
  • 如果是读,该内存应被标记为可读:如果是写,该内存应被标记为可写:如果是可执行,该内存被标记为可执行。进程决不能绕过内存访问限制

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

  • 为了向用户空间写人数据,内核提供了copy_to_ user(),它需要三个参数。第一个参数是进程空间中的目的内存地址,第二个是内核空间内的源地址,最后一个参数是需要拷贝的数据长度(字节数)。
  • 为了从用户空间读取数据,内核提供了copy_from_user(),它和copy_to_ user() 相似.该函数把第二个参数指定的位置上的数据拷贝到第一个参数指定的位置上,拷贝的数据长度由第三个参数决定。

    【注意】 copy_to user() 和copy_from_user() 都有可能引起阻塞。当包含用户数据的页被换出到硬盘上而不是在物理内存上的时候,这种情况就会发生。

 

六、系统调用上下文

1、在进程上下文中,内核可以休眠(比如在系统调用阻塞或显式调用schedule()的时候)并且可以被抢占。

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

  • 首先,在系统调用袤的最后加入一个表项。每种支持该系统调用的硬件体系都必须做这样的工作(大部分的系统调用都针对所有的体系结构)。从0 开始算起,系统调用在该表中的位置就是它的系统调用号。如第10 个系统调用分配到的系统调用号为9。
  • 对于所支持的各种体系结构,系统调用号都必须定义子<asm/unistd.h> 中。
  • 系统调用必须被编译进内核映象(不能被编译成模块〉.这只要把它放进kernel/ 下的一个相关文件中就可以了,比如sys.c ,它包含了各种各样的系统调用。

3、Linux 本身提供了一组宏,用于直接对系统调用进行访问。它会设置好寄存器并调用LINUX指令。这些宏是_syscalln(),其中n 的范围从0 到6,代表需要传递给系统调用的参数个数, 这是由于该宏必须了解到底有多少参数按照什么次序压入寄存器。

4、open()系统调用的定义是

long open ( const char •filename, int flags, int mode)

而不靠库支持,直接惆用此系统调用的宏的形式为:

#define NR_open 5
syscall3(long, open, const char• , filename , i nt, flags, int, mode)

5、建立一个新的系统调用的好处:

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

6、问题是:

  • 你需要一个系统调用号,而这需要一个内核在处于开发版本的时候由官方分配给你。
  • 系统调用被加入稳定内核后就被固化了,为了避免应用程序的崩塌,它的接口不允许改动。
  • 需要将系统调用分别注册到每个需要支持的体系结构中去。
  • 在脚本中不容易调用系统调用,也不能从文件系统直接访问系统调用。
  • 由于你需要系统调用号,因此在主内核之外是很难维护和使用系统调用的。
  • 如果仅仅进行简单的信息交换,系统调用就大材小用了。

7、替代方法:实现一个设备节点,并对此实现read() 和write()。使用ioctl()对特定的设置进行操作或者对特定的信息进行检索。

  • 像信号量这样的某些接口,可以用文件描述符来表示,因此也就可以按上述方式对其进行操作。
  • 把增加的信息作为一个文件放在sysfs的合适位置。

《Linux内核设计与实现》 第五章学习笔记的更多相关文章

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

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

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

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

  3. 《Linux内核设计与实现》课本第五章学习笔记——20135203齐岳

    <Linux内核设计与实现>课本第五章学习笔记 By20135203齐岳 与内核通信 用户空间进程和硬件设备之间通过系统调用来交互,其主要作用有三个. 为用户空间提供了硬件的抽象接口. 保 ...

  4. 《Linux内核设计与实现》第五章学习笔记

    <Linux内核设计与实现>第五章学习笔记 姓名:王玮怡  学号:20135116 一.与内核通信     在Linux中,系统调用是用户空间访问内核的唯一手段:除异常和陷入外,它们是内核 ...

  5. 《Linux内核设计与实现》第四周读书笔记——第五章

    <Linux内核设计与实现>第四周读书笔记--第五章 20135301张忻 估算学习时间:共1.5小时 读书:1.0 代码:0 作业:0 博客:0.5 实际学习时间:共2.0小时 读书:1 ...

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

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

  7. 《Linux内核设计与实现》Chapter 3 读书笔记

    <Linux内核设计与实现>Chapter 3 读书笔记 进程管理是所有操作系统的心脏所在. 一.进程 1.进程就是处于执行期的程序以及它所包含的资源的总称. 2.线程是在进程中活动的对象 ...

  8. 《Linux内核设计与实现》Chapter 1 读书笔记

    <Linux内核设计与实现>Chapter 1 读书笔记 一.Unix的特点 Unix从Multics中产生,是一个强大.健壮和稳定的操作系统. 特点 1.很简洁 2.在Unix系统中,所 ...

  9. 《Linux内核设计与实现》Chapter 5 读书笔记

    <Linux内核设计与实现>Chapter 5 读书笔记 在现代操作系统中,内核提供了用户进程与内核进行交互的一组接口,这些接口的作用是: 使应用程序受限地访问硬件设备 提供创建新进程与已 ...

  10. 《Linux内核设计与实现》Chapter 18 读书笔记

    <Linux内核设计与实现>Chapter 18 读书笔记 一.准备开始 一个bug 一个藏匿bug的内核版本 知道这个bug最早出现在哪个内核版本中. 相关内核代码的知识和运气 想要成功 ...

随机推荐

  1. February 8th, 2018 Week 6th Thursday

    When you fall in love, friends, let yourself fall. 当你坠入爱河,我的朋友,你就放手去爱吧. To love someone is like movi ...

  2. lamp/lnmp下添加PHP扩展

    在linux下安装好基本的PHP运行环境后有时候添加了新的功能,就得增加新的扩展,比如之前没有安装redis扩展,可以手动编译安装相关的扩展可以找下下载的php源码包中,ext目录下是否有相关的扩展源 ...

  3. css设置文字不能选中状态

    高版本浏览器的处理方式 -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-u ...

  4. 【九校3D2T3】世界第一的猛汉王

    [问题描述] 卡普地公司举办了「世界第一的猛汉王」全球大会,来自世界各地的猛汉为了争夺「猛汉王」的名号前来一决高下.现在举行的是弓箭组选拔赛.卡普地公司为比赛新建了一张PVP地图——「猛汉竞技场」.有 ...

  5. Ajax进阶

    "POST":请求 POST请求:(一共三个方法)<!DOCTYPE html><html lang="en"><head> ...

  6. MySql常用命令集Mysql常用命令showdatabases;显示数据库createdatab

    MySql 常用命令集 Mysql常用命令 show databases; 显示数据库 create database name; 创建数据库 use databasename; 选择数据库 drop ...

  7. ubuntu 16.04 SS安装及配置

    安装SS客户端 安装pip3 一般情况下,pip3安装的版本比pip安装的新,pip安装的版本比apt安装的新,这里选择最新版本. sudo apt install python3-pip 安装SS ...

  8. docker swarm英文文档学习-12-在集群模式中的Raft共识

    Raft consensus in swarm mode 在集群模式中的Raft共识 当Docker引擎在集群模式下运行时,manager节点实现Raft 共识算法来管理全局集群状态.Docker s ...

  9. 浏览器访问svn

    文章转自https://www.cnblogs.com/ayanmw/archive/2011/12/19/2294054.html 你看到的这个文章来自于http://www.cnblogs.com ...

  10. Python基础(6)——装饰器

    装饰器: def w1(func): def inner(): # 验证1 # 验证2 # 验证3 return func() return inner @w1 def f1(): print 'f1 ...