写一简单kernel心得
接着bios开机自检(这个不需要了解,只需了解最后跳转到0x7c00处即可.对于写kernel的人来说也是透明的.除非你是写bios的).它将自动从0盘1扇区加载mbr(主引导程序,512字节必须是以0x55,0xaa结尾..如果不够,可以用times 510-($-$$) db 0.很多都是这样定义的.) 加载到0x7c00处.因为bios执行的最后一条指令时jmp 0x7c00处.
紧接着用汇编编写mbr.. Mbr的功能是也是从硬盘读取loader(加载内核的程序). Loader存放的位置跟加载到的内存地址由自己决定...mbr就是根据硬盘提供的接口(,0x1f2~0x1f7)读取内容.movsb之类的指令复制到内存.最后一个jmp指令跳转到loader加载的地址
(注:以上的地址都是物理地址)
紧接着编写loader程序(加载内核程序).在loader程序中.我打算通过bios中断获取整个计算机的物理内存.并且进入保护模式.因此在loader.S中..我要定义代码段,跟数据段.显示段(显示字符.一般是0xb8000位置.)还是定义页表(采用二级页表
--------------------------------------------------------------------
数据段的内容是定义
全局描述符表跟代码段数据段显示段描述符..还有定义各种段选择子.位置自己定义.(这里我深深体会到了intel为了跟前代保持兼容多么拼了..定义的描述符真的好恶心.....的说
代码段:
通过int 15h中断获取物理内存.保存在某一固定物理位置.紧接着打开A20 Gate.加载gdt(全局描述符表).将cpu的cr0的pe位置置为1(由此进入保护模式,cs,ds的值不再是段基址.而是段选择子)
紧接着将各种段选择子复制到段寄存器中(保护模式)................紧接着..就开始创建二级页表(位置.自己固定.不过我看知乎上..一般是在1m物理内存以下).. .创建好以后.用sgdt将描述符表地址(现在是物理地址)加载到寄存器中(注意在创建页目录表的时候...一般我看知乎是将地址映射到0xc000000处.(windows是采用这地址)........开启映射前后....内核的虚拟地址(一个原本的物理地址变成了线性地址),还有0xc000000..这2个线性地址都要映射到1m以下物理内存.等于768页目录跟0页目录(寻址方式.一个虚拟地址的前10位为页目录表索引.中间10位为页表索引.最后10位为页框偏移量)都指向一个页表.
紧接着栈指针也加上0xc000000处...为了映射).紧接着将cr0寄存器的31位置为1,表示开启分页机制..注意之前的加载的gdt是物理地址.现在改为线性地址.需要重新加载(其实就是原来的地址+0xc000000处..一个lgdt[gdt_ptr].ok...最后.......跳转到内核所在的地址(自己定义内核所在的地址).......内核编写需要了解elf(格式.推荐<<程序员的自我修养>>..我没看..只是网友推荐罢了).定义elf头.程序头表.节头表.内核大小偏移量.等这些定义好了后.就可以加载内核了.一jmp指令就搞定了.....其实加载的内核一开始就是int main(void){while(1); return 0}....没办法.啥系统调用都没实现.啥都要自己实现....没办法...轮子是一步一步造出来的嘛.
保护模式的话.主要体现在cpu的特权级...有了操作系统后.其实一个程序分为用户部分跟内核部分(权限不同..一般用户部分是3特权级,0是最高特权级.还有IO特权级这个就懒得解释了都一样.哪些端口可以访问权限).........主要从低特权级转移到高特权级(用户态到内核态转换)只能通过各种门(任务们,调用门.陷阱门.中断门等) 条件.访问门时 cpl<=dpl[门] &cpl>=dpl[段] 这个表示r3可以使用内核服务访问段max(cpl,rpl)<=dpl[段] 这样就可以访问段. 从高特权级向低特权级返回的话.用ret弹出ip.或者retf cs:ip.......
PS:门描述符也是定义在GDT中.Call 调用门选择子.参数就是选择子.选择子找到门描述符.结果门描述符定义了代码段的基址(段选择子)跟偏移量..接着..找到内核例程.............坑爹.....对了..从用户态到内核态.所用的代码段跟数据段权限是不同的.so.所对应的栈也不同.有个TSS(任务状态段就是用来保存上下文的..其实一般只保存ss:sp... TSS效率比较低.linux只用了一个TSS并不切换).
紧接着要了解函数调用约定.查了wiki.c语言用的是cdecl约定(eax,edx,ecx由调用者保存.其他是被调用者保存.返回值在eax中.由调用者清理栈空间(就是esp+xx.实际不能说清理),参数压栈从右到左.例子自己google查吧
紧接着...卧槽..总该用c语言了吧.....之前一直用汇编..开始实现打印hello os.然后就....想顺便学学内联汇编..结果我了个大去...内联汇编是.AT&T汇编语法跟x86汇编有很大不同.没办法.....google对比下.一个一个指令弄呗......接着..想实现一个printf函数..除了 bios中断....算了..自己实现..一查显卡端口控制..我勒个去..吓得半死..VGA寄存器..一大堆..从0懒得实现了......从github抄了别人实现可以光标跟踪的输出函数....不过也懂得其原理.一个读入写出寄存器一个控制寄存器...忘了....不过不重要.略过
紧接着实现中断......哥现在理解的所谓操作系统就是由中断驱动的.所谓的时间片不过是(时间中断产生的间隔时间罢了),等于躺着被调用的代码....中断根据网上资料cpu提供统一的接口为中断信号的公共路线..毕竟没则么多引脚.分为INTR跟NMI(灾难性错误).只管INTR就可以了.编写中断向量表(IDT).并且要保存在IDTR寄存器中.一般发出的信号就是中断向量号.这个号就是中断描述符表的索引,加上IDTR得到某中断描述符具体位置.接着.中断描述符保存中断处理程序的选择子跟偏移量..接着道GDT中找.到代码段的起始地址再加上偏移量(保存在IDT描述符中).最后还要了解8259A(外设的可屏蔽中断的代理.包括键盘.时间中断).编程设置它初始化.设置主片葱片级联方式........最后编写中断处理程序(参考 unix v6实现)..最后时间中断....网上一般采用计数器8253设置时间中断发生的频率.提速降速.随你便.2333333 接下来就是哥认为最兴奋的一笔.内存管理.通过位图映射来管理内存.位图的每一位映射一个4k大小的页.0表示该页未被使用.1表示已经被使用..位图相关处理函数写好后.正式开始规划内存了..
内存池的划分参考知乎某大牛说的答案.内存池分为虚拟内存池跟物理内存池.虚拟内存池管理虚拟地址.物理内存管理物理地址.......................我的话把物理内存一半给用户,一半给内核........虚拟内存池(每个用户进程都有一个).但内核虚拟地址只有一个(因为内核只有一个.对不对).有关位图的位置就是自己定义了.......虚拟地址映射物理地址.完成此操作就OK了(不过这里还没实现用户进程.so.只完成了内核相关虚拟地址映射.).其实malloc的..就是用链表管理更小的内存块.我这里最小的内存块...是4k...太大了..要很小(16字节.32字节.64字节.等).用内存块描述符管理更小相同规格的内存块.用块描述符链表将整个链接起来.
接下来实现线程..线程哎..说实话..也没啥.就一个执行流(有自己的栈.寄存器映像)..不过只能使用进程的虚拟地址(没资源).没啥很高深的..总之进程跟线程就是人为搞的代码块.各种状态也是人自己划分的.自圆其说.2333333333333.(注:我实现的是内核线程.不然..还要自己实现.用户线程调度..太麻烦了.不然这些都交给内核调度岂不美哉.
一开始呢.要实现PCB(不然..你的上下文保存在哪?.TSS,cpu只自动获取ss:sp,需要自己手动压入当前各种寄存器状态.因为如果用TSS(在内存)切换效率相当低.so.基本linux也不用TSS).TCB分为2部分(一个是被中断打断的栈空间.一个是线程自己空间,说明一点.一般发现线程基本都是个函数.函数的地址....嘿嘿没错就是当运行这个线程时cs:ip指向函数地址.就通过赋值语句.)忘说了.pcb需要占用一页大小内存.so.从内核内存池中申请一页大小.)...有关内核调度.只不过时间中断处理程序调用了scedule函数(开始要判断是否该线程时间片用完了没).........用链表管理就绪队列.跟总队列..其实我搞的这简单内核最复杂不过是用来链表罢了.所以写个简单内核..初中生差不多都可以完成.当然linus那种大神....不是一般人可以达到的.
最后说说锁...保证原子性(不能被打断).其实最基本的就是一条关中断cli汇编指令.实现的.只不过封装了一层..哎.....没想到这么简单吧
最后说说系统调用(参考linux最初版本,实现的这功能其实现在已经被弃用了)...mov eax,子功能号. Int 号码.调用syscall_table[eax].(其中system_table保存的是子功能的函数名(地址)........)...最后就是无聊的抄...系统调用实现的代码了.............
最后要实现用户进程.用户进程最主要区别就是特权级是最低特权级3.还有有自己的虚拟地址.流程(向内核申请一页大小.创建pcb.初始化pcb.创建管理虚拟地址用户位图...紧接着创建线程...线程中创建页目录表.添加到就绪队列中...等待调度......这里最关键是要让用户进程在特权级3下运行..so.其用户进程所使用的代码段跟数据段都必须是3特权级......完结.
参考<<一个操作系统的实现>>,<<x86从实模式到保护模式>>.<<现代操作系统>>
(上面其实已经写的省略了很多.这是我一口气写完的..可能忘了很多....如果有啥不对.请求指正)
文件系统跟shell部分.(...文件系统打算照搬unix v6的..我读都没读完..shell参考学院的教材(unix/linux编程实践)......
好了.很惭愧.只做了一点微小的工作.谢谢大家.
写一简单kernel心得的更多相关文章
- linux设备驱动归纳总结(五):4.写个简单的LED驱动【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-84693.html linux设备驱动归纳总结(五):4.写个简单的LED驱动 xxxxxxxxxxx ...
- 一步一步写一个简单通用的makefile(三)
上一篇一步一步写一个简单通用的makefile(二) 里面的makefile 实现对通用的代码进行编译,这一章我将会对上一次的makefile 进行进一步的优化. 优化后的makefile: #Hel ...
- 【Linux开发】linux设备驱动归纳总结(五):4.写个简单的LED驱动
linux设备驱动归纳总结(五):4.写个简单的LED驱动 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...
- 用Python写一个简单的Web框架
一.概述 二.从demo_app开始 三.WSGI中的application 四.区分URL 五.重构 1.正则匹配URL 2.DRY 3.抽象出框架 六.参考 一.概述 在Python中,WSGI( ...
- 如何写一个简单的http服务器
最近几天用C++写了一个简单的HTTP服务器,作为学习网络编程和Linux环境编程的练手项目,这篇文章记录我在写一个HTTP服务器过程中遇到的问题和学习到的知识. 服务器的源代码放在Github. H ...
- 如何写一个简单的shell
如何写一个简单的shell 看完<UNIX环境高级编程>后我就一直想写一个简单的shell来作为练习,因为有事断断续续的写了好几个月,如今写了差不多来总结一下. 源代码放在了Github: ...
- 用C#Winform写个简单的批量清空文件内容和删除文件的小工具
用C#Winform写个简单的批量清空文件内容和删除文件的小工具 本文介绍这个简单得不能再简单的小项目.做这个项目,有以下目的. 1 当然是做个能用的工具 2 学习使用Github 关于用VS2013 ...
- 分享:计算机图形学期末作业!!利用WebGL的第三方库three.js写一个简单的网页版“我的世界小游戏”
这几天一直在忙着期末考试,所以一直没有更新我的博客,今天刚把我的期末作业完成了,心情澎湃,所以晚上不管怎么样,我也要写一篇博客纪念一下我上课都没有听,还是通过强大的度娘完成了我的作业的经历.(当然作业 ...
- linux设备驱动归纳总结(十一):写个简单的看门狗驱动【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-112879.html linux设备驱动归纳总结(十一):写个简单的看门狗驱动 xxxxxxxxxxx ...
随机推荐
- NOIP2013 乌龟棋
描述 小明过生日的时候,爸爸送给他一副乌龟棋当作礼物. 乌龟棋的棋盘是一行N个格子,每个格子上一个分数(非负整数).棋盘第1格是唯一的起点,第N格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点 ...
- Linux 用户管理(一)
一.基础知识介绍 用户 用户组的概念 每个文件和进程,都需要对应一个用户和用户组 linux 系统通过UID和 GID识别用户和组 用户名相当于人名(给人看) UID和GID相当于身份证(系统用的) ...
- VS自学日记整理
vs渣渣自学之旅 一.vs实用插件 二.制作简历之旅 1.一堆错误示范示范 2.标签的使用 3.文件的文本的样式的保存 二.美化博客园之旅 1.第一天 学python有点多这个慢慢消化
- python有三元运算符吗
所属网站分类: python基础 > 语法,变量,运算符 作者:goodbody 链接: http://www.pythonheidong.com/blog/article/12/ 来源:pyt ...
- hdu 5459
Problem Description I've sent Fang Fang around 201314 text messages in almost 5 years. Why can't she ...
- BZOJ 3590: [Snoi2013]Quare
首先有一个性质,一个双联通图一定可以拆成一个小的双联通子图和一条链 一个点可以视为权值为0的双联通图或者一个点的链 状压DP,枚举子集 O(3^n*n^2) #include<cstdio> ...
- HSL颜色处理
H--色调 S--饱和度 L--亮度 HSB(HSL) 色调饱和度亮度模式 以另外一种不同的理念进行色彩的调配 H色调 0~360 圆环形式 以角度表示 S 饱和度 0~1 之间的小数 L 亮度 0~ ...
- Win7通知区域的图标怎么去除?
由于本人有洁癖,最近在用win7的时候,很收不了已经卸载了的一些软件,在win7右下角的通知区域图标中还留有痕迹,于是上网查找了下解决方案. 用以下方法完美解决问题. 这里依然是以注册表的修改方法为主 ...
- python week08 并发编程之多线程--实践部分
一. threading模块介绍 multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因而不再详细介绍 官网链接:https://docs.pytho ...
- day03_08 变量的重新赋值02
python自动回收垃圾内存,不用了自动会回收,但是C不会 以下del代码为手动强拆,就是从内存中删除变量名