资讯详情

自己写操作系统学习总结

如何自己写一个简单的操作系统?

https://www.zhihu.com/question/20207347 我写作时的一些经历: 第一次写的时候,3000行左右的不能调试,当时主要是参考linux 0.11(基础太潜)。 第二次写作时,基本设置了几个核心模块, 启动/内存管理/过程管理/用户环境/磁盘驱动/块缓冲/文件系统/中断,然后开始启动轮子(不参考其他)OS代码),常用算法参考,wikipedia自己调试实现,驱动代码参考http://www.osdev.org,用户态写得很顺利shell可以运行,然后就没兴趣写了。 以学习为目的,可参考MIT OS几门开放课程LAB,分为6个LAB每一个都有指导书LAB都需要自己填写核心代码,可以用内置脚本评分,6个LAB最后合起来恰到好处xv6 OS(/JOS)的代码。(http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-828-operating-system-engineering-fall-2006/labs/)。 (MIT本课程再次更新: http://pdos.csail.mit.edu/6.828/2011/git添加了管理代码lwip移植的lab) 想写的OS,当然,最好的学习模板是FreeRTOS是的。整个核心只有三个文件。其中一个是链表的数据结构,操作系统的核心是一个文件。但许多单片机和ARM CPU我还想把它移植到上面。s3c6410上,没时间只写一个分析文档。 只要你学过汇编和汇编C,只满足于写一个简单的操作系统",一两天就能完成。 随直接写硬盘软件(或者自己编一个,调用你写的小程序)winapi的WriteFile函数可以),写入硬盘//U盘引导区。记住,起始地址是7c00。 这很好。请注意,指导区只有440个字节供您使用,程序不能太大,因此这部分基本上应该用汇编写。U盘分区信息,不可以乱动。乱动以后bios可能无法识别硬盘/硬盘/U盘。 最后两个字节必须是55AA,但一般你不用担心,硬盘/U盘格式化的时候已经给你写好了。 这样,您的程序在启动时直接运行。 调用大程序没问题。您需要编写一个不超过440字节的程序,负责将第二个风扇区域的内容输入内存并执行,第二个风扇区域的代码负责将所有代码输入内存。用C或其他高级语言写下后面的部分没有问题。 至于你想做什么,写什么,在屏幕上画一幅画,管理硬盘上的文件,并与用户互动。这和你通常写的程序没什么不同。简而言之,一个简单的操作系统可以完成任务。 最多几天就能搞定。这几乎可以接近。dos1.0的简单操作系统。 但接下来是你真正想学的东西。例如,文件管理、内存管理、保护模式、过程管理等。麻烦属于麻烦,但解放自由。最后,您可以编写内核状态的准确代码。你所做的一切都是用户状态下不允许做的事情。操作系统的学习从这里开始。 说难不难,先写bootloader开始吧。楼上有人提到。FreeRTOS的确,开源的bootloader很多,代码量不大。 事实上,写一个操作系统并不像你想象的那么高,只是点击硬件。 谢谢你的邀请。说到这个问题,我不得不推荐这本书 《Orange'S:实现一个操作系统 (豆瓣)》 这本书真的是从零开始教你写操作系统的雏形,包括实现。Shell, 另外,阅读Linux0.10的代码也是一个不错的入门方法, 可以参考CMU的Operating System Design and Implementation,4个月可开发一个x86上ramdisk的OS了 http://www.cs.cmu.edu/~410/ 试试 Nachos,教学用的。 他有一个完整的版本,可以参考学习。 也有框架版的,然后自己去开发相关模块(文件系统管理、进程管理、中断管理)。 这些都是用 C 语言实现。 至于底层,需要汇编。 看看uCOS/II只有几千行核心代码。 一开始,我也想自己写OS的冲动。推荐一本书吧书吧-30天自制操作系统【日】。啊,嘿,听起来像30天精通xxx”、“xxx从入门到精通流!我不看这种低端书。如果你这么想,你就错了。川合秀实际上是一个擅长轻量化编程理念的非主流开发者。2000年,他自己开发了OSASK这个项目很有名。书中幽默幽默,绝壁是入门好书。关键是自己写操作系统,对了CPU什么有更深的理解?~ 可以看看Linux 0.11/0.12(2万行以内)或参考《Linux基于0.12内核》: 或者有一本书《自己写操作系统》( http://book.douban.com/subject/1422377/ )。 前两种都是基于的x86的。 Uboot(主要是ARM平台)、UC/OS、busybox等可以参考,基本都有跨平台版。 谈谈我的想法和写作经验。 1.掌握简单操作系统最基本的两个组件: 1.1 心跳代码。 1.2 任务调度。 2.掌握代码架构: 2.1 纵向上, 硬件相关层与硬件无关层分离。 2.2 横向,根据 心跳,任务,中断,I/O、文件 不断在 1 丰富扩张的基础上OS。 三、环境建设: 3.1 在 x86 win 的PC在机器上,选择合适的 (如 VC )。 3.2 从基本组件开始,边学边写,边调。主要掌握硬件无关部分。 3.3 牛逼之后,找个开发板,学写 BIOS。开始掌握硬件相关部分。 3.4 将PC上写的OS代码移植到开发板上。 布朗大学的weenix练习 给了OS大体框架 逐步实现进程线程、文件系统、虚拟存储、多线程框架。 所有这些框架代码基本上都完成了一个可执行程序的操作系统 WeenixWiki 以下是师兄曾写的操作系统,纯处理: CosOS它是一个简单的操作系统,由于兴趣而诞生,本身没有太大的实用价值。CosOS目的是学习和研究各种计算机底层知识。 目前CosOS版本为0.01、文件系统、多任务、多控制台采用微内核结构(TTY)、分页内存管理, 为应用程序提供库函数。 标准输入输出:printf、gets 文件操作:open、close、read、write、unlink、stat 目录操作:opendir、readdir 、closedir 内存分配:malloc、free 执行程序:execv、execl 进程:fork、exit 等 由于CosOS开发时间不长。为了尽快实现雏形,许多地方使用了最简单的实现。在CosOS在代码中可以看到很多 《JamesM's kernel development tutorials》和《Orange_'s.实现操作系统的阴影。CosOS从学习和模仿这两个数据开始。这两份资料给了CosOS很大的帮助,借鉴了很多代码。CosOS结合它们的优点,使之CosOS离现代操作系统更进一步。目前CosOS文件系统是从Orange's 移植文件系统, 优化。在以后CosOS在不断完善的过程中,这些简原创代码将被更复杂、更先进的实现所取代。 SVN地址: http://svn.ecjtu.org/svn/cosos/ 你可以用SVN客户端(如 TortoiseSVN)下载源代码和文档。 一张截图: 入门推荐mit 操作系统课,代码量小,文档齐全。 假如你只是想体验一下,在windows或者linux写一个用户态程序,实现一个调度器。代码量不到1000,几天就能搞定。之后你就知道自己想做什么了。 先gentoo定制吧 目前,操作系统的工程开发很少重新开发核,一般是在某个操作系统内核的基础上,结合各种第三方开源库开发发行版,如Linux各种桌面和嵌入式发行版,Android、Firefox OS、Chrome OS、Tizen等等。 ========

如何从零开始写一个简单的操作系统?

https://www.zhihu.com/question/25628124 大二的时候,老师对我们说:如果有人能自己写一个核心,那么他平时不用来听课,也不用做作业,还能加分。怎么样,有人感兴趣吗? 和老师讨价还价后,我成了全年几百人中唯一一个不去教室听课/不做作业的人(说明我身边没有人可以提供参考,只能自己找任何资料)。 一开始,我买了一个30天的自制操作系统,上面写着需要软盘和其他模拟器。我的初衷是写一个可以在真正的机器上燃烧并按下启动按钮运行的机器,所以看了几页后就丢开了。后来又找了国人写的一本,也不是特别符合,也丢开了。        这时我看到了那本教材(俗称绿宝书),约莫800页。之后的两个星期里,我每天泡图书馆,以每小时10页的速度读完了它,在上面乱涂乱画了许多标记。800页的英文书,我从中学到了大量的基本概念(线程进程,内存算法,寻址方式等等)。       接着我寻思直接从网络上而不是从书上寻找资料,TA师兄给我提供了一个OS Development,我照着上边的例子,写了数以千记的汇编代码,习得了汇编技能。       此时,我具备基本的概念知识,对程序的语言也已经理解,知道了虚拟机的调试方法,差的,就只有对内核整体是如何协作不太明白。于是我去找来老师用于教学的PintOS,找来MIT那个项目的代码,还有国内一个高校自制的OS(是几个研究生一起写的),仔细研究了一遍,最后开始写代码。      在那个学期里,我放弃了LOL,一心看代码,写内核,写各种模块,将过程记录在博客上,花了三个月的时间,最终写出一个具备terminal的内核(文件系统没写好,时间不够),可以跑命令,运行函数,管理内存和进程,处理中断。 如果你想知道具体整个编写的过程是怎样的,可以看看我当时的记录,如下(很长): 原文:(http://www.ilovecl.com/2015/09/15/os_redleaf/ ) (一)OS说明 今后,我就要开始折腾操作系统,有了一点小小干劲。 我的计划是,先看过一份用于教育目的的系统源码,再去翻找相应的资料(我手头已有绿宝书),在翻资料的同时开始写代码,然后做好移植真机的工作,DONE!  我也明白,理性很丰满,现实很骨感,这过程不会如同我计划中这般简单和轻松。但是,见难而退可不是我的风格(那样我会被红叶二小姐调戏的),不管如何,我都会,怎么说呢,尽力吧。 出于课程需求,斯坦福那些人亲自写了一个名为“pintos”的系统。pintos的结构比较简单,分为进程管理、文件系统、用户程序、虚拟内存等几个部分,也正是因为这个原因,我选择pintos作为我的参考蓝本,现在在读它的源码。 在接下来的几个月时间里,不出意外的话,我会不断的在博客上更新我的进度。 (三)交叉编译环境 倘若我们要在ubuntu上编译另外一个完整的OS,交叉编译环境是必不可少的玩意,维基百科有云: 交叉编译器(英语:Cross compiler)是指一个在某个系统平台下可以产生另一个系统平台的可执行文件的编译器。  (想起以前,我为了给路由器编译OPENWRT,下载大量源码,愣是编译了几天几夜。那时候的我,真是“可爱”。)  为了配置好交叉编译环境,我废了好大力气,最后勉强找到了组织。  编译环境大致分为2部分,binutils和gcc。我先装好gcc-4.9.1,之后下载gcc-4.9.1和binutils-2.25的源代码,似乎gcc版本与binutils版本要对应来着… 开始编译之前,需要准备全局变量(在命令行中敲入以下命令): export PREFIX=”$HOME/opt/cross”  export TARGET=i686-elf  export PATH=”$PREFIX/bin:$PATH”  编译Binutils  cd $HOME/binutils-2.25  mkdir build-binutils  cd build-binutils  #注意是在源码目录下面新建一个文件夹,然后cd到该文件夹里,然后才配置configure,不这么做的话,嘿嘿..  ../binutils-x.y.z/configure –target=$TARGET –prefix=”$PREFIX” –with-sysroot –disable-nls –disable-werror  make  make install  –disable-nls 告诉binutils,不要添加本地语言支持 –with-sysroot 告诉binutils,在交叉编译器中允许sysroot 编译GCC  cd $HOME/gcc-4.9.1  mkdir build-gcc  cd build-gcc  #注意是在源码目录下面新建一个文件夹,然后cd到该文件夹里,然后才配置configure,不这么做的话,嘿嘿..  ../gcc-x.y.z/configure –target=$TARGET –prefix=”$PREFIX” –disable-nls –enable-languages=c,c++ –without-headers  make all-gcc  make all-target-libgcc  make install-gcc  make install-target-libgcc  –disable-nls 告诉GCC,不要添加本地语言支持。 –without-headers 告诉GCC,不要依赖任何本地库,我们必须在自己的OS中实现库。 –enable-languages 告诉GCC,不要支持除了C、C++之外的语言。 提醒  不同机器配置不同,编译速度也不同。 编译这两个软件,我花了近3个钟,机器配置之低自不必说,说了都是泪。 如果任何人的任何编译过程出了任何问题,请仔细地、认真地、用心地再看看上面的命令,在你没有弄懂它的原理之前,请不要擅自做任何“改进”(血淋淋、赤裸裸的教训呀)。 (五)OS模糊框架 翻完了手头的绿宝书,我才晓得,人都是被逼出来的。 操作系统的概念都差不多已经知道,接下来,该由“理论态”切换到“实践态”了喔(书还是不能看太多,会中毒的–)。 对了,从别人推荐的地方弄来了一个框架(曾在android平台写了几万代码,我深深体会到框架的作用),轻松开工吧。 先说明一下这个框架:Meaty Skeleton,开源示例,内核和用户分离,方便扩展,嗯,没了。 最近烦杂事情很多,心情,不算愉快也不算低落吧,近来又梦见红叶,不知道又要发生什么,不管。 (六)内核第一步任务:GDT完成 天色已晚,又下着雨,我也忘记带伞了,嗯,等会儿再回去好了,这个商城的环境还是蛮好的。 今天实现了GDT。 (也不算是实现吧,因为我打算使用纯分页的流氓招数,放弃纯分段或分段分页混合,所以就不太用心于实现GDT,只是浏览INTEL的官网,借用了几个FLAG定义之类的东西,匆匆就写完了GDT) 下面是记忆: 使用内嵌式汇编  分4个段,两个高级的内核分段,两个低级id用户分段  预留了一个TSS,虽然也不打算用硬件实现任务切换(听前辈们说,硬件实现非常的麻烦)  把 设置GDT表的函数(init_gdt)放在kernel/arch/i386/global_descriptor_table.c中,而段 segment_descriptor的定义(seg_desc)则放在kernel/include/kernel /global_descriptor_table.h  引用了英特尔的一份公开资料  一些全局或者说全世界通用的参数放在kernel/include/kernel/global_parameter.h,有些人更绝,把所有函数的原型放在一个地方,哪怕内核级函数和用户级函数混在一起  翻了太多资料,头都晕了  按进度来看,有点紧,也无妨。 (七)内核第二步任务:IDT完成 佛说人者,非人者,名人者。  已经写好IDT的载入,加上之前的GDT载入,就已经完成两个与机器硬件相关的模块(准确的说,应该是给CPU的特定单元载入内容)。不过我并没传说高手那么厉害,高手们一天一个模块,可我近几天连IDT对应的IRC和HANDLE都还没弄。 在bochs上调试时,分别 键入info gdt 和info idt 1,能看到GDT和IDT的内容。 今日要点: AT&T汇编和寻常的INTEL有些许区别,不过区别不是很大  GDT和IDT都是固定的表,必须实现,实现方法各异  之前留下的TSS并非用于切换任务,而是用于保存从“用户态”回到“内核态”时必须使用的跳转地址  未完待续  后记,IDT里面的OFFSET并没有得到正确的值,因为IRQ还没设置好,相应的HANDLE还没有弄好  2015年4月16日01:14:25补充: 设 置了IDT表中的头32个项,也就是ISR(interrupt service routines),它专门处理诸如“除以0”/“Page Fault”/“Double Fault”等exception,它对exception的处理方式也很简单,或者说根本没有处理,仅仅是打印exception的类型而已。 我 随便写了一句int a = 1/0,调试的时候,bochs提示”write_virtual_checks(): no write access to seg”。可能是内核还没具有从用户态跳转到内核态的能力吧,毕竟IDT的头32个项都拥有ring0的级别,明天再看看。 补上3种中断类型: Exception: These are generated internally by the CPU and used to alert the running kernel of an event or situation which requires its attention. On x86 CPUs, these include exception conditions such as Double Fault, Page Fault, General Protection Fault, etc. Interrupt Request (IRQ) or Hardware Interrupt: This type of interrupt is generated externally by the chipset, and it is signalled by latching onto the #INTR pin or equivalent signal of the CPU in question. There are two types of IRQs in common use today.IRQ Lines, or Pin-based IRQs: These are typically statically routed on the chipset. Wires or lines run from the devices on the chipset to an IRQ controller which serializes the interrupt requests sent by devices, sending them to the CPU one by one to prevent races. In many cases, an IRQ Controller will send multiple IRQs to the CPU at once, based on the priority of the device. An example of a very well known IRQ Controller is the Intel 8259 controller chain, which is present on all IBM-PC compatible chipsets, chaining two controllers together, each providing 8 input pins for a total of 16 usable IRQ signalling pins on the legacy IBM-PC.  Message Based Interrupts: These are signalled by writing a value to a memory location reserved for information about the interrupting device, the interrupt itself, and the vectoring information. The device is assigned a location to which it wites either by firmware or by the kernel software. Then, an IRQ is generated by the device using an arbitration protocol specific to the device’s bus. An example of a bus which provides message based interrupt functionality is the PCI Bus. Software Interrupt: This is an interrupt signalled by software running on a CPU to indicate that it needs the kernel’s attention. These types of interrupts are generally used forSystem Calls. On x86 CPUs, the instruction which is used to initiate a software interrupt is the “INT” instruction. Since the x86 CPU can use any of the 256 available interrupt vectors for software interrupts, kernels generally choose one. For example, many contemporary unixes use vector 0x80 on the x86 based platforms.  今天载入到IDT中的,正是第一种类型(Exception),只不过换了个名字叫ISR而已。 未完待续。 2015年4月18日12:06:45补充: 之前的”write_virtual_checks(): no write access to seg”错误并不是权限的问题,而是段寄存器DS的值错误,它的值应该是0x10,可我给它赋值0x08。0x08是段寄存器CS的值,0x10才是段寄存器DS的值。 另外,这at&t汇编里面,把C语言函数的地址赋给寄存器,必须在函数名前面加上$。 至此,ISR彻底完成,只是,似乎IRQ又出了点问题…. 未完待续。 (十)内核第三步任务:分页完成 稍微做下记录… 得到内存大小  首先,利用grab得到物理内存的实际大小。 物理内存管理  然后,用一个数组map来监督物理内存,数组的每一项都对应着一个4K的物理内存。在这里我遇到了一个问题:数组的大小如何设置?因为还没有内存分配功能,所以不可能allocate一块或new一块内存来存放数组。找来找去也没找到合适的方案,就自己弄一个粗鲁一点儿的:设置数组大小为1024  1024。这样一来,数组的每一项对应4K,有1024  1024项,恰好可以对应4G大小的物理内存。但这样又有一个缺陷,倘若物理内存没有4G而是128M,那么该数组就有大部分元素被废弃了。现在先,额,不管这个,之后再解决。 至于这物理内存它的实际分配,我是这么觉得的:把前64M的物理内存当作内核专属(把内核的所有内容全都加载到此处),剩余的物理内存才是空闲内存,用于allocate。 为了方便分配物理内存,我采取最最最简单的方法:把所有空闲的物理页放到一条链里,需要的时候直接拿出来就可以了。 虚拟内存管理  之后,就是把page_directory地址放入CR3并开启硬件分页功能了。 page_directory,page_table等作用于虚拟地址。对于这4G的虚拟地址空间,排在前面大小为MEM_UPPER的一大块虚拟内存都是内核空间,剩下的排在后面的都是用户空间。也就是说,在有512M的物理的情况下,虚拟内存的前512M是内核态,后面的3584M是用户态。 分页错误  内存分配的过程中,可能出现“页面不存在”、“页面只读”及“权限不足”3种错误。处理分页错误,CPU会自动调用14号ISRS,我们要做的,是把我们写的处理函数地址放到14号ISRS的函数栏即可。 每次分页错误,CUP调用14号ISRS,继而跳入我们设计好的处理函数(-_-陷阱?)。 不过我现在也是暂时先不写分页错误的处理函数,如果内存真的任性真的出错了,我也不会管它的,傲娇就傲娇吧。 到这里,分页就算是初步完成了。 致命的伤痛  很遗憾,物理内存设置好了,虚拟内存设置好了,也正常工作了,但是我一旦开启硬件的分页功能,就有”physical address not available”的错误,直接重启了,到底是怎么回事…再看看吧… 未完待续。 2015年5月1日12:54:14补充: bochs的”physical address not available”提示是这么个回事,把一个内容不对的分页目录加载进硬件(也就是把分页目录地址置入CR3)。在初始化分页目录时,我直接用了4M大页的方式初始化,但弄错了byte和KB的数量级,所以就出了一点小小的问题。 遗留:page fault函数,待日后再写。 写内存分配去吧! 未完待续。 (十一)内核第四步任务:内存分配完成 内存分配?这可是个麻烦的活,不过,如果你足够聪明的话,就没什么问题了。 ——前人  上 一次,我准备好了分页的相关内容,比如说,载入分页目录/开启硬件支持/划分物理内存/划分虚拟内存等等。这一次,不会怂,就是干(为写内存分配模块而奋 斗,高扛自由的鲜红旗帜,勇敢地向前冲….)。分页准备好之后,下一步是如何地分配内存,比如,如何分配一页空白的可用的物理内存?如何分配一块空白 的虚拟内存?如何连续地分配等等等等。  第一节:申请和释放空白物理内存  申请物理内存,在分页的机制下,就是申请一页或连续几页空白的物理内存,释放则反过来。 在 分页的时候,我已经将所有的空白物理页都放进了一个链表之中,现在要申请一个空白物理页,从链表中拿出来即可,太简单了。释放空白物理页,将物理页重新放 进链表里即可,也是非常的简单,有点简单过头了。当然啦,简单有省时省力的优点,同时,也有“无法同时分配许多页/分配大内存时(比如数十M)很吃力”的 缺点。这,按我的习惯,先留着,以后再说,现在能简单就简单。 写好allocate_page和free_page两个函数之后,分配空白页倒是正常,但是内核出现”double fault”的错误,也就是8号ISR被CPU调用了,具体为甚,现在还不清楚,待我瞧瞧再说。 未完待续。 查资料如下: Normally, when the processor detects an exception while trying to invoke the handler for a prior exception, the two exceptions can be handled serially. If, however, the processor cannot handle them serially, it signals the double-fault exception instead. To determine when two faults are to be signalled as a double fault, the 80386 divides the exceptions into three classes: benign exceptions, contributory exceptions, and page faults. Table 9-3 shows this classification. Table 9-4 shows which combinations of exceptions cause a double fault and which do not. The processor always pushes an error code onto the stack of the double-fault handler; however, the error code is always zero. The faulting instruction may not be restarted. If any other exception occurs while attempting to invoke the double-fault handler, the processor shuts down. ————————————————————————– Table 9-3. Double-Fault Detection Classes  Class ID Description 1 Debug exceptions  2 NMI  3 Breakpoint  Benign 4 Overflow  Exceptions 5 Bounds check  6 Invalid opcode  7 Coprocessor not available  16 Coprocessor error 0 Divide error  9 Coprocessor Segment Overrun  Contributory 10 Invalid TSS  Exceptions 11 Segment not present  12 Stack exception  13 General protection Page Faults 14 Page fault  ————————————————————————– Table 9-4. Double-Fault Definition  SECOND EXCEPTION Benign Contributory Page  Exception Exception Fault  Benign OK OK OK  Exception FIRST Contributory OK DOUBLE OK  EXCEPTION Exception Page  Fault OK DOUBLE DOUBLE  ————————————————————————– 大概意思是:同时出现了2个中断,CPU不知道该处理哪个先,就是这样,就是如此的简单。之前没有这个错误,但分配和释放几个物理页之后就有这个问题,我估摸着两个都是Page fault,再看看吧。  刚刚调试了一下,我发现不是分配和释放几个物理页的问题,而是cli()和sti()的成对出现,去掉它们就没这个问题;更奇怪的是,就算只有sti() 允许中断出现,也会double fault,莫非我这前面关了中断或者是前面遇到了不可解决的中断遗留到现在?难道,是irq的重定位有问题?到底是为什么呢?先算入历史遗留问题吧,还 有重要的模块要完成。  (事情有点麻烦了呢?并不是内存分配这里出了问题,而是sti()惹的祸,不管这哪个位置,只要调用sti()开启中断,就会double fault,看来必须解决这个问题才行,我不可能一直不开中断吧…-_-)  睡了一觉,起来查资料,看到了关键的一句:make sure you didn’t forget the CPU-pushed error code (for exceptions 8,10 and 14 at least)到了,我翻出代码一看,哎呀嘛,我只注意到了8号软中断,没注意到10号和14号软中断(14号处理page fault),删去两行代码后,顺利开启中断!  未完待续。  第二节:分配内存(malloc/free)  既然已经可以正常地分配和释放物理内存页,那么在这一小节之中,很自然地,我的任务就是分配内存了。 所谓“天将降大任于斯人也,必先让他实现一个内存分配的算法”,不外乎就是说,要实现void malloc(int size)和int free(void p, int num_page)两个大众情人函数。 它 的大概思路就是这样的:先初始化一个桶,把可用的内存块都塞进去,要分配内存时,直接从桶里面找,找到了当然万事大吉大家都开心,如果找不到,就调用上面 那个申请空白的物理内存页的函数,弄一个4K物理内存页过来,将这个内存页分割成小块,丢到桶里面,然后继续找,就是这样….  2015年5月5日23:19:08补充: 遇到一个bug:每次申请的时候,可以正常申请,但是一旦使用了申请的内存,内核就报”page fault”的错误。想来想去,看来看去,最终发现,我在初始化分页机制的时候出了点小小的问题。 秘技解决: 初 始化虚拟内存时,我将大小和物理内存一样大(比如129920K)的虚拟内存设为内核级别并可用,剩下3个多G的虚拟内存是用户级别但不可用,我使用4M 大页载入分页表,所以我实际上载入了129920/4096 = 31个大小为4M可用的内核级别虚拟内存页,也就是说,在虚拟内存这个空间里,仅仅有31  4096 = 126976K的可用空间,其它的虚拟内存均是不可用的非法的;而在初始化物理内存时,我将前64M留给内核,后面的物理内存用于malloc和 free,比如有129920K,我把它划分为129920 / 4 = 32480个4K大小的物理内存页,也就是说,在物理内存这个空间里,仅仅有32480  4 = 129920K的可用空间,其它的物理内存均不在管理范围之内;这样一来,就出大问题了。 假设我们要申请一个物理页,由于使用链的方式管理物理页,申请到的就是排在后面的物理内存,比如申请到了129916K到129920K这一个物理内存页,现在,我们要使用它,会发生什么呢?page fault!!!!!!! 为 什么?很明显,在虚拟内存的空间里,最大的有效内存是126976K,CPU的分页表里只能找到前126976K,现在让CPU去找129916K,它根 本就找不到!它以为这个虚拟地址并没有对应这物理地址,是个错误!(附上page fault的引发条件:A page fault exception is caused when a process is seeking to access an area of virtual memory that is not mapped to any physical memory, when a write is attempted on a read-only page, when accessing a PTE or PDE with the reserved bit or when permissions are inadequate.)  于是我稍作改正,就正常了,可以正常使用申请到的内存-_-。 未完待续。 (十二)内核第五步任务:系统时钟中断、键盘中断 我现在的状态不是很好,刚弄好系统时钟中断,每10ms发出一个中断请求;但键盘中断并没有弄好,没有识别键盘的按键SCANCODE,所以暂时只能识别第一次按键,系统收不到第二次按键中断,明个儿我再来看看,已经很晚了--!!  未完待续。 2015年5月9日 15:51:00补充: 查了一番资料,调试了一番,现在,键盘中断正常工作了,键盘可以正常工作,每输入一个字符,就在屏幕上显示出来。 嗯哼,可以进入到进程模块了。 (十三)内核第六步任务:进程创建 在自习室里,我突然想到一个问题:一个进程,如何去创建它?(虽然之前翻完了大宝书,但毕竟一个多月都过去了,忘了具体的实现-_-) 翻 翻书,找到一个和我的设想相差不多的方案:用一个特定的结构体代表一个进程,结构体中包含进程的相关信息,比如说进程的pid、上下文、已打开的文件、优 先级、已经占用的CPU时间、已经等待的时间、虚拟内存空间、错误码等等,创建进程的时候,只需要跳转到进程的虚拟内存空间即可。至于如何跳转,那就是内 核态的事情了,一般的进程都处在用户态,也就不必关心太多。 如此,我们便是可以创建并运行一个进程了(不考虑文件系统),既然可以创建进程,可以切换进程,那么进程调度就很容易了,不过就是个复杂的进程切换过程而已,下一节便是写进程的调度罢。 (十四)内核第七步任务:进程切换与进程调度 黄粱一梦。  看到这句古语,顿时感慨万千,没想到仅仅数周时间,我的人生竟发生了这么大的转折(不是一夜暴富),仿佛一夜醒来,到另外一个平行世界里去。甚至,在睡梦中我都会惊醒。 逝 者已逝,再多的话语也没用。只是,我不甘愿就这么结束而已。她也曾经说过:“此身不得自由,又何谈放纵”,现在我竟是极度赞同了。曾经想过在割腕的那一瞬 间,她的脑海里究竟有什么,有没有浮光掠影,有没有回放这一生的片段?如此年轻的生命,选择自我了断,需要多少黑暗沉淀,多少的落寞与失望…似乎一下 子也看开了。 (以上只是个人情感的流露,忍不住必须得写些什么,请忽略) 简单记录一下吧,没什么心情。 进程切换时,只需要切换进程上下文,把context刷新一遍即可。 至于进程调度,这个就简单许多了(其实也挺复杂),在时钟中断到来的时候,调整各个进程的优先级,并切换到相应的进程,就是这么简单。   叔的答案,然后看了 F 叔给的这个链接 基于 Bochs 的操作系统内核实现  ,当然是什么都看不懂,除了惊诧之外也了解了一件事情:一个人写一个简单的操作系统内核是一件非常帅气并且可行的事情。 于是我开始写了,那时候我的水平大概是:只会做 C 语言的习题,编译的话只知道按 F9,汇编知道常见的指令,另外会一点点的 Win 32 编程,能流畅使用 Windows。 一开始我找了《30 天自制操作系统》来看,每天看书,然后把从书里把代码打出来,一次一次地编译运行。因为要同时写汇编和 C,所以从那时候起就开始用 vim。 在啃完了差不多半本书后,开始觉得没意思了……因为觉得作者为了让内容更简单而省略了太多细节。也看了于渊的《Orange‘s 一个操作系统的诞生》,依然没看下去:汇编用得太多了。期间也曾斗胆发邮件给 F叔,然后他推荐了 Bran's Kernel Development Tutorial 这个教程,于是我就从这教程重新开始了: 「30天自制操作系统」 Stop & 「OS67 」 Start 那时候大概是大二上学期,于是在 github 上又开了一个 repo,一开始在 Windows 下开发,后来又切换到了 Linux 下,因为 Bran's 用的 bootloader 是 Grub,不符合我的初衷,所以就自己写了一个,之后便跟一路教程写,跨过了保护模式这道坎,完成了基本的设备驱动。  在完成 Bran's 后,我又部分参考了 写一个操作系统内核有多难?大概的内容、步骤是什么? - To浅墨的回答 中推荐的:hurley25/hurlex-doc · GitHub  文档,完成了一些简单的调试函数和库函数,printk 和内存分配。 事实证明,尽早写好调试函数诸如 panic, assert 和 printk 是非常重要的。 大量地使用有助于你尽快地发现 bug (当然前提是这些函数本身不能有 bug)。 看完了 hurlex-doc 该看的部分后,很长一段时间了都不知道该干嘛好,模仿 hurlex-doc 里的内核线程切换也一直出错。这一情况一直持续到我开始读 Xv6, a simple Unix-like teaching operating system 。 如果你去看知乎关于「自制内核」的问题,你会发现 xv6 被反复地提及并推荐,事实上它非常值得被推荐:这是我读完大部分代码之后真切体会到的。 之前的 Bran‘s 和 hurlex-doc 的篇幅都比较小,我是在电脑和 kindle 上看完的,xv6 相对来说代码量比较大,有 9000+ 行和一份文档,之后我又找到了这个:ranxian/xv6-chinese · GitHub xv6 文档的中文译版,所以我就去花了十二块钱学校打印店打印了一份中文文档和一份代码。这又是一个正确的决定,让我不必对着电脑就能看代码。 在之后的时间里,我先读了 xv6 中文件系统相关的部分,然后改写它的代码为我的内核添加了一个 类似 Minix 的文件系统。 然后几乎又照抄了其中了进程调度的部分(做了部分简化),又在原来的代码基础上为添加操作系统的接口,接着写用户程序,过程几乎是「一路顺风」。看 xv6 的那段时间也经常是处于醍醐灌顶的状态。 最后我终于在差不多一个月前完成了这个简陋的操作系统内核: LastAvenger/OS67 · GitHub  (没错其实我是来骗 star 的) 历时一年,一路点亮了不少技能树(虽然都点得不好),这样算是「从零开始写一个简单的操作系统」么? 跟进一步说,有谁不是从零开始的呢? 所以想做的话,现在就开始做好了。 这是被「翻烂」了的 xv6 源代码和中文文档(其实是放书包里被磨烂了) 「故事」讲完了,接下来说一点经验之谈吧…… * 知乎上总是有人在讨论「做一个玩具编译器和做一个玩具内核何者更有趣」之类的问题,然后总有各种大V 跳出来说内核有多 dirty 而编译器多 clean,事实上除了 CPU 上的几个表因为历史原因长得恶心一点,内核并没有什么特别 dirty 的地方,另外,想做点什么打发时间,不过是两个代码量稍多的入门项目,有什么好纠结的? * 写内核的过程中,你会接触到一些这辈子大概只会用到一次的知识,A20 线已经成为历史,日常的编程里面也不需要你懂得 GDT IDT 的结构。但是单凭内核主要部分部分(文件系统,进程,内存)给你带来的知识而言,这点冗余是值得的。  * 尽早实现调试函数并大量使用,善于利用 bochs 的内置调试器,能省下你不少时间。 * 有时候觉得书里的做法非常奇怪,你觉得你有更好的做法,一般是你想错了。(当然只是一般) * 上面说看 xv6 一路顺风是假的,20% 时间在抄代码,80% 的时间用来调试。 * 对我这种能力一般的人来说,「写内核」只是好听的说法,正确的说法是「抄内核」。当然,就算是抄一个,也算是受益匪浅了。 * 抄 xv6 的好处在于,即使你的代码出错了,你可以坚信,正确的答案肯定在 xv6 的代码里,或许只是你还没理解透而已,只要不断地看和理解,你就离正确的道路越来越近。 汇编不重要,但是要有一定计算机组成的基础,并对一个现代 kernel 的结构有大体的认识,至少大致上理解虚拟内存和文件系统有哪些东西。不要看 《the orange's》和《三十天编操作系统》,面太小,代码质量不高,就别拿 DOS 当操作系统了。个人比较推荐《莱昂氏 UNIX 源码分析》(已绝版,可淘宝打印)、《linux 0.11 内核详解/剖析》 ,写代码之前至少先都啃一遍。教程的话推荐 《bran's kernel development tutorial》和 osdev 上的一些资料。顺着它们搭开发环境,出一个简单的 bootlaoder,可以编译 C 代码即可。然后拿大把大把的时间慢慢给 bootloader 加东西就好了,能用 C 就不要用汇编。开发中的很多细节要到开发时才留意的到,这时可以自己思考,也可以去抄 linux 0.11, xv6, unixv6 这几套优秀的源码。 现在想来,开发一个 kernel 的主要内容在于“实现”而不是“设计”,更重要的是用时间去理解这些优秀的设计为什么合理,自己别出心裁的想法一般不用多想,一定是错的。 不要在 x86 的一些历史遗留问题上花太多时间,比如 bootloader 的保护模式会在开头挡住一大批人,可是这并不重要,只要知道有这个接口可以引导你的二进制代码即可。知道 GDT 可以区分用户态/内核态,IDT 可以给中断绑回调就行了。在调试上会花费大量时间,可以慢慢琢磨怎样提高调试的效率。然后需要的就只是耐性了,说实话也挺无聊。 我来写一个如何在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 uVision5  学过单片机的朋友,相信非常熟悉这个软件,使用起来也非常简单,网上随便一查,就可以下载和安装了,这里就不详细展开说了。如果不知道如何使用的,先熟悉一下这个环境,再开始写,相信半个小时即可上手。 二、参考资料 既然是运行在真机上,必然要对它有所了解,我们这里采用的是STM32(Cortex-M3架构),市面上非常火热的一款,资料丰富,大家有什么问题谷歌一下,有很多前人的经验让你借鉴。如果不知道如何谷歌的朋友,点开这个链接去操作,免费,大约15分钟就能翻墙了如何优雅的访问谷歌、谷歌学术等网站 | 欧拉的博客。 1.Cortex-M3权威指南(中文版),这本书会详细的讲解,中断处理,异常,ARM汇编等知识,我们会在任务切换的时候用到。(PDF即可) 2.嵌入式实时操作系统ucos(邵贝贝审校),ucos中有很多我们可以借鉴的地方。 3.谷歌,有一些基础知识遗忘的时候,谷歌可以让你很快补充上来 三、从写一个最简单的os做起 我们这里假设我们写一个支持32个任务并发执行的简易OS 1.任务就绪表    我们假设这里任务有两种状态,就绪态和非就绪态。 我们定义一个32位的变量OSRdyTbl(就绪表),它的最高位(第31位)为最低优先级,最低位(第0位)为最高优先级。OSSetPrioRdy()函数的功能是,你传递一个数(优先级),把这个优先级对应的任务设置为就绪态。同理,见图: OSDelPrioRdy()函数将某任务从就绪表移除 OSGetHighRdy()函数选出最高优先级的任务 这里我们就完成了,设置某任务为就绪态,从就绪态删除某任务,获得最高优先级任务的任务。 2.任务控制块 想必这个概念大家很清楚了,每个任务都对应一个任务控制块,典型的用处是,任务切换时,通过控制块来获知每个任务的堆栈(因为控制块有指针指向该任务的堆栈) 此外,再定义几个变量。 注释写的很清楚,不解释了! 3.主堆栈 在此STM32中,提供两个堆栈指针,一个是主堆栈(MSP),一个是任务堆栈(PSP),可以通过查Cortex-M3权威指南(中文版)得到。所以我们既要建立一个主堆栈,又要为每个任务建立自己的堆栈(一个任务一个),这里我们先不管任务堆栈,只看主堆栈。 OS_EXCEPT_STK_SIZE是个宏,大家可以自己设定,我这里设的是1024,一定要尽量大一些。为什么?因为裸机下,进入中断服务程序时,系统会把许多寄存器入栈,而且支持中断嵌套,也就是刚入栈完又入栈,所以有可能会堆栈溢出,非常危险。 CPU_ExceptStkBase大家先别管,它指向的是数组最后一个元素。 4.建立一个任务 我们通过Task_Create()函数来建立一个任务,第一个参数用来传递该任务的任务控制块,第二个参数用来传递函数指针,第三个传递该任务的堆栈。tcb->StkPtr=p_stk这句将该任务控制块中应当指向栈顶的指针,指向了该任务的新栈顶(前面定义了TCB,自己可以翻一翻),在写该函数时,一定要看Cortex-M3权威指南,不然你怎么知道有这么多寄存器,而不是仅仅从R1到R7?看到这里,还想看懂的,应该都是真的想写OS的朋友,这里有不懂的去看Cortex-M3权威指南,你会豁然开朗的。这里,Task_End是一个几乎永远不会执行的函数,何时会执行,先不管了,看它的内容。 5.欢迎来到主函数 ①第一行:在该主函数中,第一行我们让主堆栈指针指向了主堆栈,那么,这个主堆栈是哪里来的呢?很简单,我们自己定义的,哈哈。 很熟悉吧,前面发过这个图,大小你来指定,注意要大!!!!! ②第二、三行:建立一个任务,第一个参数传递了该任务的控制块,第二个参数是该任务的任务函数,第三个是堆栈(数组最后一个) 是不是很好奇,任务1和任务2是什么?一起了来看 大家自己随意定义,开心就好。 Task_Switch()函数又是什么呀? 这里Task_Switch()是我们用来测试的程序,当任务1运行时,完成i++后,将最高优先级设置为任务2,并用OSCtxSw()切换到任务2,OSCtxSw是用汇编写的,是一个隐藏BOOS,我们先不管。 ③第四行:程序刚运行时,是没有最高优先级的,所以我们用p_TCBHighRdy=&TCB_Task1;来随意指定一个任务为最高优先级 ④:最后一行:OSStartHighRdy()该函数也是汇编,和OSCtxSw()并称为2大BOSS,我们会在后面解密。OSStartHighRdy和OSCtxSw很相似,不过OSStartHighRdy()用于当所有任务都没有运行过时,用于初始化(当然具有任务切换的作用)并成功运行第一个任务,而OSCtxSw()是在OSStartHighRdy()之后,使用OSCtxSw()时,最起码有一个任务已经运行了(或正在运行)。 6.完工? 到这里,从宏观说已经基本完工了,这就是一个简易的OS的基本状况。看到这里,大家可以休息一下了,消化一下,马上有BOSS要出现了,解决那两个BOSS后,就真正做成了一个简易的OS。 7.两大boss之OSStartHighRdy()函数 7.1任务视图 终于开始了任务切换环节,这是我画的一副任务切换图,自我感觉非常好,不过大家应该看起来很困难,字太丑了~~~~ 通过分析上面这张图,来确定如何写OSStartHighRdy()函数: ①中:此时有一个任务1,但是任务1我们仅仅是建立了,并没有让它运行。这里我们认为任务1是由三部分组成:任务代码,任务堆栈,该任务的任务控制块。 ②中:我们想让任务1运行起来,任务1是什么?就是一堆代码,如何运行起来?-----让PC(程序计数器)指向任务代码即可(依靠出栈POP)。同时,我们还要让SP指向任务的堆栈,这里的SP当然是PSP(任务堆栈) 7.2写OSStartHighRdy()函数 下面开始写OSStartHighRdy(),它的功能就是上图的①和② 2,3,4,5行中的那些数字,是外设对应的地址,我们往相应的地址写值,即可完成某些目的。12,14,15行是我们之前定义过的几个变量,忘了的回头翻一翻。17行是将OSStartHighRdy()函数extern了一下,因为我们在主函数要用。 上图就是OSStartHighRdy的内容,我们一起来看。28,29,30行设置了PendSv异常的优先级,问题来了,什么是PendSv??? Cortex-M3权威指南中其实讲了,很详细,这里为了缩减篇幅,不详细说,大家只用知道PendSV 异常会自动延迟任务切换的请求,直到其它的中断处理程序都完成了处理后才放行。而我们只用触发PendSV异常,并把任务切换的那些步骤,写在PendSv中断处理任务中。 7.2.1PendSv处理程序 经过39和40行,我们往控制器里写值,触发了PendSv异常,现在程序会进入异常处理程序,就是下图: 在此函数中,如果PSP为0,则进入OS_CPU_PendSVHandler_nosave()函数,其实在OSStartHighRdy我们将PSP设置为了0,所以必然会进入OS_CPU_PendSVHandler_nosave()函数。 7.2.2OS_CPU_PendSVHandler_nosave()函数 在该函数中,我们让p_TCB_Cur指向了最高优先级的任务,因为有出栈,所以重新更新了SP。 7.2.3恭喜 到了此处,一个任务已经可以成功运行起来了!!!!!! BUT only one task!我们需要让它多任务切换 7.3写任务切换OSCtxSw()函数 与OSStartHighRdy非常相似,也是往中断控制器里写值,进入PendSv异常。 7.3.1任务视图 依然是这张喜闻乐见的图!!!!!!! ③:任务1要切换到任务2,因为待会要进入任务2,会破坏任务1,所以我们要保存任务1的现场,把寄存器入栈 ④:因为任务1有入栈动作,栈顶肯定变了,我们修改任务1控制块的值,让它指向新的栈顶 ⑤:什么都没有,只是想说明。我们建立了一个任务2,仅仅是建立了。 ⑥:很简单,要想让任务2运行,则让PC和SP分别指向任务2的任务代码和任务堆栈即可。 ⑦:什么都没有,只是想说明,任务2活的很开心,可以运行了 7.3.2由OSCtxSw()进入PendSv异常 与OSStartHighRdy不同的是,这时的PSP肯定不为0了,所以不会跳转,会顺序执行55行以后的程序,也就是任务视图里的③,继续执行④。执行完60行的程序后,继续顺序执行,执行下面程序: 和OSStartHighRdy函数的后续步骤几乎一样,找到优先级最高的任务,让指针指向它即可!!!! 1.大功告成!!!!!!!! 到这里,我们已经彻底完成了一个简易OS的设计!!!! 8.1如何查看成果? 8.1.1增添程序 我们在主函数中加入一个函数 大家可以把它理解为一个库,调用之后,我们就可以在串口(屏幕)显示某些字符了。 同理,在任务1和任务2中加入printf函数 8.1.2编译并下载程序 8.1.3利用串口调试助手观察 网上搜串口调试助手,会有很多工具,随意下一个就OK! 可以看到,按照我们的预期,任务1和任务2轮流输出字符在屏幕上! 8.2如何调试程序? 8.2.1调试器 必然是神器--J-link或者ST-link,一个大概50,嵌入式开发神器,记得以前刚开始玩单片机的时候,调试全靠打印消息在屏幕,觉得好用的不得了,经常有人给我说,你用调试器调试啊,我都鄙夷的回一句,需要么?(哈哈,那时确实不需要) 等开始写OS时才知道,这东西真是救命稻草,没有它,怎么看寄存器的值和异常返回的值呢? 8.2.2界面 点击DEBUG后,你可以看到寄存器的值。想想我们之前要入栈,出栈,如果哪一步错了,自己估计把串口吃了,也看不出来吧,哈哈!!!!!! 单步调试什么的,不说了,用的很多。 9.写到此处的感想 答主因为写这个OS,在凳子上久坐了两周,这两天腰疼,只好躺着写这个回答了!哈哈! 也正好因为腰疼,感觉时间比较多。不过和想象的真的不一样,本来觉得可以一气呵成,结果短短的篇幅,就写的自己的思维都大乱了,而且也挺费时间的,前后用了有6个小时了。想写OS的朋友,参考上面的步骤,加上自己琢磨,应该也能写一个出来。如果哪里有不清楚的,不要心急,如果真的是一两天就写成了,还有什么锻炼的意义,有点失去初衷了。不懂的就去查权威指南和OS的书籍,相信你会收获的非常多! 为什么要写这个回答? 这是我写了6个小时多以后来补充的问题,因为我自己也纳闷了,放着自己的事不做,跑来写这么一堆干什么........刚才路上走着想到答案了----------想留个纪念。还有不到两个月就要寒假了,我打算考研,也就是说这是大学里做的最后一个项目了(毕设除外),

标签: 2cr3z集成电路

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台