自己开发X86操作系统--LIUNUXOS

news2025/1/20 20:09:59

为了纪念Linus Torvalds创始开发的linux,我将自己开发的os命名为LIUNUXOS。

LIUNUXOS其原码分为两个部分,汇编工程和c/c++工程,地址分别为:
LIUNUXOS汇编工程原码地址
LIUNUXOS c/c++工程原码地址

在这些工程中,源程序的文件名和其功能名称一一对应,从其名字就可以猜出其具体功能。
在这里插入图片描述

1. 汇编工程。

汇编部分都是用16/32位x86汇编编写,其开发工具是masm和link。整个工程又分3个模块:

mbr.com:是一个com程序,功能是加载执行loader.com程序。x86架构规定,Bios开机自检后,加载硬盘第一个扇区到内存地址0:7c00h,然后cpu从该地址开始执行。mbr中包含64字节的DPT,在DPT前面是4字节的扇区号,存放着LIUNUX_OS_DATA结构体的扇区号,这样mbr就可以通过bios调用int13h来执行扇区读写功能。

loader.com:是一个com程序,功能是加载执行kernel.exe。因为mbr只有一个扇区,除去64字节的DPT和末尾的0x55aa最多只能有446字节的指令,无法实现kernel.exe模块的加载工作(可执行文件格式一般比较复杂,内存加载运行时一般要做各种设置和重定向的工作),所以专门设计了loader程序来加载kernel.exe程序。

笔者用的微软的masm和link程序编译汇编原码。masm和link生成的exe是16位程序,其头部带有exe特有的数据格式,而mbr扇区被加载到0:7c00后会直接跳转到该地址执行指令,所以写到磁盘第一个扇区的mbr程序需要是一个纯代码形式的文件,16位的com程序是一个纯代码文件,满足我们的要求。

由于微软的masm和link开发年代久远,只能运行于32位系统,已经不能被64位操作系统支持,故上述mbr和loader这2个程序编译时,需要在windows xp(或者windows vista/7的32位平台)下,在cmd切换到汇编工程目录,执行如下命令编译链接:
masm mbr;
link mbr;
exe2bin mbr.exe mbr.com

masm loader;
link loader;
exe2bin loader.exe loader.com
注意不要忘记前两条命令中的";",这是特殊用途符号。

这样编译出来的两个程序就是com格式的。com格式不懂的请自行百度。

kernel.exe:是一个16位和32位汇编混合的16位/32位程序,功能主要有:实模式下设置cmos时钟,8254计时器,ps2鼠标,8042/8048键盘,rs232串口,8259中断控制器以及中断门,陷阱门,任务门,IDT,GDT,LDT等,调用bios的int10h vesa接口设置图形显示模式,然后进入32位保护模式,并用32位汇编实现了windows PE文件的加载功能(大体包括虚拟段映射,导入导出表,重定位三个部分),在保护模式中加载kernel.dll后,跳转到kernel.dll中的__kernelEntry函数开始执行,__kernelEntry函数做完初始化动作并设置好进程切换的基础设置后,执行sti指令打开中断,系统进入多进程/多线程模式,cpu等待被调度。该模块也包含文本和图形化日志输出模块,在发生异常时会显示异常信息。

kernel.exe程序是整个系统的核心,因为现代计算机一般都是基于中断和异常驱动的,而该程序正是中断和异常的触发、中转中心,例如:基于8254计时器和cmos时钟的计时器中断被当作时间片切换的驱动来源,时间片的切换频率正是计时器的一个计时周期;键盘鼠标的中断被分发到kernel32.dll中的__kKeyboardProc函数和__kMouseProc函数中处理;所有的异常被调度到__kException函数中运行;等等。

整个汇编工程中没有很复杂的模块,最大的文件也就500百多行,所以,虽然汇编晦涩难懂,但是花点时间,或者对有经验的同学,还是很容易上手的。另外,开发目录中包含debug.exe和debug32.exe,这两个工具可以帮助调试和查找汇编开发的错误。

kernel.exe虽然是整个系统的核心,但却是用汇编编写的,其主要的代码开销还是在GDT、IDT、tss等初始化、kernel.dll加载上等。开发的目标模块小功能简单时,汇编跟高级语言差别还不明显,但是如果开发比较复杂和逻辑功能比较强大的程序,汇编的可维护性、可扩展性、开发速度、难度、调试等直线上升。为了可扩展性、提高开发效率、可维护性,操作系统必须使用高级语言开发,这也是必须在汇编中完成pe文件加载执行的原因。后面的几个工程kernel.dll、main.dll、liunuxSetup.exe、liunux_seutp,都是使用visual stuidio c/c++或者linux gcc高级语言开发工具完成的。

liunuxos开机启动界面:
在这里插入图片描述
kernel.exe根据硬件配置和支持,要求选择几种分辨率的图像显示模式,按数字2选择1600x1200x32位模式启动:
在这里插入图片描述

按F1启动命令行窗口程序(窗口一般只有边框、客户区和一个关闭按钮),并从键盘输入输入"hello liunuxos!",按ESC退出cmd:
在这里插入图片描述

2. c/c++工程

c/c++工程包含4个模块:liunuxSetup.exe(windows系统下的安装程序)、linux_setup(linux系统下的安装程序)、kernel.dll、main.dll。其中,windows安装程序和linux安装程序用于把程序安装到windows或者linux系统中。kernel.dll和main.dll是liunuxos的核心部分。

liunuxSetup.exe
liunuxos系统用的virtualbox虚拟机,其他虚拟机如vmware证明不支持vesa图形模式,故无法正常显示vesa支持的图形模式。

liunuxos安装时需要如下几个程序:mbr.com,loader.com,kernel.exe, kernel.dll,main.dll,liunuxSetup.exe(windows系统),linux_setup(linux系统),font.db,HZK16(汉字16X16字体,可选)。其中font.db是英文字体,根绝bios资料,bios地址空间0xffa6e开始的地址存放着所有ASCII编码的8x8点阵字体,font.db正是从该地址中提取出来的。HZK16是我自己从网上看到的汉字16x16点阵字体,显示方式和ASCII的基本一致,代码中有多处使用的方法。

下图是liunux安装时的程序汇总:
在这里插入图片描述
此时点击安装程序liunuxSetup.exe即可完成LIUNUXOS的安装。
双击liunuxSetup.exe完成安装1
双击liunuxSetup.exe完成安装2

安装程序liunuxSetup.exe和os借助于结构体LIUNUX_OS_DATA实现新系统的启动以及安装文件的存储和查找:

typedef struct  
{
	int flag;			//0		标志 LJG0
	short loaderSecCnt;	//4		loader占用扇区数
	int loaderSecOff;	//6		loader扇区号偏移
	short kernelSecCnt;	//10	kernel.exe占用扇区数
	int kernelSecOff;	//12	kernel.exe扇区号偏移
	int mbrSecOff;		//16	mbr扇区号偏移
	int mbr2SecOff;		//20	mbr2扇区号偏移
	short fontSecCnt;	//24	字体占用扇区数
	int fontSecOff;		//26	字体扇区号偏移
	short kerdllSecCnt;	//30	kernel.dll占用扇区数
	int kerdllSecOff;	//32	kernel.dll扇区号偏移
	short maindllSecCnt;	//36	main.dll占用扇区数
	int maindllSecOff;		//38	main.dll扇区号偏移
	char reserved[22];		//42	保留
}LIUNUX_OS_DATA,*LPLIUNUX_OS_DATA;

liunuxSetup.exe双击运行后,在磁盘上查找超过512kb的连续的扇区(扇区默认是512B大小),一般是利用ntfs或者fat32文件系统空闲的磁盘间隙,以这片空闲扇区的第一个扇区号为起始扇区号,分别计算LIUNUX_OS_DATA结构体中各个字段的值,接下来,先将LIUNUX_OS_DATA结构体写入空闲扇区的第一个扇区,然后将其各个字段对应的文件依次写入对应的扇区号地址。另外,写入的时候还需要重新整合mbr.com程序,其具体做法如下:将mbr.com扩展到512字节,64字节DPT 从设备"\\.\PHYSICALDRIVE0"(windows系统)或者"/dev/sda"(linux系统)读取,在DPT前面4字节写入LIUNUX_OS_DATA所在的扇区号,mbr扇区末尾写入X55AA,并将此扇区写入当前系统的MBR中,下次启动时,mbr代码将会从LIUNUX_OS_DATA中找到loader.com,loader.com又会从LIUNUX_OS_DATA中找到kernel.exe,kernel.dll,main.dll,font.db等,依次完成系统的启动。

mbr执行时会根据LIUNUX_OS_DATA结构体,找到loader.com,将loader.com读入内存中并直接跳转执行;loader程序稍微复杂一些,主要功能是汇编代码实现的16位exe文件加载程序,将kernel.exe加载到内存中重定位后执行之。

linux_setup
linux安装程序linux_setup大约也是相似功能。但是其读写的磁盘设备是/dev/sda。

kernel32.dll
kernel32.dll是系统的主要组成部分,是代码量最多的模块。其主要包括以下模块:

  1. 进程线程创建和调度。时钟计时器每过一个计时周期就会触发一次中断,这就是liunuxos的线程切换频率,大概为1ms。

    当前支持两种进程/线程切换方式,第一种只需要一个tss任务状态段。进入中断后,中断程序保存硬件寄存器等执行现场,在切换为新的内核堆栈中依次push进新线程的硬件寄存器和其他现场寄存器,然后执行iret指令后,新的寄存器和现场被弹出到新线程中并执行。

    第二种线程切换需要两个tss,一个是所有进程共用的,一个在任务门中。时钟中断发生时,进程的硬件寄存器被自动保存在共享的tss中,在中断程序中,将进程共享的tss内容拷贝到内存中的该进程信息表中(进程信息表基地址在系统启动时被设置,通过pid可以找到进程的所有信息和寄存器、现场数据),然后把要被调度执行的进程的tss数据复制到共享tss中,中断程序执行iret指令返回后,新的线程已经被切换。在进程调度时,使用的比较简单的轮转调度,就是按照pid进程号,依次轮转调度执行。
    应用进程的栈初始化大小是1MB,系统进程的栈初始化大小是64kb。

    进程是资源的载体,其每个线程都是单独调度的,只是在进程切换时修改cr3,而在进程内部的线程之间切换时并不修改cr3,代码中利用pid这一标识判断线程是否在同一个进程中。

    该模块支持任意pe文件被加载执行。

    liunuxos不支持apic模式,虽然是多进程多线程,但是只支持一个cpu。

  2. 文件系统读写。主要是ntfs、fat32、iso9660文件系统的读取操作。文件系统的资料网上很多,请自行百度,我只是重复的搬砖而已。硬盘驱动器在虚拟机创建时只能支持ATA和SATA模式。

  3. 内存管理。主要是内存分配和虚拟内存的实现。关于虚拟内存,网上资料很多,但是没有找到切实可行的方法。我的做法分为两步:
    首先,在kernel.exe进入保护模式时,开启分页,具体的分页策略是:物理地址和线性地址一一对应的关系。第二步,在进程创建时,复 制页目录表,但是只包括系统空间的部分页表,其他页表设置为0,同时调用全局的内存分配程序(采用slab算法,分配的是物理地址而不是线性地址),分配的内存大小页面对齐,然后修改进程的页表项,并将其映射为从4gb依次往低地址的递减的虚拟线性地址。
    此种分页下,系统进程的线性地址都是物理地址,并且可以被每个用户进程访问,但是应用程序的代码段、数据段、堆栈段地址都是线性递减的。

    liunuxos虽然支持虚拟内存,但是不支持内存交换等功能。

    liunuxos具体的内存地址分布可以查看def.h文件。

  4. 键盘鼠标功能。支持键盘输入,鼠标左键右键点击等。此部分内容自行百度。

  5. soundblaster声卡播放wav格式音乐。虚拟机创建时必须指定声卡为soundblaster型。

  6. x86断点,单步调试和调试寄存器的使用。

  7. 基本的图形操作接口。窗口控件、矩形、圆、点、线的绘制和填充等简单图像化接口。

  8. 浮点中断的简单处理。

  9. elf文件和pe文件的加载执行。关于这部分,网上资料也很多,可自行百度。

main.dll

  1. 试图实现一个类似于windows cmd的shell环境,支持几十条命令的执行,详细内容可看代码。

  2. 本模块试图实现类似于windows
    explorer.exe的桌面程序,支持右键菜单和简单的窗口管理。该模块中还有一个比较复杂的功能:图形化文件管理器。通过鼠标双击可以层次化的浏览和打开磁盘中的文件夹,以及文本文件和bmp文件等简单文件。

在这里插入图片描述

在os开发过程中,我感受最深的就是,自己对硬件设备的了解非常少,虽然可供驱动的设备很多,但是硬件设备知识的细节过多,操作过程过于繁琐,而且对电气特性不够了解,这是os开发中的最大障碍。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/414506.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Grafana获取URL地址中的json数据

官方教程:https://grafana.github.io/grafana-json-datasource/query-editor 文章目录配置Grafana的JSON API插件模拟URL路由配置插件展示json数据最终效果配置Grafana的JSON API插件 直接在界面中下载这个插件即可: 也可以使用命令安装: …

Python每日一练(20230411)

目录 1. 环形链表 🌟 2. 比较版本号 🌟🌟 3. 基本计算器 🌟🌟🌟 🌟 每日一练刷题专栏 🌟 Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏…

hbuildX运行uniapp项目到andiord真机调试

一、打开手机开发者模式 我的是小米手机,这里就介绍一下小米手机如何打开开发者模式。其他机型可以百度一下。 1、手机 桌面 找到【设置】点击打开 2、点击【我的设备】。 3、进入后上滑,看见【全部参数】的按钮,点击打开 4、进入后上滑…

内存管理、内存映射、mmap

内存管理 MMU:Memory Management Unit,内存管理单元,CPU中独立硬件,负责处理CPU的内存访问请求。虚拟地址到物理地址的转换(即虚拟内存管理)。 物理内存:真实存在的插在主板内存槽上的内存条&a…

springboot2.7.10整合neo4j -- 关系双向绑定(UNDIRECTED)

背景 依赖 springboot版本 2.7.10 本地neo4j安装的版本&#xff1a;4.4.19 依赖如下&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-neo4j</artifactId></dependency>sprin…

Java基础(七)面向对象编程(高级)

1. 关键字&#xff1a;static 回顾类中的实例变量&#xff08;即非static的成员变量&#xff09; class Circle{private double radius;public Circle(double radius){this.radiusradius;}public double findArea(){return Math.PI*radius*radius;} }创建两个Circle对象&…

NanoPC-T4 RK3399:(一)平台与框架介绍

概述:近几年工作一直围绕Rockchip进行,手里刚好有一块NanoPC-T4的板子,本专栏将围绕友善NanoPC-T4做一个深度探究和总结,涉及uboot,kernel,rootfs定制等知识点。 一:硬件介绍 NanoPC-T4不仅是世界上最小的全功能接口RK3399一体化主板, 而且是一款完全开源的高性能计算平…

Winnolin PK参数及BE分析

文章目录一、PK参数分析一、非房室模型分析方法操作1.创建数据模型2.建立字段映射3.选择模型参数4.设置参数单位&#xff08;Units&#xff09;5.选择输出参数6.执行&#xff0c;查看结果二、生物等效性分析1.创建数据模型2.建立字段映射3.执行查看结果一、PK参数分析 药代动力…

Python机器学习:支持向量机

这是我读本科的时候第一个接触到的机器学习算法&#xff0c;但也是第一个听完就忘的。。。 他的基本思想很简单&#xff1a;想办法把一个样本集划成两个部分&#xff1a;对于空间中的样本点集合&#xff0c;我们找到一个超平面把这个样本点集合给分成两个部分&#xff0c;其中…

电源的安规设计

目录电源的安规设计1、安规的含义2、执行安规的目的3、电子产品设备可能存在的危险4、安全的基本原则5、安全防护的基本思想6、电击对人的伤害7、危险电压8、人对温度的刺激表现9、根据设备所处的环境&#xff0c;确定其污染等级10、安全距离的确定11、安规器件的要求——X、Y电…

【51单片机Task】:led十六进制控制led灯详解、按键控制流水灯、跑马灯等任务

学习目标&#xff1a; 学习普中51单片机的按键控制led灯。 电路图&#xff1a; 学习内容&#xff1a; LED灯的十六进制怎么算&#xff1f;&#xff1f;&#xff1f; 在这给大家算一下&#xff1a;首先先看一下LED模块的电路图&#xff01; 实物图&#xff1a; 可以把 D1 …

C#基础复习--枚举

枚举 目录 枚举 设置底层类型和显式值 隐式成员编号 位标志 Flags 特性 枚举是由程序员定义的类型&#xff0c;与类或结构一样 与结构一样&#xff0c;枚举是值类型&#xff0c;因此直接存储他们的数据&#xff0c;而不是分开存储成引用和数据 枚举只是一种类型的成员&#…

C语言实践——通讯录(3)(文件版)

首先感谢上一篇博客的大佬们的点赞&#xff0c;非常感谢&#xff01;&#xff01;&#xff01; 目录 前言 一、需要添加的功能 1.增加保存数据函数——可以保存数据到文件中 主要逻辑&#xff1a; 注意事项&#xff1a; 代码实现&#xff1a; 2.修改初始化函数——新…

ChatGPT全球大封号!数10万企业停摆:第一批玩AI的人,被AI给玩了

观点| Mr.K 主笔| Wendy.L 编辑| Emma来源| 技术领导力(ID&#xff1a;jishulingdaoli)3月31日&#xff0c;Open AI就开始无征兆的进行全球大封号&#xff0c;其中亚洲是重灾区&#xff0c;官方没有给出任何声明&#xff0c;具体原因不得而知。并且暂停了这些地区新账号的注…

IDEA下载数据库驱动失败解决办法

IDEA下载数据库驱动失败解决办法 在使用IDEA连接数据库的时候可能会出现驱动下载失败的情况。我们可以到数据库官网上下载驱动进行本地安装。 下面以Sql Server为例&#xff1a; 打开官网&#xff0c;下载驱动 官网地址&#xff1a;https://www.microsoft.com/zh-cn/sql-se…

如何用WordPress搭建一个属于自己的网站、让你从0-1的保姆级教程

文章目录搭建网站必备前提第一步 拉取对应的lnpm包解压lnpm服务器开放端口号配置nginx安装对应的wordPress包第一步&#xff1a;安装 wordpress 安装包并解压到 /home/wwwroot第二步&#xff1a;登录MySQL&#xff08;密码在前面 部署LNMP 时你设置过&#xff09;&#xff0c;创…

第五章 保护模式进阶,向内核迈进

获取物理内存容量 学习Linux获取内存的方法 通过调用BIOS中断0x15实现&#xff0c;分别是BIOS中断0x15的三个子功能&#xff0c;子功能号要存放到寄存器EAX或AX中&#xff1a; BIOS中断是实模式下的方法&#xff0c;只能进入保护模式前调用。可以在实模式下用这三种方法检测…

医院PACS系统源码,PACS源码,带3D重建PACS系统源码

PACS系统源码&#xff0c;带3D重建PACS源码 专门针对医院工作流程设计的&#xff0c;完全符合医院需要&#xff0c;配置灵活的放射科信息化解决方案。采用标准接口协议&#xff0c;实现预约登记、报告诊断、图像浏览和处理、信息统计等功能。 文末获取联系&#xff01; 功能介…

基于广义benders分解法的综合能源系统优化规划(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

用友U8 cloud,信创云ERP的数智先锋

编辑&#xff1a;阿冒设计&#xff1a;沐由从来未曾有过一个春天&#xff0c;能够像当下这般被如此由衷地期待。经历了漫长的1000多个日日夜夜之后&#xff0c;我们的工作与生活终于回到正轨。自2023年以来&#xff0c;中国市场迎来“开门红”&#xff0c;消费市场加速回暖&…