Skip to content

{ Category Archives } Kernel

如何写一个微内核

导言 有时常关注我博客的朋友知道,去年我自己开发过一个很简单的微内核 Totoro,目前已经暂时停止更新。从开发到能真正运行花了一两个月的时间,当然都是我在业余时间做的啦。还记得当时那种兴奋的心情,可能跟 Linux 的最初开发者 Linus 差不多,有兴趣可以看这篇文章 Linux – a free unix-386 kernel 。 当然了,水平毕竟不是一个层次的。不过今天,我想把如何开发这样一个简单的内核给记录下来,一来是让有兴趣的后来者可以很快的摸到门路,二来是做一个阶段性的总结,再来就是看看自己还有没有兴致继续把它做得更好,毕竟现在漏洞百出,也不可能给别人拿去用。总的来说,出来做了三年的软件开发,没做过什么高端的有难度的东西,这样一个作品让我自己还是比较开心的! 关于单线程 从单片机(又称单板机)玩过来的同学应该知道,我们刚开始写代码的时候,几乎都是裸奔在处理器上的。写个流水灯,点亮数码管,高端点的来个串口打印。这对刚接触的同学其实就有很多东西可以学了。但是人也总不会一直停步不前,写了太多的单线程代码的菜鸟开始思考,多线程到底多了什么了呀?说到这其实我很想笑,假如你去问一问那些入门语言是 Java,Python 的同学,他们一定会说,多线程有什么了不起啊!对呀,对这些这些高级语言来说,多线程?开玩笑,随便调个接口,要几十个线程,甚至几百个,分分钟给你造好了。在这些人眼里,这个东西,没什么了不起。但对于出身即低端又比较吝啬的嵌入式的同学们来说,这个多线程词汇,却魔咒般的充满了太多的神秘感。那么多线程到底指的是什么?我们不去讨论太复杂的东西,不去讨论进程空间,虚拟内存等等高端神秘的词汇。我们只关注上下文环境。这个对于容易满足的嵌入式开发者来说,已经很知足了。时常感慨,是不是嵌入式开发做久了,人也会对自己变吝啬。 上下文环境 上下文(Context)环境,指的是这个线程运行时的一些状态,比如局部变量的值,寄存器里保存的值,比如,PC 指针指向那里,代表程序即将运行到那里,SP 指针,记录了调用信息,LR 记录了回去的路(返回地址)等等。那么要实现这个多线程我们需要做些什么? 保存上下文。我们在做线程调度的时候,将当前线程的上下文环境保存在那个线程申请的“栈”内存里(其实就是一个大数组)。然后通过一系列的底层操作,将要被调度的线程唤出来。其实也就是把之前保存的上下文从内存里读到寄存器上啦,只不过这个过程需要一些技巧,我们会在代码解析里面讲到。其实多线程就是这样的啦,那么我的这篇文章是不是该完结了?不尽然,我们要做到微内核的基本功能,那还得有调度器呀。 傻逼式调度器 我们要实现这样一个调度器。对于平级线程,也就是优先级一样的应用线程,我们会去分片(Time-sharing)执行它们。对于高优先级应用线程,我们会义无反顾的一直执行它,直到它愿意自己挂起。我觉得这是最自然的调度器,因为优先级既然高,那么也就得优先执行。当然,也是很傻逼的调度器啦。我们现在的目标是,保持一切尽量的简单。这样才有兴趣继续干下去嘛。这样约定好了以后,我们开辟了两个队列,一个用于保存处于准备态的线程,一个用于保存挂起的线程。而当前运行的进程会由一个 current 任务结构体指针来标记。所以一个任务执行完之后会有两种状态,一个是挂起,一个是再次处于准备状态。这样我们就需要一个链表接口模块。 基础数据结构 我们用单链表结构来实现这两个队列。 include/totoro/taskq.h struct tasklist { struct ttr_tcb *tcb; struct tasklist *next; }; 实现单链表的几个接口: static struct tasklist *migrate(struct ttr_tcb *tcb, struct tasklist **src, struct tasklist **dst) […]

Linux – module_init

Linux 利用了程序编译链接的一个技巧,在GCC里可以通过 __attribute__ 将指定代码放入到某个特定的段(section)中,内核模块就是这样被统一管理起来的。如图中所示,指向模块初始化代码的函数指针都被放置在一个 “.initcall6.init” 的代码段里,而模块初始化代码则放置在另一个代码段 “.init.text”。这样的好处就是内核启动时加载内建(built-in)内核模块阶段可以很方便地根据首指针依次将所有模块遍历出来,这有点像 Unix 哲学中将代码复杂度转移到数据上去寻求简洁,事实证明这样干确实简洁漂亮。 结合代码看图:Fig 1. snapshot of *_module_init in kernel image drivers/usb/serial/cp210x.c include/usb/serial.h    module_usb_serial_driver(serial_drivers, id_table) init/main.c include/asm-generic/vmlinux.lds.h    do_initcalls      initcall_levels include/linux/module.h include/linux/init.h    module_init(x)      __initcall(fn) 外部链接: 1,https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html

定制 Linux 系统

ChangeLog: 20180409 – remount operation、mke2fs、nls package 20180404 – build minimal rootfs with busybox 本文实验环境为 BeagleBone Black 0,编译 U-Boot 与 Linux Kernel U-Boot: 源码 -https://github.com/u-boot/u-boot.git 补丁 -https://github.com/eewiki/u-boot-patches.git 配置 -make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- am335x_boneblack_defconfig 编译 -make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j4 Kernel: 源码 - https://github.com/beagleboard/linux.git 配置 - make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- bb.org_defconfig 编译 - make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j4 1,准备一个 SD 卡,大小 […]