首页
社区
课程
招聘
[原创]自己实现的一个基于x86的操作系统
发表于: 2013-12-25 03:23 54038

[原创]自己实现的一个基于x86的操作系统

2013-12-25 03:23
54038

祝各位坛友圣诞节快乐!
不知道还有人对这些东西有兴趣么
很早就被x86的一些底层细节idt啊gdt啊cpl、dpl、rpl之类的搞的晕头转向,后来渐渐的对这些东西有了概念,但还是不清晰。断断续续的写了很多汇编程序来验证理解的是否正确,慢慢的发现自己去实现一个实验os好像也没有太多技术上的难点了。于是就搞了这个东西。

真正做的时候发现也挺烦的,r0下每写一行都要考虑要不要关中断之类的,好在本身就简单,目前好像还没发现啥重大的bug

虽然小,但这真的是一个多任务操作系统,而不是一个bootloader之类的。他能同时跑多个任务,且每个任务有独立的进程空间。任务运行于r3,支持十几个系统调用,可以完成一些简单的功能。src里有为他编写的应用范例,比如shell

附件的src可以在windows下用cygwin编译(要把cygwin的bin目录加到windows环境变量里,然后直接运行build.bat即可,oskernel即为生成的内核),也可以在linux下运行sh脚本lbuild编译

因为内核的编写遵循了grub的muiltboot规范,所以编译成的oskernel须有grub引导。
tinix.img这个软盘镜像已经安装了grub,把oskernel放到boot目录下,然后在虚拟机下就可以跑了
在实际的物理机上也可以用,修改makefile CCFLAGS里加上-D NO_FLOPPY,编译出oskernel直接用现有操作系统里的grub或grub4dos就可以引导

这是附件压缩文件的目录
|-- floppy.img                                        #最终完成的软盘镜像
|-- tinix                                                        #内核目录
|   |-- boot                                                #内核加载器目录
|   |-- kernel                                        #内核代码目录
|   |   |-- dev                                        #设备驱动目录
|   |   |-- fs                                        #文件系统目录
|   |   |-- include                                #头文件
|   |   |-- init                                        #内核代码入口
|   |   |-- kernel                                #任务管理、陷阱门、时钟、系统调用等
|   |   |-- lib                                        #内核lib
|   |   |-- mm                                        #内存管理目录
|   |   |-- Makefile                                #用于Cygwin下编译的makefile
|   |   `-- Makefile.linux                #用于linux下编译的makefile
|   |-- build.bat                                #用于Cygwin下编译的两个批处理
|   |-- clean.bat
|   |-- lbuild                                        #用于linux的shell脚本
|   |-- lclean
|   |-- build.log                                #gcc编译生成的错误输出
|   |-- loader.map                                #内核加载器符号文件
|   |-- system.map                                #内核符号文件
|   |-- system.idc                                #用于IDA的内核符号脚本
|   |-- map2idc.pl                                #生成IDA脚本的perl脚本
|   `-- oskernel                                        #最终编译出的内核镜像
`-- user                                                        #用户态代码目录
    |-- app                                                #用户态应用源代码
    |-- include                                        #用户态头文件
    |-- lib                                                #用户态lib函数源代码
    |-- Makefile.eval                        #用户态应用的几个makefile
    |-- Makefile.pi
    |-- Makefile.queens
    |-- Makefile.sh
`-- objs                                                #避免重新编译的obj文件

具体的一些实现细节见附件

贴些应用演示





>>>>>>>>>>>以下是帖子doc附件的内容<<<<<<<<<<<

简介

这是一个实验性质的操作系统,起初目的是为验证我对x86底层的一些了解是否正确,以及尝试一些不同于现有操作系统的想法。因为一开始学写makefile参考了于渊的《自己动手写操作系统》,名称也没有改,就叫tinix。 但已经和原来的tinix没有任何关系了。为了将精力尽量投入在内核本身的逻辑上,一些和硬件相关的部分尽量简化或尽量移植,一些libc功能性函数则是直接完全移植,如vsprintf等。

至今为止,仅实现了多任务管理、任务特权级管理(使用0和3两级,普通程序运行于ring3内核运行于ring0)、多控制台管理(F1~F4可切换)、不高于4g的内存管理(使用两级分页)、有限的文件系统管理、fat12文件系统、虚拟文件系统、以及软驱驱动和键盘驱动。目前还没有实现的包括多用户、文件权限、硬盘驱动、和其他外设驱动。

对于应用程序的支持,仅实现了单个任务对应单个ELF格式文件的加载,不支持写时复制机制和动态库加载机制。

此项目的目录结构如下

|-- floppy.img                                        #最终完成的软盘镜像
|-- tinix                                                        #内核目录
|   |-- boot                                                #内核加载器目录
|   |-- kernel                                        #内核代码目录
|   |   |-- dev                                        #设备驱动目录
|   |   |-- fs                                        #文件系统目录
|   |   |-- include                                #头文件
|   |   |-- init                                        #内核代码入口
|   |   |-- kernel                                #任务管理、陷阱门、时钟、系统调用等
|   |   |-- lib                                        #内核lib
|   |   |-- mm                                        #内存管理目录
|   |   |-- Makefile                                #用于Cygwin下编译的makefile
|   |   `-- Makefile.linux                #用于linux下编译的makefile
|   |-- build.bat                                #用于Cygwin下编译的两个批处理
|   |-- clean.bat
|   |-- lbuild                                        #用于linux的shell脚本
|   |-- lclean
|   |-- build.log                                #gcc编译生成的错误输出
|   |-- loader.map                                #内核加载器符号文件
|   |-- system.map                                #内核符号文件
|   |-- system.idc                                #用于IDA的内核符号脚本
|   |-- map2idc.pl                                #生成IDA脚本的perl脚本
|   `-- oskernel                                        #最终编译出的内核镜像
|-- tools
|   `-- WinImage.exe                        #用于在windows下编辑软盘镜像的工具
`-- user                                                        #用户态代码目录
    |-- app                                                #用户态应用源代码
    |-- include                                        #用户态头文件
    |-- lib                                                #用户态lib函数源代码
    |-- Makefile.eval                        #用户态应用的几个makefile
    |-- Makefile.pi
    |-- Makefile.queens
    |-- Makefile.sh
`-- objs                                                #避免重新编译的obj文件
       
下文将对内核态的设计与实现进行解释,并对该系统的实际应用进行演示。

内核加载器

主要文件
        /tinix/boot/boot.S
/tinix/boot/loader.c

        为方便内核设计和编译,内核的加载位置位于高地址0xC0000000,但物理内存无法保证有这个物理地址,因此内核需要通过一个位于低地址的加载器将其转移并映射到高地址。这就是内核加载器的作用。

        内核加载器自身的加载则需要借助grub,因此内核加载器的编写遵循了grub的ultiboot规范。同时内核加载器还有一个任务,即传递grub探测到的硬件信息。这个项目里只使用到了grub探测到的物理内存上限。

        在loader.c中,内核加载器会将kernel文件复制到物理0地址。同时将物理内存上限保存到0x00090000,将当前光标位置保存到0x00090004。加载工作完成之后,内核加载器将直接跳转到0地址,将控制权移交实际内核。

内核初始化

主要文件
        /tinix/kernel/init/head.S
        /tinix/kernel/init/main.c

        在head.S中,start为内核的入口,在这里重新设置esp为0x90000,然后依次开分页、初始化gdt、初始化中断控制器、初始化idt。

        开分页时,为保证代码地址和数据地址正确。这里将线性地址0~0x400000和0xC0000000~0xC0400000映射到相同的物理地址0~4m。映射关系如图。建立完映射关系后,init_page通过调整esp和修改返回地址转移到高3g执行。


全局描述符表中初始化了4个描述符,分别为内核代码段,内核数据段,用户代码段和用户数据段。这4个描述符在表中是连续的,这是为了兼容sysenter、sysexit的规定。中断描述符表初始状态均为空。

        head.S完成基本的初始化工作后,将进入k_main函数,在这里调用各个模块的初始化接口。操作系统所有的初始化工作完成之后,0号任务将依次在0~2三个空闲控制台上启动三个子任务shell,路径为/bin/sh。
       
        之后,0号任务将进入半睡眠状态,最大限度的让出cpu资源。

内存管理

主要文件
        /tinix/kernel/mm/memory.c

        这里的内存管理指狭义的内存管理,及映射关系的建立和映射关系的取消,是页级的。内核态内存的管理,将从物理内存1M位置开始,在1M位置,依次为内核页目录表、内核页表0~3。尾随其后的是根据loader传来的物理内存大小计算得出的一个mem_map数组。这个数组用于管理物理内存,数组地址为0x00105000。mem_map之后的内存将用于动态管理。建立完mem_map之后,为避免其他问题,将取消掉低4M的映射关系。

        因为只为内核保留了4张页表,所以内核本身能使用的内存将不能大于16M。另外,为方便内核态线性地址和物理地址之间的相互转换,内核态内存和物理内存的映射关系必须为“线性地址 - 物理地址 = PAGE_OFFSET”。因此,内核内存申请和用户内存申请的策略是不同的。内核态内存申请将在mem_map中从前往后找,用户态相反。

        最终形成的映射关系,如下图所示


        内核态内存申请与释放接口为k_get_page和k_free_page。用户态接口为u_get_pages和u_free_page_table。

文件管理

        主要文件
        /tinix/kernel/fs/fs.c

        文件系统的管理基于fs.c中维护的一张内核文件表kfile_table。向这张表中添加文件或添加目录通过fs_add_file和fs_add_dir进行。

        文件表中每一项均为一个file结构,定义如下

typedef struct _file {
    u32 f_type;
    u32 f_father;					//父目录索引
    u32 f_time;						//最后修改时间
    u32 f_base;						//文件基址,不同的文件含义不同
    s32 f_size;						//文件大小
    s32 f_pos[MAX_TASK];			//读写位置,每个任务有自己的读写位置
    struct _file_operations *f_op;	//操作文件跳转指针 不能空!!!     
    struct _file_req *f_req;		//操作请求结构,可以空
    u8  f_name[MAX_NAME];
}__attribute__((packed)) file;
typedef struct _file_operations {
    s32 (*seek) (void *, s32, u32);		//定位文件当前的读写位置
    s32 (*read) (void *, u8 *, u32);	//读文件
    s32 (*write) (void *, u8 *, u32);	//写文件
}__attribute__((packed)) file_operations;
typedef struct _file_req
{
    u8  f_req_state;                 			//设备状态
    u32 f_req_orgsize;            				//原始请求大小
    u32 f_req_completed;     					//已完成请求大小
    u32 f_req_task;                  			//发起请求的任务id
    u8  f_req_buffer[MAX_IOBUFFER];
}__attribute__((packed)) file_req;

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 5
支持
分享
最新回复 (128)
雪    币: 435
活跃值: (1282)
能力值: ( LV13,RANK:388 )
在线值:
发帖
回帖
粉丝
2
对了,user目录下的代码要在linux下编译的话makefile要改一下
把objcopy那个动作删掉就行了
2013-12-25 03:32
0
雪    币: 101
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
真羡慕楼主啊,我也想自己实现一遍,打下坚实基础,可是找工作用的东西还没学完,哎...
2013-12-25 07:08
0
雪    币: 601
活跃值: (256)
能力值: ( LV11,RANK:190 )
在线值:
发帖
回帖
粉丝
4
我勒个去,支持下
2013-12-25 08:47
0
雪    币: 76
活跃值: (27)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
支持楼主的精神
2013-12-25 09:00
0
雪    币: 124
活跃值: (469)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
楼主圣诞快乐
2013-12-25 09:04
0
雪    币: 132
活跃值: (19)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
神一样的存在
2013-12-25 09:17
0
雪    币: 371
活跃值: (72)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
8
纯帮顶~
2013-12-25 09:23
0
雪    币: 108
活跃值: (44)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
9
顶楼主,学习下
2013-12-25 09:23
0
雪    币: 19
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
坐等授精~
2013-12-25 09:25
0
雪    币: 300
活跃值: (179)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
11
高端大气上档次~
2013-12-25 09:25
0
雪    币: 6976
活跃值: (1477)
能力值: ( LV11,RANK:180 )
在线值:
发帖
回帖
粉丝
12
尼玛, 都开始写操作系统去了??
2013-12-25 09:28
0
雪    币: 218
活跃值: (62)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
神与你同在,阿门!
2013-12-25 09:33
0
雪    币: 194
活跃值: (261)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
牛叉。。。。
2013-12-25 09:35
0
雪    币: 2155
活跃值: (29)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
15
我一直想写一个,但是断断续续的,现在连bootloader都没写完,你这就已经出来跑多任务的OS了。。。

试试看能不能写成硬实时系统,感觉那个比较值钱。。。哈哈
2013-12-25 09:43
0
雪    币: 5
活跃值: (31)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
确实是值得我们学习。
2013-12-25 10:08
0
雪    币: 142
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
楼主,你这么牛逼,家里人知道嘛?
2013-12-25 10:10
0
雪    币: 3
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
求学习经验 学习路径!
2013-12-25 10:14
0
雪    币: 35
活跃值: (96)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
不服不行啊
2013-12-25 11:08
0
雪    币: 41
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
强。。学习。。。
2013-12-25 11:09
0
雪    币: 692
活跃值: (40)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
21
楼主的精神值得鼓励!
楼主所做的工作也值得鼓励!

能看到有人研究操作系统的实现,是很了不起的事。本来这些事应该是中国的计算机科研机构或者大学的教授来完成的,可是。。。

虽然目前这个离现代操作系统还有很长的路要走,但是希望看雪有更多的人投入精力去研究这些。

我给楼主一个建议,要改善一下任务的切换方式。在保存完栈之后,只切换esp寄存器,然后用x86的iretd指令来切换,会更灵活,效率更高,而且也是现代Linux内核用的方式。

如果有操作系统内核爱好者可以去osdev.org,这里有很多优秀的开源os项目。
2013-12-25 11:12
0
雪    币: 281
活跃值: (177)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
牛叉。。。都弄上os了
2013-12-25 11:25
0
雪    币: 26
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
楼主有没有,川合秀实的书啊。。。能否给分享下
2013-12-25 11:37
0
雪    币: 113
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
这个要支持,一直在酝酿,只是没行动,佩服楼主
2013-12-25 11:47
0
雪    币: 458
活跃值: (306)
能力值: ( LV12,RANK:400 )
在线值:
发帖
回帖
粉丝
25
牛气冲天。。
2013-12-25 11:51
0
游客
登录 | 注册 方可回帖
返回
//