操作系统(Linux Kernel 0.11Linux Kernel 0.12)解读整理——内核初始化(main init)之内存的划分
前言
MMU:内存管理单元(Memory Management Unit)完成的工作就是虚拟地址到物理地址的转换,可以让系统中的多个程序跑在自己独立的虚拟地址空间中,相互不会影响。程序可以对底层的物理内存一无所知,物理地址可以是不连续的,但是不妨碍映射连续的虚拟地址空间。
Linux 内核的内存管理程序采用了分页管理方式。它利用页目录和页表结构处理内核中其他部分代码对内存的申请和释放操作。内存的管理是以内存页面为单位进行的,一个内存页面是指地址连续的 4K 字节物理内存。通过页目录项和页表项,可以寻址和管理指定页面的使用情况。在 Linux 0.12 的内存管理目录中共有三个文件

其中,page.s 文件比较短,仅包含内存页异常的中断处理过程(int 14),主要实现了对缺页和页写保护的处理。memory.c是内存页面管理的核心文件,用于内存的初始化操作、页目录和页表的管理和内核其他部分对内存的申请处理过程。swap.c程序用于内存页面交换管理,其中主要包括交换映射位图管理函数和交换设备访问函数。
操作系统起始阶段如下:

前面三个汇编文件,其主要功能就是三张表的设置:全局描述符表、中断描述符表、页表;同时还设置了各种段寄存器,栈顶指针,并为后续的程序提供了设备信息。
在内核源代码的init/目录中只有一个 main.c文件。系统在执行完 boot/head.s程序后就会将执行权交给 main.c。该程序虽然不长,但却包括了内核初始化的所有工作。
main.c程序首先利用前面 setup.s程序取得的机器参数设置系统的根文件设备号以及一些内存全局变这些内存变量指明了主内存区的开始地址、系统所拥有的内存容量和作为高速缓冲区内存的末端地址。如果还定义了虚拟盘(RAMDISK),则主内存区将适当减少。高速缓冲部分还需要扣除被显示卡显存和其 BIOS 占用的部分。高速缓冲是用于磁盘等块设备临时存放数据的地方,以 1K(1024)字节为一个数据块单位。主内存区域的内存由内存管理模块mm通过分页机制进行管理分配,以 4K(4096)字节为一个内存页单位。内核程序可以自由访问高速缓冲中的数据,但需要通过 mm 才能使用分配到的内存页面。
系统中内存功能划分如下:

(本篇主要简单总结介绍操作系统在初始化内存管理都做了哪些关键工作。)
main方法如下:
void main(void) {ROOT_DEV = ORIG_ROOT_DEV;drive_info = DRIVE_INFO;memory_end = (1<<20) + (EXT_MEM_K<<10);memory_end &= 0xfffff000;if (memory_end > 16*1024*1024)memory_end = 16*1024*1024;if (memory_end > 12*1024*1024) buffer_memory_end = 4*1024*1024;else if (memory_end > 6*1024*1024)buffer_memory_end = 2*1024*1024;elsebuffer_memory_end = 1*1024*1024;main_memory_start = buffer_memory_end;mem_init(main_memory_start,memory_end);trap_init();blk_dev_init();chr_dev_init();tty_init();time_init();sched_init();buffer_init(buffer_memory_end);hd_init();floppy_init();sti();move_to_user_mode();if (!fork()) {init();}for(;;) pause();
}
main方法主要包含了三个部分,第一部分是一些参数的取值和计算(包括根设备 ROOT_DEV,之前在汇编语言中获取的各个设备的参数信息 drive_info,以及通过计算得到的内存边 main_memory_start memory_end buffer_memory_end。都是由 setup.s 这个汇编程序调用 BIOS 中断获取的各个设备的信息,并保存在约定好的内存地址 0x90000 处。)
第二部分是各种初始化 init 操作包括内存初始化 mem_init,中断初始化 trap_init、进程调度初始化 sched_init 等等。第三部分是切换到用户态模式,并在一个新的进程中做一个最终的初始化 init。这个 init 函数里会创建出一个进程,设置终端的标准 IO,并且再创建出一个执行 shell 程序的进程用来接受用户的命令。随即就是死循环,如果没有任何任务可以运行,操作系统会一直陷入这个死循环无法自拔(死循环里的进程称为进程0(idle进程)),仅执行 pause系统调用,并又会调用调度函数。从而 Linux 系统进入正常运行阶段。

内存边界划分
针对不同的内存大小,设置不同的边界值
memory_end = (1<<20) + (EXT_MEM_K<<10);
memory_end &= 0xfffff000;
if (memory_end > 16*1024*1024)memory_end = 16*1024*1024;
if (memory_end > 12*1024*1024) buffer_memory_end = 4*1024*1024;
else if (memory_end > 6*1024*1024)buffer_memory_end = 2*1024*1024;
elsebuffer_memory_end = 1*1024*1024;
main_memory_start = buffer_memory_end;
假设总内存一共就 8M 大小。那么如果内存为 8M 大小,memory_end 就是8 * 1024 * 1024, 也就只会走倒数第二个分支,那么 buffer_memory_end 就为2 * 1024 * 1024, 那么 main_memory_start 也为2 * 1024 * 1024;边界划分后如下图:

(定了三个箭头所指向的地址的三个边界变量)
缓冲区管理和分配函数
buffer_init(buffer_memory_end);
主内存管理和分配
通过内存边界进行初始化
mem_init(main_memory_start, memory_end);
本质上是给一个 mem_map 数组的各个位置上赋了值,而且显示全部赋值为 USED (也就是 100),然后对其中一部分又赋值为了 0。 赋值为 100 的部分就是 USED,也就表示内存被占用,如果再具体说是占用了 100 次。剩下赋值为 0 的部分就表示未被使用,也即使用次数为零。
#define LOW_MEM 0x100000
#define PAGING_MEMORY (15*1024*1024)
#define PAGING_PAGES (PAGING_MEMORY>>12)
#define MAP_NR(addr) (((addr)-LOW_MEM)>>12)
#define USED 100static long HIGH_MEMORY = 0;
static unsigned char mem_map[PAGING_PAGES] = { 0, };// start_mem = 2 * 1024 * 1024
// end_mem = 8 * 1024 * 1024
void mem_init(long start_mem, long end_mem)
{int i;HIGH_MEMORY = end_mem;for (i=0 ; i<PAGING_PAGES ; i++)mem_map[i] = USED;i = MAP_NR(start_mem);end_mem -= start_mem;end_mem >>= 12;while (end_mem-->0)mem_map[i++]=0;
}
(就是准备了一个表,记录了哪些内存被占用了,哪些内存没被占用。这就是所谓的“管理”,每个元素表示占用和未占用)

初始化完成后,其实就是 mem_map 这个数组的每个元素都代表一个 4K 内存是否空闲(准确说是使用次数)。
4K 内存通常叫做 1 页内存,而这种管理方式叫分页管理,就是把内存分成一页一页(4K)的单位去管理。 1M 以下的内存这个数组干脆没有记录,这里的内存是无需管理的,或者换个说法是无权管理的,也就是没有权利申请和释放,因为这个区域是内核所在的地方,不能被“污染”。 1M 到 2M 这个区间是缓冲区,2M 是缓冲区的末端,这些地方不是主内存区域,因此直接标记为 USED,产生的效果就是无法再被分配了。 2M 以上的空间是主内存区域,而主内存目前没有任何程序申请,所以初始化时统统都是零,未来等着应用程序去申请和释放这里的内存资源。
mem_map结构的使用
申请内存的过程中,使用到 mem_map 这个结构的。
在 memory.c 文件中有个函数 get_free_page(),用于在主内存区中申请一页空闲内存页,并返回物理内存页的起始地址。
比如在 fork 子进程的时候,会调用 copy_process 函数来复制进程的结构信息,其中有一个步骤就是要申请一页内存,用于存放进程结构信息 task_struct。
int copy_process(...) {struct task_struct *p;...p = (struct task_struct *) get_free_page();...
}
选择 mem_map 中首个空闲页面,并标记为已使用
unsigned long get_free_page(void) {register unsigned long __res asm("ax");__asm__("std ; repne ; scasb\n\t""jne 1f\n\t""movb $1,1(%%edi)\n\t""sall $12,%%ecx\n\t""addl %2,%%ecx\n\t""movl %%ecx,%%edx\n\t""movl $1024,%%ecx\n\t""leal 4092(%%edx),%%edi\n\t""rep ; stosl\n\t""movl %%edx,%%eax\n""1:":"=a" (__res):"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES),"D" (mem_map + PAGING_PAGES-1):"di","cx","dx");return __res;
}
关于内存管理的关键操作,诸如写时拷贝机制、缺页异常中断等将在未来作几篇简要总结介绍!!!
相关文章:
操作系统(Linux Kernel 0.11Linux Kernel 0.12)解读整理——内核初始化(main init)之内存的划分
前言 MMU:内存管理单元(Memory Management Unit)完成的工作就是虚拟地址到物理地址的转换,可以让系统中的多个程序跑在自己独立的虚拟地址空间中,相互不会影响。程序可以对底层的物理内存一无所知,物理地址可以是不连续的&#x…...
.NET MAUI进行UDP通信(二)
上篇文章有写过一个简单的demo,本次对项目进行进一步的扩展,添加tabbar功能。 1.修改AppShell.xaml文件,如下所示: <?xml version"1.0" encoding"UTF-8" ?> <Shellx:Class"mauiDemo.AppShel…...
社区养老服务平台的设计与实现(代码+数据库+LW)
摘 要 互联网发展至今,无论是其理论还是技术都已经成熟,而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播,搭配信息管理工具可以很好地为人们提供服务。针对信息管理混乱,出错率高,信息安全性差&#…...
生信软件管家——conda vs pip
pip vs conda: 安装过python包的人自然两种管理软件都用过, Pip install和Conda install在Python环境中用于安装第三方库和软件包,但它们在多个方面存在显著的区别 总的来说: pip是包管理软件,conda既是包管理软件&…...
项目文章 | PNAS 斑马鱼转录因子ChIP-seq助力解析GATA6突变相关的肝脏疾病机制
近日,西南大学阮华/黄红辉团队联合重庆大学邱菊辉/王贵学团队在PNAS发表了题为“An animal model recapitulates human hepatic diseases associated with GATA6 mutations”的研究论文。该研究构建了一个gata6敲除斑马鱼模型,它重现了gata6突变患者的大…...
JavaScript系列(44)--微服务架构实现详解
JavaScript微服务架构实现详解 🏗️ 今天,让我们来学习如何在JavaScript中实现微服务架构。微服务架构是一种将应用程序构建为一组小型服务的方法,每个服务运行在自己的进程中,并通过轻量级机制通信。 微服务基础概念 …...
Vue组件开发-使用 html2canvas 和 jspdf 库实现PDF文件导出 设置页面大小及方向
在 Vue 项目中实现导出 PDF 文件、调整文件页面大小和页面方向的功能,使用 html2canvas 将 HTML 内容转换为图片,再使用 jspdf 把图片添加到 PDF 文件中。以下是详细的实现步骤和代码示例: 步骤 1:安装依赖 首先,在项…...
Java-并发编程-特性-可见性-synchronized如何保证可见性?
synchronized 能保证可见性吗? 在Java并发编程中,synchronized 关键字不仅用于实现互斥访问,还能够保证内存可见性。理解这一点需要了解Java内存模型(Java Memory Model,JMM)以及happens-before࿰…...
iOS 权限管理:同时请求相机和麦克风权限的最佳实践
引言 在开发视频类应用时,我们常常会遇到需要同时请求相机和麦克风权限的场景。比如,在用户发布视频动态时,相机用于捕捉画面,麦克风用于录制声音;又或者在直播功能中,只有获得这两项权限,用户…...
【深入理解FFMPEG】命令行阅读笔记
这里写自定义目录标题 第三章 FFmpeg工具使用基础3.1 ffmpeg常用命令3.1.13.1.3 转码流程 3.2 ffprobe 常用命令3.2.1 ffprobe常用参数3.2.2 ffprobe 使用示例 3.3 ffplay常用命令3.3.1 ffplay常用参数3.3.2 ffplay高级参数3.3.4 ffplay快捷键 第4章 封装与解封装4.1 视频文件转…...
数据结构:二叉树—面试题(二)
1、二叉树的最近公共祖先 习题链接https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/description/ 描述: 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点…...
【C++高并发服务器WebServer】-6:信号
本文目录 信号的概念1.1 core文件1.2 kill命令1.3 alarm函数1.4 setitimer调用1.5 signal捕捉信号1.6 信号集1.7 内核实现信号捕捉的过程1.8 sigaction1.9 sigchld 信号的概念 信号是 Linux 进程间通信的最古老的方式之一,是事件发生时对进程的通知机制,…...
《探秘人工智能:从基础到未来变革》
在当今科技飞速发展的时代,人工智能(AI)无疑是最具影响力和变革性的技术之一。从手机里智能语音助手到自动驾驶汽车,从智能医疗诊断到智能金融服务,人工智能已经渗透到我们生活和工作的方方面面,悄然改变着…...
【数据分享】1929-2024年全球站点的逐月平均能见度(Shp\Excel\免费获取)
气象数据是在各项研究中都经常使用的数据,气象指标包括气温、风速、降水、湿度等指标!说到气象数据,最详细的气象数据是具体到气象监测站点的数据! 有关气象指标的监测站点数据,之前我们分享过1929-2024年全球气象站点…...
【PyTorch】3.张量类型转换
个人主页:Icomi 在深度学习蓬勃发展的当下,PyTorch 是不可或缺的工具。它作为强大的深度学习框架,为构建和训练神经网络提供了高效且灵活的平台。神经网络作为人工智能的核心技术,能够处理复杂的数据模式。通过 PyTorch࿰…...
不解释快上车
聊一聊 最近有小伙伴问我有小红书图片和短视频下载的软件吗,我心想,下载那上面的图片和视频做什么?也许是自己没有这方面的需求,不了解。 不过话又说回来,有些很多下载器可能作者没有持续的维护,所以可能…...
C++红黑树详解
文章目录 红黑树概念规则为什么最长路径不超过最短路径的二倍?红黑树的时间复杂度红黑树的结构插入叔叔节点情况的讨论只变色(叔叔存在且为红)抽象的情况变色单旋(叔叔不存在或叔叔存在且为黑)变色双旋(叔叔不存在或叔叔存在且为黑…...
csapp2.4节——浮点数
目录 二进制小数 十进制小数转二进制小数 IEEE浮点表示 规格化表示 非规格化表示 特殊值 舍入 浮点运算 二进制小数 类比十进制中的小数,可定义出二进制小数 例如1010.0101 小数点后的权重从-1开始递减。 十进制小数转二进制小数 整数部分使用辗转相除…...
神经网络|(一)加权平均法,感知机和神经元
【1】引言 从这篇文章开始,将记述对神经网络知识的探索。相关文章都是学习过程中的感悟和理解,如有雷同或者南辕北辙的表述,请大家多多包涵。 【2】加权平均法 在数学课本和数理统计课本中,我们总会遇到求一组数据平均值的做法…...
Spring 框架:配置缓存管理器、注解参数与过期时间
在 Spring 框架中,可通过多种方式配置缓存具体行为,常见配置方法如下。 1. 缓存管理器(CacheManager)配置 基于内存的缓存管理器配置(以SimpleCacheManager为例) SimpleCacheManager 是 Spring 提供的简单…...
FPGA实现任意角度视频旋转(完结)视频任意角度旋转实现
本文主要介绍如何基于FPGA实现视频的任意角度旋转,关于视频180度实时旋转、90/270度视频无裁剪旋转,请见本专栏前面的文章,旋转效果示意图如下: 为了实时对比旋转效果,采用分屏显示进行处理,左边代表旋转…...
openlayer getLayerById 根据id获取layer图层
背景: 在项目中使用getLayerById获取图层,这个getLayerById()方法不是openlayer官方文档自带的,而是自己封装的一个方法,这个封装的方法的思路是:遍历所有的layer,根据唯一标识【可能是id,也可能…...
【Jave全栈】Java与JavaScript比较
文章目录 前言一、Java1、 历史与背景2、语言特点3、应用场景4、生态系统 二、JavaScript1、历史与背景2、语言特点3、应用场景4、 生态系统 三、相同点四、不同点1、语言类型2、用途3、语法和结构4、性能5、生态系统6、开发模式 前言 Java和JavaScript是两种不同的编程语言&a…...
设计模式-建造者模式、原型模式
目录 建造者模式 定义 类图 优缺点 角色 建造者模式和工厂模式比较 使用案例 原型模式 定义 类图 优缺点 应用场景 应用类型 浅克隆 深克隆 建造者模式 定义 将一个复杂的对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,…...
PTMD2.0-疾病相关的翻译后修饰数据库
翻译后修饰(PTMs,post-translational modifications)通过调节蛋白质功能参与了几乎所有的生物学过程,而 PTMs 的异常状态常常与人类疾病相关。在此,PTMD 2.0展示与疾病相关的 PTMs 综合数据库,其中包含 93 …...
【Git版本控制器--3】Git的远程操作
目录 理解分布式版本控制系统 创建远程仓库 仓库被创建后的配置信息 克隆远程仓库 https克隆仓库 ssh克隆仓库 向远程仓库推送 拉取远程仓库 忽略特殊文件 为什么要忽略特殊文件? 如何配置忽略特殊文件? 配置命令别名 标签管理 理…...
批量创建ES索引
7.x from elasticsearch import Elasticsearch# 配置 Elasticsearch 连接 # 替换为你的 Elasticsearch 地址、端口、用户名和密码 es Elasticsearch([http://10.10.x.x:43885],basic_auth(admin, XN272G9THEAPYD5N5QORX3PB1TSQELLB) )# # 测试连接 # try: # # 尝试获取集…...
模块初阶学习
当我们在过去想要实现一个功能时,例如Swap交换函数时,我们需要不断考虑参数的正确与否。如果是在c语言,我们还需要不断更改函数名字,以防止函数名重复。在c我们可以通过函数名重载解决这个问题,但还是有一些小问题&…...
rust学习-rust中的保留字
rust学习-rust中的保留字 已使用的保留字未来可能使用的保留字 保留字是语言中预定义的标识符,不能用作变量名、函数名或其他自定义标识符,Rust的保留字大致可以分为两类:已使用的保留字和未来可能使用的保留字 已使用的保留字 as࿱…...
MySQL中的读锁与写锁:概念与作用深度剖析
MySQL中的读锁与写锁:概念与作用深度剖析 在MySQL数据库的并发控制机制中,读锁和写锁起着至关重要的作用。它们是确保数据在多用户环境下能够正确、安全地被访问和修改的关键工具。 一、读锁(共享锁)概念 读锁,也称为…...
