读Linux内核源码情景分析(一)

听完邱老师讲的内核态与用户态一课后,自己收获很多,当时邱老师问了我两个问题。

你知道你目前所写的代码跑在哪个进程上?你知道你在用户态写的代码为什么不能直接调用内核态接口,为什么要一层系统调用吗?

当时,自己很懵逼,大写的懵逼,我从来没考虑过我写的程序在哪跑,我就只知道,没有bug,能跑就算OK。于是,我默默的拿起了这本书,疯狂自己自补~

一、Linux内核简介:



二、intelX86CPU寻址方式(实模式)



从文章不难知道,Intel最早是先设计增加了段寄存器,也就设计出了分段式管理,段寻址后面说,这里先解释下16位如何变20位地址。

有两个地址,一个是有地址总线来的16位的地址加上段寄存器的内部地址,生成一个新的地址,

也就是段的基地址,拿到段基地址之后,便可在段表中查找对应的段内偏移,得到最终的访问内存的地址,这个地址也是物理内存中的绝对地址。那么CPU就可归纳数据总线的地址:


8086得CPU在内存寻址方面第一次引入了一个非常重要的概念—-段。在8086之前都是4位机和8位机的天下,那是并没有段的概念。当程序要访问内存时都是要给出内存的实际物理地址,这样在程序源代码中就会出现很多硬编码的物理地址。段寄存器的产生源于Intel 8086 CPU体系结构中数据总线与地址总线的宽度不一致。也就是为了实现16位8086 CPU实现20位地址总线位宽。为了支持分段机制,Intel在8086的CPU里新增了4个寄存器,分别是代码段CS,数据段DS,堆栈段SS和其他ES。这样一来,一个物理地址就由两个部分组成,分别是“段地址”:“段内偏移量”。在实模式中,通常寻址时都是通过段寄存器+通用寄存器,即基址+变址的方式进行寻址。例如,ES=0x1000,DI=0xFFFF,那么这个数据ES:DI在内存里的绝对物理地址就是:

AD(Absolute Address)=(ES)*(0x10)+(DI)=0x1FFFF 

段基地址左移4位然后加上段内偏移量就得到了物理内存里的绝对地址,经过这么一个变换,就可以得到一个20位的地址,8086就可以对20位的1M内存空间进行寻址了。


很明显,这种方式可以寻址的最高地址为0xFFFF:0xFFFF,其地址空间为0x00000~0x10FFEF,因为8086的地址总线是20位,最大只能访问到1MB的物理地址空间,即物理地址空间是0x00000~0xFFFFF。当程序访问0x100000~0x10FFEF这一段地址时,因为其逻辑上是正常的,CPU并不会认为其访问越界而产生异常,但这段地址确实没有实际的物理地址与其对应,怎么办?此时CPU采取的策略是,对于这部分超出1M地址空间的部分,自动将其从物理0地址处开始映射。也就是说,系统计算实际物理地址时是按照对1M求模运算的方式进行的,在有些技术文献里你会看到这种技术被称之为wrap-around。还是通过一幅图来描述一下吧: 


根据前面的讲解我们可以发现段基址有个特征,其低4位全为0,也就是说每个段的起始地址一定是16的整数倍,这是分段的一个基本原则。这样每个段的最小长度是16字节,而最大长度只能是64KB。这里我们可以计算一下,1MB的物理地址空间能划分成多少个段。

如果每个段的长度为16字节,这样1MB物理地址空间最多可以划分成64K个段;

如果每个段的长度为64KB,那么1MB的物理地址空间最多能划分成16个段。

8086这种分段基址虽然实现了寻址空间的提升,但是也带来一些问题:

  • 同一个物理地址可以有多种表示方法。例如0x01C0:0x0000和0x0000:0x1C00所表示的物理地址都是0x01C00。
  • 地址空间缺乏保护机制。对于每一个由段寄存器的内容确定的“基地址”,一个进程总是能够访问从此开始64KB的连续地址空间,而无法加以限制。另一方面,可以用来改变段寄存器内容的指令也不是什么“特权指令”,也就是说,通过改变段寄存器的内容,一个进程可以随心所欲地访问内存中的任何一个单元,而丝毫不受限制。不能对一个进程的内存访问加以限制,也就谈不上对其他进程以及系统本身的保护。与此相应,一个CPU如果缺乏对内存访问的限制,或者说保护,就谈不上什么内存管理,也就谈不上是现代意义上的中央处理器。

8086和后来的80186,这种只能访问1MB地址空间的工作模式,我们将其称之为“实模式”。我的理解就是“实际地址模式”,因为通过段基址和段偏移算出来的地址,经过模1MB之后得出来的地址都是实际内存的物理地址。

虽然现在CPU已经发展到了64位的酷睿6代,但是仍然保持着实模式这个工作模式。CPU的实模式是为了与8086处理器兼容而设置的。在实模式下,CPU处理器就相当于一个快速的8086处理器。CPU处理器被复位或加电的时候以实模式启动。这时候处理器中的各寄存器以实模式的初始化值工作。CPU处理器在实模式下的存储器寻址方式和8086基本一致,由段寄存器的内容乘以16作为基地址,加上段内的偏移地址形成最终的物理地址,这时候它的32位地址线只使用了低20位,即可访问1MB的物理地址空间。在实模式下,CPU处理器不能对内存进行分页机制的管理,所以指令寻址的地址就是内存中实际的物理地址。在实模式下,所有的段都是可以读、写和执行的。实模式下CPU不支持优先级,所有的指令相当于工作在特权级(即优先级0),所以它可以执行所有特权指令,包括读写控制寄存器CR0等。这实际上使得在实模式下不太可能设计一个有保护能力的操作系统。实模式下不支持硬件上的多任务切换。实模式下的中断处理方式和8086处理器相同,也用中断向量表来定位中断服务程序地址。中断向量表的结构也和8086处理器一样,每4个字节组成一个中断向量,其中包括两个字节的段地址和两个字节的偏移地址。应用程序可以任意修改中断向量表的内容,使得计算机系统容易受到病毒、木马等的攻击,整个计算机系统的安全性无法得到保证。

三、i386的也是内存管理机制


  在页式系统中,指令所给出的地址分为两部分:逻辑页号和页内地址。

        原理:CPU中的内存管理单元(MMU)按逻辑页号通过查进程页表得到物理页框号,将物理页框号与页内地址相加形成物理地址(见图4-4)。
        逻辑页号,页内偏移地址->查进程页表,得物理页号->物理地址:


上述过程通常由处理器的硬件直接完成,不需要软件参与。通常,操作系统只需在进程切换时,把进程页表的首地址装入处理器特定的寄存器中即可。一般来说,页表存储在主存之中。这样处理器每访问一个在内存中的操作数,就要访问两次内存:

       第一次用来查找页表将操作数的 逻辑地址变换为物理地址;

       第二次完成真正的读写操作。       

      这样做时间上耗费严重。为缩短查找时间,可以将页表从内存装入CPU内部的关联存储器(例如,快表) 中,实现按内容查找。此时的地址变换过程是:在CPU给出有效地址后,由地址变换机构自动将页号送人快表,并将此页号与快表中的所有页号进行比较,而且这 种比较是同时进行的。若其中有与此相匹配的页号,表示要访问的页的页表项在快表中。于是可直接读出该页所对应的物理页号,这样就无需访问内存中的页表。由于关联存储器的访问速度比内存的访问速度快得多。

四、Linux内核源码中C语言


五、Linux内核源码中汇编语言


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
linux内核源代码情景分析》(非扫描电子版本) 第1章 预备知识 1.1 Linux内核简介 1.2 Intel X86 CPU系列的寻址方式 1.3 i386的页式内存管理机制 1.4 Linux内核源代码中的C语言代码 1.5 Linux内核源代码中的汇编语言代码 第2章 存储管理 2.1 Linux内存管理的基本框架 2.2 地址映射的全过程 2.3 几个重要的数据结构和函数 2.4 越界访问 2.5 用户堆栈的扩展 2.6 物理页面的使用和周转 2.7 物理页面的分配 2.8 页面的定期换出 2.9 页面的换入 2.10 内核缓冲区的管理 2.11 外部设备存储空间的地址映射 2.12 系统调用brk() 2.13 系统调用mmap() 第3章 中断、异常和系统调用 3.1 X86 CPU对中断的硬件支持 3.2 中断向量表IDT的初始化 3.3 中断请求队列的初始化 3.4 中断的响应和服务 3.5 软中断与Bottom Half 3.6 页面异常的进入和返回 3.7 时钟中断 3.8 系统调用 3.9 系统调用号与跳转表 第4章 进程与进程调度 4.1 进程四要素 4.2 进程三部曲:创建、执行与消亡 4.3 系统调用fork()、vfork()与clone() 4.4 系统调用execve() 4.5 系统调用exit()与wait4() 4.6 进程的调度与切换 4.7 强制性调度 4.8 系统调用nanosleep()和pause() 4.9 内核中的互斥操作 第5章 文件系统 5.1 概述 5.2 从路径名到目标节点 5.3 访问权限与文件安全性 5.4 文件系统的安装和拆卸 5.5 文件的打开与关闭 5.6 文件的写与 5.7 其他文件操作 5.8 特殊文件系统/proc 第6章 传统的Unix进程间通信 6.1 概述 6.2 管道和系统调用pipe() 6.3 命名管道 6.4 信号 6.5 系统调用ptrace()和进程跟踪 6.6 报文传递 6.7 共享内存 6.8 信号量 第7章基于socket的进程间通信 7.1系统调用socket() 7.2函数sys—socket()——创建插口 7.3函数sys—bind()——指定插口地址 7.4函数sys—listen()——设定server插口 7.5函数sys—accept()——接受连接请求 7.6函数sys—connect()——请求连接 7.7报文的接收与发送 7.8插口的关闭 7.9其他 第8章设备驱动 8.1概述 8.2系统调用mknod() 8.3可安装模块 8.4PCI总线 8.5块设备的驱动 8.6字符设备驱动概述 8.7终端设备与汉字信息处理 8.8控制台的驱动 8.9通用串行外部总线USB 8.10系统调用select()以及异步输入/输出 8.11设备文件系统devfs 第9章多处理器SMP系统结构 9.1概述 9.2SMP结构中的互斥问题 9.3高速缓存与内存的一致性 9.4SMP结构中的中断机制 9.5SMP结构中的进程调度 9.6SMP系统的引导 第10章系统引导和初始化 10.1系统引导过程概述 10.2系统初始化(第一阶段) 10.3系统初始化(第二阶段) 10.4系统初始化(第三阶段) 10.5系统的关闭和重引导
linux内核源码情景分析是指对linux内核源代码进行深入研究和分析,以了解其结构、功能和运行原理的过程。这个过程通常包括以下几个方面的内容分析: 首先,需要对内核的整体结构进行分析,理解各个组件的关系和工作原理。例如,内核由进程管理、内存管理、文件系统等子系统构成,分析每个子系统的实现细节有助于对内核的整体工作原理有更深入的了解。 其次,需要对内核的主要函数和数据结构进行详细研究。内核源码中包含了大量的函数和数据结构,这些函数和数据结构是内核实现各种功能的基础。通过仔细分析这些函数和数据结构的实现方式,可以深入理解内核是如何实现各种功能的。 另外,还需要分析内核的关键算法和数据结构。内核中使用了许多常见的算法和数据结构来提高性能和效率,如链表、红黑树、哈希表等。对这些算法和数据结构的分析有助于理解内核的性能优化方案和实现原理。 此外,在分析过程中还要关注内核源码中的一些重要模块,如调度器、设备驱动、网络协议栈等,这些模块是内核实现各种功能的关键部分。深入了解这些模块的实现原理有助于掌握内核的核心功能和特性。 总体而言,linux内核源码情景分析需要对内核的整体结构、函数与数据结构、算法与数据结构、重要模块等进行深入研究和分析,以加深对内核实现原理和工作机制的理解。这对于深入理解操作系统和进行内核开发都具有重要的意义。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值