
从0开始的操作系统教程
文章平均质量分 96
charlie114514191
我是一个普通的嵌入式软件程序员,喜欢研究Linux(应用层跟内核从都有粗浅的涉略),单片机,操作系统和计算机体系架构等内容,目前是一枚普通的本科生。
笔者是一个朴素的开源主义者,我坚信代码和知识没有壁垒,也不应该有壁垒所在,任何人都应该平等的获取到所有的知识。笔者力所能及的将自己的代码项目开源到Github
笔者的github:https://github.com/Charliechen114514
笔者的私人博客(旧):https://charliechen114514.github.io/
笔者的私人博客(新):https://www.charliechen114514.tech/
展开
专栏收录文章
- 默认排序
- 最新发布
- 最早发布
- 最多阅读
- 最少阅读
-
从0开始的操作系统手搓教程 附五:聊一聊内联汇编
很快,我们就要到更加深入的部分了,我们需要理解一下内联汇编的知识。原创 2025-02-21 12:48:29 · 760 阅读 · 0 评论 -
从0开始的操作系统手搓教程 附四:说说我们的ELF文件
现在,我们来说一下我们的ELF文件吧!这个会在我们需要拷贝我们使用GCC和NASM编译的主代码二进制文件的时候用到,也就是我们的Loader.S文件当中的kernel_init函数原创 2025-02-18 13:47:13 · 920 阅读 · 0 评论 -
从0开始的操作系统手搓教程 附三:保护模式简单导论
笔者针对自己的手搓操作系统教程附上了简单的保护模式导论内容原创 2025-02-17 10:16:03 · 844 阅读 · 0 评论 -
从0开始的操作系统手搓教程 附二——调试我们的操作系统(bochs调试小记)
我们要学习如何使用bochs来调试我们的操作系统。毕竟伴随代码量的增大,出错的概率自然也会直线的上升。原创 2025-02-15 20:10:42 · 830 阅读 · 0 评论 -
从0开始的操作系统手搓教程 附一:实模式简单导论
本部分的额外内容是配合我们的手搓教程的第三节的实模式开始的,笔者打算简单的介绍一下实模式这个东西。实模式就是CPU的一种工作模式。与之相对的一个经典的模式是保护模式,关于保护模式,我们会在加载器准备操作系统的时候开启,这里先不谈论。CPU是一个这样的器件,我们提供一系列的流水线一般的指令,他解码指令,做这个指令蕴含含义的操作——去其他地方拿到操作数,按照给定的指令对操作数操作,然后放到一个地方进行存储。原创 2025-02-13 20:55:36 · 733 阅读 · 0 评论 -
从0开始的操作系统手搓教程47:最后的最后:实现管道
管道是一种进程间通信方式,虽然进程彼此独立,但为了实现协作,必须有通信机制。在Linux中,一切皆文件,管道也不例外,虽然它不在文件系统中存在,但可以通过open、read、write、close等系统调用操作。管道仅存在于内核内存中,是一种被多个进程共享的缓冲区,它的本质是一个环形缓冲区,核心是两个指针:读指针和写指针。写满时写进程休眠,读空时读进程休眠,彼此通过唤醒对方进行协作,从而避免数据丢失。这种机制非常类似生产者消费者模型,前面介绍的ioqueue键盘输入缓冲区也是类似实现。原创 2025-03-11 07:02:27 · 1045 阅读 · 0 评论 -
从0开始的操作系统手搓教程46——实现wait和exit
实现exit和wait(笔记,因为实现问题没有测试)原创 2025-03-10 09:50:00 · 776 阅读 · 0 评论 -
从0开始的操作系统手搓教程45——实现exec
exec函数的作用是用新的可执行文件替换当前进程的程序体。具体来说,exec会将当前正在运行的用户进程的进程体(包括代码段、数据段、堆、栈等)替换为一个新的可执行文件的进程体。这样,新的程序会接管当前进程的地址空间,继续执行新程序的代码,但该进程的 PID(进程ID)保持不变。也就是说,执行exec后,原来进程的地址空间被清除,并且新程序的内容会加载到同样的进程中,继续执行。为什么需要实现exec呢?这个问题的答案与 shell 的工作方式密切相关。在实现一些简单的命令时,我们使用了类似。原创 2025-03-10 09:42:27 · 854 阅读 · 0 评论 -
从0开始的操作系统手搓教程44——实现更好的shell
在 Linux 系统中,快捷键如“Ctrl+u”和“Ctrl+l”是由操作系统提供的标准功能,但它们的实现方式并不是直接由键盘驱动程序(keyboard.c)来处理的。如果在键盘驱动程序中加入过多的逻辑处理,会导致系统的中断处理变得非常复杂,效率降低,进而影响整个系统的响应速度。的数据类型(从16位改为32位),因为一旦改动数据类型,后续代码可能会受到影响,造成所谓的“雪崩效应”,即需要改动很多地方的代码和解释。时,输入缓冲区中的字符会被清除,屏幕会被清空,然后打印命令提示符,再显示用户已输入的命令。原创 2025-03-10 09:37:07 · 1114 阅读 · 0 评论 -
从0开始的操作系统手搓教程43——实现一个简单的shell
我们下面来实现一个简单的shell。原创 2025-03-10 09:31:08 · 1135 阅读 · 0 评论 -
从0开始的操作系统手搓教程42——实现fork
实现fork原创 2025-03-10 09:27:29 · 610 阅读 · 0 评论 -
从0开始的操作系统手搓教程41——实现stat
实现文件系统的stat功能原创 2025-03-10 09:16:27 · 979 阅读 · 0 评论 -
从0开始的操作系统手搓教程40——实现PWD
实现文件系统的pwd功能原创 2025-03-10 09:09:54 · 573 阅读 · 0 评论 -
从0开始的操作系统手搓教程39——目录的基本操作:创建,遍历和删除
实现目录的更全面的基础操作原创 2025-03-10 09:04:43 · 841 阅读 · 0 评论 -
从0开始的操作系统手搓教程38:文件删除
实现文件的删除原创 2025-03-10 08:54:11 · 834 阅读 · 0 评论 -
从0开始的操作系统手搓教程37:实现文件的读写指针定位功能
实现文件的lseek功能原创 2025-03-10 08:47:17 · 303 阅读 · 0 评论 -
从0开始的操作系统手搓教程36:文件读取
实现文件读取原创 2025-03-10 08:43:20 · 890 阅读 · 0 评论 -
从0开始的操作系统手搓教程35:文件写入
实现文件的写入原创 2025-03-10 08:38:32 · 996 阅读 · 0 评论 -
从0开始的操作系统手搓教程34:说说文件描述符与常见的操作和文件基本操作
我们还需要打开根目录并初始化文件表// filesystem_init函数下追加:*/return ret;函数将本地文件描述符转换为全局文件表索引。首先,它获取当前任务的文件描述符表,然后根据传入的本地文件描述符返回对应的全局文件描述符索引。sys_close函数用于关闭文件。首先,它检查文件描述符是否大于2(标准输入、输出和错误文件描述符不处理)。然后,调用将本地文件描述符转换为全局文件描述符,并尝试关闭文件。如果成功,更新任务的文件描述符表并返回0;否则,返回-1。原创 2025-03-10 08:33:44 · 936 阅读 · 0 评论 -
从0开始的操作系统手搓教程33:挂载我们的文件系统
挂载分区可能是一些朋友不理解的——实际上挂载就是将我们的文件系统封装好了的设备(硬盘啊,SD卡啊,U盘啊等等),挂到我们的默认分区路径下。这样我们就能访问到了(嘿!想象你是一个蚂蚁,别人把葡萄挂到了树枝上,然后你就可以爬着访问到了)文件系统的挂载和卸载在Linux中是非常重要的功能,它允许用户将一个分区的文件系统与另一个分区的目录树连接起来。通常情况下,Linux会将根分区作为默认分区,并通过mount命令将其他分区挂载到默认分区的某个目录上。原创 2025-03-10 08:07:22 · 737 阅读 · 0 评论 -
从0开始的操作系统手搓教程32:生成和检测文件系统
硬盘是低速设备,其读写单位是扇区。这个事情我相信任何一个上操作系统的朋友都知道的,不然我们不会满头大汗的学习为此而作妥协的API。为了减少频繁访问硬盘,操作系统通常不会在每次有一扇区数据时就去读写硬盘,而是等到数据积累到一定量时才进行一次性访问,这个量就是块。块的大小是扇区大小的整数倍,因此块是由多个扇区组成的。以 Windows 系统为例,格式化分区时选择 FAT32 文件系统时,可以选择不同的簇大小,如 4KB 或 32KB 等,簇即为块。原创 2025-03-10 08:01:42 · 1197 阅读 · 0 评论 -
从0开始的操作系统手搓教程31:完成硬盘驱动程序
(提示——这里的代码发生了比较大的变动,如果你担心本次实验做不成功,考虑更换一下中断的写法,参考本篇代码的interrupt.c做改写。下面这个活够硬,就是完成我们的键盘驱动程序。还记得我们最开始创建我们的主boot.img嘛,很快就要来新活了。我们要创建一个新的bochs硬盘,作为我们的辅硬盘,在上面安装文件系统。嗯,就像C盘和D盘似的。实际上在Makefile中添加一行话,就能直接添加好了:。然后稍微的修改一下我们的bochsrc添加上就完事了。原创 2025-03-09 21:22:20 · 684 阅读 · 0 评论 -
从0开始的操作系统手搓教程30:大活:实现malloc和free
之前我们虽然已经实现了内存管理,但显得过于粗糙,分配的内存都是以 4KB 大小的页框为单位的,当 我们仅需要几十字节、几百字节这样的小内存块时,显然无法满足这样的需求了,为此必须实现一种小内存块 的管理,可以满足任意内存大小的分配,这就是我们为实现malloc 要做的基础工作。原创 2025-03-09 20:21:24 · 1061 阅读 · 0 评论 -
从0开始的操作系统手搓教程29:实现用户进程的printf
我们马上就准备实现一个大的——也就是实现用户进程的printf,很快用户进程就能自己说话,告诉我们当前进程的PID了,为此,我们需要提前做一部分工作。不然的话,,因为我们的printf实际上不同于一般的函数,他是参数可变的。所以,我们务必需要先理解的是——可变参数函数的实现的根本原理。原创 2025-03-09 19:07:01 · 703 阅读 · 0 评论 -
从0开始的操作系统手搓教程28:实现Syscall架构体系
上一节中,我们已经干了一个大事情,那就是实现了用户级的线程(可以叫线程了,只需要派发一个打算以特权级为3的执行函数,我们就可以执行它)可是我们终归是要使用操作系统的一些服务的,这可咋办,直接让自己来干,好像不对,特权级不允许我们这样做,但是我们的确需要知道一些事情。这个问题的解决思路是——按照我们内核规定的模式,请求我们操作系统服务,而不是越过操作系统自己把这个事情办了。上述的“规定的模式”,就是我们所谈到的系统调用。原创 2025-03-09 12:48:00 · 724 阅读 · 0 评论 -
从0开始的操作系统手搓教程27:下一步,实现我们的用户进程
现在,我们做好了TSS的工作,就可以激流勇进,准备实现一个用户进程了。现在,“用户进程”这个词终于不是梦了。原创 2025-03-08 16:03:39 · 947 阅读 · 0 评论 -
从0开始的操作系统手搓教程26:准备好用户进程了嘛,同志们?(1:TSS手搓篇)
我们终于准备整点真大活了,在这一章中,我们就准备实现用户级别的线程,为此,我们需要做一些工作来保证。原创 2025-03-08 13:00:02 · 656 阅读 · 0 评论 -
从0开始的操作系统手搓教程25:使用环状缓冲区来让我们的键盘驱动真正的有作用起来
然而,上面的驱动程序,我相信不少朋友发现毛病了——那就是他是处理完一个字符之后,就迅速的扔掉,没法找到了。换而言之,我们需要一个缓冲区,让我们可以记住用户之前输入了什么。这不,环状缓冲区就来了。缓冲区就是为了解决生产者和消费者问题而存在的,想象一下。原创 2025-03-07 21:30:55 · 1142 阅读 · 0 评论 -
从0开始的操作系统手搓教程24——完成我们的键盘驱动子系统
我们下面来看看我们的键盘驱动子系统是一个怎么个事情。驱动程序,你可以认为是对硬件的一层封装。我们按照手册规格的规定姿势,封装好一套一套的流程,我们上层的软件想要使用这个硬件,就直接找这个驱动接口,一个调用完事。这就是一个驱动。原创 2025-03-07 21:28:15 · 1061 阅读 · 0 评论 -
从0开始的操作系统手搓教程23:构建输入子系统——实现键盘驱动1——热身驱动
我们下一步就是准备进一步完善我们系统的交互性。基于这个,我们想到的第一个可以用来进行输入的设备,就是键盘!这个不假。我们这个章节的核心内容,就是构建一个基于键盘的输入子系统。给我们之后的系统更多功能添砖加瓦!原创 2025-03-06 22:49:33 · 1353 阅读 · 0 评论 -
从0开始的操作系统手搓教程22——锁让我们的并发变得更加安全
笔者是先于此项目才知道的锁的。实际上,上一个问题的根源是——锁的缺失导致程序潜在的不安全。如果你学习过锁和信号量,那显然,你就可以直接跳到实现上,直接实现一个信号量和锁就可以结束本小节的内容了,如果你没有,请听我慢慢道来。现在呢,我们启动这个存在问题的内核。然后当运行还在正常的时候,快速的嗯下我们的Ctrl + C稍微让我们的内核休息一下。然后,我们就打开外部中断检测。OK,在执行一段时间后,我们终于看到了触发异常的地方了这里,我们看到的是时刻00191782969发生了不应该发生的东西。原创 2025-03-11 10:49:09 · 1044 阅读 · 0 评论 -
从0开始的操作系统手搓教程21:进程子系统的一个核心功能——简单的进程切换
我们下面,就要开始真正的进程切换了。在那之前,笔者想要说的是——我们实现的进程切换简单的无法再简单了——也就是实现一个超级简单的轮询调度器。每一个进程按照一个priority作为一个拥有时间的开始,然后,我们的调度器就分配给这个进程priority个时间片,每一次时钟中断发生的时候,我们当前的进程就发生时间片剥夺减少一次,当我们的运行的elapsed_ticks的值达到了priority,也就是我们预计分配的时间片的时候,剥夺这个进程的运行资格给下一个进程。很简单吧!原创 2025-03-05 00:40:40 · 2056 阅读 · 0 评论 -
从0开始的操作系统手搓教程20:进程子系统的构建——实现内核线程
好消息!我们终于可以向更近一步迈出了!那就是构建我们的进程子系统!这就意味着,我们终于可以准备向并发任务靠近了!原创 2025-03-03 10:38:25 · 1199 阅读 · 0 评论 -
从0开始的操作系统手搓教程19:构建我们的内存管理——第二步:内存子系统进化,获取页!
现在,我们将会完成简单的从虚拟内存和物理内存完成分配且在页表中构成映射的办法原创 2025-02-28 21:05:11 · 914 阅读 · 0 评论 -
从0开始的操作系统手搓教程18 构建我们的内存管理——第一步:将我们先前获取的内存大小取出来
现在我们终于回归了我们的话题,那就是进一步实现我们的操作系统。为此,我们需要做的就是构建一个内存管理子系统,现在我们只是起一个头。再完成后面的抽象中,我们每一个子系统会一步一步走向完善。原创 2025-02-28 15:37:50 · 934 阅读 · 0 评论 -
从0开始的操作系统手搓教程17:构建属于我们自己的内核库3——实现一个双向的链表
终于!马上就要结束了。我们这一次是要构建一个非常重要的东西,叫做非侵入式的链表。整个链表实际上参考的是Linux的实现,为此,让我们好好看看,Linux是如何实现一个简单的双向循环链表的!原创 2025-02-27 14:55:27 · 874 阅读 · 0 评论 -
从0开始的操作系统手搓教程16 构建属于我们自己的内核库2——实现一个位图
对于那些喜欢操作系统而对数据结构不太感冒的朋友可能很不喜欢这几节,但是没办法,为了完成我们的内核,这个路是不得不走的!我们在这一节,是要准备实现一个自己的位图。原创 2025-02-27 13:27:53 · 343 阅读 · 0 评论 -
从0开始的操作系统手搓教程15 构建属于我们自己的内核库1——实现kernel的string.h
很快,我们就要一步一步完成其他的内核子系统了。本来,笔者计划是直接进入我们的内存子系统,但是笔者回忆自己的手搓教程,非常悲伤的发现,我们实现其他子系统之前,必须提供一个kernel level的通用库(你可能很快就会着急的反驳我:为什么?不能使用我们的GLibc内建的库函数嘛?哈哈,不着急我会回答的)。本章,我们会着手构建四个重要的库。实现自己的ASSERT,从而给内核实现我们自己的提前检查。string.h作为内存和字符串中重要的操作库。bitmap.h来实现一个内核层次的位图。原创 2025-02-27 12:11:24 · 834 阅读 · 0 评论 -
从0开始的操作系统手搓教程14——进一步完成中断子系统
在这里我们进一步完善我们的中断子系统。完成对8253的编程。原创 2025-02-25 20:03:22 · 954 阅读 · 0 评论 -
从0开始的操作系统手搓教程13——动手实现我们的中断子系统
下一步,让我们实现我们的中断子系统!我们需要做的事情非常的多,所以,需要每一步的都谨慎的做好,笔者会教授一些更加高级的调试方式。我们介绍的8259A,本身就被放置在了。原创 2025-02-25 19:04:39 · 1006 阅读 · 0 评论