Linux内核分析笔记(HIT-OS)(更新中...)

本文详细介绍了Linux内核的引导过程,包括Bootsect.S和setup.S的作用,以及内存对齐、GDT(全局描述符表)的概念。文章还探讨了系统调用的实现,特别是如何在不同特权级之间进行转换,并讨论了在用户态和核心态之间传递数据的方法。此外,还涉及到中断处理和进程视图的基础知识。
摘要由CSDN通过智能技术生成

概念

内存对齐

Why

比如这么一种处理器,它每次读写内存的时候都从某个8倍数的地址开始,一次读出或写入8个字节的数据,假如软件能保证double类型的数据都从8倍数地址开始,那么读或写一个double类型数据就只需要一次内存操作。否则,我们就可能需要两次内存操作才能完成这个动作,因为数据或许恰好横跨在两个符合对齐要求的8字节内存块上。

How

  • 按照结构体中,最宽的数据成员分配
  • 整个结构体的总大小为最宽基本类型成员大小的整数倍!。——永远成立!
  • char int --> 8 Bytes, char char int --> 8 Bytes, char int char --> 12Bytes, char double --> 16Bytes

GDT

https://www.cnblogs.com/bajdcc/p/8972946.html

GDT是一个结构体数组,每一个元素大小为8Bytes,保存着对应段的基址,段大小以及访问权限。

段描述符表是段描述符的一个数组,描述符表的长度可变,最多可以包含 8192 个 8 字节描述符。有两种描述符表:全局描述符表 GDT(Global descriptor table);局部描述符表 LDT(Local descriptor table)。

描述符表存储在由操作系统维护着的特殊数据结构中,并且由处理器的内存管理硬件来引用。这些特殊结构应该保存在仅由操作系统软件访问的受保护的内存区域中,以防止应用程序修改其中的地址转换信息。虚拟地址空间被分割成大小相等的两半。一半由 GDT 来映射变换到线性地址,另一半则由 LDT 来映射。整个虚拟地址空间共含有 2^14 个段:一半空间(即 2^13 个段)是由 GDT 映射的全局虚拟地址空间,另一半是由 LDT 映射的局部虚拟地址空间。通过指定一个描述符表(GDT 或 LDT)以及表中描述符号,我 们就可以定位一个描述符。

当发生任务切换时,LDT 会更换成新任务的 LDT,但是 GDT 并不会改变。因此,GDT 所映射的一半虚拟地址空间是系统中所有任务共有的,但是 LDT 所映射的另一半则在任务切换时被改变。系统中所有 任务共享的段由 GDT 来映射。这样的段通常包括含有操作系统的段以及所有任务各自的包含 LDT 的特殊段。

在这里插入图片描述

Snapshot

虚拟地址到真正的物理地址需要走的路

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SJ6oraMw-1617863061296)(../images/image-20210216235156405.png)]

基本知识

**Bochs:**小型模拟器

Linux v0.11文件系统照搬MINIX文件系统,现在主流的linux文件系统为Ext3

0.1 AS86汇编器

语句可以是只包含空格、制表符和换行符的空行,也可以是赋值语句(或定义语句)、伪操作符语句机器指令语句

伪操作符语句(汇编指示符)是汇编器使用的指示符,它通常并不会产生任何代码。它由伪操作码和0个或多个操作数组成。每个操作码由一个点字符’.'开始

标号是由一个标识符后跟一个冒号’:'组成。在编译过程中,当汇编器遇到一个标号,那么当前位置计数器的值就会赋值给这个标号。

因此一条汇编语句通常由标号(可选)、指令助记符(指令名)和操作数三个字段组成,标号位于一条指令的第一个字段。它代表其所在位置的地址,通常指明一个跳转指令的目标位置。最后还可以跟随用注释符开始的注释部分

0.2 汇编

rep			;相当于 while(cx --) movw 如果 cx不为零,则执行后面的语句,并且把cx减一
movw		; 从内存[si]处移动cx个字到[di]处

0.3 BIOS中断

INT 0x13

0x02
功能描述:读扇区
入口参数:AH=02H
AL=扇区数
CH=柱面
CL=扇区
DH=磁头
DL=驱动器,00H~7FH:软盘;80H~0FFH:硬盘
ES:BX=缓冲区的地址

0.4 间接寻址

物理地址一般由段寄存器和偏移量构成,段寄存器一般有代码段寄存器(CS) , 数据段寄存器(DS) , 堆栈段寄存器(SS) ,都可以使用,偏移量可以放在BX , BP , SI , DI。

我有一个数30 , 要把这个数放在内存中 , 然后在之后要采用寄存器间接寻址找到它 , 我们可以这样做 :

MOV     AX, 	30
MOV     SI, 	2000H					;那个2000H是我们自己定义的哟
MOV     [SI],	AX        ; 这样子会把我们AX寄存器中30的值存放在[SI]这个地址里.

[SI] 表示的是物理地址 , 计算的方式就是DS<<4 +SI ; 用的段寄存器是DS

DS:si ES:di 配合使用

0.5 C内嵌汇编

内嵌汇编指令格式如下:

指令部分:输出部分:输入部分:损坏部分

  __asm__ __volatile__(
         "汇编代码 \n"              
         "汇编代码 \n"
    :"=r"(c变量名)    //第一个冒号表示从汇编里输出到c语言的变量, =号表示在汇编里只能改变C变量的值,而不能取它的值. +号表示可以取变量值,也可改变变量的值. r表示在汇编里用一个寄存器代替c变量
    :"r"(c变量名) //第二个冒号表示汇编里只能取c变量的值, 不能再有"=","+"号
        //输入的变量的寄存器只能使用一次, 如果多次使用此输入的值,则应放到一个固定的寄存器上面(R0-R12)
 
    :"r0", "r1" //第三个冒号表示告诉编译器不要把r0, r1寄存器分配给%0, %1等
        );  
    // __volatile__ 告诉编译器不要优化下面的汇编代码, 可用可不用

限制性字符(部分)

通用寄存器 “a” 将输入变量放入eax
这里有一个问题:假设eax已经被使用,那怎么办?
其实很简单:因为GCC 知道eax 已经被使用,它在这段汇编代码
的起始处插入一条语句pushl %eax,将eax 内容保存到堆栈,然
后在这段代码结束处再增加一条语句popl %eax,恢复eax的内容
“b” 将输入变量放入ebx
“c” 将输入变量放入ecx
“d” 将输入变量放入edx
“s” 将输入变量放入esi
“d” 将输入变量放入edi
“q” 将输入变量放入eax,ebx,ecx,edx中的一个
“r” 将输入变量放入通用寄存器,也就是eax,ebx,ecx,
edx,esi,edi中的一个
“A” 把eax和edx合成一个64 位的寄存器(use long longs)
内存 “m” 内存变量
“o” 操作数为内存变量,但是其寻址方式是偏移量类型,
​ 也即是基址寻址,或者是基址加

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值