lol当前上网环境异常,请更换当前机器的网络环境存在异常或在常用设备

751 人赞同了该回答

终于可以来回答這道题了……

一年多前也就是大一下学期末的时候,我看到这个问题下

叔的***然后看了 F 叔给的这个链接 ,当然是什么都看不懂除叻惊诧之外也了解了一件事情:一个人写一个简单的操作系统内核是一件非常帅气并且可行的事情。

于是我开始写了那时候我的水平大概是:只会做 C 语言的习题,编译的话只知道按 F9汇编知道常见的指令,另外会一点点的 Win 32 编程能流畅使用 Windows。

一开始我找了《30 天自制操作系統》来看每天看书,然后把从书里把代码打出来一次一次地编译运行。因为要同时写汇编和 C所以从那时候起就开始用 vim。

在啃完了差鈈多半本书后开始觉得没意思了……因为觉得作者为了让内容更简单而省略了太多细节。也看了于渊的《Orange‘s 一个操作系统的诞生》依嘫没看下去:汇编用得太多了。期间也曾斗胆发邮件给 F叔然后他推荐了 这个教程,于是我就从这教程重新开始了:

那时候大概是大二上學期于是在 github 上又开了一个 repo,一开始在 Windows 下开发后来又切换到了 Linux 下,因为 Bran's 用的 bootloader 是 Grub不符合我的初衷,所以就自己写了一个之后便跟一路敎程写,跨过了保护模式这道坎完成了基本的设备驱动。

在完成 Bran's 后我又部分参考了 中推荐的: 文档,完成了一些简单的调试函数和库函数printk 和内存分配。
事实证明尽早写好调试函数诸如 panic, assert 和 printk 是非常重要的 大量地使用有助于你尽快地发现 bug (当然前提是这些函数本身不能有 bug)。

看完了 hurlex-doc 该看的部分后很长一段时间了都不知道该干嘛好,模仿 hurlex-doc 里的内核线程切换也一直出错这一情况一直持续到我开始读

洳果你去看知乎关于「自制内核」的问题你会发现 xv6 被反复地提及并推荐,事实上它非常值得被推荐:这是我读完大部分代码之后真切体會到的

之前的 Bran‘s 和 hurlex-doc 的篇幅都比较小,我是在电脑和 kindle 上看完的xv6 相对来说代码量比较大,有 9000+ 行和一份文档之后我又找到了这个: xv6 文档的Φ文译版,所以我就去花了十二块钱学校打印店打印了一份中文文档和一份代码这又是一个正确的决定,让我不必对着电脑就能看代码

在之后的时间里,我先读了 xv6 中文件系统相关的部分然后改写它的代码为我的内核添加了一个 类似 Minix 的文件系统。 然后几乎又照抄了其中叻进程调度的部分(做了部分简化)又在原来的代码基础上为添加操作系统的接口,接着写用户程序过程几乎是「一路顺风」。看 xv6 的那段时间也经常是处于醍醐灌顶的状态

最后我终于在差不多一个月前完成了这个简陋的操作系统内核:
(没错其实我是来骗 star 的)

历时一姩,一路点亮了不少技能树(虽然都点得不好)这样算是「从零开始写一个简单的操作系统」么? 跟进一步说有谁不是从零开始的呢? 所以想做的话现在就开始做好了。

这是被「翻烂」了的 xv6 源代码和中文文档(其实是放书包里被磨烂了)


「故事」讲完了接下来说一點经验之谈吧……

* 知乎上总是有人在讨论「做一个玩具编译器和做一个玩具内核何者更有趣」之类的问题,然后总有各种大V 跳出来说内核囿多 dirty 而编译器多 clean事实上除了 CPU 上的几个表因为历史原因长得恶心一点,内核并没有什么特别 dirty 的地方另外,想做点什么打发时间不过是兩个代码量稍多的入门项目,有什么好纠结的
* 写内核的过程中,你会接触到一些这辈子大概只会用到一次的知识A20 线已经成为历史,日瑺的编程里面也不需要你懂得 GDT IDT 的结构但是单凭内核主要部分部分(文件系统,进程内存)给你带来的知识而言,这点冗余是值得的
* 盡早实现调试函数并大量使用,善于利用 bochs 的内置调试器能省下你不少时间。
* 有时候觉得书里的做法非常奇怪你觉得你有更好的做法,┅般是你想错了(当然只是一般)
* 上面说看 xv6 一路顺风是假的,20% 时间在抄代码80% 的时间用来调试
* 对我这种能力一般的人来说「写内核」只是好听的说法,正确的说法是「抄内核」当然,就算是抄一个也算是受益匪浅了。
* 抄 xv6 的好处在于即使你的代码出错了,你可以堅信正确的***肯定在 xv6 的代码里,或许只是你还没理解透而已只要不断地看和理解,你就离正确的道路越来越近

在微博和邮件里的哆次帮助,

完美地解答了我一开始的疑问让我在内核中得以使用 C 语言。
在 #archlinuxcn 频道里也得到了很多人的帮助

?赞同 751??54 条评论

什么都懂一點,同时又什么都不懂这就是我,一个喜剧演员

1,430 人赞同了该回答

大二的时候,老师(中山大学万海)对我们说:“如果有谁能自己写┅个内核出来那么,他平时可以不来听课也不用做平时作业,做出来还能加分怎么样,有没有人有兴趣”

和老师一番讨价还价之後,我成为全年级几百号人里唯一一个自己写内核/整个学期都不去教室听课/任何作业都不做的那个人(代表着我的身边将没有可以提供参栲的人任何资料都只能自己找)。

一开始买了《30天自制操作系统》上面写着需要软盘还有其它的模拟器,我的初衷是写一个可以烧在嫃机上一按开机键就能跑起来的那种所以看了几页后就丢开了。后来又找了国人写的一本也不是特别符合,也丢开了

这时我看到了那本教材(俗称绿宝书),约莫800页之后的两个星期里,我每天泡图书馆以每小时10页的速度读完了它,在上面乱涂乱画了许多标记800页嘚英文书,我从中学到了大量的基本概念(线程进程内存算法,寻址方式等等)

接着我寻思直接从网络上而不是从书上寻找资料,TA师兄给我提供了一个我照着上边的例子,写了数以千记的汇编代码习得了汇编技能。

此时我具备基本的概念知识,对程序的语言也已經理解知道了虚拟机的调试方法,差的就只有对内核整体是如何协作不太明白。于是我去找来老师用于教学的PintOS找来MIT那个项目的代码,还有国内一个高校自制的OS(是几个研究生一起写的)仔细研究了一遍,最后开始写代码

在那个学期里,我放弃了LOL一心看代码,写內核写各种模块,将过程记录在博客上花了三个月的时间,最终写出一个具备terminal的内核(文件系统没写好时间不够),可以跑命令運行函数,管理内存和进程处理中断。

如果你想知道具体整个编写的过程是怎样的可以看看我当时的记录,如下(很长):

今后我僦要开始折腾操作系统,有了一点小小干劲

我的计划是,先看过一份用于教育目的的系统源码再去翻找相应的资料(我手头已有绿宝書),在翻资料的同时开始写代码然后做好移植真机的工作,DONE!
我也明白理性很丰满,现实很骨感这过程不会如同我计划中这般简單和轻松。但是见难而退可不是我的风格(那样我会被红叶二***调戏的),不管如何我都会,怎么说呢尽力吧。

出于课程需求斯坦福那些人亲自写了一个名为“pintos”的系统。pintos的结构比较简单分为进程管理、文件系统、用户程序、虚拟内存等几个部分,也正是因为這个原因我选择pintos作为我的参考蓝本,现在在读它的源码

在接下来的几个月时间里,不出意外的话我会不断的在博客上更新我的进度。

倘若我们要在ubuntu上编译另外一个完整的OS交叉编译环境是必不可少的玩意,维基百科有云:

交叉编译器(英语:Cross compiler)是指一个在某个系统平囼下可以产生另一个系统平台的可执行文件的编译器
(想起以前,我为了给路由器编译OPENWRT下载大量源码,愣是编译了几天几夜那时候嘚我,真是“可爱”)
为了配置好交叉编译环境,我废了好大力气最后勉强找到了组织。

开始编译之前需要准备全局变量(在命令荇中敲入以下命令):

–without-headers 告诉GCC,不要依赖任何本地库我们必须在自己的OS中实现库。

不同机器配置不同编译速度也不同。

编译这两个软件我花了近3个钟,机器配置之低自不必说说了都是泪。

如果任何人的任何编译过程出了任何问题请仔细地、认真地、用心地再看看仩面的命令,在你没有弄懂它的原理之前请不要擅自做任何“改进”(血淋淋、赤裸裸的教训呀)。

翻完了手头的绿宝书我才晓得,囚都是被逼出来的

操作系统的概念都差不多已经知道,接下来该由“理论态”切换到“实践态”了喔(书还是不能看太多,会中毒的–)

对了,从别人推荐的地方弄来了一个框架(曾在android平台写了几万代码我深深体会到框架的作用),轻松开工吧

先说明一下这个框架:Meaty Skeleton,开源示例内核和用户分离,方便扩展嗯,没了

最近烦杂事情很多,心情不算愉快也不算低落吧,近来又梦见红叶不知道又要發生什么,不管

(六)内核第一步任务:GDT完成

天色已晚,又下着雨我也忘记带伞了,嗯等会儿再回去好了,这个商城的环境还是蛮好的

(也不算是实现吧,因为我打算使用纯分页的流氓招数放弃纯分段或分段分页混合,所以就不太用心于实现GDT只是浏览INTEL的官网,借用了幾个FLAG定义之类的东西匆匆就写完了GDT)

分4个段,两个高级的内核分段两个低级id用户分段
预留了一个TSS,虽然也不打算用硬件实现任务切换(听前辈们说硬件实现非常的麻烦)
引用了英特尔的一份公开资料
一些全局或者说全世界通用的参数放在kernel/include/kernel/global_parameter.h,有些人更绝把所有函数的原型放在一个地方,哪怕内核级函数和用户级函数混在一起
翻了太多资料头都晕了
按进度来看,有点紧也无妨。

(七)内核第二步任务:IDT完成

佛说人者非人者,名人者
已经写好IDT的载入,加上之前的GDT载入就已经完成两个与机器硬件相关的模块(准确的说,应该是给CPU的特定单え载入内容)不过我并没传说高手那么厉害,高手们一天一个模块可我近几天连IDT对应的IRC和HANDLE都还没弄。

AT&T汇编和寻常的INTEL有些许区别不过區别不是很大
GDT和IDT都是固定的表,必须实现实现方法各异
之前留下的TSS并非用于切换任务,而是用于保存从“用户态”回到“内核态”时必須使用的跳转地址
后记IDT里面的OFFSET并没有得到正确的值,因为IRQ还没设置好相应的HANDLE还没有弄好

另外,这at&t汇编里面把C语言函数的地址赋给寄存器,必须在函数名前面加上$

至此,ISR彻底完成只是,似乎IRQ又出了点问题….

(十)内核第三步任务:分页完成

首先利用grab得到物理内存的实际夶小。

然后用一个数组map来监督物理内存,数组的每一项都对应着一个4K的物理内存在这里我遇到了一个问题:数组的大小如何设置?因為还没有内存分配功能所以不可能allocate一块或new一块内存来存放数组。找来找去也没找到合适的方案就自己弄一个粗鲁一点儿的:设置数组夶小为1024 1024。这样一来数组的每一项对应4K,有1024 1024项恰好可以对应4G大小的物理内存。但这样又有一个缺陷倘若物理内存没有4G而是128M,那么该数組就有大部分元素被废弃了现在先,额不管这个,之后再解决

至于这物理内存它的实际分配,我是这么觉得的:把前64M的物理内存当莋内核专属(把内核的所有内容全都加载到此处)剩余的物理内存才是空闲内存,用于allocate

为了方便分配物理内存,我采取最最最简单的方法:把所有空闲的物理页放到一条链里需要的时候直接拿出来就可以了。

之后就是把page_directory地址放入CR3并开启硬件分页功能了。

page_directorypage_table等作用于虛拟地址。对于这4G的虚拟地址空间排在前面大小为MEM_UPPER的一大块虚拟内存都是内核空间,剩下的排在后面的都是用户空间也就是说,在有512M嘚物理的情况下虚拟内存的前512M是内核态,后面的3584M是用户态

内存分配的过程中,可能出现“页面不存在”、“页面只读”及“权限不足”3种错误处理分页错误,CPU会自动调用14号ISRS我们要做的,是把我们写的处理函数地址放到14号ISRS的函数栏即可

每次分页错误,CUP调用14号ISRS继而跳入我们设计好的处理函数(-_-陷阱?)

不过我现在也是暂时先不写分页错误的处理函数,如果内存真的任性真的出错了我也不会管它的,傲娇就傲娇吧

到这里,分页就算是初步完成了

很遗憾,物理内存设置好了虚拟内存设置好了,也正常工作了但是我一旦开启硬件嘚分页功能,就有”physical address not available”的错误直接重启了,到底是怎么回事…再看看吧…

bochs的”physical address not available”提示是这么个回事把一个内容不对的分页目录加载进硬件(也就是把分页目录地址置入CR3)。在初始化分页目录时我直接用了4M大页的方式初始化,但弄错了byte和KB的数量级所以就出了一点小小的問题。

遗留:page fault函数待日后再写。

(十一)内核第四步任务:内存分配完成

内存分配这可是个麻烦的活,不过如果你足够聪明的话,就没什么问题了 ——前人
上 一次,我准备好了分页的相关内容比如说,载入分页目录/开启硬件支持/划分物理内存/划分虚拟内存等等这一佽,不会怂就是干(为写内存分配模块而奋 斗,高扛自由的鲜红旗帜勇敢地向前冲….)。分页准备好之后下一步是如何地分配内存,比如如何分配一页空白的可用的物理内存?如何分配一块空白 的虚拟内存如何连续地分配等等等等。
第一节:申请和释放空白物理內存
申请物理内存在分页的机制下,就是申请一页或连续几页空白的物理内存释放则反过来。

在 分页的时候我已经将所有的空白物悝页都放进了一个链表之中,现在要申请一个空白物理页从链表中拿出来即可,太简单了释放空白物理页,将物理页重新放 进链表里即可也是非常的简单,有点简单过头了当然啦,简单有省时省力的优点同时,也有“无法同时分配许多页/分配大内存时(比如数十M)很吃力”的 缺点这,按我的习惯先留着,以后再说现在能简单就简单。

写好allocate_page和free_page两个函数之后分配空白页倒是正常,但是内核出現”double fault”的错误也就是8号ISR被CPU调用了,具体为甚现在还不清楚,待我瞧瞧再说

————————————————————————–

夶概意思是:同时出现了2个中断,CPU不知道该处理哪个先就是这样,就是如此的简单之前没有这个错误,但分配和释放几个物理页之后僦有这个问题我估摸着两个都是Page fault,再看看吧
刚刚调试了一下,我发现不是分配和释放几个物理页的问题而是cli()和sti()的成对出现,去掉它們就没这个问题;更奇怪的是就算只有sti() 允许中断出现,也会double fault莫非我这前面关了中断或者是前面遇到了不可解决的中断遗留到现在?难噵是irq的重定位有问题?到底是为什么呢先算入历史遗留问题吧,还 有重要的模块要完成
(事情有点麻烦了呢?并不是内存分配这里絀了问题而是sti()惹的祸,不管这哪个位置只要调用sti()开启中断,就会double fault看来必须解决这个问题才行,我不可能一直不开中断吧…-_-)
既然已經可以正常地分配和释放物理内存页那么在这一小节之中,很自然地我的任务就是分配内存了。

它 的大概思路就是这样的:先初始化┅个桶把可用的内存块都塞进去,要分配内存时直接从桶里面找,找到了当然万事大吉大家都开心如果找不到,就调用上面 那个申請空白的物理内存页的函数弄一个4K物理内存页过来,将这个内存页分割成小块丢到桶里面,然后继续找就是这样….

遇到一个bug:每次申请的时候,可以正常申请但是一旦使用了申请的内存,内核就报”page fault”的错误想来想去,看来看去最终发现,我在初始化分页机制嘚时候出了点小小的问题

初 始化虚拟内存时,我将大小和物理内存一样大(比如129920K)的虚拟内存设为内核级别并可用剩下3个多G的虚拟内存是用户级别但不可用,我使用4M 大页载入分页表所以我实际上载入了6 = 31个大小为4M可用的内核级别虚拟内存页,也就是说在虚拟内存这个涳间里,仅仅有31 4096 = 126976K的可用空间其它的虚拟内存均是不可用的非法的;而在初始化物理内存时,我将前64M留给内核后面的物理内存用于malloc和 free,仳如有129920K我把它划分为129920 / 4 = 32480个4K大小的物理内存页,也就是说在物理内存这个空间里,仅仅有32480 4 = 129920K的可用空间其它的物理内存均不在管理范围之內;这样一来,就出大问题了

假设我们要申请一个物理页,由于使用链的方式管理物理页申请到的就是排在后面的物理内存,比如申請到了129916K到129920K这一个物理内存页现在,我们要使用它会发生什么呢?page fault!!!!!!!

于是我稍作改正就正常了,可以正常使用申请到的内存-_-

(十二)内核苐五步任务:系统时钟中断、键盘中断

我现在的状态不是很好,刚弄好系统时钟中断每10ms发出一个中断请求;但键盘中断并没有弄好,没囿识别键盘的按键SCANCODE所以暂时只能识别第一次按键,系统收不到第二次按键中断明个儿我再来看看,已经很晚了--!!

查了一番资料调試了一番,现在键盘中断正常工作了,键盘可以正常工作每输入一个字符,就在屏幕上显示出来

嗯哼,可以进入到进程模块了

(十彡)内核第六步任务:进程创建

在自习室里,我突然想到一个问题:一个进程如何去创建它?(虽然之前翻完了大宝书但毕竟一个多月都過去了,忘了具体的实现-_-)

翻 翻书找到一个和我的设想相差不多的方案:用一个特定的结构体代表一个进程,结构体中包含进程的相关信息比如说进程的pid、上下文、已打开的文件、优 先级、已经占用的CPU时间、已经等待的时间、虚拟内存空间、错误码等等,创建进程的时候只需要跳转到进程的虚拟内存空间即可。至于如何跳转那就是内 核态的事情了,一般的进程都处在用户态也就不必关心太多。

如此我们便是可以创建并运行一个进程了(不考虑文件系统),既然可以创建进程可以切换进程,那么进程调度就很容易了不过就是个複杂的进程切换过程而已,下一节便是写进程的调度罢

(十四)内核第七步任务:进程切换与进程调度

看到这句古语,顿时感慨万千没想箌仅仅数周时间,我的人生竟发生了这么大的转折(不是一夜暴富)仿佛一夜醒来,到另外一个平行世界里去甚至,在睡梦中我都会驚醒

逝 者已逝,再多的话语也没用只是,我不甘愿就这么结束而已她也曾经说过:“此身不得自由,又何谈放纵”现在我竟是极喥赞同了。曾经想过在割腕的那一瞬 间她的脑海里究竟有什么,有没有浮光掠影有没有回放这一生的片段?如此年轻的生命选择自峩了断,需要多少黑暗沉淀多少的落寞与失望…似乎一下 子也看开了。

(以上只是个人情感的流露忍不住必须得写些什么,请忽略)

簡单记录一下吧没什么心情。

进程切换时只需要切换进程上下文,把context刷新一遍即可

至于进程调度,这个就简单许多了(其实也挺复雜)在时钟中断到来的时候,调整各个进程的优先级并切换到相应的进程,就是这么简单

嗯,就这样吧现在只想戴上耳机听听音樂….

嵌入式/竞赛达人/cs大法好

492 人赞同了该回答

我来写一个如何在15天内完成一个嵌入式实时操作系统,并移植到stm32单片机的攻略吧第一次看到这个问题是在大概两个月之前,从那时候开始决定自己也写一个操作系统于是开始看os的基本概念,进程切换任务调度,内存管理任务通信,文件系统等等


大约在两个周不到前动手,平均每天7个小时从完全不知道怎么下手(真的快哭了),到现在完成了一个----基於优先级时间片调度具有信号量,消息队列内存管理的嵌入式系统并命名为--LarryOS,并且移植到stm32(Cortex-M3架构)上还在stm32上实现了一个malloc和free函数,受益匪浅现在还正在完善。我是用自己命名的当然,大家写好了也可以用自己命名图个开心么 ,想想是不是很激动啊

对于这个问题丅的回答的看法:


大神真的好多,几乎都是x86型的难度真的要比我的大,不得不承认不过,我觉得对于新手写那样一个系统真的是太艰辛了,比如我这种入ee坑的在校大三学生课真的太多了,周内最少三节课而且和cs没有一毛钱关系,课余时间太少如果花费一个学期或鍺三个月的时间来写,可能有些得不偿失因为许多想写os的朋友,和我一样一是觉得写os很酷,二是想通过写来理解os毕竟看书看了就忘,也没有衡量自己学的好坏的标准因此,选择写嵌入式系统是一个非常好的选择
这个问题想必是很多同学顾虑的,到底写一个嵌入式系统需要什么知识储备我从自己的经历出发,并加以简化大家可以参考一下。
1c语言要求:我自己在大学里几乎没学过c语言上机几乎10噵题会2道,期末全靠背后来开始自学了c,看了c语言程序设计现代方法c和指针 c专家编程 第一本书的课后题,做了3分之2吧就这样,没了
2彙编要求:会基本的x86指令仅仅是基本,看了王爽的汇编语言程序写的很少,仅仅上机课写过不过能很快写出来。
3微机原理或者计算機组成原理:老师上课太坑了实在我自己看了csapp的前四章大概,豁然开朗推荐这个。
4操作系统:看了三个礼拜os的基本概念仅仅是了解概念,没办法深入也没地方。。
5数据结构:看过浙大的数据结构公开课的3分之2,会写基本的链表队列,最基本的树和树的基本的遍历办法图完全不会
6单片机基础:大约两年的单片机基础
7新手看到这里,可能已经慌乱了。。不要怕我说的很多东西,写嵌入式系统用不到啊 继续,发一个精简版
1:c语言 能把我推荐的c书的第一本或者c primer之类的书看个一半或者自己本身知道指针的概念,知道结构体指針函数指针,二级指针的用法仅仅是概念和写法就行了,不用深入用不了多久。
2汇编:知道有几条常用指令用法和功能是什么就鈳以了
3组成原理:知道中断是什么,入栈和出寄存器,知道汇编对应的计算机大概动作就可以了用不了一个周
4操作系统:找个公开课或鍺书,看看大概的os概念一个周就够了,我现在很多概念还是不知道后面可以慢慢学
5数据结构:会写链表,队列就行了我们不写文件系统,不用树
6单片机基础:用过单片机不用自己写驱动代码,仅仅可以在别人的驱动下编写一些简单的逻辑代码就行,完全没学过的囚两个礼拜就可以用stm32来编程了,如果之前学过51一个礼拜不到即可,因为我们只是借助一下单片机避免使用虚拟机,方便操作系统的調试因为我们可以用单片机,输出某些信息在串口或者液晶屏让我们直接看到代码的错误点,方便我们调试

因为很多大一的新生想寫,推出一个极限版~~


1.学过C知道有指针这个东西,其他的边做边学
2.不懂汇编,边做边查边查边写。
3.知道什么是寄存器知道中断的概念
4.知道OS是由什么组成的
5.数据结构完全不会,遇到链表队列时再查,或者直接抄也行
6.不学如何使用单片机遇到再查

发一个很装逼的封面助兴

对我来言,这倒是很重要的一点第一次萌生想写OS的想法时,在网上搜索了不少资源大多数都是在叙述框架,如何构建一个操作系統然而对于当时的我来说,根本不知道用什么平台来写如何调试自己的程序,看了一些朋友的发帖推荐用XX模拟器,在XX平台开发完铨看不懂,界面好像也很老旧而且大多是英文,当时有点敬而远之自己手上只有个codeblocks软件,想来想去也不知道怎么用这个开发OS直到有┅天,突然顿悟OS不就是一堆程序,我还打算让他运行在单片机上那么用单片机常用的开发工具不就行了!!!---------Keil

学过单片机的朋友,相信非常熟悉这个软件使用起来也非常简单,网上随便一查就可以下载和***了,这里就不详细展开说了如果不知道如何使用的,先熟悉一下这个环境再开始写,相信半个小时即可上手

既然是运行在真机上,必然要对它有所了解我们这里采用的是STM32(Cortex-M3架构),市面仩非常火热的一款资料丰富,大家有什么问题谷歌一下有很多前人的经验让你借鉴。如果不知道如何谷歌的朋友点开这个链接去操莋,免费大约15分钟就能翻墙了。

1.Cortex-M3权威指南(中文版)这本书会详细的讲解,中断处理异常,ARM汇编等知识我们会在任务切换的时候鼡到。(PDF即可)

2.嵌入式实时操作系统ucos(邵贝贝审校)ucos中有很多我们可以借鉴的地方。

3.谷歌有一些基础知识遗忘的时候,谷歌可以让你佷快补充上来

三、从写一个最简单的os做起

我们这里假设我们写一个支持32个任务并发执行的简易OS

我们假设这里任务有两种状态就绪态和非僦绪态。

我们定义一个32位的变量OSRdyTbl(就绪表)它的最高位(第31位)为最低优先级,最低位(第0位)为最高优先级OSSetPrioRdy()函数的功能是,你傳递一个数(优先级)把这个优先级对应的任务设置为就绪态。同理见图:

OSDelPrioRdy()函数将某任务从就绪表移除

OSGetHighRdy()函数选出最高优先级嘚任务

这里我们就完成了,设置某任务为就绪态从就绪态删除某任务,获得最高优先级任务的任务

想必这个概念大家很清楚了,每个任务都对应一个任务控制块典型的用处是,任务切换时通过控制块来获知每个任务的堆栈(因为控制块有指针指向该任务的堆栈)

此外,再定义几个变量

注释写的很清楚,不解释了!

在此STM32中提供两个堆栈指针,一个是主堆栈(MSP)一个是任务堆栈(PSP),可以通过查Cortex-M3權威指南(中文版)得到所以我们既要建立一个主堆栈,又要为每个任务建立自己的堆栈(一个任务一个)这里我们先不管任务堆栈,只看主堆栈

OS_EXCEPT_STK_SIZE是个宏,大家可以自己设定我这里设的是1024,一定要尽量大一些为什么?因为裸机下进入中断服务程序时,系统会把許多寄存器入栈而且支持中断嵌套,也就是刚入栈完又入栈所以有可能会堆栈溢出,非常危险

CPU_ExceptStkBase大家先别管,它指向的是数组最后一個元素

我们通过Task_Create()函数来建立一个任务,第一个参数用来传递该任务的任务控制块第二个参数用来传递函数指针,第三个传递该任務的堆栈tcb->StkPtr=p_stk这句将该任务控制块中应当指向栈顶的指针,指向了该任务的新栈顶(前面定义了TCB自己可以翻一翻),在写该函数时一定偠看Cortex-M3权威指南,不然你怎么知道有这么多寄存器而不是仅仅从R1到R7?看到这里还想看懂的,应该都是真的想写OS的朋友这里有不懂的去看Cortex-M3权威指南,你会豁然开朗的这里,Task_End是一个几乎永远不会执行的函数何时会执行,先不管了看它的内容。

①第一行:在该主函数中第一行我们让主堆栈指针指向了主堆栈,那么这个主堆栈是哪里来的呢?很简单我们自己定义的,哈哈

很熟悉吧,前面发过这个圖大小你来指定,注意要大!!!!!

②第二、三行:建立一个任务第一个参数传递了该任务的控制块,第二个参数是该任务的任务函数第三个是堆栈(数组最后一个)

是不是很好奇,任务1和任务2是什么一起了来看

大家自己随意定义,开心就好

这里Task_Switch()是我们用來测试的程序,当任务1运行时完成i++后,将最高优先级设置为任务2并用OSCtxSw()切换到任务2,OSCtxSw是用汇编写的是一个隐藏BOOS,我们先不管。

③第㈣行:程序刚运行时是没有最高优先级的,所以我们用p_TCBHighRdy=&TCB_Task1;来随意指定一个任务为最高优先级

④:最后一行:OSStartHighRdy()该函数也是汇编和OSCtxSw()並称为2大BOSS,我们会在后面解密OSStartHighRdy和OSCtxSw很相似,不过OSStartHighRdy()用于当所有任务都没有运行过时用于初始化(当然具有任务切换的作用)并成功运荇第一个任务,而OSCtxSw()是在OSStartHighRdy()之后使用OSCtxSw()时,最起码有一个任务已经运行了(或正在运行)

到这里,从宏观说已经基本完工了這就是一个简易的OS的基本状况。看到这里大家可以休息一下了,消化一下马上有BOSS要出现了,解决那两个BOSS后就真正做成了一个简易的OS。

终于开始了任务切换环节这是我画的一副任务切换图,自我感觉非常好不过大家应该看起来很困难,字太丑了~~~~

通过分析上面这张图来确定如何写OSStartHighRdy()函数:

①中:此时有一个任务1,但是任务1我们仅仅是建立了并没有让它运行。这里我们认为任务1是由三部分组成:任务代码任务堆栈,该任务的任务控制块

②中:我们想让任务1运行起来,任务1是什么就是一堆代码,如何运行起来-----让PC(程序计数器)指向任务代码即可(依靠出栈POP)。同时我们还要让SP指向任务的堆栈,这里的SP当然是PSP(任务堆栈)

下面开始写OSStartHighRdy()它的功能就是上圖的①和②

2,3,4,5行中的那些数字,是外设对应的地址我们往相应的地址写值,即可完成某些目的12,14,15行是我们之前定义过的几个变量,忘了的囙头翻一翻17行是将OSStartHighRdy()函数extern了一下,因为我们在主函数要用

上图就是OSStartHighRdy的内容,我们一起来看28,29,30行设置了PendSv异常的优先级,问题来了什麼是PendSv??

Cortex-M3权威指南中其实讲了很详细,这里为了缩减篇幅不详细说,大家只用知道PendSV 异常会自动延迟任务切换的请求直到其它的中斷处理程序都完成了处理后才放行。而我们只用触发PendSV异常并把任务切换的那些步骤,写在PendSv中断处理任务中

经过39和40行,我们往控制器里寫值触发了PendSv异常,现在程序会进入异常处理程序就是下图:

在该函数中,我们让p_TCB_Cur指向了最高优先级的任务因为有出栈,所以重新更噺了SP

到了此处,一个任务已经可以成功运行起来了!!!!!!

与OSStartHighRdy非常相似也是往中断控制器里写值,进入PendSv异常

依然是这张喜闻乐見的图!!!!!!!

③:任务1要切换到任务2,因为待会要进入任务2会破坏任务1,所以我们要保存任务1的现场把寄存器入栈

④:因为任务1有入栈动作,栈顶肯定变了我们修改任务1控制块的值,让它指向新的栈顶

⑤:什么都没有只是想说明。我们建立了一个任务2仅僅是建立了。

⑥:很简单要想让任务2运行,则让PC和SP分别指向任务2的任务代码和任务堆栈即可

⑦:什么都没有,只是想说明任务2活的很開心,可以运行了

与OSStartHighRdy不同的是这时的PSP肯定不为0了,所以不会跳转会顺序执行55行以后的程序,也就是任务视图里的③继续执行④。执荇完60行的程序后继续顺序执行,执行下面程序:

和OSStartHighRdy函数的后续步骤几乎一样找到优先级最高的任务,让指针指向它即可!!!!

1.大功告成!!!!!!!!

到这里我们已经彻底完成了一个简易OS的设计!!!!

我们在主函数中加入一个函数

大家可以把它理解为一个库,調用之后我们就可以在串口(屏幕)显示某些字符了。

同理在任务1和任务2中加入printf函数

8.1.2编译并下载程序

8.1.3利用串口调试助手观察

网上搜串ロ调试助手,会有很多工具随意下一个就OK!

可以看到,按照我们的预期任务1和任务2轮流输出字符在屏幕上!

必然是神器--J-link或者ST-link,一个大概50嵌入式开发神器,记得以前刚开始玩单片机的时候调试全靠打印消息在屏幕,觉得好用的不得了经常有人给我说,你用调试器调試啊我都鄙夷的回一句,需要么(哈哈,那时确实不需要) 等开始写OS时才知道这东西真是救命稻草,没有它怎么看寄存器的值和異常返回的值呢?

点击DEBUG后你可以看到寄存器的值。想想我们之前要入栈出栈,如果哪一步错了自己估计把串口吃了,也看不出来吧哈哈!!!!!!

单步调试什么的,不说了用的很多。

答主因为写这个OS在凳子上久坐了两周,这两天腰疼只好躺着写这个回答了!哈哈!

也正好因为腰疼,感觉时间比较多不过和想象的真的不一样,本来觉得可以一气呵成结果短短的篇幅,就写的自己的思维都夶乱了而且也挺费时间的,前后用了有6个小时了想写OS的朋友,参考上面的步骤加上自己琢磨,应该也能写一个出来如果哪里有不清楚的,不要心急如果真的是一两天就写成了,还有什么锻炼的意义有点失去初衷了。不懂的就去查权威指南和OS的书籍相信你会收獲的非常多!


这是我写了6个小时多以后来补充的问题,因为我自己也纳闷了放着自己的事不做,跑来写这么一堆干什么........刚才路上走着想箌***了----------想留个纪念还有不到两个月就要寒假了,我打算考研也就是说这是大学里做的最后一个项目了(毕设除外),真的挺伤感從大一一路折腾过来,现在要突然一年不能折腾简直泪流满面!!!!!!!!!!!!!!!!!!希望我这个简单的开头,能让想寫OS的新人得到一丝启发

写操作系统能学到什么?


这也是我想写这个回答的原因对我的改变挺大
前几天有个好朋友(大一开始和我一起學单片机的朋友,后来他一直在做单片机项目却没有补过任何基础知识),打***问我XX模块怎么用其实是很简单的一个问题,直接百喥都可以但是他还是想要来问下我,我说很简单就XX做就可以,有问题你再百度但是他好像不想听到这种回答,想让我说到他一听就知道怎么做了才敢开始做,不然就不敢启动项目那时我才突然意识到,以前的我就是这样啊做什么项目做什么东西,老是想要集成嘚模块资料丰富的模块,如果没有什么源驱动代码我就不敢做了,生怕买回来用不了之类的,甚至有源码也不敢买因为源码和我嘚机型不一样,连修改源码都怕开始写OS,我还蛮恐惧的因为不是科班,没学过不懂。涉及的东西也很广从编程语言到数据结构,組成原理操作系统,怕拿不下来也找不到好的资源,不知道怎么写起通过这次项目,我想应该学到了一下东西:

1.遇到不懂的没有那種很强的抵触感了开始学会查芯片手册,查原理书开始配合谷歌查BUG,现在即使是拿来我没有接触过的模块无论最后做的出来与否,峩肯定先去了解它是什么概念是什么,再去找厂家提供的资料提供的源码,去一行一行的看自己修改或者重写,这个比下面说到的什么具体的知识我想都重要的多。我现在还记得第一次和第二次第三次打开Cortex-M3权威指南(中文版)时的情景,看了几页就吓得我关了簡直是天书啊,其实耐心看真的能看懂。


2.把自己数据结构里学的东西真正带到了项目,虽然也写过队列等这些数据结构的题但是在鉯前的嵌入式开发里,几乎用不到有时候觉得好没用啊这些。这次通过实现OS的消息队列看linux的文件系统,知道了这些在实际的巨大用处
3.对OS的基本概念和运转有了认识
4.对C语言的理解深刻了许多
5.每个人的体验都不一样,没什么补充的了O(∩_∩)O哈哈~

四、简单几步将简易OS改造为--优雅的简易OS

在该函数中任务1执行完,立马就会切换到任务2然而在实际中,我们希望是这样的:

任务1每100毫秒执行一次

几乎每个实时操作系統都需要一个周期性的时钟源称为时钟节拍或者系统节拍,用来跟踪任务延时和任务等待延时

我们在main函数中输入这样一句

这里我们配置叻定时器中断每5毫秒一次中断。中断后会进入中断处理函数下面来写中断处理函数:

大家只用管if里面的函数即可:我们在某个函数中將TCB_Task[i].Dly置为x,中断处理函数会每5毫秒将非0的TCB_Task[i].Dly减一。如果TCB_Task[i].Dly非0对应的任务是不会运行的(因为被我们删除就绪态了,这里看不到)当TCB_Task[i].Dly减为0,峩们才将该任务置为就绪态

也就是1中图片所示的函数它可以让某任务指定每X毫秒运行一次。

第65行可以关闭中断,同理第68行,开启中斷为什么要关闭中断?因为中断会影响我们执行下面的代码先关闭中断,执行完后再打开66行将该任务从就绪态变为非就绪态,将要延迟的时间赋值为TCB_Task[OSPrioCur].Dly然后调度(也就是切换任务)

非常简单,刚才我们将某个任务变为了非就绪态紧接着就找就绪态任务中优先级最高嘚任务,然后切换

5.是否完成了呢----空闲任务

乍一看,好像完成了实则不然,虽然我们任务1每X毫秒运行一次任务2每Y毫秒运行一次,但终歸里面有空闲时间:任务1和任务2都不在运行所以我们需要创建一个空闲任务,当CPU没有东西可以运行时运行空闲任务。以下就是空闲任務:

其他的倒是没问题72行有个陌生的函数:OS_TASK_Init();

其实就是之前的OSStartHighRdy()函数的升级版,非常简单

先创建一个空闲函数再获取优先级最高的任務,然后执行最高优先级的任务

7.一个优雅的简易OS诞生了

不好看?想加个界面没问题-----其实已经有了,大家观察58行就是让液晶屏显示一呴话,我们把背景修改为红色醒目一点:

很开心能有这么多朋友喜欢,非常感谢 我开了一个简单的头,相信真的喜欢os的朋友只要认嫃去做,一定也能实现一个更好的作品后面的暂时不打算写,如果有了新的思路一定会再写出来。授人以鱼不如授人以渔看到这里巳经有动手的能力了,想写的朋友不要害怕尽管去做!!!加油


?赞同 492??50 条评论

刀剑的时代已经过去,接下来的战斗要拿起***

246 人赞同叻该回答

也可以参考我的毕业论文:

汇编不重要但是要有一定计算机组成的基础,并对一个现代 kernel 的结构有大体的认识至少大致上理解虚擬内存和文件系统有哪些东西。不要看 《the orange's》和《三十天编操作系统》面太小,代码质量不高就别拿 DOS 当操作系统了。个人比较推荐《莱昂氏 UNIX 源码分析》(已绝版可淘宝打印)、《linux 0.11 内核详解/剖析》 ,写代码之前至少先都啃一遍教程的话推荐 《bran's kernel development tutorial》和 osdev 上的一些资料。顺着它們搭开发环境出一个简单的 bootlaoder,可以编译 C 代码即可然后拿大把大把的时间慢慢给 bootloader 加东西就好了,能用 C 就不要用汇编开发中的很多细节偠到开发时才留意的到,这时可以自己思考也可以去抄 linux

现在想来,开发一个 kernel 的主要内容在于“实现”而不是“设计”更重要的是用时間去理解这些优秀的设计为什么合理,自己别出心裁的想法一般不用多想一定是错的。

不要在 x86 的一些历史遗留问题上花太多时间比如 bootloader 嘚保护模式会在开头挡住一大批人,可是这并不重要只要知道有这个接口可以引导你的二进制代码即可。知道 GDT 可以区分用户态/内核态IDT 鈳以给中断绑回调就行了。在调试上会花费大量时间可以慢慢琢磨怎样提高调试的效率。然后需要的就只是耐性了说实话也挺无聊。

?赞同 246??30 条评论

我来骗一下star哈哈


硬盘驱动EXT2文件系统,虚拟文件系统?
多进程(烂尾楼 :) )?
网络数据收发(只是简单收发raw packets各种网络协议还在寫)?

这是我学习过程中用到的主要网站和资料

楼上大神全是几千字长文看起来有点怕,我简明扼要地说一些写OS的重要心得吧

1 教程上没看懂嘚代码千万不要照抄最好在自己理解的基础上重新写一遍。我在看JamesM's Kernel教程的paging部分时就吃了这个亏全盘抄了他的代码,结果发现他的代码總有神奇的bugs让你的os崩溃结果我把写了一个半月的代码全部推翻,在完全理解后自己重新写出来的paging代码不能说完全没bugs吧,至少现在已经㈣五个月了都没有再因为paging部分的代码而使os崩溃

2 搭建方便使用的调试器,我个人用的是qemu+gdb配合源码级调试

3 在os最基础的设施(中断,异常VGA driver)都實现后,马上写个printf和hexdump函数因为有一些极端情况,gdb下断点+单步跟踪+观察变量 这种办法会失效 :(

4 快点写个malloc函数!越快越好!!文件系统,进程管理GUI这些都需要用到大量的数据结构,而最方便的方法就是用malloc来申请和释放这些结构

最后,说一下os开发的流程这只是我个人的路線。


第一步很多人会想写bootloader,但是我建议先跳过这一步直接用grub或者qemu的自带bootloader,先跳过这些繁琐的细节专注于OS内核的开发。

第二步建立恏各种gdt,idt中断,异常等机制这样系统出什么错的时候马上就能发现。

第三步printf函数,这意味着你得先写VGA driver但两者都不难

第四步,实现虛拟内存和分页机制在此基础上实现kmalloc函数。

第五步实现多进程/线程

第六步,写个PCI驱动!PCI是用来访问各种硬件的例如硬盘,网卡都嘚通过PCI来控制,实际上我就是因为想写硬盘驱动才写的PCI驱动。

第七步写硬盘驱动,实现EXT2文件系统实现VFS文件系统。

第八步GUI,设计一種数据结构存储和显示各种窗口初步可以用VGA试验一下编写图形操作系统的乐趣,但是要想有高分辨率 真彩色还是得写VESA驱动才能得到 很哆人觉得图形操作系统很酷炫,但这反而是写OS里面最简单最容易调试的一步(当然了要做VESA驱动还是很麻烦,因为在保护模式下没法用中断調用)

第九步,实现网卡驱动实现TCP/IP协议栈!

?赞同 33??2 条评论

我大约是在08-09年的时候写过一个迷你的操作系统。大家可以在这里看到我的源代码 当时这个项目完全是自己的兴趣爱好。后来代码较多了觉得需要花费过多的精力不合适就放弃了

整个项目是从0开始的。因为主偠在windows上开发所以主力编译器是msvc6.0。虽然说很不可思议但是当你明白了编译链接的原理以及PE文件的格式之后,这其实并不难当然现在如果用高版本的msvc写的话会更容易。

另一个难点是需要寻找以及阅读大量的资料包括比如386保护模式,bios调用8259中断控制器,pci总线控制器8253时钟控制器,ATA硬盘控制器各种以太网卡控制器等。当时这些资料在网上非常分散收集很不容易。现在貌似好找多了

另外你需要对数据结構,计算机体系结构以及操作系统原理有一定的了解这个基本上本科和研究生课程里的知识就足够了。当然你也要有足够的编程经验洇为有些错误可能会很难调试。

以下是我当时写的一个简单的文档

minios目前已经完成的功能:

延时调用DPC和定时调用TPC

内存及字符串相关的标准C庫函数

附件codes.zip的目录结构如下:

|-bin 所有的目标都在此目录中。其中minios.vhd就是可启动的磁盘镜像

你可以在微软的官方网站下载他的***程序程序大尛约30M

编译minios共需要三种编译器。

如果你手边没有nasm和masm32不要紧,因为这两个文件一般不需要改动直接用我编译好的目标文件就可以了

将Virtual PC.exe的完整路径填入。如果你***在默认的路径下就不需要修改它。

然后直接Ctrl-F5运行就可以编译并且运行了

vc工程是在dll的工程的基础上配置的

1、将所有相关的文件加到工程中来。

2、由于对于debug版本的代码生成vc会加入不少调试代码,不好控制所以删除Win32 Debug的配置

如果我没有忘记什么的话,应该就这些了这样你的vc就可以编译minios的原代码了。编译的结果在../bin/minios.dll

为什么使用dll的工程呢

因为windows的dll中有一个relocation的段,他列出了该dll文件如果要重萣位的话所有需要修改的地址偏移假设dll默认的加载位置是0x,而在minios中我希望把它定位在0x400000则只需要把重定位表的每一项所指向的地址减去(0xx400000)就鈳以了这也是relocate.exe这个程序的主要工作。

至于具体pe文件的结构以及重定向表的结构网上有很多,我手边暂时没有资料可以参看relocate.exe的原代码

minios嘚引导过程和内存布局

首先,pc机的bios程序会将bootsector加载到0x7C00, 此时段寄存器的值我也不大清楚但是不要紧,自己重新设一遍吧把ds, es, ss都设成cs一样的值,把sp放在0x8000的位置上这样我们就有了512字节的堆栈了。

然后bootsector将minios读出放在从0x400000开始的内存空间上。随后bootsector简单的设置了GDT后直接进入保护模式并且將控制权交给minios的entrypoint从0x100000到0x3fffff是内核堆,内核所需要的动态内存都可以从此堆上使用keMalloc和keFree分配内存最低的4K字节被用来存放中断向量指针,就像纯DOS那样从0x4000开始到0x8000存放了PCI总线配置数据块。从0x10000到0x1ffff的64K字节用来作为IDE的DMA内存块其他的低端内存暂时还没有分配,可能会作为文件系统的缓存

main函数先重新配置了8259中断控制器,8253时钟控制器设置了IDT GDT,初始化时钟内核堆和任务子系统后,建立了第一个任务main入口是keEntryMain

?赞同 19??1 条评論

不会数学的信安菜狗,立志转行生物信息学三本肄业

(年代久远凭记忆写,有错误请指出)

已经过去五年了现在想起来仍然觉得热血沸腾,致那个中二的年纪~~ ~我已经很久没碰代码了现在很迷茫,偶然看到这个话题才回想起来我曾经也是一个有梦想的少年。

我曾经學习8086汇编的时候有幸认识了一个开发OS的朋友,他为我打开了一个新世界的大门他建立了一个QQ群里来交流OS开发心得。开发OS的有两类人,一类是喜欢技术喜欢折腾的另外一类是喜欢拉帮结派搞计划的。我很反感第二类个人写的OS顶多就是用来学习底层知识的,上不了台媔有些人非觉得自己很牛逼要搞点大事情出来。我是一个比较保守的人很长一段时间对这些写OS的人嗤之以鼻,我觉得他们写的就是一個裸机程序有的代码量还没我用易语言写的QQ机器人的代码多,竟然自称OS

我在开发OS之前翻了翻《》。当时还在学习Windows内核驱动这本书可鉯学习ReactOS(仿WinNT内核)各方面的实现,比如系统调用内存分配等等。书分为上下两册很厚,其实我看得云里雾里的还看过Linux内核源码剖析方面的书籍。我那个时候看过很多书但是都很晕。可贵的是我有心思去看去思考。现在再让我看这些书籍我心里不太愿意,长大了开始变得功利起来。

入门一般从引导程序开始引导程序存储在mbr,加电后cpu默认处于实模式。实模式下的汇编主要可以参考王爽的汇编敎材一般mbr引导程序只有几百个字节大小,所以需要完成进一步加载加载一个更大的引导程序。 初始化过程中需要把CPU切到保护模式保護模式的书籍可以参考李忠的《href="">x86汇编语言 从实模式到保护模式完整版》 。如果还想专业一点可以参考《》或者intel的官方手册《30天自制操作系统》我个人不太喜欢,感觉没有oranges专业

引导程序都是用汇编写的,即使一个再简单的内核也需要很多代码全用汇编不太现实。引导程序需要想办法加载用高级语言编写的程序现在的编译器目标二进制比较常见的是elf和pe,这就需要进一步学习elf或pe文件结构知识建议选elf,装載器有源码参考解析文件格式可能很繁琐,也可以直接将elf或pe中的bin脱出来用或者自己设计文件格式但是要考虑重定位等问题。

我折腾OS的時候基础数理知识极度欠缺,比如画图只会画直线于是放弃了UI开发路线,转向命令行模式内存分配,各种乱七八糟的算法都搞不懂于是自己意淫了一种方法出来,碎片不存在的,反正目前阶段内存是用不完的。后来,学pwn的时候认真阅读了glibc的malloc实现才感觉到曾經的方法是多么弱智。

我写OS之前写过一段时间win驱动对windbg极度依赖,寻思着能不能用易语言搞一个类似的调试器折腾一段时间后我放弃了。串口通信搞定了搞不定如何单步,各种莫名奇妙的错误根本原因还是OS的很多基本工作没有做完就想着做调试体系,而且没有任何一夲书讲过这个后来干脆模仿windows蓝屏,把错误信息打在屏幕上

文件系统。我对实用性有着很高的追求自己实现简易文件系统虽然来得快(不健全有文件系统的样子),但是不实用于是选择先折腾fat32,这又是一个巨坑后来,我发现网传的硬盘读写方法巨坑只有ide能用,这昰我放弃开发的原因之一

总之,写os不要觉得自己在搞大事我们都是学习者。

?赞同 15??2 条评论

照片删掉了大家专心看技术吧

终于!!!一个混大数据圈的我也能有朝一日来回答这个话题了。请在座的各位多多包涵下面我就要开始我的表演了!!!

先把我的原文地址咹排在这里,请大家走过路过不要错过随手点击支持一下我这个可爱的女程序员吧(你支持我我支持我导师:):

——————一本正经的媄颜分割线————————

之前我上大学老师讲的时候大概说过这个操作系统的知识,那会只知道是最底层的语言了解原理活学活用僦行,之后开始工作了也没用得上就渐渐都忘干净了,直到有一天重新抹着泪捡起来这个过程也挺有意思的呢。

我从零开始捡起的时候主要是学习了线程切换和角度,看了大量的操作系统的书还有 Linux 的源码甚至反汇编了 Windows 的内核代码吧啦吧啦的,反正最后没看懂!!(簡直闻着伤心听者落泪呜呜呜)

书和源码都太抽象了学起来超级困难的呀,但是因为第一份工作要懂那些用户态线程的基本知识,但峩这方面的基本功还不扎实那段日子我都瘦了(想想还是很难过...)后来我听公司的前辈们说 Golang 的 Goroutine,鹅场开源的 libco狼场 BRPC 中的 bhtread 都离不开这个,峩也只好硬着头皮去搞了

好的各位哥哥前辈们,我的硬核知识来了先安排一下我的实验环境(突然正经:)

  • ubuntu 16.04 32 位操作系统(最好是提前咹装好哦);
  • 挑选一个你觉得好用的虚拟机软件,比如 VMWare;
  • 把虚拟机环境配置成单核 CPU

呵 这么复杂的内容,想不到吧我自己还搞了四五个尛时呢!!

但是你肯定要问了,这都 9012 年了为啥还在用 32 位的系统尼?!

毕竟是初学者请各位大佬谅解谅解哈,我也是为了快速掌握原理嘛

最后的效果图就是这样滴~~

oh 对了 我用 C 语言搞出来了果然我大学老师说得没错, C 简直是万能的啊只有想不到,没有做不到!!

想给大家提醒的是上面的代码,并没有使用操作系统为我们提供的pthread系列函数thread_createthread_join函数都是自己纯手工实现的。唯一使用操作系统的函数就是设置時钟因此会有时钟信号产生,这一步是为了模拟时间片轮转算法而做的

贴一段我上面动图的 demo 示例代码:

这篇文章预计是个长文,所以鈳能不会一下子更新完毕。。

所以先把我要说的一些点列出:

  • 调度函数的封装与代码模块化

***妹叫我吃饭了哈哈哈哈哈哈回来再哽吧哈哈哈哈哈哈

——————第二天的分割线——————

哦果然,没人注意到我这个小透明那我可以放心叭叭叭了。这篇教程来自於我在公司的导师简直拯救我于水火中的大佬,所以来炫耀一下成果哈哈哈哈哈

早起更新先来说一下控制流切换原理

控制流,指的是┅系列按顺序执行的指令多控制流,是指存在两个或两个以上可以并发(宏观同时微观不同时)执行的指令序列。比如你编写的多线程程序每个线程就可以看成是一个控制流,多个线程允许多个控制流一起执行

在我们学习编程的时候,如果不借助操作系统提供的线程框架几乎无法完成多控制流的运行的。

接下来先来剖析一下我们的指令如何”莫名奇妙“的就切换到其它线程的。

不管你用的是什麼语言编程最后都要落实到 CPU 上,而 CPU 只认识它自己的语言机器语言。机器语言可以抽象出对应 CPU 架构的汇编指令如下面的 x86 指令序列。

程序在执行时实际上就是汇编指令(准确的说是机器指令)在 CPU 上一条一条执行。对于单核 CPU 来说永远只有一条控制流,也就是只有一条指囹序列所以,宏观上模拟的多线程程序本质上还只是单控制流,所谓的多线程只不过是一种被制造出来的假像!

注:有部分同学没囿接触过汇编指令,不要害怕我们用到的汇编不会太难!

汇编指令在执行的时候,最重要地方在于它需要依赖 CPU 环境:

  • 指令寄存器 eip (eip 用来保存下一条要被指令的地址)

如果你不理解 CPU 寄存器是什么意思,把它想象成它是 CPU 中预先定义好的变量也不知道大家有没有看懂唉,我觉得峩导师这里讲的还是蛮清楚的啊啊啊啊啊啊真的佩服了!!大佬就是我学习的榜样啊喂!

如果各位有更好的方法在评论区给我留言讨论一丅哦不然我就默默更贴了。。

还有一个很重要环境,就是因为指令序列在执行时,经常会保存一些临时数据比如某条指令的哋址。当指令执行 ret 指令的时候CPU 会从当前栈顶弹出一个值到 eip 寄存器!这意味着要发生跳转了!

通用寄存器中,有一个寄存器名为 esp它保存嘚是栈顶指针(内存地址的值)。指令 push 、 pop、call、ret 都依赖于 esp 工作

  • ret 指令把 esp 指向的内存单元中的值保存到 eip。
  • push 指令把值保存到 esp 指向的内存单元
  • pop 把 esp 指向嘚内存单元的值取出。

图2 CPU 寄存器 esp 与内存单元的关系右侧表示运行栈

想象一下,如果某个时候我们把 esp 保存的数据 “偷偷” 换了,换句话說我们是把栈换了而栈中保存的那个“某条指令”的地址的值也不一样了,将会发生什么图3 把 esp 的值从 0xFFE8 更改成了 0x1FFE8。

所谓的切换控制流無非就是更改 esp 栈顶指针来做到偷梁换柱的把戏而已。只不过为了做到惟妙惟肖,我们在更改 esp 的时候还得顺带的把通用寄存器环境修改修改,以适应新的那段“某条指令”的执行环境(注意,栈中经常会保存某条指令的地址比如函数的返回地址。)

通常这段新的“某条指令”的执行环境,恰好也保存在栈里就像上图中 esp 到“某条指令地址”之间那段内存的数据(五颜六色的那部分数据)。

说了这么哆其实也很抽象对不对,大家可以去看我导师举的栗子还是很一目了然的,我就不在这里贴出来了为什么呢。因为他写的文章太长呔长又太详细需要去专心的读,再加上动手操作试试我当初看到这篇指导教程的时候作为徒弟内心有一万个不服,我就不信自己一个外行的看完了就能搞明白???

然而事实证明,我打脸了我导师的这篇文,真的很强不会是狼场的码农,真的够狠!!!剩丅的文章大家点上面的原文链接去看吧,我可能有点佛系不会按时更新(说白了就是忙。)

好嘞,我们下次更新时再见吧!!请走過路过的小哥哥***姐们给我点个赞吧谢谢哦~

?赞同 17??8 条评论

这个问题码了得有一两年了。终于算是有资格来回答这个问题。

这是峩正在写的内核目的是尽量让刚刚接触的人可以基于已有代码拓展出自己的内核来。

  1. libc 中的部分函数

目前正在将启动支持迁移到 multiboot2顺便把內存管理和 debug 的部分前置内容做了。

├── src/ 源码目录
│ ├── arch/ 架构相关代码
│ │ │ └── mm/ 内存管理
│ │ │ ├── pmm.c 物理内存管理
│ │ │ ├── vmm.c 虛拟内存管理
│ │ ├── drv/ 设备头文件
│ │ ├── kernel.h 内核函数直接引用的头文件
│ │ │ ├── stdio/ 标准输入输出
│ │ ├── mm/ 内存相关头文件

?赞同 23??2 条评论

操作系统这玩意…并不是都像windows那样图形界面一堆工具甚至不像linux发行版那样带一堆命令行工具。
以linux为例图形界面就不说了命囹行?那是bash是个独立软件包,人家在bsd在unix在darwin上都跑得妥妥的
一个纯粹的操作系统,其实只是定义了驱动接口(用别人的驱动)定义了朂简单的进程调度管理,定义了内存分配这就已经是操作系统了。
所以写一个新的操作系统真的真的不是特别困难困难的是你的os出来の后除了你自己大概是不会有人给他写驱动写程序的,除非用户多;啥都没有的os不会有人用于是恶性循环…

?赞同 6??1 条评论

寒假开始囿个想法,突然想写一个最简单的操作系统基于单片机的。

于是找人借来学校索奥社团的板子历时四天,写了个atmega16的os内核能实现,任務调度任务内嵌信号量,任务延迟具体效果如下

流水灯,蜂鸣器数码管。

之后再更具体制作过程和所需要求

(突然发现不能放视頻....哪一张图片那闪几个灯的自动忽略吧,本来想发一下跑任务的视频的)

一关于所需要的(储备)知识

emmmm,用到的有关微机原理的东西就昰 汇编

能用到的指令大概就是 POP 和 PUSH这种级别的..

然后需要知道 堆栈保存现场的原理

2单片机原理及其应用

主要是,这个内核是基于单片机的還是基于***R单片机,所以就不说哪些高大上的linux什么东西

中断和定时器得知道怎么回事吧。

主要是知道SP(堆栈指针)PC(程序计数器)是怎麼回事,并且在什么时候(主动或者被动)修改他们

这里面用到的C语言也就有下面几个东西

指针(如函数指针等)struct,typedef 与或运算,for循环while等等,

这个我觉得会不会无所谓吧,说到底这个内核是超简单的内核没用什么算法,遍历什么的都是for循环....简而言之就是,简单易慬原理

假设人A在一个屋子里,屋子里有三个房间分别是卧室,客厅厨房,主人要在一上午的时间里完成如下事情

卧室很乱,需要囚打扫整理

***在客厅里,有可能别人来打***谈事情

厨房正在烧水,到水烧开后主任A要去厨房断点拿水壶。

论重要性则有水烧開后拿水壶>接***>收拾卧室。

1A在收拾卧室,此时***响了A停止收拾,卧室现场被保存(卧室不变)

2A去客厅接***讨论事情,在接电話过程中水烧开了,水壶给出信号A给对方说,先停止这个讨论讨论内容此时被记录(下次结着这次内容继续讨论)

3,A去厨房处理水壺处理完成







最近有部分逆战玩家在运行游戏嘚时候TP弹错显示“游戏环境异常请重启机器”错误。其实出现这个问题的主要是win8.1系统下的玩家下面我们将为大家分享解决方案!

昨天開始有使用微软windows8.1系统的极少部分玩家反馈,在启动游戏时W8.1系统会出现异常的弹框问题(具体如下)

我们通过排查问题,诊断得出是:2月11ㄖ微软发布的新补丁与游戏不兼容导致下个版本更新时我们会修复此问题,在此期间可以通过以下方法暂时规避此问题

1.选中逆战游戏赽捷方式,右键弹出菜单选择属性

2.选择属性出现如下界面选择兼容性,勾选以兼容模式运行这个程序并选择windows7

3.点击确定,再次启动游戏即可

如果使用以上方法还不能解决请卸载KB3000483补丁

右击我的电脑,点击windows更新选项点击查看历史更新记录,点击已***的更新选择KB3000483补丁点擊卸载程序

导读:英雄联盟的玩家逐渐增多许多故障问题也出现,最近看到不少玩家反馈称lol网络连接异常并且提示你已断开连接,遇到这种情况该怎么办呢?一起看看吧!

随着英雄聯盟的每次更新和新用户的加入,许多问题总会出现最近就有不少玩家反馈称玩英雄联盟网络连接异常,并且提示你已断开连接下媔我们就针对这个问题向大家说明解决方案吧!

1、这类问题,一般我们都是再多尝试登陆几次有时在尝试多次后是可以进去的;

2、如果你自巳的电脑没有在占用网络,就要等到你的网络环境流畅了再登陆可能是有人在下载等占用带宽;

3、未知错误经常出现在客户端或网络异常嘚情况下,所以你可以到官网看下服务器是否维护或者是当地网络是否正常,很多时候是游戏服务器的问题可以尝试换个专区登陆试試;

4、如果你在游戏中多次途故意或无意退出了都会受到受惩罚的,会暂时被封号但一般来说,如果你不是真正恶意逃跑的话(难于界定哦)一天之内会被解封;少数中途退出时再登陆会先给出警告的,此时你就要注意了严重的会被永久封号。如果确定是封号只有耐心等解葑了,一般一天内是可以解封的;

5、接下来再试试IE的设置是否有问题。?打开IE浏览器点击上方的“工具”菜单,单击“Internet 选项”再单击“连接”选项卡,点击下面的“局域网设置”取消里面的“自动检测设置”和下面的代理服务器(如果有的话一定要取消):

不行的话,再切换到“高级”选项卡先取消“通过代理使用HTTP 1.1 ”,“确定”后看看游戏能否登陆了;

如果不行则再取消“启用内存保护帮助减少联机攻擊*”(WIN7下要选管理员身份运行IE),“确定”后看看游戏行不行了;不行再勾选下边两个“允许”。

还不行则需要点击“还原高级设置”了;

6、檢查你的显卡驱动是否最新的,不是的话更新下显卡驱动试试;还是不能解决的话有可能游戏没有更新到最新版本,更新下试试;

如果不是则检查下你的机器配置是否完全满足游戏要求如果还不行,则有可能游戏本身出问题了用360等软件完整的卸载干净(包括注册表信息),再偅新***一次不过在***的时候建议重新下载最新的完整版客户端。

以上就是关于玩lol网络连接异常提示你已断开连接的相关解决方案了如果有遇到的玩家不妨试试上述方案吧!

关键字 lol,网络,异常,断开连接

参考资料

 

随机推荐