手动撸OS遇到的问题:(第6章以后)

博客详细解析了C语言中的cdecl调用约定,包括参数压栈方向和清理责任,并通过printf函数实例说明其变长参数特性。同时,深入探讨了系统调用的两种方式,以及在汇编和C语言中如何导出和引用外部符号。文章还介绍了中断处理的概念,区分了外部中断、内部中断(包括软中断和异常)的类型和特点,特别是中断向量和中断描述符表在中断处理中的作用。最后,概述了中断处理程序的执行流程和保护机制。
摘要由CSDN通过智能技术生成

第六章 完善内核
cdecl 调用约定又称为 C 调用约定,是 C 语言默认的调用约定。
(1)调用者将所有参数从右向左入栈。
(2)调用者清理参数所占的栈空间。
它和 stdcall 一样都是从右向左将参数入拢的,区别就是 cdecl 由调用者清理技空间 。

cdecl调用约定最大的亮点是它允许函数中参数的数量不固定,我们熟识的 printf 函数,它能够支持变长参数,就是利用此 cdecl 调用约定的性质设计出来的,它的原理是利用字符串参数 format 中的’%’来匹配栈中的参数。

浅析 C 库函数与系统调用
关于系统调用:系统调用很像 BIOS 中断调用(在很久很久以前咱们有说过 BIOS 中断、 DOS 中断等内容),只不过系统调用的入口只有一个,即第 0x80 号中断,它不像 BIOS 中断那样,几乎一个功能就有一个入口。

调用“系统调用”有两种方式:
( 1) 将系统调用指令封装为 c 库函数,通过库函数进行系统调用,操作简单。
(2 )不依赖任何库函数,直接通过汇编指令 int 与操作系统通信。

有关混合编程的部分就说完了,总结一下。
• 在汇编代码中导出符号供外部引用是用的关键字 global ,引用外部文件的符号是用的关键宇extern 。
• 在 C 代码中只要将符号定义为全局便可以被外部引用(一般情况下无需用额外关键宇修饰,具体请参考 C 语言手册),引用外部符号时用 extern 声明即可。

打印字符那里,遇到个问题:
Linux gnu/stubs-32.h: No such file or directory
解决方案(centos) : sudo yum install glibc-devel.i686

在上机运行之后,我这里提醒下大家链接文件时的顺序问题。在上面的链接阶段,目标文件链接顺序是 main.o 在前, print.o 在后。大家知道, main.c 文件中用到了 print.o 中的 put_ch缸函数,在链接顺序上,属于“调用在前,实现在后”的顺序。

第七章 中断

由于 CPU 获知了计算机中发生的某些事, CPU 暂停正在执行的程序,转而去执行处理该事件的程序,
当这段程序执行完毕后, CPU 继续执行刚才的程序。整个过程称为中断处理,也称为中断。

中断类型划分:
把中断按事件来源分类,来自 CPU 外部的中断就称为外部中断,来自 CPU 内部的中断称为内部中断。其实还可以再细分,外部中断按是否导致宕机来划分,可分为可屏蔽中断和不可屏蔽中断两种,而内部中断按中断是否正常来划分,可分为软中断和异常。

外部中断是指来自 CPU 外部的中断,而外部的中断源必须是某个硬件,所以外部中断又称为硬件中断。比如说网卡收到了来自网络的数据包,这时候网卡就会主动通知 CPU, CPU 得到通知后便将数据拷贝到内核缓冲区。

CPU 为大家提供了两条信号线。外部硬件的中断是通过两根信号线通知 CPU 的,这两根信号线就是INTR (INTeRrupt)和 NMI (Non Maskable Interrupt),示意图如下:
在这里插入图片描述

  • 可屏蔽中断:
    (1)可屏蔽中断是通过时TR 引脚进入 CPU 的,外部设备如硬盘、网卡等发出的中断都是可屏蔽中断。CPU 可以不理会,因为它不会让系统宕机。 甚至在Linux 当中,把中断分为上半部和下半部分开处理。把中断处理程序分为上半部和下半部两部分,把中断处理程序中需要立即执行的部分(分分钟不能耽误的部分)划分到上半部,这部分是要限时执行的,所以通常情况下只完成中断应答或硬件复位等重要紧迫的工作。不紧急的部分则被推迟到下半部中去完成。由于中断处理程序的上半部是刻不容缓要执行的,所以上半部是在关中断不被打扰的情况下执行的。当上半部执行完成后就把中断打开了,下半部也属于中断处理程序,所以中断处理程序下半部则是在开中断的情况下执行的,如果有新的中断发生,原来这个旧中断的下半部就会被换下 CPU,先执行新的中断处理程序的上半部,等待线程调度机制为旧中断处理程序择一 日期(就是指调度算法认为的某个恰当时机)后,再调度其上 CPU 完成其下半部的执行。
    (2)总而言之:上半部分是关中断的时候完成,下半部分是开中断的时候完成,所以下半部分运行的时候,可以被其他更紧要的中断打断。
    (3)举例:
    还是拿网卡举例子,网络中的数据通过网线到达网卡后,首先会被存储到网卡自己的缓冲区中,这个缓冲区容量不大,容易被写满,所以里面的数据必须立即被 CPU拿走,否则由于网卡缓冲区中无空余空间,后续到来的数据只能丢掉。鉴于这个刻不容缓的理由,网卡会立即发中断通知 CPU:“数据到了, CPU 立即放下手里的工作,马上执行网卡的中断处理程序,将网卡缓冲区中的数据拷贝到内核缓冲区中,至此,救火工作算是完成了,这就是所说的上半部。 CPU 拿到网络数据后,处理数据的工作就不那么紧急了,它将在下半部中完成,这部分将在适当的时机被启动。

  • 不可屏蔽中断:
    (1)是通过 NMI 引脚进入 CPU 的,它表示系统中发生了致命的错误了。
    (2)常见三种:电源掉电、内存读写错误、总线奇偶校验错误。(可能是硬件问题,软件无法解决)
    (3)首先为每一种中断分配一个中断向量号,中断向量号就是一个整数,它就是中断向量表或中断描述符表中的索引下标,用来索引中断项。中断发起时,相应的中断向量号通过 NMI 或 INTR引脚被传入 CPU,中断向量号是中断向量表或中断描述符表里中断项的下标, CPU 根据此中断向量号在中断向量表或中断描述符表中检索对应的中断处理程序井去执行。

内部中断:

  • 软中断:由软件主动发起的中断。如“int 8位立即数”,“int3”等。
  • 异常: 是另一种内部中断,是指令执行期间 CPU 内部产生的错误引起的。由于是运行时错误,所以它不受标志寄存器 eflags 中的 E 位影响,无法向用户隐瞒。
  • 对于中断是否无视 eflags 中的 E 位,可以这么理解:
    (1 )首先,只要是导致运行错误的中断类型都会无视IF位,不受IF位的管束,如 NMI 、异常。
    (2 )其次,由于 int n 型的软中断用于实现系统调用功能,不能因为IF位为 0 就不顾用户请求,所以为了用户功能正常,软中断必须也无视IF位。
    总结:只要中断关系到“正常”运行,就不受IF位影响。另外,这里所说的运行错误,是说指令语法方面的错误。

异常分三种:
(1)Fault,也称为故障。最典型的例子就是操作系统课程中所说的缺页异常 page fault,话说 Linux 的虚拟内存就是基于 page fault 的,这充分说明这种异常是极易被修复的,甚至是有益的。
(2)Trap,也称为陷阱。此异常通常用在调试中,比如 int3 指令便引发此类异常,为了让中断处理程序返回后能够继续向下执行, CPU将中断处理程序的返回地址指向导致异常指令的下一个指令地址。
(3)Abort,也称为终止。程序将无法继续运行,操作系统为了自保,只能将此程序从进程表中去掉。

中断机制的本质:中断机制的本质是来了一个中断信号后,调用相应的中断处理程序。所以, CPU 不管有多少种类型的中断,为了统一中断管理,把来自外部设备、内部指令的各种中断类型统统归结为一种管理方式,即为每个中断信号分配一个整数,用此整数作为中断的田,而这个整数就是所谓的中断向量,然后用此ID作为中断描述符表中的索引,这样就能找到对应的表项,进而从中找到对应的中断处理程序。

中断描述符表(IDT)

  • IDT 中只有这种称为门的描述符。之前在介绍特权级时介绍了门,这里给大伙复习 一 下。门,顾名思义,是通往某处的入口。在计算机中,用门来表示一段程序的入口。拿它和段描述符对比一下就容易理解了,段描述符中描述的是一片内存区域,而门描述符中描述的是一段代码。所有的描述符大小都是 8 字节,而门其实就是描述符,想通过门进入里面的世界是有条件的,就像娶媳妇“过门”一样,两个家庭得“门当户对”,这就是咱们人类社会中所说的门槛。这和之前咱们看到的段描述符功能类似,在门描述符中添加了各种属性,这就是进门的条件。 当处理器把这些条件检查通过后
    就不再限制程序了。

关于各种门的描述:
在这里插入图片描述

各种门都属于系统段, S 都等于 0,所以它们主要的区别就是 type 位不同 。 现代操作系统主要用中断门,像 Linux 那样,用它来实现系统调用 。

一个中断源就会产生一个中断向量,每个中断向量都对应中断描述符表中的一个门描述符,任何中断源都通过中断向量对应到中断描述符表中的门描述符,通过该门描述符就找到了对应的中断处理程序。可见,中断发生后,采取什么样的动作是由中断处理程序决定的,但该程序是在中断描述符表中找到的,该表决定了中断信号落到哪个程序上,中断向量相当于子弹,门描述符相当于靶子,中断描述符表相当于祖击手,人家指哪就打哪,门描述符位置错了子弹就打错地方了,下面我们来看看这个威武霸气中断描述符表。

对比中断向量表,中断描述符表有两个区别 。
(1)中断描述符表地址不限制,在哪里都可以 。
(2)中断描述符表中的每个描述符用 8 字节描述。

在 CPU 内部有个中断描述符表寄存器( Interrupt Descriptor Table Register, IDTR),该寄存器分为两部分:第 O~ 15 位是表界眼,即 IDT 大小减 1 ,第 16~47 位是 IDT 的基地址,这和咱们之前介绍的 GDTR是一样的原理。好啦,你懂啦,咱们的中断描述符表地址肯定要加载到这个寄存器中,只有寄存器 IDTR指向了 IDT,当 CPU 接收到中断向量号时才能找到中断向量处理程序,这样中断系统才能正常运作 。

中断处理过程及保护:

  • 完整的中断过程分为 CPU 外和 CPU 内两部分 。
    (1)CPU 外:外部设备的中断由中断代理芯片接收,处理后将该中断的中断向量号发送到 CPU 。(涉及硬件)
    (2)CPU 内: CPU 执行该中断向量号对应的中断处理程序。

  • 对于CPU内:
    (1)处理器根据中断向量号定位中断门描述符。(找到对应的门描述符)
    (2)处理器进行特权级检查。
    (3)执行中断处理程序。
    在这里插入图片描述
    暂时停更,后期有时间继续学习再更新。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值