操作系统真相还原——进一步完善内核

系统调用

系统调用就是让用户进程申请操作系统的帮助,让操作系统帮其完成某项工作,也就是相当于用户进程调用了操作系统的功能,Linux系统调用是用中断门实现的,只占用一个中断向量号(0x80)。
处理器执行指令int 0x80时,便触发了系统调用,通过在eax中写入子功能号,来调用不同的系统功能

实现框架

在这里插入图片描述

一个系统调用分两个部分,一部分是暴露给用户进程的接口函数,它属于用户空间, 此部分只是使用系统调用的途径,只负责发需求,另一部分是对应内核具体实现,属于内核空间。为区分这两个部分,内核空间函数名前要加“sys_”.
实现思路:
(1)用中断门实现系统调用,效仿Linux用0x80号中断作为系统调用的入口。
(2)在IDT中安装0x80号中断对应的描述符,在该描述符中注册系统调用的中断处理例程
(3)建立系统调用子功能表syscall_table利用eax寄存器的子功能号在该表中索引相应的处理函数
(4)用宏实现用户空间系统调用接口_syscall,最大支持3个参数的系统调用,只用完成_syscall[0-3],eax保存子功能号,ebx保存第一个参数,ecx保存第二个参数,edx保存第三个参数

添加0x80号中断描述符

在这里插入图片描述

修改最大支持中断数IDT_DESC_CNT.
将syscall_handle(系统调用对应的中断入口例程),写入中断描述符,注意dpl应该为用户级3。

增加0x80号中断处理例程

在这里插入图片描述
注意点:
1.一律压入三个参数,子功能处理函数都有自己的原型声明,声明中包括参数个数及类型,编译时编译器会根据函数声明在栈中匹配出正确数量的参数
2.syscall_table是个数组,类似idt_table,里面存储的是函数地址,通过eax,索引对应子功能号
3.根据二进制编程接口abi约定,寄存器eax用来存储返回值,[esp + 8*4]是栈中存储eax的位置。

初始化系统调用和实现sys_getpid()

在这里插入图片描述

在这里插入图片描述

大家加入现在写完的功能,我目前已经写完大多数功能了。
在这里插入图片描述

通过enum宏定义各种子功能号,其实际宏代表着其的序列号。
t添加系统调用的步骤:
(1)在syscall.h添加新的子功能号
(2)在syscall.c增加系统调用的用户接口
(3)在syscall.init.c 中定义子功能处理函数井在 syscall_table 中注

用户进程

可变参数原理

在这里插入图片描述

得益于编译器使用C调用约定(由调用者把参数以从右向左的顺序压入栈中,并且由调用者清理栈中的参数)来处理函数的传参方式,调用者能够完好地回收栈空间,不必担心栈溢出等问题。
我们很容易知道压入栈后,参数的起始地址到结束地址,但又怎么知道具体有多少个参数,及其类型呢?
重点在format(格式化字符串),里面依次表示着各类型。

实现printf

printf原型是
int printf(const char *format,…)
在这里插入图片描述
(1) va_start(ap,v),参数 ap 是用于指向可变参数的指针变量,参数v是支持可变参数的函数的第
参数(如对于 printf 来说,参数 就是宇符串 format )。此宏的功能是使指针 ap 指向v的地址,它的调用必须先于其他两个宏,相当于初始化 ap 指针的作用。
(2) va_arg(ap,t),参数 ap 是用于指向可变参数的指针变量,参数t是可变参数的类型,此宏的功能是使指针 ap 指向战中下一个参数的地址并返回其值。
(3) va_end(ap),将指向可变参数的变量 ap 置为 null ,也就是清空指针变量ap
在这里插入图片描述
vsprintf是实现printf的主体.
在这里插入图片描述
用来定位参数类型,不是%直接跳过。
若找到%,开始用Switch判断后面的字符,以便分类处理。各种处理比较简单,认真看看能明白,就不细说了。

完善堆内存

虽然我们现在已经实现了内存管理,但过于粗糙了,分配的内存都是以4KB为单位 。我们现在来实现更小内存(arena)的分配。
arena是由一大块4k内存划分为若干个小内存,为了支持多种容量的内存块的分配,我们要提前建立多种不同容量内存块的arena。arena 是个提供内在分配的数据结构,它分为两部分,一部分是元信息,用来描述自己内存池中空闲内存块数量,这其中包括内存块描述符指针(后面介绍),通过它可以间接获知本 arena 所包含内存块的规格大小,此部分占用的空间是固定的,约为 12 字节。另一部分就是内存地区域,这里面有无数的内存块,此部分占用 arena 大量的空间。我们把每个内存块命名为 mem block ,它们是内存分配粒度更细的资源,最终为用户分配的就是这其中的一个内存块。在咱们的实现中,针对小内存块的 arena 占用 页框内存,除了元信息外的剩下的内存被平均分成多个小内存块。
为了跟踪每个arena值中的空闲内存块,分别为每种规格的内存块建立一个内存块描述符,即mem_block_desc。内存块描述符将所有同类 arena 中空闲内存块汇总,因此它相当于内存块
超级大仓库,分配小块内存时必须先经过此入口,系统从它的空闲内存块链表 free_Iist创中挑一块内存
基本逻辑结构图
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

大内存分配,指针为NULL
在这里插入图片描述
小内存分配,指针指向men_block_desc,free_list指向空闲块
在这里插入图片描述
但小内存占用多个页框进行分配时
在这里插入图片描述

sys_malloc实现

实现对应内存池的匹配
在这里插入图片描述
大于1024字节直接分配页框
小于1024字节的分配:
找到容纳的最小内存规格->
若没有可用的mem_block创建新的mem_block,就是将一定处理后,将新的free_list接在我们空闲块链表之后。
在这里插入图片描述

内存释放

分配内存的一般步骤:
(1)在虚拟地址池分配虚拟地址
(2)在物理内存池分配物理地址
(3)在页表中完成虚拟地址到物理地址的映射
因而释放内存的步骤应该为;
(1)物理地址池中释放物理页地址.(位图清零)
(2)在页表中去掉虚拟地址的映射(pte直接把p位置0)
(3)在虚拟地址池释放虚拟地址(位图清零)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值