《30天自制操作系统总结》
发布时间
阅读量:
阅读量
文章目录
核心变量
系统功能模块
GDT, IDT, LDT
键盘和鼠标设备
存储管理
* 空闲分区优化
* 内存分配策略
-
多任务
-
- TSS
- 任务管理数据结构
- 任务切换
- 生命周期
- 调度策略
-
文件管理
-
- 文件控制块
- FAT
-
命令行窗口
-
- 数据结构
- 打开窗口
- 命令执行
- 系统命令
-
应用程序
-
- 文件头
- 应用程序启动
- API
- 应用程序示例
- 保护
-
图层管理
-
定时器
-
缓冲区
-
配置
-
- 显示模式
- 窗口显示
重要变量
| name | file | |
|---|---|---|
| mdec | 每次鼠标中断产生的数据 | bootpack.c |
| mx,my | 鼠标位置 | bootpack.c |
| mmx, mmy | 鼠标移动前位置 | bootpack.c |
| fifo | 缓冲区,存放鼠标、键盘、定时器缓冲数据 | bootpack.c |
| task_timer | 用于任务切换的定时器 | mtask.c |
| key_win | 获得焦点的窗口 | bootpack.c |
系统功能
| id | 功能 | 所在文件 | 说明 |
|---|---|---|---|
| 1 | 显卡模式设置 | haribote.nas | INT 0x10 |
| 2 | 调色板的设置 | graphic.c | set_palette |
| 3 | 绘制矩形 | graphic.c | boxfill8 |
| 4 | 屏幕初始化 | graphic.c | init_screen8 |
| 5 | 显示字符串 | graphic.c | putfonts8_asc |
| 6 | 鼠标显示 | graphic.c | init_mouse_cursor8,putblock8_8 |
| 7 | GDT、IDT段设置 | dsctbl.c | load_gdtr, load_idtr |
| 8 | PIC初始化 | int.c | - |
| 9 | 键盘中断处理 | keyboard.c | inthandler21 |
| 9.1 | Shift+F1 | bootpack.c | |
| 10 | 鼠标中断处理 | mouse.c | inthandler2c |
| 10.1 | 鼠标数据解析 | mouse.c | mouse_decode |
| 11 | 内存分配 | memory.c | memman_alloc_4k |
| 11.1 | 内存释放 | memory.c | memman_free_4k |
| 12 | 刷新图层,并避免刷新画面外 | sheet.c | sheet_refreshsub,sheet_refreshsub |
| 12.1 | 上下移动图层 | sheet.c | sheet_updown |
| 12.2 | 左右移动图层 | sheet.c | sheet_slide |
| 13 | 窗口制作 | windows.c | make_window8 |
| 14 | 定时器分配 | timer.c | timer_alloc |
| 15 | 任务切换 | timer.c | inthandler20 |
GDT, IDT,LDT


- gdt :
| 段号 | 说明 | 基址 | 限长 | G | C/D | - |
|---|---|---|---|---|---|---|
| 1 | 说明CPU可以管理全部4GB内存 | 0x00000000 | 0xffffffff | 1 | D | 系统 |
| 2 | 存放haribote.hrb | 0x00280000 | 0x0007ffff | 1 | C | 系统 |
| 3-1002 | 任务的TSS | |||||
| 1003-2002 | 任务的LDT描述符 |
- IDT
需要将中断处理程序注册到 IDT 中。
| 中断号 | 说明 | - | 中断处理 |
|---|---|---|---|
| 0x0c | 栈异常 | 如数组越界 | |
| 0x0d | 访问权限异常 | 主要是访问异常 | 强制结束任务并返回 |
| 0x20 | PIT 定时器 | IRQ0 | 产生定时中断 |
| 0X21 | 键盘中断 | IRQ1 | 数据输出到keyfifo |
| 0X2c | 鼠标中断 | IRQ12 | 数据输出到mousefifo |
- 0x00~0x1f都是异常所使用的中断,因此IRQ的中断号都是从0x20之后开始的。
LDT
- GDT的内存地址通过gdtr寄存器传递给CPU;LDT的内存地址则以段描述符的形式存储于GDT中(如同TSS模型),随后LDTR中的选择位将用于定位相应的段描述符。
- LDTR在TSS框架内运行
键盘与鼠标
键盘
在bootpack 中处理的键盘数据有即时捕获来自1号中断的数据,并不包括以下这些数据之外的其他内容,则直接发送给终端处理。
| 按键 | 键码 | 作用 |
|---|---|---|
| Tab | 0x0f | 切换窗口 |
| LShift | 0x2a(on),0xaa(off) | |
| RShift | 0x36(on),0xb6(off) | |
| CapsLock | 0x3a | |
| Shift+F1 | - | 关闭当前窗口 |
| Shift+F2 | - | 打开新的console |
| F11 | - | - |
- 在console 中处理的键盘数据有
| 按键 | 键码 | 作用 |
|---|---|---|
| 字母,数字,符号 | - | - |
鼠标
| 事件 | 动作 | case |
|---|---|---|
| 左键按下 | 找到点击图层 | 1:点击区域最上层且不为透明色 |
| 左键按下 | 切换窗口 | 2:1+点击窗口不是key_win |
| 更新mmx,mmy | ||
| 关闭应用窗口 | 4:1+点击x+(sht->flags & 0x10) != 0 | |
| 关闭命令行窗口 | 5:1+点击x+(sht->flags & 0x10) == 0 | |
| 鼠标移动 | 移动图层 | fifo为空 |
存储器管理
空闲分区管理
- 数据结构:
struct FREEINFO { /* information */
unsigned int addr, size;
};
struct MEMMAN { /* Memory management */
int frees, maxfrees, lostsize, losts;
struct FREEINFO free[MEMMAN_FREES]; //MEMMAN_FREE=4090
};
- 基于段式存储管理
- 在分配过程中,使用首次适应算法,并未进行碎片整理
- 在释放内存时使用拼接技术
内存分配
| BIOS,VRAM等 | 0x0000_0000 - 0x000f_ffff | |
| 软盘内容 | 0x0010_0000 - 0x0026_7fff (1440KB) | |
| FAT | 0x0100_0200 ~ 0x0100_13ff | |
| 文件控制块 | 0x0100_2600 ~ 0x0100_4200 | 32*224 |
| 空 | 0x0026_8000-0x0026_f799 | |
| IDT | x0026_f800 ~ 0x0026_ffff | 28*8 bytes |
| GDT | 0x0027_0000~0x0027_ffff | 213*8 bytes |
| bootpack.hrb | 0x0028_0000 ~ 0x002f_ffff | |
| 栈及其它 | 0x0030_0000 ~ 0x003f_ffff | |
| 空闲块列表 | 0x003c_0000 ~ | 约32KB |
| malloc | 0x0000_1000~ 0x0009_e000 | |
| malloc | 0x00400000 ~ memtotal - 0x00400000 |
总共32MB=225B=0x0200_0000
多任务
TSS
struct TSS32 {
int backlink, esp0, ss0, esp1, ss1, esp2, ss2, cr3;
// eip-程序计数器,指向当前执行代码位置,设置为任务代码的起始地址
// esp - 指向栈顶
int eip, eflags, eax, ecx, edx, ebx, esp, ebp, esi, edi;
// cs-代码所在的段号
int es, cs, ss, ds, fs, gs;
//ldtr ldt的选择符(指向GDT中的LDT描述符)
int ldtr, iomap;
};
任务管理数据结构
struct TASK {
// sel-指向GDT中的TSS
// flags: 0-未使用 1-sleep 2-running
int sel, flags; /* Sel is the number of GDT. */
// level-任务级别 priority*0.01s=分配的时间
int level, priority;
struct FIFO32 fifo;
struct TSS32 tss;
struct SEGMENT_DESCRIPTOR ldt[2];
struct CONSOLE *cons;
int ds_base, cons_stack;
struct FILEHANDLE *fhandle;
int *fat;
char *cmdline;
};
struct TASKLEVEL {
// 每个level 最多100个任务
int running; /* The number of tasks in operation */
int now; /* Variables that will let us know which task we're working on */
struct TASK *tasks[MAX_TASKS_LV];
};
struct TASKCTL {
// level0 优先级最高,最多10个level
int now_lv; /* At the operating level */
char lv_change; /*Do you want me to change levels next time with the taskup switch? */
struct TASKLEVEL level[MAX_TASKLEVELS];
struct TASK tasks0[MAX_TASKS];
};
任务切换
- 只需注册任务,inthandler20 自动进行切换
- 切换规则:
生命周期
task_alloc
task_run
task_sleep, 时间片用完
close_contask
初始状态0
就绪状态1
运行状态2
销毁状态3
task_run 还可以进行调整以影响任务的优先级(即level)和重要性(即priority)。函数定义如下:void task_run(struct TASK *task, int level, int priority); 当设置为0时会保持该任务的优先级不变;而当设置为1时会保持该任务的重要性不变。
调度策略
- 多级反馈队列
文件管理
- 应用程序也是将 name.hrb 以文件的形式存储到内存中
文件控制块
- 32 字节
struct FILEINFO {
// name[0]: 0xe5-此文件被删除 0x00-该段不包含文件信息
unsigned char name[8], ext[3], type;
char reserve[10];
// clusto-表示此文件从哪一个扇区开始存放
unsigned short time, date, clustno;
unsigned int size;
};
- 其中的 type(属性)
| 值 | 类型 |
|---|---|
| 0x01 | 只读文件 |
| 0x02 | 隐藏文件 |
| 0x04 | 系统文件 |
| 0x08 | 非文件信息(比如磁盘名称) |
| 0x10 | 目录 |
| 注 | 当同时具有多个属性,只需相加即可 |
FAT
- 文件存储位置: clustno乘以512加上十六进制数3E乘以千分之一兆字节加上ADR_DISKIMG在十六进制十六千十千零一十六位的位置。
- FAT存储位置: 计算结果是将十六进制数值零到二零零零加上十六进制零到一千三百FF的结果再加上ADR-DiskImg。
- 备份FAT的位置是通过从十六进制一四千到二五千的位置开始并加上ADR-DiskImg来确定的。
- 每个FAT表项占用一又二分之一字节的空间用于存储下一个簇号信息,在遇到特定的FF8到FFF序列时则表示文件已结束。
- 操作之前必须先解压缩文件内容。
命令行窗口
数据结构
struct CONSOLE {
// sht: 对应图层 0-没有图层(ncst 命令用到)
struct SHEET *sht;
// x, y, c: 光标的位置、颜色
int cur_x, cur_y, cur_c;
// timer 用于光标闪烁的定时
struct TIMER *timer;
};
struct FILEHANDLE {
char *buf;
int size;
int pos;
};
打开窗口
- 快捷键组合:Shift + F2
- 将变量key_win赋值为open_console函数返回的结果;
- 设置对象sht的任务属性为open_constask函数返回的结果;
命令执行
- 将字符串保存至 cmdline 变量中
- 按下 Enter 键后调用 cons_runcmd 函数以执行前述操作
- 如果该指令是由命令行提供的,则会触发相应的处理流程
- 否则可能由应用程序 cmd_app 处理该指令
- 若上一步操作返回成功(即返回值为零),则表示输入正确
系统命令
| 名称 | 说明 | 名称 | 说明 |
|---|---|---|---|
| mem | - | cls | - |
| dir | - | type [filename] | 显示文件内容 |
| exit | 关闭命令行 | start [app] | 运行应用(阻塞式) |
| ncst [app] | 运行应用(非阻塞) |
应用程序
文件头
| 地址 | 说明 | |
|---|---|---|
| 0x0000 | 请求操作系统为应用程序准备的数据段的大小 | segsize |
| 0x0004 | “Hari” | |
| 0x0008 | - | |
| 0x000c | ESP初始值&数据部分传送目的地址 | esp |
| 0x0010 | hrb文件内数据部分的大小 | datasize |
| 0x0014 | 需要传送的数据在.hrb文件中的起始地址 | |
| 0x0018 | 0xe9000000(JMP), | 当跳转到0x1b,马上执行JMP指令,跳转到应用程序入口地址 |
| 0x001c | 应用程序入口地址-0x20 | |
| 0x0020 | malloc空间在数据段的起始地址 |
应用程序启动
- 在命令行界面中键入应用程序名称,并按回车键执行。
- 识别当前目录下的文件控制块,并解析.hrb文件记录。
- 为栈区域分配内存空间,并初始化LDT的代码段与数据段配置。
q = (char *) memman_alloc_4k(memman, segsiz);
task->ds_base = (int) q;
set_segmdesc(task->ldt + 0, finfo->size - 1, (int) p, AR_CODE32_ER + 0x60); // p为代码起始地址
set_segmdesc(task->ldt + 1, segsiz - 1, (int) q, AR_DATA32_RW + 0x60);
- 启动:start_app(0x1b, 0 * 8 + 4, esp, 1 * 8 + 4, &(task->tss.esp0));
API
API制作流程
- 将 asm_hrb_api 注册到 IDT(0x40),应用程序将通过中断号调用asm_hrb_api。同时asm_hrb_api使用EDX作为功能号寄存器,因此在调用时可指定功能号以调用不同功能。
- asm_hrb_api 调用 _hrb_api
- hrb_api 根据 EDX 调用不同API
注: 中断返回:IRETD
| 功能号 | 功能 |
|---|---|
| 1 | 显示单个字符(AL=字符编码) |
| 2 | 显示字符串(EBX=字符串地址) |
| 3 | 显示字符串(EBX=字符串地址,ECX=字符串长度) |
| 4 | 程序结束 |
| 5 | 显示窗口 |
| 6 | 向窗口中输入字符 |
| 7 | 在窗口中绘制矩形 |
| 8 | memman初始化 |
| 9 | malloc |
| 10 | free |
| 11 | 窗口中画点 |
| 12 | 刷新窗口 |
| 13 | 窗口中画直线 |
| 14 | 关闭窗口 |
| 15 | 响应键盘输入(从和应用绑定的缓冲区中取字符) |
| 16 | 获取定时器 |
| 17 | 设置定时器发送数据 |
| 18 | 定时器事件设定 |
| 19 | 释放定时器 |
| 20 | 蜂鸣器发声 |
| 21 | 打开文件 |
| 22 | 关闭文件 |
| 23 | 文件定位 |
| 24 | 获取文件大小 |
| 25 | 文件读取 |
| 26 | 获取命令行输入 |
- 显示窗口、窗口中输入字符、图形API
| 寄存器 | 显示窗口 | 输入字符 | 输入矩形 |
|---|---|---|---|
| EDX | 5 | 6 | 7 |
| EBX | 窗口缓冲区 | 窗口句柄 | 窗口句柄 |
| ESI | x轴方向大小 | 显示位置x坐标 | x1 |
| EDI | y轴方向大小 | 显示位置y坐标 | y1 |
| EAX | 透明色 | 色号 | x0 |
| ECX | 窗口名称 | 字符串长度 | y0 |
| EBP | - | 字符串 | 色号 |
| 返回值 | |||
| EAX | 用于操作窗口的句柄 |
- 内存管理ap i
| 初始化 | malloc | free | |
|---|---|---|---|
| EDX | 8 | 9 | 10 |
| EBX | menman 的地址 | menman 的地址 | menman 的地址 |
| EAX | menman所管理内存起始地址 | 分配的内存空间地址 | 释放的内存空间地址 |
| ECX | 管理的字节数 | 分配的字节数 | 释放的字节数 |
- 绘图类api
| 点 | 直线 | - | |
|---|---|---|---|
| EDX | 11 | 13 | - |
| EBX | 窗口句柄 | 窗口句柄 | - |
| ESI | x坐标 | x1 | x1 |
| EDI | y坐标 | y1 | y1 |
| EAX | 色号 | x0 | x0 |
| ECX | 窗口名称 | y0 | y0 |
| EBP | - | 色号 | 色号 |
| 返回值 | |||
| EAX | 用于操作窗口的句柄 |
- 窗口和键盘api
| 刷新窗口 | 关闭窗口 | 键盘输入 | ||
|---|---|---|---|---|
| EDX | 12 | 14 | 15 | |
| EBX | 窗口句柄 | 窗口句柄 | - | |
| ESI | x1 | - | - | |
| EDI | y1 | - | - | |
| EAX | x0 | - | 0 | 1 (1-休眠,0-不休眠) |
| ECX | y0 | - | - | |
| 返回值 | ||||
| EAX | 按键的字符编码 |
- 定时器API
| alloc 获取定时器 | init 设置发送标志值 | set 时间设定 | free 释放 | |
|---|---|---|---|---|
| EDX | 16 | 17 | 18 | 19 |
| EAX | 定时器句柄(返回) | 数据 | 时间 | - |
| EBX | - | 定时器句柄 | 定时器句柄 | 定时器句柄 |
- 文件操作API
| open | close | seek | size | read | |
|---|---|---|---|---|---|
| EDX | 21 | 22 | 23 | 24 | 25 |
| EAX(I) | - | 文件句柄 | 文件句柄 | 文件句柄 | 文件句柄 |
| EBX | 文件名 | - | 偏移量 | - | 缓冲区地址 |
| ECX | - | - | 定位模式[1] | 文件大小获取模式[2] | 最大读取字节数 |
| EAX(O) | 文件句柄 | - | - | 文件大小 | 读取字节数 |
注:[1]: 0:定位起点为开头, 1:起点为当前访问位置 2:起点为文件末尾
[2]:0:普通文件大小, 1: 当前读取位置从文件开头起算的偏移量
2:当前读取位置从文件末尾起算的偏移量
应用程序示例
| 作用 | 程序 |
|---|---|
| 显示字符串 | hello3,hello4,hello5 |
| 显示窗口,并在窗口中输入字符串,绘制矩形 | winhelo2,winhelo3 |
| 窗口中画点 | star1,stars |
| 窗口中画直线 | lines |
| 响应键盘输入 | walk |
| 定时器 | noodle |
| 色彩显示 | color, color2 |
保护
- 通过区分系统段与应用段
| AR_DATA32_RW | AR_CODE32_ER | 中断门属性 | |
|---|---|---|---|
| 系统段 | 0x4092 | 0x409a | 0x008e |
| 应用段 | 0x4092+0x60 | 0x409a+0x60 | 0x008e+0x60 |
图层管理
- 数据结构
struct SHEET {
unsigned char *buf;
// flags: 0-未使用 1-使用中 (flags&0x10)!=0-该图层属于应用程序
// height: -1-invisibility max-top
// col_inv: 透明色色号
int bxsize, bysize, vx0, vy0, col_inv, height, flags;
struct SHTCTL *ctl;
struct TASK *task;
};
struct SHTCTL {
unsigned char *vram, *map;
int xsize, ysize, top;
struct SHEET *sheets[MAX_SHEETS];
struct SHEET sheets0[MAX_SHEETS];
};
-
刷新map sheet_refreshmap
得到区域内的map -
层级局部更新 sheet_refreshsub 基于给定的刷新区域和刷新层级,按照map执行更新。
-
图层垂直方向上进行调整 sheet_updown
- 图层平行方向上进行调整 sheet_slide
通过比较调整前后的布局情况来判断是否完成操作
- 图层平行方向上进行调整 sheet_slide
定时器
- 数据结构
struct TIMER {
struct TIMER *next;
unsigned int timeout;
// flags: 0-未使用 1-已配置 2-使用中
// flags2: 0-一般定时器 1-应用程序申请定时器
char flags, flags2;
struct FIFO32 *fifo;
// 此定时器的标志数据
int data;
};
struct TIMERCTL {
// count 当前时间
// next 下一个timeout时间
// t0 当前计时器
unsigned int count, next;
struct TIMER *t0;
struct TIMER timers0[MAX_TIMER];
};
- 计时器初始化频率设置为100Hz
- 定时采用列表组织结构,并在列表末尾设置了超时标志位(timeout=0xffff_ffff)
- timer_alloc函数负责将定时器从无用态转为配置态
- timer_settime函数用于设定超时值,并将该值赋给当前时间段
- inthandler20函数执行cout++操作;当触发超时事件时,则会将输出结果发送至缓冲区
- 调用timer_free函数后会将该定时器重置回无用态
- timer_cancel函数的作用是将指定的定时器从使用态转回配置态,在此过程中超时值并未被触发
缓冲区
数据结构
struct FIFO32 {
int *buf;
int p, q, size, free, flags;
struct TASK *task; // 当有数据时需要被唤醒的任务
};
- 每个任务有自己单独的缓冲区
公共缓冲区
| 记号 | 作用 | 说明 |
|---|---|---|
| 0,1 | 光标闪烁用定时器 | |
| 2, 3 | 2-显示光标,3-不显示光标 | 只要是用于console |
| 10 | 10秒定时器 | |
| 256 ~ 511 | 键盘输入 | =读入值+256 |
| 512~767 | 鼠标输入 | =读入值+512 |
| 768 ~ 1023 | 关闭命令行窗口及任务 | sht -= 768 |
| 1024 ~ 2023 | 关闭命令行任务 | task_id -= 1024 |
| 2024 ~ 2279 | 仅关闭命令行窗口 | 在运行应用时关闭窗口 |
配置
显示模式
| 功能 | 说明 | |
|---|---|---|
| 显示 | 对显卡的内存进行修改 | 显卡模式决定了整个画面分辨率、画面的变化也就是显卡内容的变化 |
非VBE
MOV AL,0x13 ; 3x200x8bit color VGA graphics
MOV AH,0x00
INT 0x10
MOV BYTE [VMODE],8 ; Note screen mode (see C language)
MOV WORD [SCRNX],320
MOV WORD [SCRNY],200
MOV DWORD [VRAM],0x000a0000
VBE
AX: 0x4f02, VRAM: 0xe0000000
| 画面模式号码 | BX | XSIZE | YSIZE |
|---|---|---|---|
| 0x101 | 0x4101 | 640 | 480 |
| 0x103 | 0x4103 | 800 | 600 |
| 0x105 | 0x4105 | 1024 | 768 |
| 0x107 | 0x4107 | 1280 | 1024 |
窗口显示
- 色彩
标题栏颜色:COL8_000084(深蓝)
标题栏背景:COL8_C6C6C6(灰)
透明色号——99
“桌面背景”:COL8_008484
全部评论 (0)
还没有任何评论哟~
