当前课程知识点:操作系统 > 第三讲 启动、中断、异常和系统调用 > 3.4 系统调用 > 3.4 系统调用
接下来我们讨论系统调用
系统调用是操作系统对上提供服务的接口
它是在怎么实现的
然后我们在这里会说
你提供的功能可以通过函数调用
也可以通过系统调用
这两者之间到底有什么区别
然后他们在实现的时候开销是什么样子
我们在这一部分来进行讨论
首先我们看一个标准的函数调用
比如说我想在这里的
我的应用程序这个大家前面都写过
我有个printf我想在屏幕上输出一份信息
我在这里有一个printf就可以了
背后实际上他怎么做
你写程序设计的时候
看到情况是说他有一个标准C库
C库里提供了printf底下就是printf
来实现功能给你用就行了
但是他背后实际他是转成了操作系统的write
系统调用 然后write系统调用
它的实现是在内核里的
根据你write里头参数不同
会把你print输出的文件
可能输出的屏幕
那把这个图再转换一种形式来看
我们更关心系统调用这个接口的话
你会看到跟这个很相似
但是这是说这是我关心的 上面是应用程序
把我们传统说的应用程序
和你那个系统库都算到这个上面去了
这是我看到系统调用接口
在这个系统调用接口里头
进到内核里头去 由于write
你这个系统调用的编号的不同
我在这选择不同的系统调用编号
这个编号导致我write里头采用它的实现
最后给出结果来 返回回去
那我在屏幕上或者文件里
就写出了我的printf内容了
这是我们说函数调用
那么对于系统调用来说
实际上是说他在底下提供操作系统服务的接口
通常情况下 我们是用C
或者C++高级语言来使用这些的
而你在写程序的时候
通常并不直接去使用系统调用
而把系统调用封装到一个库里头
比如像我们标准C库
应用程序是访问这些库里的库函数来实现的
实际上接见访问系统调用
下面这几个是我们常见的应用编程接口
也就相当于我们系统调用最后封装完了之后
用户用到接口是什么样的
在不同的系统里面是不一样
比如说在Windows它有一个Win32 API
是用Windows操作系统内核的服务来实现Win32 库
那在这个库里用户来使用
而在Unix Linux这一类系列的系列有一个POSIX接口
这个POSIX接口提供了用户需要用到各种各样的库
而这个POSIX接口底下会访问系统调用接口
来实现它的服务
而对于Java虚拟机来说
实际他上面有一个Java API
让虚拟机里的应用程序间接的
会转到我的系统调用接口上来
这是外界使用的情况
他内部实现是什么样
每一个系统调用都有一个系统调用编号
然后依据这个编号不同
我们来使用不同的功能
在这张图我们可以看到这是系统调用进来的入口
从这通过软中断进到系统内核里来
他首先体现为一个中断
中断看是系统调用软中断
这个时候就转到这个地方来
这个地方你那里系统调用的编号
在这编号
在这是体现为一个功能编号
功能编号不同我会选取不同系统调用实现
OK 得到它的结果之后返回回来
这是对外提供的系统调用编号
从用户的角度讲
我们关心只是从这进 这个地方出来
其他部分我不关心了
你不关心的底下内容
但是在这里我们需要关心一个事情
就是我在使用系统调用接口的时候
我需要把我需要的服务告诉内核
这是我要准备参数的地方
你把这信息准备好之后
我就可以系统调用在内部实现
就可以知道我如何做处理
处理完之后把结果返给你
而我们通常在这用的时候
这部分又封装到函数库
而函数库我们在上面用就不用关心了
接下来我们说系统调用和函数调用的不同
那对于所使用的指令来讲
系统调用使用的是int和iret
而函数调用使用的是call ret
这四条指令 他是在指令级是完全不同的
那么他们的区别体现在什么地方
实际功能区别在于函数调用
我们知道你在程序的设计里已经学到过了
我为了调用一个函数
我需要把参数压到堆栈里头去
然后转到相应函数去执行
执行时候从堆栈获取我的参数信息执行
返回的结果放在那里在返回回来
这样的话你在上面的函数调用
就知道我相关的返回的结果
然后用这个结果继续往下执行
而对于系统调用来讲
他由于内核是受保护的
而应用程序是他自己的区域
在这为了保护内核的实现
这个地方内核和用户态的
应用程序之间使用不同的堆栈
所以在这会有一个堆栈的切换
切换之后由于处于内核态
我就可以使用特权指令
这些特权指令所导致的结果
就是我这个时候可以直接对设备进行控制
而你在用户态是不可能进行的
这就好比说我们在银行里头
你可以告诉银行的营业员
我需要从我的某一个帐号里取多少钱
这个取钱操作到银行的内部
他的营业员是可以去直接打开保险柜
打开保险柜取出你所需要的钱
并且在你帐号上做相应的记录
这些记录就好比说你记到你堆栈一样的
他记到他自己内部的堆栈上
以至于说如果说
我们两用同一个堆栈会有什么问题
我们两用同一个堆栈
用户其他代码可以改你堆栈的信息
这对于系统来说是不安全
好比说你从里面取了钱
结果你帐号上的金额并未减少
这银行不干或者反过来说
你把钱存到银行里去了
但是银行帐号上钱并没有增加
这个时候你是不干
基于这种理由我这会有相应切换
银行可以在里进行特权的操作
当然我们在这里只是对int iret call ret
这几条指令的最主要的区别做一个介绍
如果说大家在实现新的系统调用的时候
你可能需要去查详细的区别
这是对X86来讲 它的CPU指令手册
那在手册里头有相应链接
因特尔在不停更新它的手册
你会看到在这里不同的CPU上
这些指令它的实现
有什么样的一致性和相应的区别
你要想在特定平台上正确的运行
最后需要准确了解它的行为
最后我们还有一个问题需要来讨论
就是我在什么时候用中断
用系统调用 什么时候用函数调用
这个时候我们说系统调用
会比你函数调用更安全
但是它也有它的问题 它的开销会比你大
原因是我有一个用户态到内核态的切换
具体有哪些开销 我们在这列出来
首先你有一个切换的引导
这是硬件上需要做的事情
再有一个是你在内核里有另外一个堆栈
如果说第一次调用的时候
这个时候会有内核堆栈的建立
然后我在这里传参数的时候
这个参数的有效性合法性是需要做验证的
切换到内核执行的时候
由于我访问的代码有切换
那么在这种情况下
内核需要访问到用户态的一些信息
这个时候会把做一个地址空间上的映射
这些映射会导致你的缓存会有变化
这个时候你的TLB的内容也会有失效
所有这些都会导致用户态和内核态切换的时候
你的系统调用开销是大于函数调用的
-0.1 Piazza讨论区
--html
-0.2 在线实验平台
--实验平台使用帮助
--平台使用帮助
-0.2在线实验平台
--Raw HTML
-1.1 课程概述
--视频
-第一讲 操作系统概述--练习
-1.2 教学安排
--视频
-1.3 什么是操作系统
--Video
-1.4 为什么学习操作系统,如何学习操作系统
--Video
-1.5 操作系统实例
--视频
-1.6 操作系统的演变
--视频
-1.7 操作系统结构
--视频
-2.1 前言和国内外现状
-2.2 OS实验目标
-2.3 8个OS实验概述
-2.4 实验环境搭建
-2.5 x86-32硬件介绍
-2.6 ucore部分编程技巧
-2.7 演示实验操作过程
--Q6
--Q7
--Q10
-3.1 BIOS
--3.1 BIOS
-3.2 系统启动流程
-3.3 中断、异常和系统调用比较
-第三讲 启动、中断、异常和系统调用--3.3 中断、异常和系统调用比较
-3.4 系统调用
--3.4 系统调用
-第三讲 启动、中断、异常和系统调用--3.4 系统调用
-3.5 系统调用示例
-3.6 ucore+系统调用代码
-4.1 启动顺序
--4.1 启动顺序
-4.2 C函数调用的实现
-4.3 GCC内联汇编
-4.4 x86中断处理过程
-4.5 练习一
--4.5 练习一
-4.6 练习二
--4.6 练习二
-4.7 练习三
--4.7 练习三
-4.8 练习四 练习五
-4.9 练习六
--4.9 练习六
-5.1 计算机体系结构和内存层次
-5.2 地址空间和地址生成
-5.3 连续内存分配
-5.4 碎片整理
--5.4 碎片整理
-5.5 伙伴系统
--5.5 伙伴系统
-第五讲 物理内存管理: 连续内存分配--5.6 练习
-6.1 非连续内存分配的需求背景
-6.2 段式存储管理
-- 6.2 段式存储管理
-6.3 页式存储管理
-6.4 页表概述
--6.4 页表概述
-6.5 快表和多级页表
-6.6 反置页表
--6.6 反置页表
-6.7 段页式存储管理
-第六讲 物理内存管理: 非连续内存分配--6.8 练习
-7.1 了解x86保护模式中的特权级
-第七讲 实验二 物理内存管理--7.1 了解x86保护模式中的特权级
-7.2 了解特权级切换过程
-第七讲 实验二 物理内存管理--7.2 了解特权级切换过程
-7.3 了解段/页表
-第七讲 实验二 物理内存管理--7.3 了解段/页表
-7.4 了解UCORE建立段/页表
-第七讲 实验二 物理内存管理--7.4 了解UCORE建立段/页表
-7.5 演示lab2实验环节
-8.1 虚拟存储的需求背景
-8.2 覆盖和交换
-8.3 局部性原理
-8.4 虚拟存储概念
-8.5 虚拟页式存储
-8.6 缺页异常
--8.6 缺页异常
-9.1 页面置换算法的概念
-9.2 最优算法、先进先出算法和最近最久未使用算法
-第九讲 页面置换算法--9.2 最优算法、先进先出算法和最近最久未使用算法
-9.3 时钟置换算法和最不常用算法
-第九讲 页面置换算法--9.3 时钟置换算法和最不常用算法
-9.4 Belady现象和局部置换算法比较
-第九讲 页面置换算法--9.4 Belady现象和局部置换算法比较
-9.5 工作集置换算法
-第九讲 页面置换算法--9.5 工作集置换算法
-9.6 缺页率置换算法
-第九讲 页面置换算法--9.6 缺页率置换算法
-9.7 抖动和负载控制
-10.1 实验目标:虚存管理
-第十讲 实验三 虚拟内存管理--10.1 实验目标:虚存管理
-10.2 回顾历史和了解当下
-第十讲 实验三 虚拟内存管理--10.2 回顾历史和了解当下
-10.3 处理流程、关键数据结构和功能
-第十讲 实验三 虚拟内存管理--10.3 处理流程、关键数据结构和功能
-10.4 页访问异常
-第十讲 实验三 虚拟内存管理--10.4 页访问异常
-10.5 页换入换出机制
-第十讲 实验三 虚拟内存管理--10.5 页换入换出机制
-11.1 进程的概念
-第十一讲 进程和线程--11.1 进程的概念
-11.2 进程控制块
-第十一讲 进程和线程--11.2 进程控制块
-11.3 进程状态
-第十一讲 进程和线程--11.3 进程状态
-11.4 三状态进程模型
-11.5 挂起进程模型
-第十一讲 进程和线程--11.5 挂起进程模型
-11.6 线程的概念
-第十一讲 进程和线程--11.6 线程的概念
-11.7 用户线程
-第十一讲 进程和线程--11.7 用户线程
-11.8 内核线程
-第十一讲 进程和线程--11.8 内核线程
-12.1 进程切换
-第十二讲 进程控制--12.1 进程切换
-12.2 进程创建
-第十二讲 进程控制--12.2 进程创建
-12.3 进程加载
-第十二讲 进程控制--12.3 进程加载
-12.4 进程等待与退出
-第十二讲 进程控制--12.4 进程等待与退出
-13.1 总体介绍
-13.2 关键数据结构
-13.3 执行流程
-13.4 实际操作
-14.1 总体介绍
-14.2 进程的内存布局
-14.3 执行ELF格式的二进制代码-do_execve的实现
--14.3 执行ELF格式的二进制代码-do_execve的实现
-14.4 执行ELF格式的二进制代码-load_icode的实现
--14.4 执行ELF格式的二进制代码-load_icode的实现
-14.5 进程复制
-14.6 内存管理的copy-on-write机制
-15.1 处理机调度概念
-第十五讲 处理机调度--15.1 处理机调度概念
-15.2 调度准则
-15.3 先来先服务、短进程优先和最高响应比优先调度算法
--15.3 先来先服务、短进程优先和最高响应比优先调度算法
-第十五讲 处理机调度--15.3 先来先服务、短进程优先和最高响应比优先调度算法
-15.4 时间片轮转、多级反馈队列、公平共享调度算法和ucore调度框架
--15.4 时间片轮转、多级反馈队列、公平共享调度算法和ucore调度框架
-第十五讲 处理机调度--15.4 时间片轮转、多级反馈队列、公平共享调度算法和uc
-15.5 实时调度和多处理器调度
-第十五讲 处理机调度--15.5 实时调度和多处理器调度
-15.6 优先级反置
-第十五讲 处理机调度--15.6 优先级反置
-16.1 总体介绍和调度过程
-16.2 调度算法支撑框架
-16.3 时间片轮转调度算法
-16.4 Stride调度算法
-17.1 背景
--17.1 背景
-17.2 现实生活中的同步问题
-第十七讲 同步互斥--17.2 现实生活中的同步问题
-17.3 临界区和禁用硬件中断同步方法
-第十七讲 同步互斥--17.3 临界区和禁用硬件中断同步方法
-17.4 基于软件的同步方法
-第十七讲 同步互斥--17.4 基于软件的同步方法
-17.5 高级抽象的同步方法
-第十七讲 同步互斥--17.5 高级抽象的同步方法
-18.1 信号量
--18.1 信号量
-第十八讲 信号量与管程--18.1 信号量
-18.2 信号量使用
-第十八讲 信号量与管程--18.2 信号量使用
-18.3 管程
--18.3 管程
-第十八讲 信号量与管程--18.3 管程
-18.4 哲学家就餐问题
-18.5 读者-写者问题
-19.1 总体介绍
-19.2 底层支撑
-第十九讲 实验七 同步互斥--19.2 底层支撑
-19.3 信号量设计实现
-第十九讲 实验七 同步互斥--19.3 信号量设计实现
-19.4 管程和条件变量设计实现
-第十九讲 实验七 同步互斥--19.4 管程和条件变量设计实现
-19.5 哲学家就餐问题
-20.1 死锁概念
-第二十讲 死锁和进程通信--20.1 死锁概念
-20.2 死锁处理方法
-第二十讲 死锁和进程通信--20.2 死锁处理方法
-20.3 银行家算法
-第二十讲 死锁和进程通信--20.3 银行家算法
-20.4 死锁检测
-第二十讲 死锁和进程通信--20.4 死锁检测
-20.5 进程通信概念
-第二十讲 死锁和进程通信--20.5 进程通信概念
-20.6 信号和管道
-第二十讲 死锁和进程通信--20.6 信号和管道
-20.7 消息队列和共享内存
-第二十讲 死锁和进程通信--20.7 消息队列和共享内存
-21.1 文件系统和文件
-第二十一讲 文件系统--21.1 文件系统和文件
-21.2 文件描述符
-第二十一讲 文件系统--21.2 文件描述符
-21.3 目录、文件别名和文件系统种类
-第二十一讲 文件系统--21.3 目录、文件别名和文件系统种类
-21.4 虚拟文件系统
-第二十一讲 文件系统--21.4 虚拟文件系统
-21.5 文件缓存和打开文件
-第二十一讲 文件系统--21.5 文件缓存和打开文件
-21.6 文件分配
-第二十一讲 文件系统--21.6 文件分配
-21.7 空闲空间管理和冗余磁盘阵列RAID
-第二十一讲 文件系统--21.7 空闲空间管理和冗余磁盘阵列RAID
-22.1 总体介绍
-第二十二讲 实验八 文件系统--22.1 总体介绍
-22.2 ucore 文件系统架构
-第二十二讲 实验八 文件系统--22.2 ucore 文件系统架构
-22.3 Simple File System分析
-第二十二讲 实验八 文件系统--22.3 Simple File System分析
-22.4 Virtual File System分析
-第二十二讲 实验八 文件系统--22.4 Virtual File System分
-22.5 I/O设备接口分析
-第二十二讲 实验八 文件系统--22.5 I/O设备接口分析
-22.6 执行流程分析
-23.1 I/O特点
--视频
-第二十三讲 I/O子系统--23.1 I/O特点
-23.2 I/O结构
--816C80A0F5E3B8809C33DC5901307461
-第二十三讲 I/O子系统--23.2 I/O结构
-23.3 I/O数据传输
--C58221E14388B9DB9C33DC5901307461
-第二十三讲 I/O子系统--23.3 I/O数据传输
-23.4 磁盘调度
--567A3F1FCBFB3F4C9C33DC5901307461
-第二十三讲 I/O子系统--23.4 磁盘调度
-23.5 磁盘缓存
--C327536B80D25CE79C33DC5901307461
-第二十三讲 I/O子系统--23.5 磁盘缓存
-html
--html