访管指令的由来

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了访管指令的由来相关的知识,希望对你有一定的参考价值。

参考技术A

我们可以进一步问另一个问题:为什么要在程序执行中引入“访管指令”呢?
我们知道,用户程序只能在目态下运行,并且不能使用特权指令。因而就存在这么一个问题:如果用户程序想要启动外设,或者要完成在目态下无法完成的工作,该怎么办?要解决这一问题必须有三个条件:
(1)需要有一条指令,能使处理机从目态进入管态,并向操作系统提交要代为完成的工作;
(2)在管态下由操作系统完成用户程序的请求;
(3)操作系统完成所做工作后能返回到用户程序,即从管态回到原来的目态。
解决这个问题要靠访管指令。访管指令本身不是特权指令,其基本功能是让程序拥有“自愿进管”的手段,从而引起访管中断。
自愿性中断事件(软件中断)是正在运行的程序所期待的事件。这种事件是由于执行了一条访管指令而引起的,它表示正在运行的程序对操作系统有某种需求。一旦机器执行这一中断时,便自愿停止现行程序而转入访管中断处理程序处理。例如,要求操作系统协助启动外围设备工作。
所谓系统调用就是用户在程序中能用访管指令调用的由操作系统提供的子功能集合。其中每一个子功能称为一个系统调用命令。

操作系统运行环境与运行机制(系统调用篇)

系统调用:

用户在编程是可以调用的操作系统功能(使CPU可以从用户态陷入内核态)

 

 应用程序,C函数,API,和内核函数关系

 

 

系统调用和函数调用区别:

系统调用:

INT,IRET指令用于系统调用,堆栈切换,特权级切换

函数调用:

CALL,RET指令用于函数调用,不涉及堆栈切换和特权级改变

 

系统调用机制的设计

①中断/异常机制

支持系统调用服务的实现:选择一条陷入指令(访管指令)即可

②选择一条特殊指令:陷入指令(亦称访管指令)

引发异常完成用户态到内核态的切换

③系统调用号和参数:

每个系统调用都实现给定一个编号(功能号)

④系统调用表:

存放系统调用服务例程入口地址

 

参数传递问题(怎样实现用户程序的参数(存在于用户栈)传递给内核(存在于内核栈)?):

由陷入指令自带参数:陷入指令的长度有限,且还要携带系统调用功能号,只能自带有限的参数

②通过通用寄存器传递参数:这些寄存器是操作系统和用户程序都能访问的,但寄存器的个数会限制传递参数的数量

③在内存中开辟专用堆栈区来传递参数

现代参数传递使用第2种方法

 

 

系统调用例子:

1 #include <unistd.h>
2 int main(){
3 char string[5] = {‘H’, ‘e’, ‘l’, ‘l’, ‘o’, ‘!’, ‘\\n’};
4 write(1, string, 7);
5 return 0;
6 }
7 输出结果:Hello!

这是用高级语言写的程序,编译后转成汇编指令为:

1..section .data
2.output:
3. .ascii “Hello!\\n”
4.output_end:
5. .equ len, output_end - output
6..section .text
7..globl _start
8._start:                                                                                     ---
9. movl $4, %eax #eax寄存器存放系统调用号                                   |
10. movl $1, %ebx                                                 |这一段是write函数的简要的汇编指令代码
11. movl $output, %ecx                                                                           |
12. movl $len, %edx                                                                              |
13. int $0x80 #引发一次系统调用  0x80 = 128(d)即为Linux中断向量表中专门用于陷入指令的中断号,调用异常机制  |
14. end:                                                                                      ---
15. movl $1, %eax #1这个系统调用的作用?  -----    1是系统调用号,对应的是返回退出程序         
16. movl $0, %ebx                   |   这一段属于return 0 简要的汇编代码
17. int $0x80                          -----

 

系统调用执行过程

当CPU执行到特殊的陷入指令时:
1.中断/异常机制:软中断发出中断信号,硬件保护现场;通过查中断向量表把控制权转给系统调用总入口程序  (之前查到的都是包含中断处理程序入口地址的中断向量,现在是系统调用总入口程序地址
2.系统调用总入口程序:保存现场;将参数保存在内核堆栈里(参数传递;根据eax寄存器中的系统调用号,通过查系统调用表把控制权转给相应的系统调用处理例程或内核函数
3.执行系统调用例程
4.恢复现场,返回用户程序

 

Linux系统调用:

陷入指令中断号选择128号
int $0x80
门描述符
  ①系统初始化时:对IDT表中的128号门初始化
  ②门描述符(中断描述符表)的2、3两个字节设置为内核代码段选择符
      0、1、6、7四个字节则设置为偏移量(段选择符从GDT中选择段描述符,然后段描述符有段基地址,从而最终指向system_call(),由sched_init()中set_system_gate(0x80, &system_call)设置)
  ③门类型:15,陷阱门,没有禁止中断
  ④DPL:3,与用户级别相同,因为只有当前执行程序的特权级大于等于门的DPL,特权级才能发生由用户态变成内核态(数值越小特权级越高)

 

Linux内核中Include/ASM-1386/UNISTD.h文件:

#define __NR_exit 1
#define __NR_fork 2
#define __NR_read 3
#define __NR_write 4
#define __NR_open 5
#define __NR_close 6

存了大量的系统调用号

 

系统执行INT 0x80具体过程:

1.由于特权级的改变,要切换栈

用户栈 → 内核栈

2.CPU从任务状态段TSS中装入新的栈指针(SS︰ESP),指向内核栈                  PS.任务状态段(Task State Segment, TSS)是x86架构电脑上是一个保存任务信息的数据结构,保存了内层堆栈指针

3.用户栈的信息(SS︰ESP)、EFLAGS、用户态CS 、EIP 寄存器的内容压栈(返回用)
4.将EFLAGS压栈后,复位TF,IF位保持不变                                                           PS.TF(bit 8) [Trap flag] 将该位设置为1以允许单步调试模式,清零则禁用该模式。

5.用128在IDT中找到该门描述符,从中找出段选择符装入代码段寄存器CS
6.段描述符中的基地址 + 陷阱门描述符中的偏移量 → 定位 system_call()的入口地址

 

Linux执行流程

 

对应的SAVE_ALL执行内容

 

底层工作总结:

1. 硬件压栈:程序计数器等
2. 硬件从中断向量装入新的程序计数器等
3. 汇编语言过程保存寄存器值
4. 汇编语言过程设置新的堆栈
5. C语言中断服务程序运行(例:读并缓冲输入)
6. 进程调度程序决定下一个将运行的进程
7. C语言过程返回至汇编代码
8. 汇编语言过程开始运行新的当前进程

 

      作者水平有限,文章肯定有错还请各位指点!!!感谢!!!

参考链接:

http://blog.csdn.net/jn1158359135/article/details/7761011

 

以上是关于访管指令的由来的主要内容,如果未能解决你的问题,请参考以下文章

06-系统调用(执行过程访管指令库函数与系统调用)

操作系统 王道考研2019 第一章:计算机系统概述 -- 中断和异常系统调用(陷入指令 / trap 指令 / 访管指令)

C++,怎么让程序模拟用户输入指令,并自行回车以执行该指令(不是写个输出函数只做样子)?

操作系统运行环境与运行机制(系统调用篇)

CPU状态分为目态和管态两种,从目态转换到管态的惟一途径是?

Linux内核开发——新添内核用户接口