当人按下笔记本开机键时.cpu的cs寄存器(基址)跟ip(偏移量)寄存器加电.被强制初始化为(jmp xxx:xxx) 跳转到bios所在的地址.

接着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编程实践)......

好了.很惭愧.只做了一点微小的工作.谢谢大家.

转载于:https://www.cnblogs.com/zengyiwen/p/5517018.html

写一简单kernel心得相关推荐

  1. linux下Qt编写串口调试助手,如何在linux下用QT写一个简单的串口调试助手

    如何在linux下用QT写一个简单的串口调试助手 QT5串口类 在QT5以前,编写串口一般使用的是qextserialport类,但在QT5之后有了QT自带的串口类SerialPort(串口基础类)和 ...

  2. linux的led驱动的实验总结,linux设备驱动归纳总结(五):4.写个简单的LED驱动

    linux设备驱动归纳总结(五):4.写个简单的LED驱动 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  3. 自己动手写一个简单的bootloader

    自己动手写一个简单的bootloader 15年10月31日19:44:27 (一) start.S 写这一段代码前,先要清楚bootloader开始的时候都做什么了.无非就是硬件的初始化,我们想要写 ...

  4. 写着简单跑得又快的数据库语言 SPL

    数据库语言的目标 要说清这个目标,先要理解数据库是做什么的. 数据库这个软件,名字中有个"库"字,会让人觉得它主要是为了存储的.其实不然,数据库实现的重要功能有两条:计算.事务!也 ...

  5. 用java做一个简单记事本_用记事本写一个简单的java程序

    用记事本写一个简单的java程序 第一步: 安装好jdk,并设置好环境变量. 桌面-计算机(右键)-属性-高级系统设置-环境变量-path-在变量值后加上:和jdk安装路径加上(路径即为C:\Prog ...

  6. 如何搭建python框架_从零开始:写一个简单的Python框架

    原标题:从零开始:写一个简单的Python框架 Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发. 你为什么想搭建一个Web框架?我想有下面几个原因: 有一个 ...

  7. ipad php mysql_如何用PHP/MySQL为 iOS App 写一个简单的web服务器(译) PART1

    原文:http://www.raywenderlich.com/2941/how-to-write-a-simple-phpmysql-web-service-for-an-ios-app 作为一个i ...

  8. python推荐系统-用python写个简单的推荐系统示例程序

    用python写个简单的推荐系统示例程序 作者:阿俊 发布于:2011-11-26 16:03 Saturday 分类:推荐系统 python这门语言写程序代码量非常少,短短几行就可以把程序写的很清楚 ...

  9. python123程序设计题说句心里话_用c++写一个简单的计算器程序

    // 050305.cpp : 定义控制台应用程序的入口点. // // 050304.cpp : 定义控制台应用程序的入口点. // //四则运算 #include "stdafx.h&q ...

  10. 用java写一个简单的区块链(下)

    用java写一个简单的区块链(下) 2018年03月29日 21:44:35 java派大星 阅读数:725 标签: 区块链java 更多 个人分类: 区块链 版权声明:本文为博主原创文章,转载请标明 ...

最新文章

  1. 7.1 pdo 宝塔面板php_宝塔面板配置阿里云服务器步骤和教程
  2. 良心直播!看完你的Python入门基础就妥了!
  3. Sublime text3关闭自动更新(hosts屏蔽)
  4. Android为spinner设置适配器,Android Spinner与适配器模式详解及实例代码
  5. Linux运维系统工程师系列---11
  6. linux emacs 编译,linux 编译安装emacs和安装spacemacs
  7. PID控制的输入量与输出量的关系
  8. Intelli IDEA 快捷键操作,咱来点不一样的
  9. 1秒替换视频背景,阿里AI获CVPR 2020四项世界冠军
  10. 最简单的基于FFmpeg的封装格式处理:视音频分离器(demuxer)
  11. 第一次工作面试(蘑菇街)
  12. c语言画bode图程序,根据上位机测得的Bode图的幅频特性,就能确定系统(或环节)的相频特性,试问这在什么系统时才能实现?...
  13. 计算机系统组成复习及CRC循环冗余校验码计算
  14. 如何成为团队核心?从写出不可维护的代码开始
  15. arduino的L298N电机驱动模块
  16. hdu 5285 wyh2000 and pupil【二分图+染色法】
  17. 保护你的隐私,五种控制Android应用的权限的方法
  18. 属于多媒体计算机必不可少的配置是,制作多媒体作品选择(答案)
  19. 通过Excel VBA对序列实现自动分级
  20. linux 处理匹配文本的前后行

热门文章

  1. regsvr32.exe是什么东西
  2. java final一点
  3. Java来做马里奥[0]—让精灵再次舞动
  4. 推荐:用ogr和PIL把矢量数据转化成栅格图像
  5. android webview 选择图片上传,Android webview打开本地图片上传实现代码
  6. 山西农业大学c语言程序设计试卷答案,2016年宁夏医科大学公共卫生与管理学院C语言程序设计(加试)复试笔试最后押题五套卷...
  7. 生日python十种日期格式_Python可视化-二十四节气与生日间隔天数统计
  8. JavaScript数据类型之String
  9. python3 pygame load图片不显示_关于pygame image.load函数的问题
  10. async spring 默认线程池_springboot中@Async默认线程池导致OOM问题