欢迎来到DragonOS的文档!

DragonOS简介

DragonOS龙操作系统(以下简称“DragonOS”)是一个面向服务器领域的,从0开发内核及用户态环境,并提供Linux兼容性的64位操作系统。它使用Rust与C语言进行编写,并正在逐步淘汰原有的C代码,以在将来提供更好的安全性与可靠性。

我们致力于打造完全自主可控的数字化未来!

DragonOS的目标是,构建一个完全独立自主的、开源的、高性能及高可靠性的服务器操作系统,为国家数字基础设施建设提供完全独立自主的底层核心动力。

作为一个社区驱动的开源操作系统,为了促进开源社区建设,并避免让其遭受一些不遵守开源协议的商业公司的侵权,我们决定使用GPLv2协议开放源代码,以严格的开源协议来保护DragonOS。

你可能对DragonOS中已经实现了哪些功能感兴趣,您可以转到这里:功能特性

DragonOS的功能

规范

  • 启动引导:Multiboot2

  • 接口:posix 2008

内核层

内存管理
  • 页帧分配器

  • 小对象分配器

  • VMA

  • MMIO地址空间自动分配

  • 页面映射器

  • 硬件抽象层

  • 独立的用户地址空间管理机制

  • C接口兼容层

多核
  • 多核引导

  • ipi框架

进程管理
  • 进程创建

  • 进程回收

  • 内核线程

  • fork

  • exec

  • 进程睡眠(支持高精度睡眠)

  • kthread机制

  • 可扩展二进制加载器

同步原语
  • mutex互斥量

  • semaphore信号量

  • atomic原子变量

  • spinlock自旋锁

  • wait_queue等待队列

调度
  • CFS调度器

  • 实时调度器(FIFO、RR)

  • 单核调度

  • 多核调度

  • 负载均衡

IPC
  • 匿名pipe管道

  • signal信号

文件系统
  • VFS

  • fat12/16/32

  • Devfs

  • RamFS

  • Procfs

  • Sysfs

异常及中断处理
  • APIC

  • softirq 软中断

  • 内核栈traceback

内核数据结构
  • 普通二叉树

  • kfifo缓冲区

  • 循环链表

  • IDR

内核实用库
  • LZ4压缩库(1.9.3)

  • 字符串操作库

  • ELF可执行文件支持

  • printk

  • 基础数学库

  • 屏幕管理器

  • textui框架

  • CRC函数库

  • 通知链

系统调用

  请见系统调用文档

测试框架
  • ktest

驱动程序
  • ACPI 高级电源配置模块

  • IDE硬盘

  • AHCI硬盘

  • PCI、PCIe总线

  • XHCI(usb3.0)

  • ps/2 键盘

  • ps/2 鼠标

  • HPET高精度定时器

  • RTC时钟

  • local apic定时器

  • UART串口

  • VBE显示

  • VirtIO网卡

  • x87FPU

  • TTY终端

  • 浮点处理器

用户层

LibC
  • 基础系统调用

  • 基础标准库函数

  • 部分数学函数

shell命令行程序
  • 基于字符串匹配的解析

  • 基本的几个命令

Http Server
  • 使用C编写的简单的Http Server,能够运行静态网站。

软件移植

构建DragonOS

1.写在前面

  无论您采用后文中的何种方式来编译DragonOS,您必须先按照本小节中的步骤,初始化您的开发环境。

  开始之前,您需要一台运行Linux或MacOS的计算机,并且处理器架构为X86-64.

  对于Linux发行版,建议使用Ubuntu22、Debian、Arch Linux这样的,仓库软件版本较新的发行版,这能为您减少很多麻烦。

1.1 下载DragonOS的源代码

  假设您的计算机上已经安装了git,您可以通过以下命令,获得DragonOS的最新的源代码:

git clone https://github.com/DragonOS-Community/DragonOS
cd DragonOS

警告

DragonOS的源代码托管在Github上,但是,由于Github在国内的访问速度较慢。可能出现克隆失败的情况,这时只要重试即可。

当然,我们建议您在github上绑定您的电脑的ssh公钥, 然后通过以下命令来克隆代码,防止频繁出现git clone、pull、push失败的情况。

git clone git@github.com:DragonOS-Community/DragonOS.git

2.使用一键初始化脚本进行安装(推荐)

  我们提供了一键初始化脚本,可以一键安装,只需要在控制台运行以下命令:

cd tools
bash bootstrap.sh  # 这里请不要加上sudo, 因为需要安装的开发依赖包是安装在用户环境而非全局环境

备注

一键配置脚本目前只支持以下系统:

  • Ubuntu/Debian/Deepin/UOS 等基于Debian的衍生版本

欢迎您为其他的系统完善构建脚本!

如果一键初始化脚本能够正常运行,并输出最终的“祝贺”界面(如下所示),请关闭当前终端,然后重新打开。

|-----------Congratulations!---------------|
|                                          |
|   你成功安装了DragonOS所需的依赖项!          |
|                                          |
|   请关闭当前终端, 并重新打开一个终端          |
|   然后通过以下命令运行:                     |
|                                          |
|                make run                  |
|                                          |
|------------------------------------------|

接着,请直接跳到编译命令讲解进行阅读!

3.手动安装

3.1 依赖清单

  如果自动安装脚本不能支持您的操作系统,那么您需要手动安装依赖程序。以下是依赖项的清单:

  在以下依赖项中,除了docker-ceRust及其工具链以外,其他的都能通过系统自带的包管理器进行安装。关于docker以及rust的安装,请看后文。

  • docker-ce

  • llvm-dev

  • libclang-dev

  • clang

  • gcc-multilib

  • qemu qemu-system qemu-kvm

  • build-essential

  • fdisk

  • lsb-release

  • git

  • dosfstools

  • Rust以及其工具链

请留意,若您的Linux系统是在虚拟机中运行的,还请您在您的VMware/Virtual Box虚拟机的处理器设置选项卡中,开启Intel VT-x或AMD-V选项,否则,DragonOS将无法运行。

备注

在某些Linux发行版的软件仓库中构建的Qemu可能由于版本过低而不兼容DragonOS,如果遇到这种问题,请卸载Qemu,并采用编译安装的方式重新安装Qemu

在该地址下载Qemu源代码: https://download.qemu.org/

解压后进入源代码目录,然后执行下列命令:

# 安装编译依赖项
sudo apt install -y autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev \
              gawk build-essential bison flex texinfo gperf libtool patchutils bc \
              zlib1g-dev libexpat-dev pkg-config  libglib2.0-dev libpixman-1-dev libsdl2-dev \
              git tmux python3 python3-pip ninja-build

./configure --enable-kvm
make -j 8
sudo make install
# 编译安装完成

请注意,编译安装的QEMU,将通过VNC模式进行链接,因此,您还需要在您的计算机上安装VNC viewer以连接至QEMU虚拟机。

3.2 安装Docker

  您可以在docker官网下载安装docker-ce.

详细信息请转到: https://docs.docker.com/engine/install/

3.3 安装Rust

警告

【常见误区】:如果您打算采用docker进行编译,尽管docker镜像中已经安装了Rust编译环境,但是,为了能够在VSCode中使用Rust-Analyzer进行代码提示,以及make clean命令能正常运行,您的客户机上仍然需要安装rust环境。

  您可以在控制台输入以下命令,安装rust。

# 这两行用于换源,加速Rust的安装过程
export RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static
export RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup
# 安装Rust
curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly
# 把Rustup加到环境变量
echo "export PATH=\"\$HOME/.cargo/bin:\$PATH\"" >> ~/.bashrc
source ~/.cargo/env
source "$HOME/.cargo/env"

# 更换cargo的索引源
touch ~/.cargo/config
echo -e "[source.crates-io]   \n \
registry = \"https://github.com/rust-lang/crates.io-index\"  \n \
\n \
replace-with = 'dragonos-gitee' \n \
[source.dragonos-gitee] \n \
registry = \"https://gitee.com/DragonOS/crates.io-index.git\"	 \n \
" > ~/.cargo/config

# 安装DragonOS所需的工具链
cargo install cargo-binutils
rustup toolchain install nightly
rustup default nightly
rustup component add rust-src
rustup component add llvm-tools-preview
rustup target add x86_64-unknown-none
# Rust安装完成

至此,公共依赖项已经安装完成,您可以根据自己的需要,阅读后续章节

关于编译命令的用法,请见:编译命令讲解

4.从Docker构建(不推荐)

  DragonOS发布了一个Docker编译环境,便于开发者运行DragonOS。但是,由于编码过程仍需要在客户机上进行,因此,您需要在客户机上安装Rust编译环境。

  本节假设以下操作均在Linux下进行。

4.1 安装qemu虚拟机

  在本节中,我们建议您采用命令行安装qemu:

sudo apt install -y qemu qemu-system qemu-kvm

4.2 创建磁盘镜像

  首先,您需要使用tools文件夹下的create_hdd_image.sh,创建一块虚拟磁盘镜像。您需要在tools文件夹下运行此命令。

bash create_hdd_image.sh

4.3 运行DragonOS

  如果不出意外的话,这将是运行DragonOS的最后一步。您只需要在DragonOS的根目录下方,执行以下命令,即可运行DragonOS。

make run-docker

  稍等片刻,DragonOS将会被运行。

  在qemu虚拟机被启动后,我们需要在控制台输入字母c,然后回车。这样,虚拟机就会开始执行。

备注

  1. 首次编译时,由于需要下载Rust相关的索引(几百MB大小),因此需要一定的时间,请耐心等候!

  2. 输入命令可能需要加上sudo

关于编译命令的用法,请见:编译命令讲解

5.其他注意事项

5.1 创建磁盘镜像

  首先,您需要使用普通用户权限运行tools/create_hdd_image.sh,为DragonOS创建一块磁盘镜像文件。该脚本会自动完成创建磁盘镜像的工作,并将其移动到bin/目录下。   请注意,由于权限问题,请务必使用普通用户权限运行此脚本。(运行后,需要提升权限时,系统可能会要求您输入密码)

5.2 编译、运行DragonOS

  1. 安装编译及运行环境

  2. 进入DragonOS文件夹

  3. 输入make run即可编译并写入磁盘镜像,并运行

  在qemu虚拟机被启动后,我们需要在控制台输入字母c,然后回车。这样,虚拟机就会开始执行。

备注

首次编译时,由于需要下载Rust相关的索引(几百MB大小),因此需要一定的时间,请耐心等候!

关于编译命令的用法,请见:编译命令讲解

6.编译命令讲解

  • 本地编译,不运行: make all -j 您的CPU核心数

  • 本地编译,并写入磁盘镜像,不运行: make build

  • 本地编译,写入磁盘镜像,并在QEMU中运行: make run

  • Docker编译,并写入磁盘镜像,: make docker

  • Docker编译,写入磁盘镜像,并在QEMU中运行: make run-docker

  • 不编译,直接从已有的磁盘镜像启动: make qemu

  • 清理编译产生的文件: make clean

  • 编译文档: make docs (需要手动安装sphinx以及docs下的requirements.txt中的依赖)

  • 清理文档: make clean-docs

备注

如果您需要在vnc中运行DragonOS,请在上述命令后加上-vnc后缀。如:make run-vnc

DragonOS镜像站

您可以从以下镜像站下载到DragonOS的源代码和其他文件:

引导加载

DragonOS采用GRUB2作为其引导加载程序,支持Multiboot2协议引导。目前仅支持GRUB2.06版本。

引导加载程序

原理

  目前,DragonOS支持Legacy BIOS以及UEFI两种方式,进行启动引导。

  在head.S的头部包含了Multiboot2引导头,里面标志了一些Multiboot2相关的特定信息,以及一些配置命令。

  在DragonOS的启动初期,会存储由GRUB2传来的magic number以及multiboot2_boot_info_addr。当系统进入Start_Kernel函数之后,将会把这两个信息保存到multiboot2驱动程序之中。信息的具体含义请参照Multiboot2 Specification进行理解,该部分难度不大,相信读者经过思考能理解其中的原理。

参考资料

Multiboot2支持模块

  Multiboot2支持模块提供对Multiboot2协议的支持。位于kernel/driver/multiboot2文件夹中。

  根据Multiboot2协议,操作系统能够从BootLoader处获得一些信息,比如基本的硬件信息以及ACPI表的起始地址等。


数据结构

  kernel/driver/multiboot2/multiboot2.h中按照Multiboot2协议的规定,定义了大部分的数据结构,具体细节可查看该文件: DragonOS/multiboot2.h at master · fslongjin/DragonOS · GitHub


迭代器

  由于Multiboot2的信息存储在自multiboot2_boot_info_addr开始的一段连续的内存空间之中,且不同类型的header的长度不同,因此设计了一迭代器multiboot2_iter

函数原型
void multiboot2_iter(bool (*_fun)(const struct iter_data_t *, void *, unsigned int *),
                     void *data, unsigned int *count)

_fun

  指定的handler。当某个header的tag与该handler所处理的tag相同时,handler将处理该header,并返回true。

  其第一个参数为tag类型,第二个参数为返回的数据的指针,第三个值为计数(某些没有用到该值的地方,该值可以为空)

data

  传递给_fun的第二个参数,_fun函数将填充该指针所指向的内存区域,从而返回数据。

count

  当返回的data为一个列表时,通过该值来指示列表中有多少项。


迭代工作函数

  在模块中,按照我们需要获取不同类型的tag的需要,定义了一些迭代器工作函数。

核心API文档

这里是DragonOS的核心api文档。

DragonOS内核核心API

循环链表管理函数

  循环链表是内核的重要的数据结构之一。包含在kernel/common/list.h中。

void list_init(struct List *list)
描述

  初始化一个List结构体,使其prev和next指针指向自身

参数

list

  要被初始化的List结构体

void list_add(struct List *entry, struct List *node)
描述

  将node插入到entry的后方

参数

entry

  已存在于循环链表中的一个结点

node

  待插入的结点

void list_append(struct List *entry, struct List *node)
描述

  将node插入到entry的前方

参数

entry

  已存在于循环链表中的一个结点

node

  待插入的结点

void list_del(struct List *entry)
描述

  从链表中删除结点entry

参数

entry

  待删除的结点

list_del_init(struct List *entry)
描述

  从链表中删除结点entry,并将这个entry使用list_init()进行重新初始化。

参数

entry

  待删除的结点

bool list_empty(struct List *entry)
描述

  判断链表是否为空

参数

entry

  链表中的一个结点

struct List *list_prev(struct List *entry)
描述

  获取entry的前一个结点

参数

entry

  链表中的一个结点

struct List *list_next(struct List *entry)
描述

  获取entry的后一个结点

参数

entry

  链表中的一个结点

void list_replace(struct List *old, struct List *new)
描述

  将链表中的old结点替换成new结点

参数

old

  要被换下来的结点

new

  要被换入链表的新的结点

list_entry(ptr, type, member)
描述

  该宏能通过ptr指向的List获取到List所处的结构体的地址

参数

ptr

  指向List结构体的指针

type

  要被换入链表的新的结点

member

  List结构体在上述的“包裹list结构体的结构体”中的变量名

list_first_entry(ptr, type, member)
描述

  获取链表中的第一个元素。请注意,该宏要求链表非空,否则会出错。

参数

  与list_entry()相同

list_first_entry_or_null(ptr, type, member)
描述

  获取链表中的第一个元素。若链表为空,则返回NULL。

参数

  与list_entry()相同

list_last_entry(ptr, type, member)
描述

  获取链表中的最后一个元素。请注意,该宏要求链表非空,否则会出错。

参数

  与list_entry()相同

list_last_entry_or_full(ptr, type, member)
描述

  获取链表中的最后一个元素。若链表为空,则返回NULL。

参数

  与list_entry()相同

list_next_entry(pos, member)
描述

  获取链表中的下一个元素

参数

pos

  指向当前的外层结构体的指针

member

  链表结构体在外层结构体内的变量名

list_prev_entry(pos, member)
描述

  获取链表中的上一个元素

参数

  与list_next_entry()相同

list_for_each(ptr, head)
描述

  遍历整个链表(从前往后)

参数

ptr

  指向List结构体的指针

head

  指向链表头结点的指针(struct List*)

list_for_each_prev(ptr, head)
描述

  遍历整个链表(从后往前)

参数

  与list_for_each()相同

list_for_each_safe(ptr, n, head)
描述

  从前往后遍历整个链表(支持删除当前链表结点)

  该宏通过暂存中间变量,防止在迭代链表的过程中,由于删除了当前ptr所指向的链表结点从而造成错误.

参数

ptr

  指向List结构体的指针

n

  用于存储临时值的List类型的指针

head

  指向链表头结点的指针(struct List*)

list_for_each_prev_safe(ptr, n, head)
描述

  从后往前遍历整个链表.(支持删除当前链表结点)

  该宏通过暂存中间变量,防止在迭代链表的过程中,由于删除了当前ptr所指向的链表结点从而造成错误.

参数

  与list_for_each_safe()相同

list_for_each_entry(pos, head, member)
描述

  从头开始迭代给定类型的链表

参数

pos

  指向特定类型的结构体的指针

head

  指向链表头结点的指针(struct List*)

member

  struct List在pos的结构体中的成员变量名

list_for_each_entry_reverse(pos, head, member)
描述

  逆序迭代给定类型的链表

参数

  与list_for_each_entry()相同

list_for_each_entry_safe(pos, n, head, member)
描述

  从头开始迭代给定类型的链表(支持删除当前链表结点)

参数

pos

  指向特定类型的结构体的指针

n

  用于存储临时值的,和pos相同类型的指针

head

  指向链表头结点的指针(struct List*)

member

  struct List在pos的结构体中的成员变量名

list_prepare_entry(pos, head, member)
描述

  为list_for_each_entry_continue()准备一个’pos’结构体

参数

pos

  指向特定类型的结构体的,用作迭代起点的指针

head

  指向要开始迭代的struct List结构体的指针

member

  struct List在pos的结构体中的成员变量名

list_for_each_entry_continue(pos, head, member)
描述

  从指定的位置的【下一个元素开始】,继续迭代给定的链表

参数

pos

  指向特定类型的结构体的指针。该指针用作迭代的指针。

head

  指向要开始迭代的struct List结构体的指针

member

  struct List在pos的结构体中的成员变量名

list_for_each_entry_continue_reverse(pos, head, member)
描述

  从指定的位置的【上一个元素开始】,【逆序】迭代给定的链表

参数

  与list_for_each_entry_continue()的相同

list_for_each_entry_from(pos, head, member)
描述

  从指定的位置开始,继续迭代给定的链表

参数

  与list_for_each_entry_continue()的相同

list_for_each_entry_safe_continue(pos, n, head, member)
描述

  从指定的位置的【下一个元素开始】,继续迭代给定的链表.(支持删除当前链表结点)

参数

pos

  指向特定类型的结构体的指针。该指针用作迭代的指针。

n

  用于存储临时值的,和pos相同类型的指针

head

  指向要开始迭代的struct List结构体的指针

member

  struct List在pos的结构体中的成员变量名

list_for_each_entry_safe_continue_reverse(pos, n, head, member)
描述

  从指定的位置的【上一个元素开始】,【逆序】迭代给定的链表。(支持删除当前链表结点)

参数

  与list_for_each_entry_safe_continue()的相同

list_for_each_entry_safe_from(pos, n, head, member)
描述

  从指定的位置开始,继续迭代给定的链表.(支持删除当前链表结点)

参数

  与list_for_each_entry_safe_continue()的相同


基础C函数库

  内核编程与应用层编程不同,你将无法使用LibC中的函数来进行编程。为此,内核实现了一些常用的C语言函数,并尽量使其与标准C库中的函数行为相近。值得注意的是,这些函数的行为可能与标准C库函数不同,请在使用时仔细阅读以下文档,这将会为你带来帮助。

字符串操作
int strlen(const char *s)
描述

  测量并返回字符串长度。

参数

src

  源字符串

long strnlen(const char *src, unsigned long maxlen)
描述

  测量并返回字符串长度。当字符串长度大于maxlen时,返回maxlen

参数

src

  源字符串

maxlen

  最大长度

long strnlen_user(const char *src, unsigned long maxlen)
描述

  测量并返回字符串长度。当字符串长度大于maxlen时,返回maxlen。

  该函数会进行地址空间校验,要求src字符串必须来自用户空间。当源字符串来自内核空间时,将返回0.

参数

src

  源字符串,地址位于用户空间

maxlen

  最大长度

char *strncpy(char *dst, const char *src, long count)
描述

  拷贝长度为count个字节的字符串,返回dst字符串

参数

src

  源字符串

dst

  目标字符串

count

  要拷贝的源字符串的长度

char *strcpy(char *dst, const char *src)
描述

  拷贝源字符串,返回dst字符串

参数

src

  源字符串

dst

  目标字符串

long strncpy_from_user(char *dst, const char *src, unsigned long size)
描述

  从用户空间拷贝长度为count个字节的字符串到内核空间,返回拷贝的字符串的大小

  该函数会对字符串的地址空间进行校验,防止出现地址空间越界的问题。

参数

src

  源字符串

dst

  目标字符串

size

  要拷贝的源字符串的长度

int strcmp(char *FirstPart, char *SecondPart)
描述

比较两个字符串的大小。

返回值

情况

返回值

FirstPart == SecondPart

0

FirstPart > SecondPart

1

FirstPart < SecondPart

-1

参数

FirstPart

  第一个字符串

SecondPart

  第二个字符串

printk(const char* fmt, ...)
描述

  该宏能够在控制台上以黑底白字格式化输出字符串.

参数

fmt

  源格式字符串

  可变参数

printk_color(unsigned int FRcolor, unsigned int BKcolor, const char* fmt, ...)
描述

  在控制台上以指定前景色和背景色格式化输出字符串.

参数

FRcolor

  前景色

BKcolor

  背景色

fmt

  源格式字符串

  可变参数

int vsprintf(char *buf, const char *fmt, va_list args)
描述

  按照fmt格式化字符串,并将结果输出到buf中,返回写入buf的字符数量。

参数

buf

  输出缓冲区

fmt

  源格式字符串

args

  可变参数列表

int sprintk(char *buf, const char *fmt, ...)
描述

  按照fmt格式化字符串,并将结果输出到buf中,返回写入buf的字符数量。

参数

buf

  输出缓冲区

fmt

  源格式字符串

  可变参数

内存操作
void *memcpy(void *dst, const void *src, uint64_t size)
描述

  将内存从src处拷贝到dst处。

参数

dst

  指向目标地址的指针

src

  指向源地址的指针

size

  待拷贝的数据大小

void *memmove(void *dst, const void *src, uint64_t size)
描述

  与memcpy()类似,但是在源数据区域与目标数据区域之间存在重合时,该函数能防止数据被错误的覆盖。

参数

dst

  指向目标地址的指针

src

  指向源地址的指针

size

  待拷贝的数据大小

CRC函数

函数列表

uint8_t crc7(uint8_t crc, const uint8_t *buffer, size_t len)

uint8_t crc8(uint8_t crc, const uint8_t *buffer, size_t len)

uint16_t crc16(uint16_t crc, uint8_t const *buffer, size_t len)

uint32_t crc32(uint32_t crc, uint8_t const *buffer, size_t len)

uint64_t crc64(uint64_t crc, uint8_t const *buffer, size_t len)

描述

  用于计算循环冗余校验码

参数说明

crc

  传入的CRC初始值

buffer

  待处理的数据缓冲区

len

  缓冲区大小(字节)

原子变量

简介

  DragonOS实现了原子变量,类型为atomic_t. 原子变量是基于具体体系结构的原子操作指令实现的。具体实现在kernel/common/atomic.h中。

API

   请注意,以下API均为原子操作。

inline void atomic_add(atomic_t *ato, long val)
描述

  原子变量增加指定值

参数

ato

  原子变量对象

val

  变量要增加的值

inline void atomic_sub(atomic_t *ato, long val)
描述

  原子变量减去指定值

参数

ato

  原子变量对象

val

  变量要被减去的值

void atomic_inc(atomic_t *ato)
描述

  原子变量自增1

参数

ato

  原子变量对象

void atomic_dec(atomic_t *ato)
描述

  原子变量自减1

参数

ato

  原子变量对象

inline void atomic_set_mask(atomic_t *ato, long mask)
描述

  将原子变量的值与mask变量进行or运算

参数

ato

  原子变量对象

mask

  与原子变量进行or运算的变量

inline void atomic_clear_mask(atomic_t *ato, long mask)
描述

  将原子变量的值与mask变量进行and运算

参数

ato

  原子变量对象

mask

  与原子变量进行and运算的变量

内核数据结构

  内核中实现了常用的几种数据结构,这里是他们的api文档。


kfifo先进先出缓冲区

  kfifo先进先出缓冲区定义于common/kfifo.h中。您可以使用它,创建指定大小的fifo缓冲区(最大大小为4GB)

kfifo_alloc

int kfifo_alloc(struct kfifo_t *fifo, uint32_t size, uint64_t reserved)

描述

  通过动态方式初始化kfifo缓冲队列。fifo缓冲区的buffer将由该函数进行申请。

参数

fifo

  kfifo队列结构体的指针

size

  缓冲区大小(单位:bytes)

reserved

  当前字段保留,请将其置为0

返回值

  当返回值为0时,表示正常初始化成功,否则返回对应的errno

kfifo_init

void kfifo_init(struct kfifo_t *fifo, void *buffer, uint32_t size)

描述

  使用指定的缓冲区来初始化kfifo缓冲队列

参数

fifo

  kfifo队列结构体的指针

buffer

  缓冲区基地址指针

size

  缓冲区大小(单位:bytes)

kfifo_free_alloc

void kfifo_free_alloc(struct kfifo_t* fifo)

描述

  释放通过kfifo_alloc创建的fifo缓冲区. 请勿通过该函数释放其他方式创建的kfifo缓冲区。

参数

fifo

  kfifo队列结构体的指针

kfifo_in

uint32_t kfifo_in(struct kfifo_t *fifo, const void *from, uint32_t size)

描述

  向kfifo缓冲区推入指定大小的数据。当队列中空间不足时,则不推入数据。

参数

fifo

  kfifo队列结构体的指针

from

  源数据基地址指针

size

  数据大小(单位:bytes)

返回值

  返回成功被推入的数据的大小。

kfifo_out

uint32_t kfifo_out(struct kfifo_t *fifo, void *to, uint32_t size)

描述

  从kfifo缓冲区取出数据,并从队列中删除数据。当队列中数据量不足时,则不取出。

参数

fifo

  kfifo队列结构体的指针

to

  目标缓冲区基地址指针

size

  数据大小(单位:bytes)

返回值

  返回成功被取出的数据的大小。

kfifo_out_peek

uint32_t kfifo_out_peek(struct kfifo_t *fifo, void *to, uint32_t size)

描述

  从kfifo缓冲区取出数据,但是不从队列中删除数据。当队列中数据量不足时,则不取出。

参数

fifo

  kfifo队列结构体的指针

to

  目标缓冲区基地址指针

size

  数据大小(单位:bytes)

返回值

  返回成功被取出的数据的大小。

kfifo_reset

kfifo_reset(fifo)

描述

  忽略kfifo队列中的所有内容,并把输入和输出偏移量都归零

参数

fifo

  kfifo队列结构体的指针

kfifo_reset_out

kfifo_reset_out(fifo)

描述

  忽略kfifo队列中的所有内容,并将输入偏移量赋值给输出偏移量

参数

fifo

  kfifo队列结构体的指针

kfifo_total_size

kfifo_total_size(fifo)

描述

  获取kfifo缓冲区的最大大小

参数

fifo

  kfifo队列结构体的指针

返回值

  缓冲区最大大小

kfifo_size

kfifo_size(fifo)

描述

  获取kfifo缓冲区当前已使用的大小

参数

fifo

  kfifo队列结构体的指针

返回值

  缓冲区当前已使用的大小

kfifo_empty

kfifo_empty(fifo)

描述

  判断kfifo缓冲区当前是否为空

参数

fifo

  kfifo队列结构体的指针

返回值

情况

返回值

1

非空

0

kfifo_full

kfifo_full(fifo)

描述

  判断kfifo缓冲区当前是否为满

参数

fifo

  kfifo队列结构体的指针

返回值

情况

返回值

1

不满

0


ID Allocation

   ida的主要作用是分配+管理id. 它能分配一个最小的, 未被分配出去的id. 当您需要管理某个数据结构时, 可能需要使用id来区分不同的目标. 这个时候, ida将会是很好的选择. 因为ida的十分高效, 运行常数相对数组更小, 而且提供了基本管理id需要用到的功能, 值得您试一试.

  IDA定义于idr.h文件中. 您通过DECLARE_IDA(my_ida)来创建一个ida对象, 或者struct ida my_ida; ida_init(&my_ida);来初始化一个ida.

ida_init

void ida_init(struct ida *ida_p)

描述

  通初始化IDA, 你需要保证调用函数之前, ida的free_list为空, 否则会导致内存泄漏.

参数

ida_p

   指向ida的指针

返回值

  无返回值

ida_preload

int ida_preload(struct ida *ida_p, gfp_t gfp_mask)

描述

  为ida预分配空间.您可以不自行调用, 因为当ida需要空间的时候, 内部会自行使用kmalloc函数获取空间. 当然, 设计这个函数的目的是为了让您有更多的选择. 当您提前调用这个函数, 可以避免之后在开辟空间上的时间开销.

参数

ida_p

   指向ida的指针

gfp_mask

   保留参数, 目前尚未使用.

返回值

  如果分配成功,将返回0; 否则返回负数错误码, 有可能是内存空间不够.

ida_alloc

int ida_alloc(struct ida *ida_p, int *p_id)

描述

  获取一个空闲ID. 您需要注意, 返回值是成功/错误码.

参数

ida_p

   指向ida的指针

p_id

   您需要传入一个int变量的指针, 如果成功分配ID, ID将会存储在该指针所指向的地址.

返回值

  如果分配成功,将返回0; 否则返回负数错误码, 有可能是内存空间不够.

ida_count

bool ida_count(struct ida *ida_p, int id)

描述

  查询一个ID是否被分配.

参数

ida_p

   指向ida的指针

id

   您查询该ID是否被分配.

返回值

  如果分配,将返回true; 否则返回false.

ida_remove

void ida_remove(struct ida *ida_p, int id)

描述

  删除一个已经分配的ID. 如果该ID不存在, 该函数不会产生异常错误, 因为在检测到该ID不存在的时候, 函数将会自动退出.

参数

ida_p

   指向ida的指针

id

   您要删除的id.

返回值

  无返回值.

ida_destroy

void ida_destroy(struct ida *ida_p)

描述

  释放一个IDA所有的空间, 同时删除ida的所有已经分配的id.(所以您不用担心删除id之后, ida还会占用大量空间.)

参数

ida_p

   指向ida的指针

返回值

  无返回值

ida_empty

void ida_empty(struct ida *ida_p)

描述

   查询一个ida是否为空

参数

ida_p

   指向ida的指针

返回值

  ida为空则返回true,否则返回false。


IDR

   idr是一个基于radix-tree的ID-pointer的数据结构. 该数据结构提供了建id与数据指针绑定的功能, 它的主要功能有以下4个:

  1. 获取一个ID, 并且将该ID与一个指针绑定

  2. 删除一个已分配的ID

  3. 根据ID查找对应的指针

  4. 根据ID使用新的ptr替换旧的ptr
       您可以使用DECLARE_idr(my_idr)来创建一个idr。或者您也可以使用struct idr my_idr; idr_init(my_idr);这两句话创建一个idr。    至于什么是radix-tree,您可以把他简单理解为一个向上生长的多叉树,在实现中,我们选取了64叉树。

idr_init

void idr_init(struct idr *idp)

描述

  通初始化IDR, 你需要保证调用函数之前, idr的free_list为空, 否则会导致内存泄漏.

参数

idp

   指向idr的指针

返回值

  无返回值

idr_preload

int idr_preload(struct idr *idp, gfp_t gfp_mask)

描述

  为idr预分配空间.您可以不自行调用, 因为当idr需要空间的时候, 内部会自行使用kmalloc函数获取空间. 当然, 设计这个函数的目的是为了让您有更多的选择. 当您提前调用这个函数, 可以避免之后在开辟空间上的时间开销.

参数

idp

   指向idr的指针

gfp_mask

   保留参数, 目前尚未使用.

返回值

  如果分配成功,将返回0; 否则返回负数错误码, 有可能是内存空间不够.

idr_alloc

int idr_alloc(struct idr *idp, void *ptr, int *id)

描述

   获取一个空闲ID. 您需要注意, 返回值是成功/错误码.    调用这个函数,需要您保证ptr是非空的,即: ptr != NULL, 否则将会影响 idr_find/idr_find_next/idr_find_next_getid/...等函数的使用。(具体请看这三个函数的说明,当然,只会影响到您的使用体验,并不会影响到idr内部函数的决策和逻辑)

参数

idp

   指向ida的指针

ptr

   指向数据的指针

id

   您需要传入一个int变量的指针, 如果成功分配ID, ID将会存储在该指针所指向的地址.

返回值

  如果分配成功,将返回0; 否则返回负数错误码, 有可能是内存空间不够.

idr_remove

void* idr_remove(struct idr *idp, int id)

描述

  删除一个id, 但是不释放对应的ptr指向的空间, 同时返回这个被删除id所对应的ptr。    如果该ID不存在, 该函数不会产生异常错误, 因为在检测到该ID不存在的时候, 函数将会自动退出,并返回NULL。

参数

idp

   指向idr的指针

id

   您要删除的id.

返回值

  如果删除成功,就返回被删除id所对应的ptr;否则返回NULL。注意:如果这个id本来就和NULL绑定,那么也会返回NULL

idr_remove_all

void idr_remove_all(struct idr *idp)

描述

  删除idr的所有已经分配的id.(所以您不用担心删除id之后, idr还会占用大量空间。)

   但是你需要注意的是,调用这个函数是不会释放数据指针指向的空间的。 所以您调用该函数之前, 确保IDR内部的数据指针被保存。否则当IDR删除所有ID之后, 将会造成内存泄漏。

参数

idp

   指向idr的指针

返回值

  无返回值

idr_destroy

void idr_destroy(struct idr *idp)

描述

  释放一个IDR所有的空间, 同时删除idr的所有已经分配的id.(所以您不用担心删除id之后, ida还会占用大量空间.) - 和idr_remove_all的区别是, 释放掉所有的空间(包括free_list的预分配空间)。

参数

idp

   指向idr的指针

返回值

  无返回值

idr_find

void *idr_find(struct idr *idp, int id)

描述

  查询一个ID所绑定的数据指针

参数

idp

   指向idr的指针

id

   您查询该ID的数据指针

返回值

   如果分配,将返回该ID对应的数据指针; 否则返回NULL.(注意, 返回NULL不一定代表这ID不存在,有可能该ID就是与空指针绑定。)    当然,我们也提供了idr_count函数来判断id是否被分配,具体请查看idr_count介绍。

idr_find_next

void *idr_find_next(struct idr *idp, int start_id)

描述

  传进一个start_id,返回满足 “id大于start_id的最小id” 所对应的数据指针。

参数

idp

   指向idr的指针

start_id

  您提供的ID限制

返回值

   如果分配,将返回该ID对应的数据指针; 否则返回NULL.(注意, 返回NULL不一定代表这ID不存在,有可能该ID就是与空指针绑定。)    当然,我们也提供了idr_count函数来判断id是否被分配,具体请查看idr_count介绍。

idr_find_next_getid

void *idr_find_next_getid(struct idr *idp, int start_id, int *nextid)

描述

  传进一个start_id,返回满足 “id大于start_id的最小id” 所对应的数据指针。同时,你获取到这个满足条件的最小id, 即参数中的 *nextid。

参数

idp

   指向idr的指针

start_id

   您提供的ID限制

返回值

   如果分配,将返回该ID对应的数据指针; 否则返回NULL.(注意, 返回NULL不一定代表这ID不存在,有可能该ID就是与空指针绑定。)    当然,我们也提供了idr_count函数来判断id是否被分配,具体请查看idr_count介绍。

idr_replace

int idr_replace(struct idr *idp, void *ptr, int id)

描述

  传进一个ptr,使用该ptr替换掉id所对应的Old_ptr。

参数

idp

   指向idr的指针

ptr

  您要替换原来的old_ptr的新指针

id

   您要替换的指针所对应的id

返回值

   0代表成功,否则就是错误码 - 代表错误。

idr_replace_get_old

int idr_replace_get_old(struct idr *idp, void *ptr, int id, void **oldptr)

描述

  传进一个ptr,使用该ptr替换掉id所对应的Old_ptr,同时你可以获取到old_ptr。

参数

idp

   指向idr的指针

ptr

  您要替换原来的old_ptr的新指针

id

   您要替换的指针所对应的id

old_ptr

   您需要传进该(void**)指针,old_ptr将会存放在该指针所指向的地址。

返回值

   0代表成功,否则就是错误码 - 代表错误。

idr_empty

void idr_empty(struct idr *idp)

描述

   查询一个idr是否为空

参数

idp

   指向idr的指针

返回值

  idr为空则返回true,否则返回false。

idr_count

bool idr_count(struct idr *idp, int id)

描述

  查询一个ID是否被分配.

参数

ida_p

   指向idr的指针

id

   您查询该ID是否被分配.

返回值

  如果分配,将返回true; 否则返回false.

类型转换库API

  内核提供了一些函数来帮助你在不同的类型之间进行转换。包括以下类型:

  • 数值类型转换 (使用num-traits库)

  • Arc类型转换

  上述没有特殊标明的函数,都是在kernel/src/libs/casting.rs中实现的。

1. 数值类型转换

1.1. 整数类型与枚举类型之间的转换

  您可以使用num-traits库提供的宏,实现枚举类型和整数类型之间的转换。 SystemError枚举类型使用了这种方式,您可以在kernel/src/syscall/mod.rs中找到它的用法。

  它首先继承了FromPrimitive, ToPrimitive两个trait,然后这样转换:

impl SystemError {
    /// @brief 把posix错误码转换为系统错误枚举类型。
    pub fn from_posix_errno(errno: i32) -> Option<SystemError> {
        // posix 错误码是小于0的
        if errno >= 0 {
            return None;
        }
        return <Self as FromPrimitive>::from_i32(-errno);
    }

    /// @brief 把系统错误枚举类型转换为负数posix错误码。
    pub fn to_posix_errno(&self) -> i32 {
        return -<Self as ToPrimitive>::to_i32(self).unwrap();
    }
}

  这两个函数很好的说明了如何使用这两个trait。

2. Arc类型转换

2.1 从Arc转换为Arc

  当我们需要把一个Arc<dyn U>转换为Arc<T>的具体类型指针时,我们要为U这个trait实现DowncastArctrait。这个trait定义在kernel/src/libs/casting.rs中。它要求trait U实现Any + Sync + Sendtrait.

  为trait U: Any + Send + Sync实现DowncastArctrait,需要这样做:

impl DowncastArc for dyn U {
    fn as_any_arc(self: Arc<Self>) -> Arc<dyn Any> {
        return self;
    }
}

  使用DowncastArctrait,我们可以这样转换:

let arc: Arc<dyn U> = ...;
let arc_t: Arc<T> = arc.downcast_arc::<T>().unwrap();

  如果arc的具体类型不是Arc<T>,那么downcast_arc::<T>()会返回None

Notifier Chian 通知链

1.原理概要

  通知链是内核中各个子系统之间或子系统内部各个模块之间的事件通知机制。通知链本质上是一个事件处理函数的列表,每个通知链都与某个类型的事件相关(例如 reboot 事件)。当特定的事件发生时,就会调用相应事件的通知链中的回调函数,使子系统/模块对事件响应,并进行相对应的处理。

  通知链和订阅功能有些类似,可以理解为:有个“通知者”维护了一个列表,“订阅者”将自己的回调函数注册进这个列表(“订阅者”当然也可以注销自己的回调函数)。当某个事件发生需要通知时,“通知者”就遍历这个列表中所有的回调函数并进行调用,这样所有注册的“订阅者”就能针对这个事件做出自己的响应和处理。

2.核心功能

2.1 注册回调函数

  将回调函数封装成特定的结构体,并将该结构体注册到指定的通知链当中。相关方法为 register,由“订阅者”使用。

2.2 注销回调函数

  将回调函数从指定的通知链当中进行注销,即从通知链中删去该回调函数。相关方法为 unregister,由“订阅者”使用。

2.3 事件通知

  当某个事件发生时,该事件相关的通知链通过该方法来进行事件通知。call_chain 这个方法会遍历通知链中的所有元素,并依次调用注册的回调函数。该方法由“通知者”使用。

3.通知链类型

  每种通知链都有相对应的 registerunregister 以及 call_chain 的接口,其功能同上面所述的核心功能。

  • AtomicNotifierChain:原子的通知链,不可睡眠,建议用于中断上下文。

  • BlockingNotifierChain:可阻塞的通知链,可以睡眠,建议用在进程上下文。

  • RawNotifierChain:原始的通知链,由调用者自行考虑线程安全。

4. 其它问题

  BlockingNotifierChain 暂时没实现可睡眠的功能。

软中断

  软件中断,也可以被称为中断的下半部,用于延迟处理硬中断(中断上半部)未完成的工作。将中断分为两个阶段可以有效解决中断处理时间过长和中断丢失的问题。

1. 设计思路

  每个cpu都有自己的pending,软中断是“哪个cpu发起,就哪个cpu执行”,每个cpu的pending不共享。同一个软中断向量可以在多核上同时运行。

  当我们需要注册一个新的软中断时,需要为软中断处理程序实现SoftirqVec特征,然后调用register_softirq函数,将软中断处理程序注册到软中断机制内。

  请注意,由于软中断的可重入、可并发性,所以软中断处理程序需要自己保证线程安全。

2. 软中断向量号

pub enum SoftirqNumber {
    /// 时钟软中断信号
    TIMER = 0,
    /// 帧缓冲区刷新软中断
    VideoRefresh = 1, 
}

3. 软中断API

3.1. SoftirqVec特征
pub trait SoftirqVec: Send + Sync + Debug {
    fn run(&self);
}

  软中断处理程序需要实现的特征,需要实现run函数,用于处理软中断。当软中断被执行时,会调用run函数。

3.2. Softirq的API
3.2.1. 注册软中断向量
pub fn register_softirq(&self,
        softirq_num: SoftirqNumber,
        handler: Arc<dyn SoftirqVec>,
    ) -> Result<i32, SystemError>
  • 参数:

    • softirq_num:中断向量号

    • hanlder:中断函数对应的结构体,需要指向实现了SoftirqVec特征的结构体变量

  • 返回:

    • Ok(i32):0

    • Err(SystemError):错误码

3.2.2. 解注册软中断向量
pub fn unregister_softirq(&self, softirq_num: SoftirqNumber)
  • 参数:

    • softirq_num:中断向量号

3.2.3. 软中断执行
pub fn do_softirq(&self)
  • 作用:执行软中断函数(只在硬中断执行后调用

3.2.4. 清除软中断的pending标志
pub unsafe fn clear_softirq_pending(&self, softirq_num: SoftirqNumber)
  • 作用:清除当前CPU上,指定软中断的pending标志。请注意,这个函数是unsafe的,因为它会直接修改pending标志,而没有加锁。

  • 参数:

    • softirq_num:中断向量号

3.2.5. 标志软中断需要执行
pub fn raise_softirq(&self, softirq_num: SoftirqNumber)
  • 作用:标志当前CPU上,指定的软中断需要执行

  • 参数:

    • softirq_num:中断向量号

3.3. 使用实例
#[derive(Debug)]
/// SoftirqExample中断结构体
pub struct SoftirqExample {
    running: AtomicBool,
}
/// SoftirqExample中断需要处理的逻辑
fn softirq_example_func() {
    println!("addressed SoftirqExample");
}
impl SoftirqVec for SoftirqExample {
    fn run(&self) {
        if self.set_run() == false {
            return;
        }

        softirq_example_func();

        self.clear_run();
    }
}
impl SoftirqExample {
    pub fn new() -> SoftirqExample {
        SoftirqExample {
            running: AtomicBool::new(false),
        }
    }

    fn set_run(&self) -> bool {
        let x = self
            .running
            .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed);
        if x.is_ok() {
            return true;
        } else {
            return false;
        }
    }

    fn clear_run(&self) {
        self.running.store(false, Ordering::Release);
    }
}
fn main() {
    let softirq_example = Arc::new(SoftirqExample::new());
    let softirq_num = 2;
    // 注册SoftirqExample中断
    softirq_vectors()
        .register_softirq(SoftirqNumber::from(softirq_num as u64), softirq_example)
        .expect("failed to register SoftirqExample");

    // 标志SoftirqExample中断需要执行
    softirq_vectors().raise_softirq(SoftirqNumber::from(softirq_num as u64));

    // 标志SoftirqExample中断不需要执行
    softirq_vectors().clear_softirq_pending(SoftirqNumber::from(softirq_num as u64));

    // 解注册SoftirqExample中断
    softirq_vectors().unregister_softirq(SoftirqNumber::from(softirq_num as u64));
}
3.4. 为C提供的接口
extern void rs_softirq_init();
extern void rs_raise_softirq(uint32_t sirq_num);
extern void rs_unregister_softirq(uint32_t sirq_num);
extern void rs_do_softirq();
extern void rs_clear_softirq_pending(uint32_t softirq_num);

这里是DragonOS的锁变量的说明文档。

锁的类型及其规则

简介

  DragonOS内核实现了一些锁,大致可以分为两类:

  • 休眠锁

  • 自旋锁

锁的类型

休眠锁

  休眠锁只能在可抢占的上下文之中被获取。

  在DragonOS之中,实现了以下的休眠锁:

  • semaphore

  • mutex_t

自旋锁
  • spinlock_t

  • RawSpinLock(Rust版本的spinlock_t,但与spinlock_t不兼容)

  • SpinLock —— 在RawSpinLock的基础上,封装了一层守卫(Guard), 将锁及其要保护到的数据绑定在一个结构体内,并能在编译期避免未加锁就访问数据的问题。

  进程在获取自旋锁后,将改变pcb中的锁变量持有计数,从而隐式地禁止了抢占。为了获得更多灵活的操作,spinlock还提供了以下的方法:

后缀

说明

_irq()

在加锁时关闭中断/在放锁时开启中断

_irqsave()/_irqrestore()

在加锁时保存中断状态,并关中断/在放锁时恢复中断状态

  当您同时需要使用自旋锁以及引用计数时,一个好的方法是:使用lockref. 这是一种额外的加速技术,能额外提供“无锁修改引用计数”的功能。详情请见:lockref

详细介绍

自旋锁的详细介绍

  关于自旋锁的详细介绍,请见文档:自旋锁

semaphore信号量

  semaphore信号量是基于计数实现的。

  当可用资源不足时,尝试对semaphore执行down操作的进程将会被休眠,直到资源可用。

mutex互斥量

  请见Mutex文档

备注

作者:龙进 longjin@RinGoTek.cn

自旋锁

1.简介

  自旋锁是用于多线程同步的一种锁,线程反复检查锁变量是否可用。由于线程在这一过程中保持运行的状态,因此是一种忙等待。一旦获取了自旋锁,线程会一直保持该锁,直至显式释放自旋锁。

  DragonOS在kernel/src/lib/spinlock.rs文件中,实现了自旋锁。根据功能特性的略微差异,分别提供了RawSpinLockSpinLock两种自旋锁。

2. RawSpinLock - 原始自旋锁

  RawSpinLock是原始的自旋锁,其数据部分包含一个AtomicBool, 实现了自旋锁的基本功能。其加锁、放锁需要手动确定对应的时机,也就是说,和我们在其他语言中使用的自旋锁一样, 需要先调用lock()方法,然后当离开临界区时,手动调用unlock()方法。我们并没有向编译器显式地指定该自旋锁到底保护的是哪些数据。

  RawSpinLock为程序员提供了非常自由的加锁、放锁控制。但是,正是由于它过于自由,因此在使用它的时候,我们很容易出错。很容易出现“未加锁就访问临界区的数据”、“忘记放锁”、“双重释放”等问题。当使用RawSpinLock时,编译器并不能对这些情况进行检查,这些问题只能在运行时被发现。

警告

RawSpinLock与C版本的spinlock_t不具有二进制兼容性。如果由于暂时的兼容性的需求,要操作C版本的spinlock_t,请使用spinlock.rs中提供的C版本的spinlock_t的操作函数。

但是,对于新开发的功能,请不要使用C版本的spinlock_t,因为随着代码重构的进行,我们将会移除它。

3. SpinLock - 具备守卫的自旋锁

  SpinLockRawSpinLock的基础上,进行了封装,能够在编译期检查出“未加锁就访问临界区的数据”、“忘记放锁”、“双重释放”等问题;并且,支持数据的内部可变性。

  其结构体原型如下:

#[derive(Debug)]
pub struct SpinLock<T> {
    lock: RawSpinlock,
    /// 自旋锁保护的数据
    data: UnsafeCell<T>,
}
3.1. 使用方法

  您可以这样初始化一个SpinLock:

let x = SpinLock::new(Vec::new());

  在初始化这个SpinLock时,必须把要保护的数据传入SpinLock,由SpinLock进行管理。

  当需要读取、修改SpinLock保护的数据时,请先使用SpinLock的lock()方法。该方法会返回一个SpinLockGuard。您可以使用被保护的数据的成员函数来进行一些操作。或者是直接读取、写入被保护的数据。(相当于您获得了被保护的数据的可变引用)

  完整示例如下方代码所示:

let x :SpinLock<Vec<i32>>= SpinLock::new(Vec::new());
    {
        let mut g :SpinLockGuard<Vec<i32>>= x.lock();
        g.push(1);
        g.push(2);
        g.push(2);
        assert!(g.as_slice() == [1, 2, 2] || g.as_slice() == [2, 2, 1]);
        // 在此处,SpinLock是加锁的状态
        kdebug!("x={:?}", x);
    }
    // 由于上方的变量`g`,也就是SpinLock守卫的生命周期结束,自动释放了SpinLock。因此,在此处,SpinLock是放锁的状态
    kdebug!("x={:?}", x);

  对于结构体内部的变量,我们可以使用SpinLock进行细粒度的加锁,也就是使用SpinLock包裹需要细致加锁的成员变量,比如这样:

pub struct a {
  pub data: SpinLock<data_struct>,
}

  当然,我们也可以对整个结构体进行加锁:

struct MyStruct {
  pub data: data_struct,
}
/// 被全局加锁的结构体
pub struct LockedMyStruct(SpinLock<MyStruct>);
3.2. 原理

  SpinLock之所以能够实现编译期检查,是因为它引入了一个SpinLockGuard作为守卫。我们在编写代码的时候,保证只有调用SpinLocklock()方法加锁后,才能生成一个SpinLockGuard。 并且,当我们想要访问受保护的数据的时候,都必须获得一个守卫。然后,我们为SpinLockGuard实现了Drop trait,当守卫的生命周期结束时,将会自动释放锁。除此以外,没有别的方法能够释放锁。因此我们能够得知,一个上下文中,只要SpinLockGuard的生命周期没有结束,那么它就拥有临界区数据的访问权,数据访问就是安全的。

3.3. 存在的问题
3.3.1. 双重加锁

  请注意,SpinLock支持的编译期检查并不是万能的。它目前无法在编译期检查出“双重加锁”问题。试看这样一个场景:函数A中,获得了锁。然后函数B中继续尝试加锁,那么就造成了“双重加锁”问题。这样在编译期是无法检测出来的。

  针对这个问题,我们建议采用这样的编程方法:

  • 如果函数B需要访问临界区内的数据,那么,函数B应当接收一个类型为&SpinLockGuard的参数,这个守卫由函数A获得。这样一来,函数B就能访问临界区内的数据。

lockref

  lockref是将自旋锁与引用计数变量融合在连续、对齐的8字节内的一种技术。

  目前,DragonOS中,通过C、Rust各实现了一个版本的lockref。请注意,二者不兼容。对于新的功能模块,请使用Rust版本的lockref。随着代码重构工作的进行,我们将会删除C版本的lockref。

1. lockref结构

1.1. Rust版本
/// 仅在x86_64架构下使用cmpxchg
#[cfg(target_arch = "x86_64")]
/// 由于需要cmpxchg,所以整个lockref按照8字节对齐
#[repr(align(8))]
#[derive(Debug)]
pub struct LockRef {
    pub lock: RawSpinlock,
    pub count: i32,
}

/// 除了x86_64以外的架构,不使用cmpxchg进行优化
#[cfg(not(target_arch = "x86_64"))]
pub struct LockRef {
    lock: RawSpinlock,
    count: i32,
}
1.2. C版本
struct lockref
{
    union
    {
#ifdef __LOCKREF_ENABLE_CMPXCHG__
        aligned_u64 lock_count; // 通过该变量的声明,使得整个lockref的地址按照8字节对齐
#endif
        struct
        {
            spinlock_t lock;
            int count;
        };
    };
};

2. 特性描述

  由于在高负载的情况下,系统会频繁的执行“锁定-改变引用变量-解锁”的操作,这期间很可能出现spinlock和引用计数跨缓存行的情况,这将会大大降低性能。lockref通过强制对齐,尽可能的降低缓存行的占用数量,使得性能得到提升。

  并且,在x64体系结构下,还通过cmpxchg()指令,实现了无锁快速路径。不需要对自旋锁加锁即可更改引用计数的值,进一步提升性能。当快速路径不存在(对于未支持的体系结构)或者尝试超时后,将会退化成“锁定-改变引用变量-解锁”的操作。此时由于lockref强制对齐,只涉及到1个缓存行,因此性能比原先的spinlock+ref_count的模式要高。

3. 关于cmpxchg_loop

  在改变引用计数时,cmpxchg先确保没有别的线程持有锁,然后改变引用计数,同时通过lock cmpxchg指令验证在更改发生时,没有其他线程持有锁,并且当前的目标lockref的值与old变量中存储的一致,从而将新值存储到目标lockref。这种无锁操作能极大的提升性能。如果不符合上述条件,在多次尝试后,将退化成传统的加锁方式来更改引用计数。

4. Rust版本的API

4.1. 引用计数自增
  • pub fn inc(&mut self)

  • pub fn inc_not_zero(&mut self) -> Result<i32, SystemError>

  • pub fn inc_not_dead(&mut self) -> Result<i32, SystemError>

4.1.1. inc
说明

  原子的将引用计数加1。

返回值

  无

4.1.2. inc_not_zero
说明

  原子地将引用计数加1.如果原来的count≤0,则操作失败。

返回值

返回值

说明

Ok(self.count)

成功,返回新的引用计数

Err(SystemError::EPERM)

失败,返回EPERM

4.1.3. inc_not_dead
说明

  引用计数自增1。(除非该lockref已经被标记为死亡)

返回值

返回值

说明

Ok(self.count)

成功,返回新的引用计数

Err(SystemError::EPERM)

失败,返回EPERM

4.2. 引用计数自减
  • pub fn dec(&mut self) -> Result<i32, SystemError>

  • pub fn dec_return(&mut self) -> Result<i32, SystemError>

  • pub fn dec_not_zero(&mut self) -> Result<i32, SystemError>

  • pub fn dec_or_lock_not_zero(&mut self) -> Result<i32, SystemError>

4.2.1. dec
说明

  原子地将引用计数-1。如果已处于count≤0的状态,则返回Err(SystemError::EPERM)

  本函数与lockref_dec_return()的区别在于,当在cmpxchg()中检测到count<=0或已加锁,本函数会再次尝试通过加锁来执行操作,而lockref_dec_return()会直接返回错误

返回值

返回值

说明

Ok(self.count)

成功,返回新的引用计数

Err(SystemError::EPERM)

失败,返回EPERM

4.2.2. dec_return

  原子地将引用计数减1。如果处于已加锁或count≤0的状态,则返回SystemError::EPERM

  本函数与lockref_dec()的区别在于,当在cmpxchg()中检测到count<=0或已加锁,本函数会直接返回错误,而lockref_dec()会再次尝试通过加锁来执行操作.

备注

若当前处理器架构不支持cmpxchg,则退化为self.dec()

返回值

返回值

说明

Ok(self.count)

成功,返回新的引用计数

Err(SystemError::EPERM)

失败,返回EPERM

4.2.3. dec_not_zero
说明

  原子地将引用计数减1。若当前的引用计数≤1,则操作失败.

  该函数与lockref_dec_or_lock_not_zero()的区别在于,当cmpxchg()时发现old.count≤1时,该函数会直接返回Err(-1),而lockref_dec_or_lock_not_zero()在这种情况下,会尝试加锁来进行操作。

返回值

返回值

说明

Ok(self.count)

成功,返回新的引用计数

Err(SystemError::EPERM)

失败,返回EPERM

4.2.4. dec_or_lock_not_zero
说明

  原子地将引用计数减1。若当前的引用计数≤1,则操作失败.

  该函数与lockref_dec_not_zero()的区别在于,当cmpxchg()时发现old.count≤1时,该函数会尝试加锁来进行操作,而lockref_dec_not_zero()在这种情况下,会直接返回Err(SystemError::EPERM).

返回值

返回值

说明

Ok(self.count)

成功,返回新的引用计数

Err(SystemError::EPERM)

失败,返回EPERM

4.3. 其他
  • pub fn mark_dead(&mut self)

4.3.1. mark_dead
说明

  将引用计数原子地标记为死亡状态.

参考资料

  Introducing lockrefs - LWN.net, Jonathan Corbet

备注

作者:龙进 longjin@RinGoTek.cn

mutex互斥量

  mutex是一种轻量级的同步原语,只有被加锁、空闲两种状态。

  当mutex被占用时,尝试对mutex进行加锁操作的进程将会被休眠,直到资源可用。

1. 特性

  • 同一时间只有1个任务可以持有mutex

  • 不允许递归地加锁、解锁

  • 只允许通过mutex的api来操作mutex

  • 在硬中断、软中断中不能使用mutex

2. 定义

  mutex定义在lib/mutex.rs中,定义如下所示:

/// @brief Mutex互斥量结构体
/// 请注意!由于Mutex属于休眠锁,因此,如果您的代码可能在中断上下文内执行,请勿采用Mutex!
#[derive(Debug)]
pub struct Mutex<T> {
    /// 该Mutex保护的数据
    data: UnsafeCell<T>,
    /// Mutex内部的信息
    inner: SpinLock<MutexInner>,
}

#[derive(Debug)]
struct MutexInner {
    /// 当前Mutex是否已经被上锁(上锁时,为true)
    is_locked: bool,
    /// 等待获得这个锁的进程的链表
    wait_list: LinkedList<&'static mut process_control_block>,
}

3. 使用

  与SpinLock类似,Rust版本的Mutex具有一个守卫。使用的时候,需要将要被保护的数据的所有权移交Mutex。并且,守卫只能在加锁成功后产生,因此,每个时刻,每个Mutex最多存在1个守卫。

  当需要读取、修改Mutex保护的数据时,请先使用Mutex的lock()方法。该方法会返回一个MutexGuard。您可以使用被保护的数据的成员函数来进行一些操作。或者是直接读取、写入被保护的数据。(相当于您获得了被保护的数据的可变引用)

  完整示例如下方代码所示:

let x :Mutex<Vec<i32>>= Mutex::new(Vec::new());
    {
        let mut g :MutexGuard<Vec<i32>>= x.lock();
        g.push(1);
        g.push(2);
        g.push(2);
        assert!(g.as_slice() == [1, 2, 2] || g.as_slice() == [2, 2, 1]);
        // 在此处,Mutex是加锁的状态
        kdebug!("x={:?}", x);
    }
    // 由于上方的变量`g`,也就是Mutex守卫的生命周期结束,自动释放了Mutex。因此,在此处,Mutex是放锁的状态
    kdebug!("x={:?}", x);

  对于结构体内部的变量,我们可以使用Mutex进行细粒度的加锁,也就是使用Mutex包裹需要细致加锁的成员变量,比如这样:

pub struct a {
  pub data: Mutex<data_struct>,
}

  当然,我们也可以对整个结构体进行加锁:

struct MyStruct {
  pub data: data_struct,
}
/// 被全局加锁的结构体
pub struct LockedMyStruct(Mutex<MyStruct>);

4. API

4.1. new - 初始化Mutex
原型
pub const fn new(value: T) -> Self
说明

  new()方法用于初始化一个Mutex。该方法需要一个被保护的数据作为参数。并且,该方法会返回一个Mutex。

4.2. lock - 加锁
原型
pub fn lock(&self) -> MutexGuard<T>
说明

  对Mutex加锁,返回Mutex的守卫,您可以使用这个守卫来操作被保护的数据。

  如果Mutex已经被加锁,那么,该方法会阻塞当前进程,直到Mutex被释放。

4.3. try_lock - 尝试加锁
原型
pub fn try_lock(&self) -> Result<MutexGuard<T>, i32>
说明

  尝试对Mutex加锁。如果加锁失败,不会将当前进程加入等待队列。如果加锁成功,返回Mutex的守卫;如果当前Mutex已经被加锁,返回Err(错误码)

5. C版本的Mutex(在将来会被废弃)

  mutex定义在common/mutex.h中。其数据类型如下所示:

typedef struct
{

    atomic_t count; // 锁计数。1->已解锁。 0->已上锁,且有可能存在等待者
    spinlock_t wait_lock;   // mutex操作锁,用于对mutex的list的操作进行加锁
    struct List wait_list;  // Mutex的等待队列
} mutex_t;
5.1. API
mutex_init

void mutex_init(mutex_t *lock)

  初始化一个mutex对象。

mutex_lock

void mutex_lock(mutex_t *lock)

  对一个mutex对象加锁。若mutex当前被其他进程持有,则当前进程进入休眠状态。

mutex_unlock

void mutex_unlock(mutex_t *lock)

  对一个mutex对象解锁。若mutex的等待队列中有其他的进程,则唤醒下一个进程。

mutex_trylock

void mutex_trylock(mutex_t *lock)

  尝试对一个mutex对象加锁。若mutex当前被其他进程持有,则返回0.否则,加锁成功,返回1.

mutex_is_locked

void mutex_is_locked(mutex_t *lock)

  判断mutex是否已被加锁。若给定的mutex已处于上锁状态,则返回1,否则返回0。

RwLock读写锁

备注

本文作者: sujintao

Email: sujintao@dragonos.org

1. 简介

  读写锁是一种在并发环境下保护多进程间共享数据的机制. 相比于普通的spinlock,读写锁将对 共享数据的访问分为读和写两种类型: 只读取共享数据的访问使用读锁控制,修改共享数据的访问使用 写锁控制. 读写锁设计允许同时存在多个”读者”(只读取共享数据的访问)和一个”写者”(修改共享数据 的访问), 对于一些大部分情况都是读访问的共享数据来说,使用读写锁控制访问可以一定程度上提升性能.

2. DragonOS中读写锁的实现

2.1 读写锁的机理

  读写锁的目的是维护多线程系统中的共享变量的一致性. 数据会被包裹在一个RwLock的数据结构中, 一切的访问必须通过RwLock的数据结构进行访问和修改. 每个要访问共享数据的会获得一个守卫(guard), 只读进程获得READER(读者守卫),需要修改共享变量的进程获得WRITER(写者守卫),作为RwLock的”影子”, 线程都根据guard来进行访问和修改操作.

  在实践中, 读写锁除了READER, WRITER, 还增加了UPGRADER; 这是一种介于READER和WRITER之间的守卫, 这个守卫的作用就是防止WRITER的饿死(Staration).当进程获得UPGRADER时,进程把它当成READER来使用;但是UPGRADER可以进行升级处理,升级后的UPGRADER相当于是一个WRITER守卫,可以对共享数据执行写操作.

  所有守卫都满足rust原生的RAII机理,当守卫所在的作用域结束时,守卫将自动释放.

2.2 读写锁守卫之间的关系

  同一时间点, 可以存在多个READER, 即可以同时有多个进程对共享数据进行访问;同一时间只能存在一个WRITER,而且当有一个进程获得WRITER时,不能存在READER和UPGRADER;进程获得UPGRADER的前提条件是,不能有UPGRADER或WRITER存在,但是当有一个进程获得UPGRADER时,进程无法成功申请READER.

2.3 设计的细节
2.3.1 RwLock数据结构
pub struct RwLock<T> {
    lock: AtomicU32,//原子变量
    data: UnsafeCell<T>,
}
2.3.2 READER守卫的数据结构
pub struct RwLockReadGuard<'a, T: 'a> {
    data: *const T,
    lock: &'a AtomicU32,
}
2.3.3 UPGRADER守卫的数据结构
pub struct RwLockUpgradableGuard<'a, T: 'a> {
    data: *const T,
    inner: &'a RwLock<T>,
}
2.3.4 WRITER守卫的数据结构
pub struct RwLockWriteGuard<'a, T: 'a> {
    data: *mut T,
    inner: &'a RwLock<T>,
}
2.3.5 RwLock的lock的结构介绍

lock是一个32位原子变量AtomicU32, 它的比特位分配如下:

                                                       UPGRADER_BIT     WRITER_BIT
                                                         ^                   ^
OVERFLOW_BIT                                             +------+    +-------+
  ^                                                             |    |
  |                                                             |    |
+-+--+--------------------------------------------------------+-+--+-+--+
|    |                                                        |    |    |
|    |                                                        |    |    |
|    |             The number of the readers                  |    |    |
|    |                                                        |    |    |
+----+--------------------------------------------------------+----+----+
  31  30                                                    2   1    0

  (从右到左)第0位表征WRITER是否有效,若WRITER_BIT=1, 则存在一个进程获得了WRITER守卫; 若UPGRADER_BIT=1, 则存在一个进程获得了UPGRADER守卫,第2位到第30位用来二进制表示获得READER守卫的进程数; 第31位是溢出判断位, 若OVERFLOW_BIT=1, 则不再接受新的读者守卫的获得申请.

3. 读写锁的主要API

3.1 RwLock的主要API
///功能:  输入需要保护的数据类型data,返回一个新的RwLock类型.
pub const fn new(data: T) -> Self
///功能: 获得READER守卫
pub fn read(&self) -> RwLockReadGuard<T>
///功能: 尝试获得READER守卫
pub fn try_read(&self) -> Option<RwLockReadGuard<T>>
///功能: 获得WRITER守卫
pub fn write(&self) -> RwLockWriteGuard<T>
///功能: 尝试获得WRITER守卫
pub fn try_write(&self) -> Option<RwLockWriteGuard<T>>
///功能: 获得UPGRADER守卫
pub fn upgradeable_read(&self) -> RwLockUpgradableGuard<T>
///功能: 尝试获得UPGRADER守卫
pub fn try_upgradeable_read(&self) -> Option<RwLockUpgradableGuard<T>>
3.2 WRITER守卫RwLockWriteGuard的主要API
///功能: 将WRITER降级为READER
pub fn downgrade(self) -> RwLockReadGuard<'rwlock, T>
///功能: 将WRITER降级为UPGRADER
pub fn downgrade_to_upgradeable(self) -> RwLockUpgradableGuard<'rwlock, T>
3.3 UPGRADER守卫RwLockUpgradableGuard的主要API
///功能: 将UPGRADER升级为WRITER
pub fn upgrade(mut self) -> RwLockWriteGuard<'rwlock, T> 
///功能: 将UPGRADER降级为READER
pub fn downgrade(self) -> RwLockReadGuard<'rwlock, T>

4. 用法实例

static LOCK: RwLock<u32> = RwLock::new(100 as u32);

fn t_read1() {
    let guard = LOCK.read();
    let value = *guard;
    let readers_current = LOCK.reader_count();
    let writers_current = LOCK.writer_count();
    println!(
        "Reader1: the value is {value}
    There are totally {writers_current} writers, {readers_current} readers"
    );
}

fn t_read2() {
    let guard = LOCK.read();
    let value = *guard;
    let readers_current = LOCK.reader_count();
    let writers_current = LOCK.writer_count();
    println!(
        "Reader2: the value is {value}
    There are totally {writers_current} writers, {readers_current} readers"
    );
}

fn t_write() {
    let mut guard = LOCK.write();
    *guard += 100;
    let writers_current = LOCK.writer_count();
    let readers_current = LOCK.reader_count();
    println!(
        "Writers: the value is {guard}
    There are totally {writers_current} writers, {readers_current} readers",
        guard = *guard
    );
    let read_guard=guard.downgrade();
    let value=*read_guard;
    println!("After downgraded to read_guard: {value}");
}

fn t_upgrade() {
    let guard = LOCK.upgradeable_read();
    let value = *guard;
    let readers_current = LOCK.reader_count();
    let writers_current = LOCK.writer_count();
    println!(
        "Upgrader1 before upgrade: the value is {value}
    There are totally {writers_current} writers, {readers_current} readers"
    );
    let mut upgraded_guard = guard.upgrade();
    *upgraded_guard += 100;
    let writers_current = LOCK.writer_count();
    let readers_current = LOCK.reader_count();
    println!(
        "Upgrader1 after upgrade: the value is {temp}
    There are totally {writers_current} writers, {readers_current} readers",
        temp = *upgraded_guard
    );
    let downgraded_guard=upgraded_guard.downgrade_to_upgradeable();
    let value=*downgraded_guard;
    println!("value after downgraded: {value}");
    let read_guard=downgraded_guard.downgrade();
    let value_=*read_guard;
    println!("value after downgraded to read_guard: {value_}");
}

fn main() {
    let r2=thread::spawn(t_read2);
    let r1 = thread::spawn(t_read1);
    let t1 = thread::spawn(t_write);
    let g1 = thread::spawn(t_upgrade);
    r1.join().expect("r1");
    t1.join().expect("t1");
    g1.join().expect("g1");
    r2.join().expect("r2");
}

进程管理模块

kthread 内核线程

  内核线程模块定义在common/kthread.h中,提供对内核线程的及支持功能。内核线程作为内核的“分身”,能够提升系统的并行化程度以及故障容错能力。

原理

  每个内核线程都运行在内核态,执行其特定的任务。

  内核线程的创建是通过调用kthread_create()或者kthread_run()宏,向kthreadd守护线程发送创建任务来实现的。也就是说,内核线程的创建,最终是由kthreadd来完成。

  当内核线程被创建后,虽然会加入调度队列,但是当其被第一次调度,执行引导程序kthread()后,将进入休眠状态。直到其他模块使用process_wakeup(),它才会真正开始运行。

  当内核其他模块想要停止一个内核线程的时候,可以调用kthread_stop()函数。该函数将会置位内核线程的worker_private中的KTHREAD_SHOULD_STOP标志位,并等待内核线程的退出,然后获得返回值并清理内核线程的pcb。

  内核线程应当经常检查KTHREAD_SHOULD_STOP标志位,以确定其是否要退出。当检测到该标志位被置位时,内核线程应当完成数据清理工作,并调用kthread_exit()或直接返回一个返回码,以退出内核线程。

创建内核线程

kthread_create()
原型

  kthread_create(thread_fn, data, name_fmt, arg...)

简介

  在当前NUMA结点上创建一个内核线程(DragonOS目前暂不支持NUMA,因此node可忽略。)

  请注意,该宏会创建一个内核线程,并将其设置为停止状态.

参数

thread_fn

  该内核线程要执行的函数

data

  传递给 thread_fn 的参数数据

name_fmt

  printf-style format string for the thread name

arg

  name_fmt的参数

返回值

  创建好的内核线程的pcb

kthread_run()
原型

  kthread_run(thread_fn, data, name_fmt, ...)

简介

  创建内核线程并加入调度队列。

  该宏定义是kthread_create()的简单封装,提供创建了内核线程后,立即运行的功能。

kthread_run_rt()
原型

  kthread_run_rt(thread_fn, data, name_fmt, ...)

简介

  创建内核实时线程并加入调度队列。

  类似kthread_run(),该宏定义也是kthread_create()的简单封装,提供创建了内核实时线程后,在设置实时进程的参数后,立即运行的功能。

停止内核线程

kthread_stop()
原型

  int kthread_stop(struct process_control_block * pcb)

简介

  当外部模块希望停止一个内核线程时,调用该函数,向kthread发送停止消息,请求其结束。并等待其退出,返回内核线程的退出返回值。

参数

pcb

  内核线程的pcb

返回值

  内核线程的退出返回码。

kthread_should_stop()
原型

  bool kthread_should_stop(void)

简介

  内核线程可以调用该函数得知是否有其他进程请求结束当前内核线程。

返回值

  一个bool变量

解释

true

有其他进程请求结束该内核线程

false

该内核线程没有收到停止消息

kthread_exit()
原型

  void kthread_exit(long result)

简介

  让当前内核线程退出,并返回result参数给kthread_stop()函数。

参数

result

  内核线程的退出返回码

PCB 进程控制块

PCB的全称为process control block, 它是每个进程/线程的核心控制结构。定义于kernel/src/process/proc-types.h中。

PCB详解

Todo:

与PCB的管理相关的API

根据pid寻找pcb

process_find_pcb_by_pid

该API提供了根据pid寻找pcb的功能,定义在kernel/src/process/process.h中。

当找到目标的pcb时,返回对应的pcb,否则返回NULL。

参数

pid 进程id

返回值

struct process_control_block 目标pcb

加载程序

1. 二进制程序装载

  在小节,你将了解DragonOS的二进制程序加载器的原理。

  DragonOS在装载二进制程序时,执行了“探测-装载”的过程。

  在探测阶段,DragonOS会读取文件首部,然后依次调用各个二进制加载器的探测函数,判断该二进制程序是否适用于该加载器。如果适用,则使用这个加载器进行装载。

  在装载阶段,DragonOS会使用上述加载器进行装载。装载器会将二进制程序的各个段映射到内存中,并且得到二进制程序的入口地址。

备注

目前DragonOS不支持动态链接,因此所有的二进制程序都是静态链接的。并且暂时支持的只有ELF加载器。

DragonOS调度

这里是DragonOS中,与进程调度相关的说明文档。

与“等待”相关的api(C语言)

警告

随着内核的发展,我们将会逐步将C语言的等待机制替换为Rust语言的等待机制。在这个过程中,我们将会同时保留C语言和Rust语言的等待机制,以便于我们在开发过程中进行对比。 待时机成熟,我们将会逐步将C语言的等待机制移除。

  如果几个进程需要等待某个事件发生,才能被运行,那么就需要一种“等待”的机制,以实现进程同步。

一. wait_queue等待队列

  wait_queue是一种进程同步机制,中文名为“等待队列”。它可以将当前进程挂起,并在时机成熟时,由另一个进程唤醒他们。

  当您需要等待一个事件完成时,使用wait_queue机制能减少进程同步的开销。相比于滥用自旋锁以及信号量,或者是循环使用usleep(1000)这样的函数来完成同步,wait_queue是一个高效的解决方案。

警告

wait_queue.h中的等待队列的实现并没有把队列头独立出来,同时没有考虑为等待队列加锁。所以在后来的开发中加入了wait_queue_head_t的队列头实现,实质上就是链表+自旋锁。它与wait_queue.h中的队列wait_queue_node_t是兼容的,当你使用struct wait_queue_head作为队列头时,你同样可以使用等待队列添加节点的函数。

简单用法

  等待队列的使用方法主要分为以下几部分:

  • 创建并初始化一个等待队列

  • 使用wait_queue_sleep_on_系列的函数,将当前进程挂起。晚挂起的进程将排在队列的尾部。

  • 通过wait_queue_wakeup()函数,依次唤醒在等待队列上的进程,将其加入调度队列

  要使用wait_queue,您需要#include<common/wait_queue.h>,并创建一个wait_queue_node_t类型的变量,作为等待队列的头部。这个结构体只包含两个成员变量:

typedef struct
{
    struct List wait_list;
    struct process_control_block *pcb;
} wait_queue_node_t;

  对于等待队列,这里有一个好的命名方法:

wait_queue_node_t wq_keyboard_interrupt_received;

  这样的命名方式能增加代码的可读性,更容易让人明白这里到底在等待什么。

初始化等待队列

  函数wait_queue_init(wait_queue_node_t *wait_queue, struct process_control_block *pcb)提供了初始化wait_queue结点的功能。

  当您初始化队列头部时,您仅需要将wait_queue首部的结点指针传入,第二个参数请设置为NULL

将结点插入等待队列

  您可以使用以下函数,将当前进程挂起,并插入到指定的等待队列。这些函数大体功能相同,只是在一些细节上有所不同。

函数名

解释

wait_queue_sleep_on()

将当前进程挂起,并设置挂起状态为PROC_UNINTERRUPTIBLE

wait_queue_sleep_on_unlock()

将当前进程挂起,并设置挂起状态为PROC_UNINTERRUPTIBLE。待当前进程被插入等待队列后,解锁给定的自旋锁

wait_queue_sleep_on_interriptible()

将当前进程挂起,并设置挂起状态为PROC_INTERRUPTIBLE

从等待队列唤醒一个进程

  您可以使用void wait_queue_wakeup(wait_queue_node_t * wait_queue_head, int64_t state);函数,从指定的等待队列中,唤醒第一个挂起时的状态与指定的state相同的进程。

  当没有符合条件的进程时,将不会唤醒任何进程,仿佛无事发生。


        

二. wait_queue_head等待队列头

   数据结构定义如下:

typedef struct
{
    struct List wait_list;
    spinlock_t lock;  // 队列需要有一个自旋锁,虽然目前内部并没有使用,但是以后可能会用.
} wait_queue_head_t;

   等待队列头的使用逻辑与等待队列实际是一样的,因为他同样也是等待队列的节点(仅仅多了一把锁)。且wait_queue_head的函数基本上与wait_queue一致,只不过多了***_with_node_***的字符串。

   同时,wait_queue.h文件中提供了很多的宏,可以方便您的工作。

提供的宏

解释

DECLARE_WAIT_ON_STACK(name, pcb)

在栈上声明一个wait_queue节点,同时把pcb所代表的进程与该节点绑定

DECLARE_WAIT_ON_STACK_SELF(name)

传在栈上声明一个wait_queue节点,同时当前进程(即自身进程)与该节点绑定

DECLARE_WAIT_ALLOC(name, pcb)

使用kzalloc声明一个wait_queue节点,同时把pcb所代表的进程与该节点绑定,请记得使用kfree释放空间

DECLARE_WAIT_ALLOC_SELF(name)

使用kzalloc声明一个wait_queue节点,同时当前进程(即自身进程)与该节点绑定,请记得使用kfree释放空间

创建等待队列头

   您可以直接调用宏

DECLARE_WAIT_QUEUE_HEAD(m_wait_queue_head);  // 在栈上声明一个队列头变量

   也可以手动声明

struct wait_queue_head_t m_wait_queue_head = {0}; 
wait_queue_head_init(&m_wait_queue_head);
将结点插入等待队列

函数名

解释

wait_queue_sleep_with_node(wait_queue_head_t *head, wait_queue_node_t *wait_node)

传入一个等待队列节点,并设置该节点的挂起状态为PROC_UNINTERRUPTIBLE

wait_queue_sleep_with_node_unlock(wait_queue_head_t *q, wait_queue_node_t *wait, void *lock)

传入一个等待队列节点,将该节点的pcb指向的进程挂起,并设置挂起状态为PROC_UNINTERRUPTIBLE。待当前进程被插入等待队列后,解锁给定的自旋锁

wait_queue_sleep_with_node_interriptible(wait_queue_head_t *q, wait_queue_node_t *wait)

传入一个等待队列节点,将该节点的pcb指向的进程挂起,并设置挂起状态为PROC_INTERRUPTIBLE

从等待队列唤醒一个进程

   在wait_queue.h中的wait_queue_wakeup函数直接kfree掉了wait_node节点。对于在栈上的wait_node,您可以选择wait_queue_wakeup_on_stack(wait_queue_head_t *q, int64_t state)来唤醒队列里面的队列头节点。


        

三. completion完成量

简单用法

  完成量的使用方法主要分为以下几部分:

  • 声明一个完成量(可以在栈中/使用kmalloc/使用数组)

  • 使用wait_for_completion等待事件完成

  • 使用complete唤醒等待的进程

   等待操作

void wait_fun() {
    DECLARE_COMPLETION_ON_STACK(comp);  // 声明一个completion 

    // .... do somethind here 
    // 大部分情况是你使用kthread_run()创建了另一个线程
    // 你需要把comp变量传给这个线程, 然后当前线程就会等待他的完成

    if (!try_wait_for_completion(&comp))  // 进入等待
        wait_for_completion(&comp);
}

   完成操作

void kthread_fun(struct completion *comp) {
    // ...... 做一些事  .......
    // 这里你确定你完成了目标事件

    complete(&comp);
    // 或者你使用complete_all
    complete_all(&comp);
}
更多用法

   kernel/sched/completion.c文件夹中,你可以看到 __test 开头的几个函数,他们是completion模块的测试代码,基本覆盖了completion的大部分函数.你可以在这里查询函数使用方法.

初始化完成量

   函数completion_init(struct completion *x)提供了初始化completion的功能。当你使用DECLARE_COMPLETION_ON_STACK来创建(在栈上创建)的时候,会自动初始化.

关于完成量的wait系列函数

函数名

解释

wait_for_completion(struct completion *x)

将当前进程挂起,并设置挂起状态为PROC_UNINTERRUPTIBLE。

wait_for_completion_timeout(struct completion *x, long timeout)

将当前进程挂起,并设置挂起状态为PROC_UNINTERRUPTIBLE。当等待timeout时间(jiffies时间片)之后,自动唤醒进程。

wait_for_completion_interruptible(struct completion *x)

将当前进程挂起,并设置挂起状态为PROC_INTERRUPTIBLE。

wait_for_completion_interruptible_timeout(struct completion *x, long timeout)

将当前进程挂起,并设置挂起状态为PROC_INTERRUPTIBLE。当等待timeout时间(jiffies时间片)之后,自动唤醒进程。

wait_for_multicompletion(struct completion x[], int n)

将当前进程挂起,并设置挂起状态为PROC_UNINTERRUPTIBLE。(等待数组里面的completion的完成)

关于完成量的complete系列函数

函数名

解释

complete(struct completion *x)

表明一个事件被完成,从等待队列中唤醒一个进程

complete_all(struct completion *x)

表明与该completion有关的事件被标记为永久完成,并唤醒等待队列中的所有进程

其他用于查询信息的函数

函数名

解释

completion_done(struct completion *x)

查询completion的done变量是不是大于0,如果大于0,返回true;否则返回false。在等待前加上这个函数有可能加速?(暂未经过实验测试,有待证明)

try_wait_for_completion(struct completion *x)

查询completion的done变量是不是大于0,如果大于0,返回true(同时令done-=1);否则返回false。在等待前加上这个函数有可能加速?(该函数和completion_done代码逻辑基本一致,但是会主动令completion的done变量减1)

与“等待”相关的api(rust语言)

  如果几个进程需要等待某个事件发生,才能被运行,那么就需要一种“等待”的机制,以实现进程同步。

1. WaitQueue等待队列

   WaitQueue是一种进程同步机制,中文名为“等待队列”。它可以将当前进程挂起,并在时机成熟时,由另一个进程唤醒他们。

  当您需要等待一个事件完成时,使用 WaitQueue机制能减少进程同步的开销。相比于滥用自旋锁以及信号量,或者是循环使用usleep(1000)这样的函数来完成同步, WaitQueue是一个高效的解决方案。

1.1 WaitQueue的使用

   WaitQueue的使用非常简单,只需要三步:

  1. 初始化一个WaitQueue对象。

  2. 调用这个WaitQueue的挂起相关的API,将当前进程挂起。

  3. 当事件发生时,由另一个进程,调用这个WaitQueue的唤醒相关的API,唤醒一个进程。

  下面是一个简单的例子:

1.1.1 初始化一个WaitQueue对象

   WaitQueue对象的初始化非常简单,只需要调用WaitQueue::INIT即可。

let mut wq = WaitQueue::INIT;
1.1.2 挂起进程

   您可以这样挂起当前进程:

wq.sleep();

   当前进程会被挂起,直到有另一个进程调用了wq.wakeup()

1.1.3 唤醒进程

   您可以这样唤醒一个进程:

// 唤醒等待队列头部的进程(如果它的state & PROC_INTERRUPTIBLE 不为0)
wq.wakeup(PROC_INTERRUPTIBLE);

// 唤醒等待队列头部的进程(如果它的state & PROC_UNINTERRUPTIBLE 不为0)
wq.wakeup(PROC_UNINTERRUPTIBLE);

// 唤醒等待队列头部的进程(无论它的state是什么)
wq.wakeup((-1) as u64);
1.2 API
1.2.1 挂起进程

  您可以使用以下函数,将当前进程挂起,并插入到指定的等待队列。这些函数大体功能相同,只是在一些细节上有所不同。

函数名

解释

sleep()

将当前进程挂起,并设置进程状态为PROC_INTERRUPTIBLE

sleep_uninterruptible()

将当前进程挂起,并设置进程状态为PROC_UNINTERRUPTIBLE

sleep_unlock_spinlock()

将当前进程挂起,并设置进程状态为PROC_INTERRUPTIBLE。待当前进程被插入等待队列后,解锁给定的自旋锁

sleep_unlock_mutex()

将当前进程挂起,并设置进程状态为PROC_INTERRUPTIBLE。待当前进程被插入等待队列后,解锁给定的Mutex

sleep_uninterruptible_unlock_spinlock()

将当前进程挂起,并设置进程状态为PROC_UNINTERRUPTIBLE。待当前进程被插入等待队列后,解锁给定的自旋锁

sleep_uninterruptible_unlock_mutex()

将当前进程挂起,并设置进程状态为PROC_UNINTERRUPTIBLE。待当前进程被插入等待队列后,解锁给定的Mutex

1.2.2 唤醒进程

  您可以使用wakeup(state)函数,唤醒等待队列中的第一个进程。如果这个进程的state与给定的state进行and操作之后,结果不为0,则唤醒它。

  返回值:如果有进程被唤醒,则返回true,否则返回false。

1.2.3 其它API

函数名

解释

len()

返回等待队列中的进程数量

进程调度器相关的api

   定义了DragonOS的进程调度相关的api,是系统进行进程调度的接口。同时也抽象出了Scheduler的trait,以供具体的调度器实现

1. 调度器介绍

   一般来说,一个系统会同时处理多个请求,但是其资源是优先的,调度就是用来协调每个请求对资源的使用的方法。

1.1 主要函数
  1. cpu_executing(): 获取指定的cpu上正在执行的进程的pcb

  2. sched_enqueue(): 将进程加入调度队列

  3. sched_init(): 初始化进程调度器模块

  4. sched_update_jiffies(): 当时钟中断到达时,更新时间片。请注意,该函数只能被时钟中断处理程序调用

  5. sys_sched(): 让系统立即运行调度器的系统调用。请注意,该系统调用不能由ring3的程序发起

完全公平调度器相关的api

   CFS(Completely Fair Scheduler),顾名思义,完全公平调度器。CFS作为主线调度器之一,也是最典型的O(1)调度器之一

1. CFSQueue 介绍

   CFSQueue是用来存放普通进程的调度队列,每个CPU维护一个CFSQueue,主要使用Vec作为主要存储结构来实现。

1.1 主要函数
  1. enqueue(): 将pcb入队列

  2. dequeue(): 将pcb从调度队列中弹出,若队列为空,则返回IDLE进程的pcb

  3. sort(): 将进程按照虚拟运行时间的升序进行排列

2. SchedulerCFS 介绍

   CFS调度器类,主要实现了CFS调度器类的初始化以及调度功能函数。

2.1 主要函数
  1. sched(): 是对于Scheduler trait的sched()实现,是普通进程进行调度时的逻辑处理,该函数会返回接下来要执行的pcb,若没有符合要求的pcb,返回None

  2. enqueue(): 同样是对于Scheduler trait的sched()实现,将一个pcb加入调度器的调度队列

  3. update_cpu_exec_proc_jiffies(): 更新这个cpu上,这个进程的可执行时间。

  4. timer_update_jiffies(): 时钟中断到来时,由sched的core模块中的函数,调用本函数,更新CFS进程的可执行时间

实时进程调度器相关的api

   RT(realtime scheduler),实时调度器。实时调度是为了完成实时处理任务而分配CPU的调度方法。

  DragonOS的进程分为“实时进程”和“普通进程”两类;实时进程的优先级高于普通进程,如果当前的系统的执行队列中有“实时进程”,RT调度器会优先选择实时进程;如果队列中会有多个实时进程,调度器会选择优先级最高的实时进程来执行;

1. RTQueue 介绍

   RTQueue是用来存放state为running的实时进程的调度队列,每个CPU维护一个RTQueue,主要使用Vec作为主要存储结构来实现。

1.1 主要函数
  1. enqueue(): 将pcb入队列

  2. dequeue(): 将pcb出队列

2. SchedulerRT 介绍

   RT调度器类,主要实现了RT调度器类的初始化以及调度功能函数。

2.1 主要函数
  1. pick_next_task_rt(): 获取当前CPU中的第一个需要执行的RT pcb

  2. sched(): 是对于Scheduler trait的sched()实现,是实时进程进行调度时的逻辑处理,该函数会返回接下来要执行的pcb,若没有符合要求的pcb,返回None

  3. enqueue(): 同样是对于Scheduler trait的sched()实现,将一个pcb加入调度器的调度队列

2.2 内核调度策略

   目前在DragonOS中,主要的调度策略有SCHED_NORMAL 策略 | SCHED_FIFO 策略 | SCHED_RT 策略,具体的调度策略为:

  1. SCHED_NORMAL 策略: SCHED_NORMAL 是“绝对公平调度策略”,该策略的进程使用CFS进行调度。

  2. SCHED_FIFO 策略: SCHED_FIFO是“实时进程调度策略”,这是一种先进先出的调度策略,该策略不涉及到CPU时间片机制,在没有更高优先级进程的前提下,只能等待其他进程主动释放CPU资源; 在SCHED_FIFO策略中,被调度器调度运行的进程,其运行时长不受限制,可以运行任意长的时间。

  3. SCHED_RR 策略: SCHED_RR是“实时进程调度策略”,使用的是时间片轮转机制,对应进程的time_slice会在运行时减少,进程使用完CPU时间片后,会加入该CPU的与该进程优先级相同的执行队列中。 同时,释放CPU资源,CPU的使用权会被分配给下一个执行的进程

3. Q&A

   几种常用的方法

  1. 如何创建实时进程

    struct process_control_block *pcb_name = kthread_run_rt(&fn_name, NULL, "test create rt pcb");
    

    其中kthread_run_rt,是创建内核实时线程的宏

  2. pcb中涉及到实时进程的字段含义

    1. policy:实时进程的策略,目前有:SCHED_FIFO与SCHED_RR

    2. priority: 实时进程的优先级,范围为0-99,数字越大,表示优先级越高

    3. rt_time_slice: 实时进程的时间片,默认为100,随着CPU运行而减少,在rt_time_slice为0时,将时间片赋初值并将该进程加入执行队列。

  3. 如何实时进程存储队列

    • 目前是使用Vec来保存,因为具体实现的逻辑原因,目前的入队列和出队列都是对队尾的操作,因此会有如下现象:系统中有多个优先级相同的实时进程等待运行时,会出现饥饿现象,也即上一个因为时间片耗尽的进程会在下一个执行,造成同优先级等待的进程饥饿。

  4. todo

    1. 将存储实时进程的队列使用双向链表存储(或者其他办法解决上述的饥饿问题)

    2. 目前的实时调度是针对单CPU的,需要实现多CPU的实时调度

    3. 实现RT进程和普通进程之间的分配带宽的比例

    4. 多个CPU之间实现负载均衡

内核定时器

1. 简介

  内核定时器是内核中的一种定时器,内核定时器的工作方式是:添加定时器到队列,为每个定时器设置到期时间。当定时器到期时,会执行定时器对应的函数。

2. 设计思路

  定时器类型为Timer结构体,而TimerSpinLock<InnerTimer>组成。全局中使用元素类型为Arc<Timer>的队列TIMER_LIST存储系统创建的定时器。创建定时器时,应调用Timer::new(timer_func,expire_jiffies),timer_func为定时器要执行的操作,expire_jiffies为定时器的结束时间,timer_func参数的类型是实现了TimerFunction特性的结构体。在创建定时器后,应使用Timer::activate()将定时器插入到TIMER_LIST中。

  如果只是希望当前pcb休眠一段时间,应调用schedule_timeout(timeout),timeout指定pcb休眠的时间长度。

3. 定时器应实现的特性

  定时器要执行的函数应实现TimerFunction特性,其定义如下:

/// 定时器要执行的函数的特征
pub trait TimerFunction: Send + Sync {
    fn run(&mut self);
}

  一种典型的实现方式是:新建一个零长的结构体,实现TimerFunction特性,然后在run函数中实现定时器要执行的操作。

4. 定时器API

4.1. Timer的API
4.1.1. 创建一个定时器
pub fn new(timer_func: Box<dyn TimerFunction>, expire_jiffies: u64) -> Arc<Self>

参数

  • timer_func:定时器需要执行的函数对应的结构体,其实现了TimerFunction特性

  • expire_jiffies:定时器结束时刻(单位:jiffies

返回

  • 定时器结构体指针

4.1.2. 将定时器插入到定时器链表中
pub fn activate(&self)
4.2. 其余API

  若想要在.c的模块中使用以下函数,请在函数名之前加上rs_

4.2.1. 让进程休眠一段时间
pub fn schedule_timeout(mut timeout: i64) -> Result<i64, SystemError>

功能

  让进程休眠timeout个jiffies

参数

  • timeout:需要休眠的时间 (单位:jiffies

返回值

  • Ok(i64):剩余需要休眠的时间 (单位:jiffies

  • Err(SystemError):错误码

4.2.2. 获取队列中第一个定时器的结束时间
pub fn timer_get_first_expire() -> Result<u64, SystemError>

功能

  获取队列中第一个定时器的结束时间,即最早结束的定时器的结束时间

返回值

  • Ok(i64):最早结束的定时器的结束时间 (单位:jiffies

  • Err(SystemError):错误码

4.2.3. 获取当前系统时间
pub fn clock() -> u64 

功能

  获取当前系统时间(单位:jiffies

4.2.4. 计算接下来n毫秒或者微秒对应的定时器时间片
4.2.4.1. 毫秒
pub fn next_n_ms_timer_jiffies(expire_ms: u64) -> u64

功能

  计算接下来n毫秒对应的定时器时间片

参数

  • expire_ms:n毫秒

返回值

  对应的定时器时间片(单位:毫秒

4.2.4.2. 微秒
pub fn next_n_us_timer_jiffies(expire_us: u64) -> u64

功能

  计算接下来n微秒对应的定时器时间片

参数

  • expire_ms:n微秒

返回值

  对应的定时器时间片(单位:微秒

5. 创建定时器实例

struct TimerExample {
    /// 结构体的成员对应函数的形参
    example_parameter: i32,
}
impl TimerExample {
    pub fn new(para: i32) -> Box<TimerExample> {
        return Box::new(TimerExample {
            example_parameter: para,
        });
    }
}
/// 为结构体实现TimerFunction特性
impl TimerFunction for TimerExample {
    /// TimerFunction特性中的函数run
    fn run(&mut self) {
        // 定时器需要执行的操作
        example_func(self.example_parameter);
    }
}
fn example_func(para: i32) {
    println!("para is {:?}", para);
}
fn main() {
    let timer_example: Box<TimerExample> = TimerExample::new(1);
    // 创建一个定时器
    let timer: Arc<Timer> = Timer::new(timer_example, 1);
    // 将定时器插入队列
    timer.activate();
}

进程间通信

这里是DragonOS进程间通信(IPC)的说明文档。

Signal信号

备注

本文Maintainer: 龙进

Email: longjin@RinGoTek.cn

  信号是一种进程间通信机制,当我们发送一个信号给特定的进程,就能触发它的特定行为(例如退出程序,或者运行一些信号处理程序)。信号是发送到进程或同一进程内的特定线程的异步通知,用于通知它有事件发生。信号的常见用途是中断、挂起、终止或终止进程。发送信号时,操作系统会中断目标进程的正常执行流程以传递信号。可以在任何非原子指令期间中断执行。如果该进程之前注册了一个信号处理程序,则执行该例程。否则,将执行默认信号处理程序。

  信号类似于中断,区别在于中断由CPU调解并由内核处理,而信号在内核中产生(也可通过系统调用让内核产生)并由各个进程或内核的默认处理函数处理。

1.信号处理概要

1.1 信号发送

  当进程A想发送信号给进程B的时候,使用kill(pid, signal)接口进行发送。然后陷入内核的sys_kill()函数中进行处理。然后内核将会把信号加入目标进程的pcb的sigpending中。

示意图如下:

   ┌────────────┐
   │ Process A: │
   │            │
   │  sys_kill  │
   └──────┬─────┘
          │
          │
   ┌──────▼──────┐    ┌────────────────────┐
   │ Send Signal ├────►Add to sigpending of│
   └─────────────┘    │   process B.       │
                      └────────────────────┘
1.2 信号处理

  进程会在退出内核态的时候,跳转到do_signal()函数内,检查当前是否有需要被处理的信号,如果有的话,就会开启信号处理流程。

信号处理流程示意图:

 ┌───────────────────────┐
 │       Process B:      │
 │                       ◄─────────────────────────────────┐
 │ Return from syscall...│                                 │
 └─────────┬─────────────┘                                 │
           │                                               │
           │                                               │
           │                ┌────────────────┐             │
     ┌─────▼─────┐ default  │                │             │
     │ do_signal ├────────► │ stop process B.│             │
     └─────┬─────┘  action  │                │             │
           │                └────────────────┘             │
           │ custom action                                 │
    ┌──────▼───────┐                                       │
    │ setup signal │                                       │
    │    frame     │                                       │
    └──────┬───────┘                                       │
           │jump to                                        │
    ┌──────▼───────┐ ┌────────────┐ sys_sigreturn ┌────────┴────────┐
    │   userland   ├─►sa_restorer ├──────────────►│Restore the stack│
    │ sig handler  │ └────────────┘               │    frame.       │
    └──────────────┘                              └─────────────────┘
  • 如果内核检查发现,进程没有指定信号处理函数,且信号处理动作不是“忽略”,那就会杀掉进程。

  • 如果内核发现该信号没有被忽略,那么将会:

    • 备份当前内核栈

    • 设置信号处理的用户态栈帧

    • 回到用户态,执行信号处理函数

    • 信号处理函数结束之后,将会进入由libc提供的__sa_restorer中,发起sys_sigreturn()系统调用,回到内核态

    • 内核恢复处理信号之前的内核栈。

    • 信号处理流程结束,内核继续执行“返回用户态”的过程。

  • 如果内核发现当前信号被忽略,那么就检查下一个信号。

  • 发现没有任何需要处理的信号时,返回用户态。

2. 其他问题

  暂无。

内存管理

这里讲解了内存管理模块的一些设计及实现原理,以及相应的接口。

内存管理模块简介

1. 概述

  DragonOS实现了具有优秀架构设计的内存管理模块,对内核空间和用户空间的内存映射、分配、释放、管理等操作进行了封装,使得内核开发者可以更加方便地进行内存管理。

  DragonOS的内存管理模块主要由以下类型的组件组成:

  • 硬件抽象层(MemoryManagementArch) - 提供对具体处理器架构的抽象,使得内存管理模块可以在不同的处理器架构上运行

  • 页面映射器(PageMapper)- 提供对虚拟地址和物理地址的映射,以及页表的创建、填写、销毁、权限管理等操作。分为两种类型:内核页表映射器(KernelMapper)和用户页表映射器(位于具体的用户地址空间结构中)

  • 页面刷新器(PageFlusher) - 提供对页表的刷新操作(整表刷新、单页刷新、跨核心刷新)

  • 页帧分配器(FrameAllocator) - 提供对页帧的分配、释放、管理等操作。具体来说,包括BumpAllocator、BuddyAllocator

  • 小对象分配器 - 提供对小内存对象的分配、释放、管理等操作。指的是内核里面的SlabAllocator (SlabAllocator的实现目前还没有完成)

  • MMIO空间管理器 - 提供对MMIO地址空间的分配、管理操作。(目前这个模块待进一步重构)

  • 用户地址空间管理机制 - 提供对用户地址空间的管理。

    • VMA机制 - 提供对用户地址空间的管理,包括VMA的创建、销毁、权限管理等操作

    • 用户映射管理 - 与VMA机制共同作用,管理用户地址空间的映射

  • 系统调用层 - 提供对用户空间的内存管理系统调用,包括mmap、munmap、mprotect、mremap等

  • C接口兼容层 - 提供对原有的C代码的接口,使得C代码能够正常运行。

内存分配指南

  本文将讲述如何在内核中进行内存分配。在开始之前,请您先了解一个基本点:DragonOS的内核使用4KB的页来管理内存,并且具有伙伴分配器和slab分配器。并且对用户空间、内核空间均具有特定的管理机制。

1. 安全的分配内存

  在默认情况下,KernelAllocator被绑定为全局内存分配器,它会根据请求分配的内存大小,自动选择使用slab还是伙伴分配器。因此,在内核中,使用Rust原生的 内存分配函数,或者是创建一个Box对象等等,都是安全的。

2. 手动管理页帧

警告

请格外小心! 手动管理页帧脱离了Rust的内存安全机制,因此可能会造成内存泄漏或者是内存错误。

  在某些情况下,我们需要手动分配页帧。例如,我们需要在内核中创建一个新的页表,或者是在内核中创建一个新的地址空间。这时候,我们需要手动分配页帧。使用LockedFrameAllocatorallocate()函数,能够分配在物理地址上连续的页帧。请注意,由于底层使用的是buddy分配器,因此页帧数目必须是2的n次幂,且最大大小不超过1GB。

  当需要释放页帧的时候,使用LockedFrameAllocatordeallocate()函数,或者是deallocate_page_frames()函数,能够释放在物理地址上连续的页帧。

  当您需要映射页帧的时候,可使用KernelMapper::lock()函数,获得一个内核映射器对象,然后进行映射。由于KernelMapper是对PageMapper的封装,因此您在获取KernelMapper之后,可以使用PageMapper相关接口对内核空间的映射进行管理。

警告

千万不要 使用KernelMapper去映射用户地址空间的内存,这会使得这部分内存脱离用户地址空间的管理,从而导致内存错误。

3. 为用户程序分配内存

  在内核中,您可以使用用户地址空间结构体(AddressSpace)的mmap(),map_anonymous()等函数,为用户程序分配内存。这些函数会自动将用户程序的内存映射到用户地址空间中,并且会自动创建VMA结构体。您可以使用AddressSpacemunmap()函数,将用户程序的内存从用户地址空间中解除映射,并且销毁VMA结构体。调整权限等操作可以使用AddressSpacemprotect()函数。

MMIO

MMIO是“内存映射IO”的缩写,它被广泛应用于与硬件设备的交互之中。

地址空间管理

DragonOS中实现了MMIO地址空间的管理机制,本节将介绍它们。

为什么需要MMIO地址空间自动分配?

  由于计算机上的很多设备都需要MMIO的地址空间,而每台计算机上所连接的各种设备的对MMIO地址空间的需求是不一样的。如果我们为每个类型的设备都手动指定一个MMIO地址,会使得虚拟地址空间被大大浪费,也会增加系统的复杂性。并且,我们在将来还需要为不同的虚拟内存区域做异常处理函数。因此,我们需要一套能够自动分配MMIO地址空间的机制。

这套机制提供了什么功能?
  • 为驱动程序分配4K到1GB的MMIO虚拟地址空间

  • 对于这些虚拟地址空间,添加到VMA中进行统一管理

  • 可以批量释放这些地址空间

这套机制是如何实现的?

  这套机制本质上是使用了伙伴系统来对MMIO虚拟地址空间进行维护。在mm/mm.h中指定了MMIO的虚拟地址空间范围,这个范围是0xffffa10000000000开始的1TB的空间。也就是说,这个伙伴系统为MMIO维护了这1TB的虚拟地址空间。

地址空间分配过程
  1. 初始化MMIO-mapping模块,在mmio的伙伴系统中创建512个1GB的__mmio_buddy_addr_region

  2. 驱动程序使用mmio_create请求分配地址空间。

  3. mmio_create对申请的地址空间大小按照2的n次幂进行对齐,然后从buddy中申请内存地址空间

  4. 创建VMA,并将VMA标记为VM_IO|VM_DONTCOPY。MMIO的vma只绑定在initial_mm下,且不会被拷贝。

  5. 分配完成

一旦MMIO地址空间分配完成,它就像普通的vma一样,可以使用mmap系列函数进行操作。

MMIO的映射过程

  在得到了虚拟地址空间之后,当我们尝试往这块地址空间内映射内存时,我们可以调用mm_map函数,对这块区域进行映射。

  该函数会对MMIO的VMA的映射做出特殊处理。即:创建Page结构体以及对应的anon_vma. 然后会将对应的物理地址,填写到页表之中。

MMIO虚拟地址空间的释放

  当设备被卸载时,驱动程序可以调用mmio_release函数对指定的mmio地址空间进行释放。

  释放的过程中,mmio_release将执行以下流程:

  1. 取消mmio区域在页表中的映射。

  2. 将释放MMIO区域的VMA

  3. 将地址空间归还给mmio的伙伴系统。

MMIO的伙伴算法

伙伴的定义

  同时满足以下三个条件的两个内存块被称为伙伴内存块:

  1. 两个内存块的大小相同

  2. 两个内存块的内存地址连续

  3. 两个内存块由同一个大块内存分裂得到

伙伴算法

  伙伴(buddy)算法的作用是维护以及组织大块连续内存块的分配和回收,以减少系统时运行产生的外部碎片。伙伴系统中的每个内存块的大小均为\(2^n\)。 在DragonOS中,伙伴系统内存池共维护了1TB的连续存储空间,最大的内存块大小为\(1G\),即\(2^{30}B\),最小的内存块大小为\(4K\),即 \(2^{12}B\)

  伙伴算法的核心思想是当应用申请内存时,每次都分配比申请的内存大小更大的最小内存块,同时分配出去的内存块大小为\(2^nB\)。(e.g. 假设某应用申请了\(3B\)内存,显然并没有整数值n,使\(2^n = 3\) ,且\(3 \in [2^1,2^2]\),所以系统会去取一块大小为\(2^2B\)的内存块,将它分配给申请的应用,本次申请内存操作顺利完成。)

  那么当伙伴系统中没有如此“合适”的内存块时该怎么办呢?系统先会去寻找更大的内存块,如果找到了,则会将大内存块分裂成合适的内存块分配给应用。(e.g. 假设申请\(3B\)内存,此时系统中比\(3B\)大的最小内存块的大小为\(16B\),那么\(16B\)会被分裂成两块\(8B\)的内存块,一块放入内存池中,一块继续分裂成两块\(4B\)的内存块。两块\(4B\)的内存块,一块放入内存池中,一块分配给应用。至此,本次申请内存操作顺利完成。)

  如果系统没有找到更大的内存块,系统将会尝试合并较小的内存块,直到符合申请空间的大小。(e.g. 假设申请\(3B\)内存,系统检查内存池发现只有两个\(2B\)的内存块,那么系统将会把这两个\(2B\)的内存块合并成一块\(4B\)的内存块,并分配给应用。至此,本次申请内存操作顺利完成。)

  最后,当系统既没有找到大块内存,又无法成功合并小块内存时,就会通知应用内存不够,无法分配内存。

伙伴算法的数据结构

                                  MmioBuddyMemPool

┌─────────────────────────────────────────────────────────────────────────────────────┐
│                                                                                     │
│                                 pool_start_addr                                     │
│                                                                                     │
├─────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                     │
│                                    pool_size                                        │
│                                                                                     │
├─────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                     │
│                                                                                     │
│                                   free_regions                                      │
│                                                                                     │
│                                  ┌────────────┐                                     │
│                                  │            │     ┌───────┐     ┌────────┐        │
│                                  │ ┌────────┬─┼────►│       ├────►│        │        │
│                                  │ │  list  │ │     │ vaddr │     │ vaddr  │        │
│                                  │ │        │◄├─────┤       │◄────┤        │        │
│                  MmioFreeRegionList├────────┤ │     └───────┘     └────────┘        │
│                                  │ │num_free│ │                                     │
│                                  │ └────────┘ │  MmioBuddyAddrRegion                │
│          MMIO_BUDDY_MIN_EXP - 12 │      0     │                                     │
│                                  ├────────────┤                                     │
│                                  │      1     │                                     │
│                                  ├────────────┤                                     │
│                                  │      2     │                                     │
│                                  ├────────────┤                                     │
│                                  │      3     │                                     │
│                                  ├────────────┤                                     │
│                                  │     ...    │                                     │
│                                  ├────────────┤                                     │
│                                  │     ...    │                                     │
│                                  ├────────────┤                                     │
│          MMIO_BUDDY_MAX_EXP - 12 │     18     │                                     │
│                                  └────────────┘                                     │
│                                                                                     │
│                                                                                     │
│                                                                                     │
└─────────────────────────────────────────────────────────────────────────────────────┘
/// 最大的内存块为1G,其幂为30
const MMIO_BUDDY_MAX_EXP: u32 = PAGE_1G_SHIFT;
/// 最小的内存块为4K,其幂为12                                  
const MMIO_BUDDY_MIN_EXP: u32 = PAGE_4K_SHIFT;
/// 内存池数组的大小为18
const MMIO_BUDDY_REGION_COUNT: u32 = MMIO_BUDDY_MAX_EXP - MMIO_BUDDY_MIN_EXP + 1;

/// buddy内存池
pub struct MmioBuddyMemPool {
    /// 内存池的起始地址
    pool_start_addr: u64, 
    /// 内存池大小:初始化为1TB
    pool_size: u64,
    /// 空闲内存块链表数组
    /// MMIO_BUDDY_REGION_COUNT = MMIO_BUDDY_MAX_EXP - MMIO_BUDDY_MIN_EXP + 1
    free_regions: [SpinLock<MmioFreeRegionList>; MMIO_BUDDY_REGION_COUNT as usize],
}

/// 空闲内存块链表结构体
pub struct MmioFreeRegionList {
    /// 存储了空闲内存块信息的结构体的链表
    list: LinkedList<Box<MmioBuddyAddrRegion>>,
    /// 当前链表空闲块的数量
    num_free: i64,
}

/// mmio伙伴系统内部的地址区域结构体
pub struct MmioBuddyAddrRegion {
    /// 内存块的起始地址
    vaddr: u64,
}                                  
                  
设计思路

  DragonOS中,使用MmioBuddyMemPool结构体作为buddy(为表述方便,以下将伙伴算法简称为buddy)内存池的数据结构,其记录了内存池的起始地址(pool_start_addr)以及内存池中内存块的总大小(pool_size),同时其维护了大小为MMIO_BUDDY_REGION_COUNT的双向链表数组(free_regions)。free_regions中的各个链表维护了若干空闲内存块(MmioBuddyAddrRegion)。

  free_regions的下标(index)与内存块的大小有关。由于每个内存块大小都为\(2^{n}\) bytes,那么可以令\(exp = n\)。index与exp的换算公式如下:\(index = exp - 12\)。e.g. 一个大小为\(2^{12}\) bytes的内存块,其\(exp = 12\),使用上述公式计算得\(index = 12 -12 = 0\),所以该内存块会被存入free_regions[0].list中。通过上述换算公式,每次取出或释放\(2^n\)大小的内存块,只需要操作free_regions[n -12]即可。DragonOS中,buddy内存池最大的内存块大小为\(1G = 2^{30}bytes\),最小的内存块大小为 \(4K = 2^{12} bytes\),所以\(index\in[0,18]\)

  作为内存分配机制,buddy服务于所有进程,为了解决在各个进程之间实现free_regions中的链表数据同步的问题,free_regions中的链表类型采用加了 自旋锁(SpinLock)的空闲内存块链表(MmioFreeRegionList),MmioFreeRegionList中封装有真正的存储了空闲内存块信息的结构体的链表(list)和对应链表长度(num_free)。有了自选锁后,同一时刻只允许一个进程修改某个链表,如取出链表元素(申请内存)或者向链表中插入元素(释放内存)。

  MmioFreeRegionList中的元素类型为MmioBuddyAddrRegion结构体,MmioBuddyAddrRegion记录了内存块的起始地址(vaddr)。

伙伴算法内部api

P.S 以下函数均为MmioBuddyMemPool的成员函数。系统中已经创建了一个MmioBuddyMemPool类型的全局引用MMIO_POOL,如要使用以下函数,请以MMIO_POOL.xxx()形式使用,以此形式使用则不需要传入self。

函数名

描述

__create_region(&self, vaddr)

将虚拟地址传入,创建新的内存块地址结构体

__give_back_block(&self, vaddr, exp)

将地址为vaddr,幂为exp的内存块归还给buddy

__buddy_split(&self,region,exp,list_guard)

将给定大小为\(2^{exp}\)的内存块一分为二,并插入内存块大小为\(2^{exp-1}\)的链表中

__query_addr_region(&self,exp,list_guard)

从buddy中申请一块大小为\(2^{exp}\)的内存块

mmio_buddy_query_addr_region(&self,exp)

对query_addr_region进行封装,请使用这个函数,而不是__query_addr_region

__buddy_add_region_obj(&self,region,list_guard)

往指定的地址空间链表中添加一个内存块

__buddy_block_vaddr(&self, vaddr, exp)

根据地址和内存块大小,计算伙伴块虚拟内存的地址

__pop_buddy_block( &self, vaddr,exp,list_guard)

寻找并弹出指定内存块的伙伴块

__buddy_pop_region( &self, list_guard)

从指定空闲链表中取出内存区域

__buddy_merge(&self,exp,list_guard,high_list_guard)

合并所有\(2^{exp}\)大小的内存块

__buddy_merge_blocks(&self,region_1,region_2,exp,high_list_guard)

合并两个已经从链表中取出的内存块

伙伴算法对外api

函数名

描述

__mmio_buddy_init()

初始化buddy系统,在mmio_init()中调用,请勿随意调用

__exp2index(exp)

\(2^{exp}\)的exp转换成内存池中的数组的下标(index)

mmio_create(size,vm_flags,res_vaddr,res_length)

创建一块根据size对齐后的大小的mmio区域,并将其vma绑定到initial_mm

mmio_release(vaddr, length)

取消地址为vaddr,大小为length的mmio的映射并将其归还到buddy中

文件系统

DragonOS的文件系统模块由VFS(虚拟文件系统)及具体的文件系统组成。

todo: 由于文件系统模块重构,文档暂时不可用,预计在2023年4月10日前补齐。

备注

本文作者: 龙进

Email: longjin@DragonOS.org

概述

  在本文中,我们将介绍DragonOS文件系统的架构设计。

总览

  如下图所示,DragonOS的文件系统相关的机制主要包括以下几个部分:

  • 系统调用接口

  • 虚拟文件系统

    • 文件抽象(File)

    • 挂载文件系统(MountFS)

  • 具体的文件系统

            ┌─────────────────────────────────────────────────┐
            │                                                 │
Syscall:    │   sys_open, sys_read, sys_write, sys_close,     │
            │                                                 │
            │   sys_lseek, etc..                              │
            │                                                 │
            └───────────────────────┬─────────────────────────┘
                                    │
                                    │
    VFS:                     ┌──────▼─────┐
                             │            │
                             │    File    │
                             │            │
                             └──────┬─────┘
                                    │
                           ┌────────▼────────┐
                           │                 │
                           │     MountFS     │
                           │                 │
                           └────┬────────────┘
                                │
   Filesystems:   ┌─────────────┼─────────────┬────────────┐
                  │             │             │            │
            ┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼────┐ ┌─────▼─────┐
            │           │ │           │ │          │ │           │
            │    FAT    │ │   DevFS   │ │  ProcFS  │ │   RamFS   │
            │           │ │           │ │          │ │           │
            └───────────┘ └───────────┘ └──────────┘ └───────────┘

系统调用接口

  DragonOS的文件系统相关的系统调用接口主要包括以下几个:

  • sys_open:打开文件

  • sys_read:读取文件

  • sys_write:写入文件

  • sys_close:关闭文件

  • sys_lseek:定位文件指针

  • sys_mkdir:创建目录

  • sys_unlink_at:删除文件或目录(通过参数flag区分到底是删除文件还是目录)

  • sys_ioctl:控制设备 (未实现)

  • sys_fstat:获取文件状态(未实现)

  • sys_fsync:同步文件(未实现)

  • sys_ftruncate:截断文件(未实现)

  • sys_fchmod:修改文件权限(未实现)

  • 其他系统调用接口(未实现)

  关于接口的具体含义,可以参考 DragonOS系统调用接口

虚拟文件系统(VFS)

  VFS是DragonOS文件系统的核心,它提供了一套统一的文件系统接口,使得DragonOS可以支持多种不同的文件系统。VFS的主要功能包括:

  • 提供统一的文件系统接口

  • 提供文件系统的挂载和卸载机制(MountFS)

  • 提供文件抽象(File)

  • 提供文件系统的抽象(FileSystem)

  • 提供IndexNode抽象

  • 提供文件系统的缓存、同步机制(尚未实现)

  关于VFS的详细介绍,请见DragonOS虚拟文件系统

具体的文件系统

  DragonOS目前支持的文件系统包括:

  • FAT文件系统(FAT12、FAT16、FAT32)

  • DevFS

  • ProcFS

  • RamFS

VFS虚拟文件系统

在DragonOS中,VFS作为适配器,遮住了具体文件系统之间的差异,对外提供统一的文件操作接口抽象。

VFS是DragonOS文件系统的核心,它提供了一套统一的文件系统接口,使得DragonOS可以支持多种不同的文件系统。VFS的主要功能包括:

  • 提供统一的文件系统接口

  • 提供文件系统的挂载和卸载机制(MountFS)

  • 提供文件抽象(File)

  • 提供文件系统的抽象(FileSystem)

  • 提供IndexNode抽象

  • 提供文件系统的缓存、同步机制(尚未实现)

备注

本文作者: 龙进

Email: longjin@DragonOS.org

设计

  VFS的架构设计如下图所示:

                      ┌─────────┐
                      │         │
                      │  read   │
            File      │         │
                      │  write  │
             │        │         │
             │        │  ioctl  │
             │        │         │
             │        │  lseek  │
             │        │         │
             │        │  etc..  │
             │        └─────────┘
             │
             ▼        ┌──────────────────────────────────────────────────────────────────────────────┐
            MountFS   │ Maintain the mount tree and handle the mounting of file systems.             │
               │      │    In particular, it handles the "crossing file system boundaries" condition │
               │      │    while doing "lookup" or "find" operations.                                │
               │      └──────────────────────────────────────────────────────────────────────────────┘
               │
               │
               │
Filesystems:   │
               │
               ▼      ┌────────────────────────────────────────────────────────────────────┐
          xxxFSInode  │ Implement corresponding operations based on different file systems │
                      └────────────────────────────────────────────────────────────────────┘
1. File

  File结构体是VFS中最基本的抽象,它代表了一个打开的文件。每当进程打开了一个文件,就会创建一个File结构体,用于维护该文件的状态信息。

2. Traits

  对于每个具体文件系统,都需要实现以下的trait:

  • FileSystem:表明某个struct是一个文件系统

  • IndexNode: 表明某个struct是一个索引节点

  一般情况下,FileSystem和IndexNode是一对一的关系,也就是,一个文件系统对应一种IndexNode。但是,对于某些特殊的文件系统,比如DevFS,根据不同的设备类型,会有不同的IndexNode,因此,FileSystem和IndexNode是一对多的关系。

3. MountFS

  挂载文件系统虽然实现了FileSystem和IndexNode这两个trait,但它并不是一个“文件系统”,而是一种机制,用于将不同的文件系统挂载到同一个文件系统树上. 所有的文件系统要挂载到文件系统树上,都需要通过MountFS来完成。也就是说,挂载树上的每个文件系统结构体的外面,都套了一层MountFS结构体。

  对于大部分的操作,MountFS都是直接转发给具体的文件系统,而不做任何处理。同时,为了支持跨文件系统的操作,比如在目录树上查找,每次lookup操作或者是find操作,都会通过MountFSInode的对应方法,判断当前inode是否为挂载点,并对挂载点进行特殊处理。如果发现操作跨越了具体文件系统的边界,MountFS就会将操作转发给下一个文件系统,并执行Inode替换。这个功能的实现,也是通过在普通的Inode结构体外面,套一层MountFSInode结构体来实现的。

VFS API文档

SysFS

备注

本文作者:黄厅

Email: huangting@DragonOS.org

1. SysFS和设备驱动模型

1.1. 设备、驱动、总线等彼此之间关系错综复杂

  如果想让内核运行流畅,那就必须为每个模块编码实现这些功能。如此一来,内核将变得非常臃肿、冗余。而设备模型的理念即是将这些代码抽象成各模块共用的框架,这样不但代码简洁了,也可让设备驱动开发者摆脱这本让人头痛但又必不可少的一劫,将有限的精力放于设备差异性的实现。

  设备模型恰是提供了一个模板,一个被证明过的最优的思路和流程,这减少了开发者设计过程中不必要的错误,也给以后的维护扫除了障碍。

1.2. sysfs是一个基于内存的文件系统,它的作用是将内核信息以文件的方式提供给用户程序使用。

  sysfs可以看成与proc,devfs和devpty同类别的文件系统,该文件系统是虚拟的文件系统,可以更方便对系统设备进行管理。它可以产生一个包含所有系统硬件层次视图,与提供进程和状态信息的proc文件系统十分类似。sysfs把连接在系统上的设备和总线组织成为一个分级的文件,它们可以由用户空间存取,向用户空间导出内核的数据结构以及它们的属性。

2. DragosOS中的设备驱动模型

2.1 由设备和驱动构成基本元素
2.1.1. 设备
/// @brief: 所有设备都应该实现该trait
pub trait Device: Any + Send + Sync + Debug {}

  DragonOS采用全局设备管理器管理系统中所有的设备。

/// @brief Device管理器
#[derive(Debug, Clone)]
pub struct DeviceManager {
    devices: BTreeMap<IdTable, Arc<dyn Device>>, // 所有设备
    sys_info: Option<Arc<dyn IndexNode>>,  // sys information
}
2.1.2. 驱动
/// @brief: 所有驱动驱动都应该实现该trait
pub trait Driver: Any + Send + Sync + Debug {}

  同样的,驱动也使用全局的驱动管理器来管理

/// @brief: 驱动管理器
#[derive(Debug, Clone)]
pub struct DriverManager {
    drivers: BTreeMap<IdTable, Arc<dyn Driver>>, // 所有驱动
    sys_info: Option<Arc<dyn IndexNode>>, // sys information
}
2.2. 总线

  总线属于设备的一种类型,同样需要驱动来初始化,同时由于总线的特殊性,使用全局的总线管理器来进行管理。

/// @brief: 总线驱动trait,所有总线驱动都应实现该trait
pub trait BusDriver: Driver {}

/// @brief: 总线设备trait,所有总线都应实现该trait
pub trait Bus: Device {}

/// @brief: 总线管理结构体
#[derive(Debug, Clone)]
pub struct BusManager {
    buses: BTreeMap<IdTable, Arc<dyn Bus>>,          // 总线设备表
    bus_drvs: BTreeMap<IdTable, Arc<dyn BusDriver>>, // 总线驱动表
    sys_info: Option<Arc<dyn IndexNode>>,            // 总线inode
}

  可以看到,每个管理器中均存在sys_info,设备模型通过该成员与sysfs建立联系,sys_info指向sysfs中唯一的inode。对于device而言,对应sysfs下的devices文件夹,其他亦是如此。

3. 驱动开发如何进行

  以平台总线platform为例,platform总线是一种虚拟总线,可以对挂载在其上的设备和驱动进行匹配,并驱动设备。该总线是一类设备,同时也是一类总线,编程时需要创建该设备实例,并为设备实例实现Device trait和Bus trait,以表明该结构是一类总线设备。同时,应该实现总线上的匹配规则,不同的总线匹配规则不同,该总线采用匹配表方式进行匹配,设备和驱动都应该存在一份匹配表,表示驱动支持的设备以及设备支持的驱动。

pub struct CompatibleTable(BTreeSet<&'static str>);

  对于bus设备而言,需要调用bus_register,将bus注册进系统,并在sysfs中可视化。

/// @brief: 总线注册,将总线加入全局总线管理器中,并根据id table在sys/bus和sys/devices下生成文件夹
/// @parameter bus: Bus设备实体
/// @return: 成功:()   失败:DeviceError
pub fn bus_register<T: Bus>(bus: Arc<T>) -> Result<(), DeviceError> {
    BUS_MANAGER.add_bus(bus.get_id_table(), bus.clone());
    match sys_bus_register(&bus.get_id_table().to_name()) {
        Ok(inode) => {
            let _ = sys_bus_init(&inode);
            return device_register(bus);
        }
        Err(_) => Err(DeviceError::RegisterError),
    }
}

  通过bus_register源码可知,该函数不仅在sysfs/bus下生成总线文件夹,同时内部调用device_register,该函数将总线加入设备管理器中,同时在sys/devices下生成设备文件夹。

内核调试模块

这里是DragonOS的内核调试模块文档。

内核栈traceback

简介

  内核栈traceback的功能位于kernel/debug/traceback/文件夹中。为内核态提供traceback的功能,打印调用栈到屏幕上。


API

void traceback(struct pt_regs * regs)
作用

  该接口定义于kernel/debug/traceback/traceback.h中,将会对给定内核栈进行traceback,并打印跟踪结果到屏幕上。

参数
regs

  要开始追踪的第一层内核栈栈帧(也就是栈的底端)


实现原理

  当内核第一次链接之后,将会通过Makefile中的命令,运行kernel/debug/kallsyms程序,提取内核文件的符号表,然后生成kernel/debug/kallsyms.S。该文件的rodata段中存储了text段的函数的符号表。接着,该文件将被编译为kallsyms.o。最后,Makefile中再次调用ld命令进行链接,将kallsyms.o链接至内核文件。

  当调用traceback函数时,其将遍历该符号表,找到对应的符号并输出。


未来发展方向

  • 增加写入到日志文件的功能

内核测试

本章节将介绍如何测试内核,包括手动测试以及自动测试。

我们需要尽可能的对内核进行完善的测试,以便我们能够更好的保证内核的稳定性,且减少其他模块的debug难度。

设置完善的测试用例能帮助我们尽可能的检测到问题,防止我们在写新的模块的时候,被已有的模块的一些藏得很深的bug“背刺一刀”。

由于您难以借助GDB等工具进行调试,因此在内核中进行手动测试比应用程序测试要困难一些。

对于一些模块,我们可以使用编写代码进行单元测试,并输出异常信息。遗憾的是,并非所有模块都可以进行单元测试。比如我们常见的内存管理、进程管理等模块都不能进行单元测试。

内核测试框架

  DragonOS提供了一个测试框架,旨在对内核的一些模块进行自动化测试。内核测试框架位于ktest/下。

  我们可以使用这个测试框架,按照规范编写测试代码,然后在合适的地方使用ktest_start()创建一个全新的内核线程并发起测试。

使用方法

创建自动测试程序

  假如您要对kfifo模块进行自动测试,您可以在ktest/下,创建一个名为test-kfifo.c的测试文件,并编写Makefile。

  在test-kfifo.c中,包含ktest_utils.hktest.h这两个头文件。

  您需要像下面这样,在test-kfifo.c中,创建一个测试用例函数表,并把测试用例函数填写到其中:

static ktest_case_table kt_kfifo_func_table[] = {
    ktest_kfifo_case0_1,
};

  然后创建一个函数,作为kfifo测试的主函数。请注意,您需要将它的声明添加到ktest.h中。

uint64_t ktest_test_kfifo(uint64_t arg)
{
    kTEST("Testing kfifo...");
    for (int i = 0; i < sizeof(kt_kfifo_func_table) / sizeof(ktest_case_table); ++i)
    {
        kTEST("Testing case %d", i);
        kt_kfifo_func_table[i](i, 0);
    }
    kTEST("kfifo Test done.");
    return 0;
}
编写测试用例

  您可以创建一个或多个测试用例,命名为:ktest_kfifo_case_xxxxx. 在这个例子中,我创建了一个测试用例,命名为:ktest_kfifo_case0_1.如下所示:

static long ktest_kfifo_case0_1(uint64_t arg0, uint64_t arg1)

  这里最多允许我们传递两个参数到测试函数里面。

  那么,我们该如何编写测试用例呢?

  我们主要是需要设置一些情节,以便能测试到目标组件的每个情况。为了检验模块的行为是否符合预期,我们需要使用assert(condition)宏函数,对目标condition进行校验。若condition为1,则表明测试通过。否则,将会输出一行assert failed信息到屏幕上。

发起测试

  我们可以在pid≥1的内核线程中发起测试。由于DragonOS目前尚不完善,您可以在process/process.c中的initial_kernel_thread()函数内,发起内核自动测试。具体的代码如下:

ktest_start(ktest_test_kfifo, 0);

  这样就发起了一个内核测试,它会创建一个新的内核线程进行自动测试,您不必担心第一个内核线程会被阻塞。   

API文档

ktest_start

pid_t ktest_start(uint64_t (*func)(uint64_t arg), uint64_t arg)

描述

  开启一个新的内核线程以进行测试

参数

func

  测试函数. 新的测试线程将会执行该函数,以进行测试。

arg

  传递给测试函数的参数

返回值

  测试线程的pid

assert

#define assert(condition)

描述

  判定condition是否为1,若不为1,则输出一行错误日志信息:

[ kTEST FAILED ] Ktest Assertion Failed, file:%s, Line:%d
kTEST

#define kTEST(...)

描述

  格式化输出一行以[ kTEST ] file:%s, Line:%d开头的日志信息。

ktest_case_table

typedef long (*ktest_case_table)(uint64_t arg0, uint64_t arg1)

描述

  ktest用例函数的类型定义。

处理器架构

该部分文档提供了和处理器架构相关的一些编程实现细节的描述。

x86-64相关文档

USB Legacy支持

简介

  usb legacy support指的是,由BIOS提供的,对USB鼠标、USB键盘的支持。在支持并启用USB Legacy Support的计算机上,USB鼠标、键盘由BIOS提供模拟,在操作系统看来,就像接入了PS/2鼠标、键盘一样。

相关
  • 在初始化USB控制器时,需要关闭它的USB Legacy Support

LibC文档

简介

  LibC是连接用户程序和操作系统的纽带,LibC为应用程序提供了一系列标准库函数。应用程序可以通过DragonOS的LibC,快速地与操作系统进行交互。 DragonOS的LibC主要依照POSIX 2008规范实现,与Linux下的glibC具有相似之处。

API文档

ctype.h

函数列表(这里只列出已实现的函数):
``int isprint(int c)`` : 传入一个字符,判断是否可以被输出  

``int islower(int c)`` : 传入一个字符,判断是否是小写字母  

``int isupper(int c)`` : 传入一个字符,判断是否是大写字母  

``int isalpha(int c)`` : 传入一个字符,判断是否是字母  

``int isdigit(int c)`` : 传入一个字符,判断是否是数字  

``int toupper(int c)`` : 传入一个小写字母字符,返回这个字母的大写形式  

``int tolower(int c)`` : 传入一个大写字母字符,返回这个字母的小写形式  

``int isspace(int c)``  : 传入一个字符,判断是否是空白字符  
宏定义:
### 暂无用处  

``#define _U 01`` 

``#define _L 02``  

``#define _N 04``  

``#define _S 010``  

``#define _P 020``  

``#define _C 040``  

``#define _X 0100``  

``#define _B 0200`` 

dirent.h

简介
与文件夹有关的头文件。
结构体列表:
``struct DIR`` : 
    
    变量列表:

    ``int fd`` : 文件夹id(不推荐修改)

    ``int buf_pos`` : 文件夹缓冲区指针的位置
    
    ``int buf_len`` : 文件夹缓冲区的大小(默认为256)

``struct dirent`` : 
    
    变量列表: 

    ``ino_t(see libc/sys/types.h) ino`` : 文件序列号(不推荐修改)

    ``off_t d_off`` : dir偏移量(不推荐修改)

    ``unsigned short d_reclen`` : 文件夹中的记录数

    ``unsigned char d_type`` : 目标的类型(有可能是文件,文件夹,磁盘)

    ``char d_name[]`` : 目标的名字
函数列表(这里只列出已实现的函数):
``DIR opendir(const char *path)``  
    
    传入文件夹的路径,返回文件夹结构体

``int closedir(DIR *dirp)`` 

    传入文件夹结构体,关闭文件夹,释放内存

    若失败,返回-1

``dirent readdir(DIR *dir)``

    传入文件夹结构体,读入文件夹里的内容,并打包为dirent结构体返回
宏定义:
文件夹类型:

``#define VFS_IF_FILE (1UL << 0)``

``#define VFS_IF_DIR (1UL << 1)``

``#define VFS_IF_DEVICE (1UL << 2)``

缓冲区长度的默认值

``#define DIR_BUF_SIZE 256``

errno.h

简介:
共享错误号码
属性:
``extern int errno`` : 通用错误代码
宏定义(复制自代码,了解即可):
#define E2BIG 1            /* 参数列表过长,或者在输出buffer中缺少空间 或者参数比系统内建的最大值要大 Argument list too long.*/

#define EACCES 2           /* 访问被拒绝 Permission denied*/ 

#define EADDRINUSE 3       /* 地址正在被使用 Address in use.*/

#define EADDRNOTAVAIL 4    /* 地址不可用 Address  not available.*/

#define EAFNOSUPPORT 5     /* 地址family不支持 Address family not supported.*/

#define EAGAIN 6           /* 资源不可用,请重试。 Resource unavailable, try again (may be the same value as [EWOULDBLOCK]).*/

#define EALREADY 7         /* 连接已经在处理 Connection already in progress.*/

#define EBADF 8            /* 错误的文件描述符 Bad file descriptor.*/

#define EBADMSG 9          /* 错误的消息 Bad message.*/


#define EBUSY 10           /* 设备或资源忙 Device or resource busy.*/

#define ECANCELED 11       /* 操作被取消 Operation canceled.*/

#define ECHILD 12          /* 没有子进程 No child processes.*/

#define ECONNABORTED 13    /* 连接已断开 Connection aborted.*/

#define ECONNREFUSED 14    /* 连接被拒绝 Connection refused.*/

#define ECONNRESET 15      /* 连接被重置 Connection reset.*/

#define EDEADLK 16         /* 资源死锁将要发生 Resource deadlock would occur.*/

#define EDESTADDRREQ 17    /* 需要目标地址 Destination address required.*/

#define EDOM 18            /* 数学参数超出作用域 Mathematics argument out of domain of function.*/

#define EDQUOT 19          /* 保留使用 Reserved*/



#define EEXIST 20          /* 文件已存在 File exists.*/

#define EFAULT 21          /* 错误的地址 Bad address*/

#define EFBIG 22           /* 文件太大 File too large.*/

#define EHOSTUNREACH 23    /* 主机不可达 Host is unreachable.*/

#define EIDRM 24           /* 标志符被移除 Identifier removed.*/

#define EILSEQ 25          /* 不合法的字符序列 Illegal byte sequence.*/

#define EINPROGRESS 26     /* 操作正在处理 Operation in progress.*/

#define EINTR 27           /* 被中断的函数 Interrupted function.*/

#define EINVAL 28          /* 不可用的参数 Invalid argument.*/

#define EIO 29             /* I/O错误 I/O error.*/



#define EISCONN 30         /* 套接字已连接 Socket is connected.*/

#define EISDIR 31          /* 是一个目录 Is a directory*/

#define ELOOP 32           /* 符号链接级别过多 Too many levels of symbolic links.*/

#define EMFILE 33          /* 文件描述符的值过大 File descriptor value too large.*/

#define EMLINK 34          /* 链接数过多 Too many links.*/

#define EMSGSIZE 35        /* 消息过大 Message too large.*/

#define EMULTIHOP 36       /* 保留使用 Reserved.*/

#define ENAMETOOLONG 37    /* 文件名过长 Filename too long.*/

#define ENETDOWN 38        /* 网络已关闭 Network is down.*/

#define ENETRESET 39       /* 网络连接已断开 Connection aborted by network.*/

#define ENETUNREACH 40     /* 网络不可达 Network unreachable.*/

#define ENFILE 41          /* 系统中打开的文件过多 Too many files open in system.*/

#define ENOBUFS 42         /* 缓冲区空间不足 No buffer space available.*/

#define ENODATA 43         /* 队列头没有可读取的消息 No message is available on the STREAM head read queue.*/

#define ENODEV 44          /* 没有指定的设备 No such device.*/

#define ENOENT 45          /* 没有指定的文件或目录 No such file or directory.*/

#define ENOEXEC 46         /* 可执行文件格式错误 Executable file format error.*/

#define ENOLCK 47          /* 没有可用的锁 No locks available.*/

#define ENOLINK 48         /* 保留 Reserved.*/


#define ENOMEM 49          /* 没有足够的空间 Not enough space.*/


#define ENOMSG 50          /* 没有期待类型的消息 No message of the desired type.*/

#define ENOPROTOOPT 51     /* 协议不可用 Protocol not available.*/

#define ENOSPC 52          /* 设备上没有空间 No space left on device.*/

#define ENOSR 53           /* 没有STREAM资源  No STREAM resources.*/

#define ENOSTR 54          /* 不是STREAM Not a STREAM*/

#define ENOSYS 55          /* 功能不支持 Function not supported.*/

#define ENOTCONN 56        /* 套接字未连接 The socket is not connected.*/

#define ENOTDIR 57         /* 不是目录 Not a directory.*/

#define ENOTEMPTY 58       /* 目录非空 Directory not empty.*/

#define ENOTRECOVERABLE 59 /* 状态不可覆盖 State not recoverable.*/



#define ENOTSOCK 60        /* 不是一个套接字 Not a socket.*/

#define ENOTSUP 61         /* 不被支持 Not supported (may be the same value as [EOPNOTSUPP]).*/

#define ENOTTY 62          /* 不正确的I/O控制操作 Inappropriate I/O control operation.*/

#define ENXIO 63           /* 没有这样的设备或地址 No such device or address.*/

#define EOPNOTSUPP 64      /* 套接字不支持该操作 Operation not supported on socket (may be the same value as [ENOTSUP]).*/

#define EOVERFLOW 65       /* 数值过大,产生溢出 Value too large to be stored in data type.*/

#define EOWNERDEAD 66      /* 之前的拥有者挂了 Previous owner died.*/

#define EPERM 67           /* 操作不被允许 Operation not permitted.*/

#define EPIPE 68           /* 断开的管道 Broken pipe.*/

#define EPROTO 69          /* 协议错误 Protocol error.*/



#define EPROTONOSUPPORT 70 /* 协议不被支持 Protocol not supported.*/

#define EPROTOTYPE 71      /* 对于套接字而言,错误的协议 Protocol wrong type for socket.*/

#define ERANGE 72          /* 结果过大 Result too large.*/

#define EROFS 73           /* 只读的文件系统 Read-only file system.*/

#define ESPIPE 74          /* 错误的寻道 Invalid seek.*/

#define ESRCH 75           /* 没有这样的进程 No such process.*/

#define ESTALE 76          /* 保留 Reserved.*/

#define ETIME 77           /* 流式ioctl()超时 Stream ioctl() timeout*/

#define ETIMEDOUT 78       /* 连接超时 Connection timed out.*/

#define ETXTBSY 79         /* 文本文件忙 Text file busy.*/



#define EWOULDBLOCK 80     /* 操作将被禁止 Operation would block (may be the same value as [EAGAIN]).*/

#define EXDEV 81           /* 跨设备连接 Cross-device link.*/

fcntl.h

简介
文件操作
函数列表:
``int open(const char * path,int options, ...)``

传入文件路径,和文件类型(详细请看下面的宏定义),将文件打开并返回文件id。
宏定义(粘贴自代码,了解即可):
#define O_RDONLY 00000000 // Open Read-only

#define O_WRONLY 00000001 // Open Write-only

#define O_RDWR 00000002   // Open read/write

#define O_ACCMODE 00000003 // Mask for file access modes


#define O_CREAT 00000100 // Create file if it does not exist

#define O_EXCL 00000200 // Fail if file already exists

#define O_NOCTTY 00000400 // Do not assign controlling terminal

#define O_TRUNC 00001000 // 文件存在且是普通文件,并以O_RDWR或O_WRONLY打开,则它会被清空

#define O_APPEND 00002000   // 文件指针会被移动到文件末尾

#define O_NONBLOCK 00004000 // 非阻塞式IO模式


#define O_EXEC 00010000 // 以仅执行的方式打开(非目录文件)

#define O_SEARCH 00020000   // Open the directory for search only

#define O_DIRECTORY 00040000 // 打开的必须是一个目录

#define O_NOFOLLOW 00100000 // Do not follow symbolic links

math.h

简介:
数学库
函数列表:
``double fabs(double x)`` : 返回 x 的绝对值

``float fabsf(float x)`` : 返回 x 的绝对值

``long double fabsl(long double x)``: 返回 x 的绝对值

``double round(double x)`` 四舍五入 x

``float roundf(float x)`` 四舍五入 x

``long double roundl(long double x)`` 四舍五入 x

``int64_t pow(int64_t x,int y)`` 返回 x 的 y 次方 

stdio.h

简介:
向标准输入输出里操作
函数列表:
``int64_t put_string(char *str, uint64_t front_color, uint64_t bg_color)``

    输出字符串(带有前景色,背景色)

``int printf(const char *fmt, ...)``
    
    就是正常的 ``printf`` 

``int sprintf(char *buf,const char *fmt,...)```

    就是正常的 ``sprintf``

``int vsprintf(char *buf,const char *fmt,va_list args)``

    格式化,不建议调用,请用 printf 或 sprintf 替代。
宏定义
### 字体颜色的宏定义

    ``#define COLOR_WHITE 0x00ffffff  //白``

    ``#define COLOR_BLACK 0x00000000  //黑``

    ``#define COLOR_RED 0x00ff0000    //红``

    ``#define COLOR_ORANGE 0x00ff8000 //橙``

    ``#define COLOR_YELLOW 0x00ffff00 //黄``

    ``#define COLOR_GREEN 0x0000ff00  //绿``

    ``#define COLOR_BLUE 0x000000ff   //蓝``

    ``#define COLOR_INDIGO 0x0000ffff //靛``

    ``#define COLOR_PURPLE 0x008000ff //紫``
### 无需使用
    
    ``#define SEEK_SET 0 /* Seek relative to start-of-file */``
    
    ``#define SEEK_CUR 1 /* Seek relative to current position */``
    
    ``#define SEEK_END 2 /* Seek relative to end-of-file */``

    ``#define SEEK_MAX 3``

printf.h

不建议引用,需要 printf 函数请引用 stdio.h

stddef.h

简介:
定义了关于指针的常用类型
定义:
``typedef __PTDIFF_TYPE__ ptrdiff_t``  : 两个指针相减的结果类型

``NULL ((void *) 0)`` : 空指针

stdlib.h

简介:
一些常用函数
函数列表:
``void *malloc(ssize_t size)`` : 普通的 ``malloc``

``void free(void *ptr)`` : 释放内存

``int abs(int x)`` : x 的绝对值

``long labs(long x)`` : x 的绝对值

``long long llabs(long long x)`` : x 的绝对值

``int atoi(const char *str)`` 字符串转数字

``void exit(int status)`` : 普通的 ``exit``

string.h

简介:
字符串操作
函数列表:
``size_t strlen(const char *s)`` : 返回字符串长度

``int strcmp(const char *a,const char *b)`` 比较字符串的字典序

``char* strncpy(char *dst,const char *src,size_t count)`` 

    拷贝制定字节数的字符串

    dst: 目标地址

    src: 原字符串

    count: 字节数

``char* strcpy(char *dst,const char *src)`` : 复制整个字符串

``char* strcat(char *dest,const char* src)`` : 拼接两个字符串

time.h

简介:
时间相关

时刻以纳秒为单位

结构体:
``struct timespec`` : 时间戳
    
    ### 变量列表:
        
        ``long int tv_sec`` : 秒
        
        ``long int tv_nsec`` : 纳秒 
宏定义:
``#define CLOCKS_PER_SEC 1000000`` 每一秒有1000000个时刻(纳秒)
函数列表:
``int nanosleep(const struct timespec *rdtp,struct timespec *rmtp)``

    休眠指定时间

    rdtp : 指定休眠的时间

    rmtp : 返回剩余时间

``clock_t clock()`` : 获得当前系统时间

unistd.h

简介:
也是一些常用函数
函数列表:
``int close(int fd)`` : 关闭文件

``ssize_t read(int fd,void *buf,size_t count)`` : 从文件读取
    
    传入文件id,缓冲区,以及字节数
    
    返回成功读取的字节数

``ssize_t write(int fd,void const *buf,size_t count)`` : 写入文件

    传入文件id,缓冲区,字节数

    返回成功写入的字节数

``off_t lseek(int fd,off_t offset,int whence)`` : 调整文件访问位置

    传入文件id,偏移量,调整模式

    返回结束后的文件访问位置

``pid_t fork(void)`` : fork 当前进程

``pid_t vfork(void)`` : fork 当前进程,与父进程共享 VM,flags,fd

``uint64_t brk(uint64_t end_brk)`` : 将堆内存调整为end_brk
    
    若end_brk 为 -1,返回堆区域的起始地址

    若end_brk 为 -2,返回堆区域的结束地址

    否则调整堆区的结束地址域,并返回错误码

``void *sbrk(int64_t increment)`` :  
    
    将堆内存空间加上offset(注意,该系统调用只应在普通进程中调用,而不能是内核线程)

    increment : 偏移量

``int64_t chdir(char *dest_path)``

    切换工作目录(传入目录路径)

``int execv(const char* path,char * const argv[])`` : 执行文件
    path : 路径
    argv : 执行参数列表

``extern int usleep(useconds_t usec)`` : 睡眠usec微秒

这里是所有libc头文件的集合,在代码里可以这样引用: #include<libc/src/xxx.h>

设计文档

[内容待完善]

系统调用API

简介

参与开发

DragonOS社区欢迎您的加入!学习技术本身固然重要,但是以下这些文档将会帮助您了解DragonOS社区需要什么。

阅读这些文档将会帮助您参与到开发之中,并且能让您的代码能更快合并到主线。

开发流程介绍

  作为一名想要参与开发的新人,您可能迫切想要了解如何参与开发,仔细阅读这篇文章将能帮助您了解整个开发流程,以及一些注意事项。

  注:本文参考了Linux文档中的一些思想、内容,非常感谢Linux社区的工作者们的经验!

1.概要

对于新人而言,参与开发的过程主要包括以下几步:

  • 运行DragonOS:按照文档:构建DragonOS中的教程,编译DragonOS,并成功运行。在运行过程中,如果有任何的疑问,欢迎您在交流群或者BBS上进行反馈!

  • 联系Maintainer:您可以通过邮箱longjin@DragonOS.org或者QQ184829088与龙进取得联系,或者是对应的模块的开发者进行联系(目前您可以通过发行日志上的邮箱与他们建立联系,在将来我们将编写一份“Maintainers List”以便于您能快速找到要联系的人)。 为了节省您的时间,请简要说明:

    • 如何称呼您

    • 您目前掌握的技术能力

    • 您希望为DragonOS贡献什么功能,或者进行某个方面的开发,亦或者是想要按照社区的需要来参与新功能开发及bug的修复。

    • 如果您是来自高校/科研单位/企业/组织的代表,希望与社区开展合作研究、开发。那么,除使用QQ交流之外,还请麻烦您通过您的教师邮箱/学生邮箱/企业邮箱向contact@DragonOS.org发送一封相关内容的邮件!这么做的目的是为了确认您是来自您的单位,而不是网络上其他人员冒充您的身份。

  • 加入工作群:在进一步了解,确认您愿意参与开发后,我们将邀请您加入工作群。

  • 开始开发:正式开始代码开发工作。

备注

一些小功能的改进以及Bug修复并不一定需要提前与社区建立正式的联系,对于这些patch,您可以直接开发,然后在Github上进行Pull Request. 这也是可以的。

但是,如果您愿意的话,与Maintainer联系会是一个更好的选择。

2.开发过程是如何运作的?

  如今的DragonOS由于正处于开发的早期阶段,开发者数量不超过50人,因此,现在DragonOS的开发过程是通过比较松散的方式组织起来的。

2.1.版本发布周期

  自从2022年11月6日,DragonOS发布第一个版本以来,版本发布就被定义为15~21天发布一个更新版本。由于开发人员数量仍然较少,因此,目前这个时间是21天。我们将版本号定义为x.y.z的格式,每21天发布一个z版本. 在积累了2~3个月后,当DragonOS引入了足够的新功能,则发布一个y版本。请注意,我们仍未定义x版本的发行周期。当前,x版本号仍然是0

  创建没有BUG的、具有尽可能少BUG的代码,是每个开发者的目标之一。我们希望在每个y版本发布时,能够修复已知的问题。然而,由于在操作系统中,影响问题的变量太多了,以至于我们只能尽全力去减少BUG,我们无法保证y版本不存在bug.

2.2.每个补丁的生命周期

  当您向DragonOS的仓库发起一次PR,那么这次PR就是一个补丁。我们会对您的补丁进行Review,以确保每个补丁都实现了一个希望在主线中进行的更改。并且,Maintainer或者感兴趣的小伙伴会对您的补丁提出修改意见。当时机合适时,您的代码将被合入主线。

  如果您的补丁的规模比较小,那么,它将会比较快的被合入主线。如果补丁的规模较大,或者存在一些争议,那么我们需要对其进行进一步的讨论及修改、审查,直到它符合要求。

  每个Patch都会经历这么一个过程(这只是一个概述,详细的描述请看后文):

  • 设计:在这个阶段,我们将明确,这个补丁将要实现什么功能,或者是解决什么问题,以及我们将要采用什么样的方式去完成它。通常来说,这项工作是开发者自己完成的。但是,我们建议您,在设计了这个补丁之后,能够把您的设计方案公开,和大家讨论这项工作。 闭门造车容易出错,在与大家沟通的过程中,则能及早的发现设计上的错误,从而节省很多的时间。

  • 代码编写:经过了设计阶段,您已经能够明白自己要实现的到底是一个什么样的东西。您在这个阶段进行代码编写、调试。

  • 代码审查:当完成代码编写后,您可以通过Github发起一个PR,然后您的补丁进入了代码审查阶段。在这一阶段,开发者们,或者是Maintainer会和您进行沟通交流,对您的代码进行评论,以及对发现的问题提出修改建议。

  • 合并主线:如果一切顺利,那么代码将会被合并入主线。若该补丁在合并主线后,被发现具有较大的问题,那么它可能会被回退。重新进入前面的阶段,进行修改。

  • 长期维护:虽然说,代码被合并之后,原来的开发人员可能会在很久一段时间后,不再理会这些代码,但是这种行为可能会给其他开发者们留下不好的印象。其实,当补丁被合并入主线后,其他开发人员会尝试继续维护这些代码,这样能够很大程度的减轻您的维护负担。但是,如果想要这些代码能够长期的被保留下来,持续的发光发热,那么离不开原始开发人员的支持(不然的话,后来的人可能难以理解、维护这些代码),这是一件很有意义的事情。

  对于没有参与过开源项目的同学来说,他们可能会想当然的,简单的把上述流程简化成合并主线这一个步骤,这样是不可取的。因为这样会导致很多问题,包括但不限于“写了代码但是效果很差”、“写的东西由于无法满足项目的需求,而不能被合并”。

2.3.开发工具

  从上面的描述可以看出,为了让开发人员们能高效地进行协作,那么必须使用版本控制工具来管理这个过程。目前,DragonOS使用Git来进行源代码管理。它是一个非常优秀的工具,这是不必多说的。对于每个开发者而言,Git的使用是一项必备的技能;哪怕您只是想学习DragonOS的源代码,您也需要git来获取、同步最新的代码。虽然Git的使用,对于新手来说,有些困难,但是经过一些时间的学习后,还是可以掌握的。

  git的官方网站为https://git-scm.com/

2.4.沟通交流

  DragonOS的主要开发工作是通过飞书以及bbs进行的。对于正准备参与的开发者而言,您可以加入我们的交流讨论QQ群,具体的群号可以在 与社区建立联系 一文中找到。

  何时使用即时通讯软件? 我们在飞书上创建了工作群,为提高即时沟通的效率,这个群仅供那些真正有意愿、且正在进行或准备进行(能够保证会进行)代码开发的开发者们加入。

  何时使用BBS? 对于一些正式的、需要大家广泛参与,或者是能够帮助尚未参与开发的同学了解当前的开发进度的主题,请您在https://bbs.DragonOS.org上,使用类似于写信件一样的,正式的语言,完整地描述清楚您想表达的内容。这样有助于更多的人快速明白您要表达的是什么,也能提高整体的沟通效率。并且,bbs能够长期保存以往的帖子,这样后来者能更方便的了解“当初开发的时候,人们究竟是怎么想的”。

  关于交流讨论会 除由于法定节假日放假,或特殊情况以外,我们每周末都会召开线上交流讨论会,同步介绍每周的进展。社区成员可以在这里分享自己的方案设计或是一些操作系统相关的知识(分享的话,需要提前跟会议主持人提出,以便妥善安排)。

  如何提问? 下面这些建议能够帮助您与他人开展高效率的对话:

  • 对于具有主题性的问题,在bbs上发帖进行讨论。 这样能够让讨论更具有目标性。当谈论到新的主题的时候,请开一个新的帖子,并在原来的帖子中,添加对特定的子问题所在的主题的链接。

  • 请礼貌的交流。 文明的语言能够减少不必要的冲突。技术意见上的冲突是思维的碰撞,但是如果涉及到了不文明的语言,或者在非技术层面,对他人进行攻击,这将破坏和谐讨论的氛围,这是我们反对的。如果有人试图激怒你,请忽略他的消息,别理他就好了。

  • 在提问之前,请确保您已经搜索了bbs以及互联网上的解决方案,并描述清楚您的问题的上下文情景、您的思考以及网络上的解决方案。 一些开发人员会对“明显没有进行认真思考”的问题,表现出不耐烦的态度(因为未经思考的问题会浪费他们大量的时间)。

  • 当别人向您提问时,请您耐心听他人的问题。如果您认为对方的问题过于简单或是未经思考,还请您为对方指个路,告诉对方,在哪些地方,使用什么样的方式搜索,能够得到对解决问题有帮助的资料。有时候,新手需要的是一个指路人,他会非常感谢您的!

2.5.如何入门开发?

  DragonOS原采用C语言进行开发,目前正在用Rust重构原有代码、开发新的模块,也就是说,除非您要进行对C语言代码的BUG修复,否则,其余的开发工作,我们都建议您通过Rust完成。因为,它能从语言层面解决那些让我们头疼的内存安全问题。从长期来看,能够提升开发效率以及软件质量。

  如何开发第一个补丁,是一个非常常见的问题。可以理解的是,个人开发者面对这样一个项目,常常会不知道从哪个地方开始入手。这是一件很正常的事情,因此我们建议您通过上文提到的方式,与社区建立联系,了解目前社区正在做什么,以及需要什么。

  对于一个新的参与者来说,我们建议您从这样一个步骤开始:

阅读文档,编译、运行DragonOS,并且尝试使用它目前已有的功能。

  然后,您可以通过查看DragonOS的GitHub仓库的project面板,看看目前仍有哪些待解决的问题。可以肯定的是,永远不会缺少待解决的问题,您在解决这些问题的过程中,能够获得一些宝贵的经验。

3.早期设计

  对于软件开发而言,写代码永远不是第一步。在编写代码之前,进行一些必要的设计(提出架构、技术方案),是项目成功的基础工作。在新的补丁开发的早期,花费一些时间进行计划和沟通,往往能够在接下来的阶段节省更多的时间。

3.1.定义我们要解决的问题

  与大多数的工程项目一样,在DragonOS中进行开发,首先需要清晰的描述要解决的问题。只有精准的定义了问题,才能找到正确的解决方案。有时候,我们能很轻易的定义问题,比如“编写串口驱动程序,使得它能把屏幕上打印的字符,输出到串口”。

  但是,有时候,我们容易将真正的问题与某个解决方案相混淆,并且还没意识到这一点。

  在2022年10月时,我发现,在真机调试的时候,需要频繁的拔插硬盘(先连接到电脑,待数据拷贝完毕后,再连接到测试机)。我对这一过程非常的不满,因为很浪费时间。我的直觉想法是:“有没有一种设备,能够一头连接电脑,另一头连接测试机的SATA接口。从测试机的角度看来,这是一块硬盘;测试机对这块磁盘的操作,都转换为了对我的电脑上面的一个磁盘镜像文件的操作。”我的想法就是:“购买/定制一款设备,能够实现上面的这个功能,那就能解决频繁拔插硬盘的烦恼了!”然后我为了寻找这样的设备,询问了一些硬件公司,他们的开价都在2万元左右。

  我在上面的这个过程中,就犯了一个错误:将真正的问题与某个解决方案相混淆了。真正的问题是:“解决需要频繁的拔插硬盘”,但是,在我的思考的过程中,不知不觉间,将问题转换成了“如何实现一款具有硬盘模拟功能的设备”。后面这个问题,只是某个解决方案下,需要解决的问题,并不是我们要解决的根本问题。

  对于要解决的根本问题,事实上有更好的解决方案:“制作一个类似于开关一样的转换器,当数据从电脑拷贝到磁盘后,把开关拨向另一边,使得电路与测试机接通”。这个方案的成本估摸着就十几二十块钱。

  上面的这个故事,告诉我们的是,在早期设计阶段,我们应当关注的是问题本身——而不是特定的解决方案

  我们需要关注系统的稳定性、长期的可维护性,解决方案必须考虑到这两点。由于系统比较复杂,因此,请您在开始编码之前,与社区的小伙伴讨论您的设计方案,以便您的方案能充分地,从全局的角度,考虑到系统的稳定性、可维护性。

  因此,在开发的早期,我们应当对以下三个问题,拥有一个答案

  • 要解决的本质问题是什么?

  • 这个问题会影响哪些方面/哪些用户?提出的解决方案应当解决哪些用例、场景?

  • DragonOS目前在解决该问题的方面,具有哪些不足/问题?

  只有考虑清楚了上面三个问题,讨论的解决方案才是有意义的。这是一个架构设计的过程,需要进行仔细的思考。尽管我们目前提倡敏捷开发,但是前期的架构设计仍然是非常重要的。

3.2.早期讨论

  在实施开发之前,与社区的成员们进行讨论是非常有意义的。这能够通过多种方式节省您的时间,并减少许多不必要的麻烦:

  • DragonOS可能以您不知道、不理解的方式,已经解决了相关的问题。DragonOS里面的一些特性、功能细节不是很明显,他们不一定会被写进文档。也许这些细节只是在某个不起眼的注释里面提到了,因此您很难注意到这些。这种细节可能只有一些开发人员知道。因此,与社区沟通能够避免您进行重复的工作。

  • 您提出的解决方案中,可能会有一些东西,由于一些原因(比如方案中的一些设计会在将来造成问题、方案的架构设计具有明显缺陷),无法合入主线。

  • 其他的开发人员可能已经考虑过这个问题;他们可能有更好的解决方案,或者是更好的想法。并且,他们可能愿意帮助你一起完善你的解决方案。

  Linux文档中提到:闭门造车式的设计和开发,所产生的代码总会有问题,这些问题只有在发布到社区里面的时候才会暴露出来。因此,我们必须吸取前人之鉴,通过与社区开发人员进行早期讨论,从而避免大量的痛苦和额外的工作。

3.3.在何时发布帖子?

  如果可能的话,在开发的早期阶段发布您的计划、设计,会是一个不错的选择。发帖的时候,您可以描述您正在解决的问题,以及已经制定的一些计划。包括但不限于:如何将设计付诸实施。您在社区内发布帖子,不仅能够帮助您获得一些有用的建议,也能帮助整个DragonOS社区提供有用的信息,使得社区沟通更高效。

  在这个阶段,可能您发布的帖子并不会引来很多评论,这并不一定意味着您做的不好,或者大家对您所做的工作不感兴趣。当然,也不能就此认为您的设计、想法不存在问题。可能只是因为大家比较忙,看了您的帖子之后,了解到了您的工作,但是大家并不一定有时间进行回复。(但是事实上您发布的信息对他人来说是有用的)

  在这种情况下,请不要气馁,您最好的做法就是,继续您的工作,并且不时地在您的帖子下分享您的工作,这样能够让社区的成员们随时了解到您的最新进展。

3.4.获得您所在的组织的支持

  如果您对DragonOS的开发工作,是在您的公司内完成的。那么,很显然,在您把计划、代码发布到社区论坛之前,您必须取得您的经理或上级的许可。

  同时,请注意,根据我们的授权许可,基于DragonOS操作系统的内核、官方开源的用户库而开发的代码,或者为DragonOS操作系统本身而开发的代码,根据开源授权许可,必须同样以GPLv2协议进行授权发布。如果您所在的组织,违背了GPLv2协议中的要求,以除GPLv2以外的协议开放源代码,或者是进行“闭源使用”,那么DragonOS社区对您的公司/组织所授予的使用DragonOS源代码的授权,将会被自动撤销。这将会面临一系列的法律问题。因此,在这个问题上,公司的管理人员、法律人员如果能越早地就公司要在DragonOS中开发的软件项目达成一致,将能促进您的公司在该项目上的进展。

  如果您的公司的项目/或者是您研究的项目根据您所在组织的保密规定,不能将其进行过早的披露,那也没有问题。只要您的公司能够确保这部分代码,在其编译而成的二进制产品被发布之时,按照GPLv2协议进行开源,并向开源社区贡献这部分代码即可。

4.如何正确的编写代码

5.发起Pull Request

6.后期跟进

7.另外的一些话题

8.更多信息

9.结语

C语言代码风格

  这份文档将会简要的介绍DragonOS的C语言代码风格。每个人的代码风格都各不相同,这是一件非常正常的事情。但是,对于一个开源项目的可维护性而言,我们希望制定一些代码规范,以便包括您在内的每个开发者都能在看代码的时候更加舒服。一个充斥着各种不同代码风格的项目,是难以维护的。

  我们在这里提出一些建议,希望您能够尽量遵循这些建议。这些建议与Linux的代码规范相似,但又略有不同。在变量命名上,DragonOS采用Linux的风格;对于缩进,DragonOS采用Microsoft风格。

0. 代码格式化工具

  在提出下面的建议之前,我们建议您在开发的时候使用Visual Studio Code的C/C++ Extension Pack插件作为代码格式化工具。这些插件能为您提供较好自动格式化功能,使得您的代码的基本格式符合DragonOS的要求。

  当您在编码时,经常性的按下Ctrl+shift+I或您设置的代码格式化快捷键,能帮助您始终保持良好的代码格式。

1. 缩进

  一个制表符的宽度等于4个空格。代码的缩进是按照制表符宽度(在多数编辑器上为4个字符)进行缩进的。

  这样能够使得您的代码变得更加容易阅读,也能更好的看出代码的控制结构。这样能避免很多不必要的麻烦!

举个例子:在switch语句中,将switch和case放置在同一缩进级别。并且将每个case的代码往右推进一个tab。这样能让代码可读性变得更好。

switch (cmd)
{
case AHCI_CMD_READ_DMA_EXT:
    pack->blk_pak.end_handler = NULL;
    pack->blk_pak.cmd = AHCI_CMD_READ_DMA_EXT;
    break;
case AHCI_CMD_WRITE_DMA_EXT:
    pack->blk_pak.end_handler = NULL;
    pack->blk_pak.cmd = AHCI_CMD_WRITE_DMA_EXT;
    break;
default:
    pack->blk_pak.end_handler = NULL;
    pack->blk_pak.cmd = cmd;
    break;
}

2. 分行

  我们建议,每行不要超过120个字符。如果超过了,除非有必要的理由,否则应当将其分为两行。

  在分行时,我们需要从被分出来的第二行开始,比第一行的起始部分向右进行一个缩进,以表明这是一个子行。使用代码格式化的快捷键能让你快速完成这件事。

  对于一些日志字符串而言,为了能方便的检索到他们,我们不建议对其进行分行。

  对于代码的分行,请不要试图通过以下的方式将几个语句放置在同一行中,这样对于代码可读性没有任何好处:

// 错误示范(1)
if(a) return 1;

// 错误示范(2)
if(b)
    do_a(),do_b();

3. 大括号和空格

3.1 大括号

  大括号的放置位置的选择是因人而异的,主要是习惯原因,而不是技术原因。我们推荐将开始括号和结束括号都放置在一个新的行首。如下所示:

while(i<10)
{
    ++i;
}

  这种规定适用于所有的代码块。

  这么选择的原因是,在一些编辑器上,这样放置括号,编辑器上将会出现辅助的半透明竖线,且竖线两端均为括号。这样能帮助开发者更好的定位代码块的层次关系。

下面通过一些例子来演示:

  在下面这个代码块中,我们需要注意的是,else if语句需要另起一行,而不是跟在上一个}后方。这是因为我们规定{必须在每行的起始位置,并且还要保持缩进级别的缘故。

if (*fmt == '*')
{
    ++fmt;
}
else if (is_digit(*fmt))
{
    field_width = skip_and_atoi(&fmt);
}

  当循环中有多个简单的语句的时候,需要使用大括号。

while (condition) 
{
    if (test)
        do_something();
}

  当语句只有1个简单的子句时,我们不必使用大括号。

if(a)
    return 1;
3.2 空格

  对于大部分关键字,我们需要在其后添加空格,以提高代码的可读性。

  请您在所有这些关键字后面输入一个空格:

if, switch, case, for, do, while

  关键字sizeof、typeof、alignof、__atrribute__的后面则不需要添加空格,因为使用他们的时候,就像是使用函数一样。

  对于指针类型的变量,*号要贴近变量名而不是贴近类型名。如下所示:

char *a;
void *func(char* s, int **p);

  在大多数二元和三元运算符周围(在每一侧)使用一个空格,如下所示:

=  +  -  <  >  *  /  %  |  &  ^  <=  >=  ==  !=  ?  :

  这些一元运算符后方没有空格

&  *  +  -  ~  !  sizeof  typeof  alignof  __attribute__  defined

  特殊的例子,以下运算符的前后都不需要空格:

++  -- . ->

4. 命名

  DragonOS中的命名规范不使用诸如TempValue这样的驼峰命名法的函数名,而是使用tmp这样言简意赅的命名。

  注意,这里指的是我们在整个项目内都不希望使用驼峰命名法。并不意味着程序员可以随便的使用一些难以理解的缩写来作为变量名。

  对于全局变量或者全局可见的函数、结构体而言,我们需要遵循以下的命名规定:

  • 名称需要易于理解,且不具有歧义。如:对于一个计算文件夹大小的函数而言,我们建议使用count_folder_size()来命名,而不是cntfs()这样令其他人头大的命名。

  • 全局的,非static的名称,除非有特别的必要,命名时需要遵循以下格式:模块名缩写前缀_函数/变量名。这样的命名能便于别人区分这个名称位于哪个模块内,也减少了由于命名冲突所导致的麻烦。

  • 不需要让其他代码文件可见的全局名称,必须添加static修饰符。

  对于函数内的局部变量而言,命名规范则是需要言简意赅。过长的名称在局部变量中没有太大的意义。

【文档未完成,待继续完善】

Rust语言代码风格

  这篇文档将会介绍DragonOS中的Rust语言代码风格。随着开发的进行,这些风格可能会发生变化,但是我们会尽量保持风格的一致性。

1. 命名

  这部分基于Rust语言圣经中的命名规范进行修改,本文未提及的部分,请参考Rust语言圣经中的命名规范

2. 格式

2.1 缩进

  请在提交代码之前,使用cargo fmt命令对代码进行格式化。

2.2 函数返回值

  尽管Rust可以返回函数的最后一行的语句的值,但是,这种方式会使代码的可读性变差。因此,我们推荐您在函数的最后一行使用return语句,而不是直接返回值。

// 不推荐
fn foo() -> i32 {
    1 + 2
}

// 推荐
fn foo() -> i32 {
    return 1 + 2;
}
2.3 错误处理

  DragonOS采用返回Posix错误码作为模块间错误处理的方式。为了确保在模块之间,错误处理代码的一致性,我们推荐在发生错误的时候,返回SystemError类型,该类型表示posix错误码。这样做的优点尤其体现在跨模块调用函数时,可以直接返回通用的错误码,从而降低错误处理代码的耦合度。

// 函数跨越模块边界时(由其他模块调用当前函数),不推荐
fn foo() -> Result<(), CustomErr> {
    if 1 + 2 == 3 {
        return Ok(());
    } else {
        return Err(CustomErr::error);
    }
}

// 函数跨越模块边界时(由其他模块调用当前函数),推荐
fn foo() -> Result<(), SystemError> {
    if 1 + 2 == 3 {
        return Ok(());
    } else {
        return Err(SystemError::EINVAL);
    }
}

  在模块内部,您既可以采用返回自定义错误enum的方式,也可以采用返回SystemError的方式。但是,我们推荐您在模块内部,采用返回自定义错误enum的方式,这样可以使错误处理代码更加清晰。

  TODO: 将原有的使用i32作为错误码的代码,改为使用SystemError

3. 注释

  DragonOS的注释风格与Rust官方的不太一样,我们部分结合了Linux的注释风格。同时,我们推荐您在代码中加入尽可能多的有效注释,以便于其他人理解您的代码。并且,变量、函数等声明,遵守第一节中提到的命名规范,使其能够“自注释”。

3.1 函数注释

  函数注释应该包含以下内容:

  • 函数的功能

  • 函数的参数

  • 函数的返回值

  • 函数的错误处理

  • 函数的副作用或者其他的需要说明的内容

  函数注释的格式如下:

/// @brief 函数的功能
/// 
/// 函数的详细描述
/// 
/// @param 参数1 参数1的说明
/// 
/// @param 参数2 参数2的说明
/// 
/// @return 返回值的说明

  如果函数的返回值是Result类型,那么返回值应当这样进行解释:

/// @return Ok(返回值类型) 返回值的说明
/// 
/// @return Err(错误值类型) 错误的说明

与社区建立联系

联系方式

社区公共邮箱:contact@DragonOS.org

DragonOS社区负责人: 龙进

工作邮箱: longjin@DragonOS.org

开发交流QQ群: 115763565

DragonOS官网: https://DragonOS.org

了解开发动态、开发任务,请访问DragonOS的zulip社群: https://DragonOS.zulipchat.com

赞助及捐赠

DragonOS是一个开源项目,我们欢迎任何形式的赞助和捐赠,您的捐赠将用于DragonOS的开发和维护,以及社区的运营。

您可以通过以下方式赞助和捐赠:

财务及捐赠信息公开

DragonOS社区的捐赠信息将按年进行公开。赞助商、赞助者信息将在收到赞助后,15天内进行公开。

社区管理、财务及法务主体

DragonOS社区的管理、财务及法务主体为:灵高计算机系统(广州)有限公司。

我们是一家开源公司,我们坚信,开源能为我国将来的IT,打下更好的基础。我们也通过其他业务创收,投入到DragonOS的研发之中。

公司负责DragonOS社区的运营、财务、法务事项处理工作。

地址:广东省广州市番禺区小谷围街广州大学城华南理工大学大学城校区

邮件:contact@DragonOS.org

官网:https://ringotek.com.cn

发行日志

这里是DragonOS的发行日志,会记录DragonOS的每一个版本的更新内容。

V0.1.8

备注

本文作者:龙进 longjin@DragonOS.org

2023年8月16日

贡献者名单

DragonOS V0.1.8版本由以下小伙伴贡献代码:

赞助者名单

感谢以下同学的赞赏,我们将不断努力!

更新内容-内核

新特性
  • refactor: 重构系统调用模块 (#267)

  • feature: 添加AlignBox和int_like宏 (#272)

  • refactor: 新的ipi功能&kick_cpu功能的重写 (#274)

  • feature: 实现gettimeofday()系统调用和clocksource+timekeeping子模块 (#278)

  • refactor: PCI设备中断重构,并删去USB相关代码 (#285)

  • feature: 注册串口设备,创建字符设备框架(#290)

  • refactor: 新的内存管理模块 (#303)

  • feature: 新的二进制加载器、elf解析器 (#303)

  • feature: 增加 ListenTable 来检测端口占用 (#291)

  • feature: 替换 local_irq_save 为 IrqFlagsGuard 实现 (#317)

  • feature: 实现系统调用Fstat (#295)

  • feature: 实现内核通知链 notifier chain (#316)

  • feature: 增加fcntl系统调用 (#323)

  • feature: 添加per cpu变量支持 (#327)

  • feature: spinlock守卫新增leak,spinlock新增force unlock功能.(#329)

bugfix
  • bugfix: 修复无法正常读取stdin的问题 (#264)

  • bugfix: 修复了当传入ahci驱动的缓冲区地址为用户缓冲区时,产生的内存越界问题.(采用分配内核缓冲区的方式临时解决) (#265)

  • bugfix: 解决由于local_irq_save、local_irq_restore函数的汇编不规范导致影响栈行为的bug。 (#303)

  • bugfix: 解决local_irq_save未关中断的错误 (#303)

  • bugfix: 解决arch_try_cmpxchg对于指针处理的错误 (#307)

  • bugfix: 修复了wait4的异常报错 (#312)

  • bugfix: 修正null设备以及zero设备无法open、行为不符合预期的问题 (#314)

  • bugfix: 修正fat文件系统未能正确的扩展文件大小的bug (#323)

  • bugfix: 修正rwlock有的地方由于未使用ManuallyDrop导致的use after free问题 (#329)

更新内容-用户环境

新特性
  • feature: 新增http server (#265)

bugfix
  • bugfix: 解决链接时,由于crt*.o未按照升序排列导致init段链接错误的问题 (#265)

更新内容-其他

  • bugfix: 固定编译工具链、修复由于新版rust编译器问题导致的报错。 (#258)

  • feature: Makefile: 根目录下添加make help命令 (#271)

  • doc: 更新github issue模板 (#277)

  • bugfix: 解决relibc的头文件没能识别__dragonos__定义的问题 (#315)

  • feature: 设置内核、relibc的远程为dragonos的git镜像站,防止国内网络问题导致编译失败 (#318)

  • feature: 自动安装、更新dadk (#319)

更新内容-软件移植

  • feature: 移植了sqlite3 (#323)

源码、发布版镜像下载

  您可以通过以下方式获得源代码:

通过Git获取
通过DragonOS软件镜像站获取

  为解决国内访问GitHub慢、不稳定的问题,同时为了方便开发者们下载DragonOS的每个版本的代码,我们特意搭建了镜像站,您可以通过以下地址访问镜像站:

  您可以通过镜像站获取到DragonOS的代码压缩包,以及编译好的可运行的磁盘镜像。

开放源代码声明

备注

为促进DragonOS项目的健康发展,DragonOS以GPLv2开源协议进行发布。所有能获得到DragonOS源代码以及相应的软件制品(包括但不限于二进制副本、文档)的人,都能享有我们通过GPLv2协议授予您的权利,同时您也需要遵守协议中规定的义务。

这是一个相当严格的,保护开源软件健康发展,不被侵占的协议。

对于大部分的善意的人们而言,您不会违反我们的开源协议。

我们鼓励DragonOS的自由传播、推广,但是请确保所有行为没有侵犯他人的合法权益,也没有违反GPLv2协议。

请特别注意,对于违反开源协议的,尤其是商业闭源使用以及任何剽窃、学术不端行为将会受到严肃的追责。(这是最容易违反我们的开源协议的场景)。

并且,请注意,按照GPLv2协议的要求,基于DragonOS修改或二次开发的软件,必须同样采用GPLv2协议开源,并标明其基于DragonOS进行了修改。亦需保证这些修改版本的用户能方便的获取到DragonOS的原始版本。

您必须使得DragonOS的开发者们,能够以同样的方式,从公开渠道获取到您二次开发的版本的源代码,否则您将违反GPLv2协议。

关于协议详细内容,还敬请您请阅读项目根目录下的LICENSE文件。请注意,按照GPLv2协议的要求,只有英文原版才具有法律效力。任何翻译版本都仅供参考。

开源软件使用情况

  DragonOS在开发的过程中,参考了一些开源项目的设计,或者引入了他们的部分代码,亦或是受到了他们的启发。现将他们列在下面。我们对这些开源项目的贡献者们致以最衷心的感谢!

格式:<项目名> - <链接> - <开源协议>

  • Linux - https://git.kernel.org/ - GPLv2

  • skiftOS - https://github.com/skiftOS/skift - MIT

  • FYSOS - https://github.com/fysnet/FYSOS - FYSOS’ License

  • LemonOS - https://github.com/LemonOSProject/LemonOS.git - BSD 2-Clause License

  • LZ4 - https://github.com/lz4/lz4 - BSD 2-Clause license

  • SerenityOS - https://github.com/SerenityOS/serenity.git - BSD 2-Clause license

  • MINE - 《一个64位操作系统的设计与实现》田宇; 人民邮电出版社

  • chcore - 《现代操作系统:设计与实现》陈海波,夏虞斌; 机械工业出版社

  • SimpleKernel - https://github.com/Simple-XX/SimpleKernel - MIT

  • rcore-fs - https://github.com/rcore-os/rcore-fs.git - MIT

  • redox - https://gitlab.redox-os.org/redox-os/redox - MIT

当前版本的所有提交记录

commit 40176b1c6603d487b7eb66fb81e641f0932ab90a
Author: longjin <longjin@RinGoTek.cn>
Date:   Tue Aug 15 15:06:57 2023 +0000

    删除无用代码,并把about app的版本号更新为0.1.8

commit 67b481888770c6469f572f244a4f97e42da77d1f
Author: houmkh <1119644616@qq.com>
Date:   Mon Aug 14 12:18:46 2023 +0800

    移动fstat (#330)
    
    * 移动fstat

commit 90a0a49048fdaf5e31320d0c87f8bed8db1bd882
Author: LoGin <longjin@DragonOS.org>
Date:   Mon Aug 14 01:24:49 2023 +0800

    修正rwlock有的地方由于未使用ManuallyDrop导致的use after free && spinlock守卫新增leak,spinlock新增force unlock功能.(#329)
    
    1.修正rwlock有的地方由于未使用ManuallyDrop导致的use after free
    2. spinlock守卫新增leak,spinlock新增force unlock功能.

commit c3dad0011d331d782670e14723aa48e98fbac787
Author: LoGin <longjin@DragonOS.org>
Date:   Sun Aug 13 16:28:24 2023 +0800

    添加per cpu变量支持 (#327)

commit 42c97fa7f4fee7eeefeda5d2b7ed14f598a58493
Author: LoGin <longjin@DragonOS.org>
Date:   Tue Aug 8 23:45:04 2023 +0800

    删除旧的libELF (#324)

commit 6d81180b3b7328466b976b69c5f7782aa66d8a89
Author: LoGin <longjin@DragonOS.org>
Date:   Tue Aug 8 23:39:22 2023 +0800

    移植sqlite3,并修复一些bug (#323)
    
    * bugfix: 程序加载器映射内存时,计算要映射的大小不正确的问题。
    
    * 修正brk系统调用不符合规范的地方
    
    * bugfix: 修正fat文件系统未能正确的扩展文件大小的bug
    
    * 增加fcntl系统调用
    
    * 移植sqlite3

commit 26887c6334cdca2d13ad71dec27fb69faa0a57be
Author: LoGin <longjin@DragonOS.org>
Date:   Mon Aug 7 01:38:52 2023 +0800

    bugfix: 解决取消低地址映射时,错误的把重映射的物理页释放,从而导致的use after free问题。 (#321)

commit 729a96ef47f473d535d8317a2ace5ba141fd282a
Author: Xshine <gshine@m.scnu.edu.cn>
Date:   Sun Aug 6 12:53:47 2023 +0800

    实现内核通知链 notifier chain (#316)
    
    * 实现通知链块结构
    
    * 实现通知链的基本功能
    
    * 实现 atomic notifier chain
    
    * 实现 blocking notifier chain
    
    * 使用 rust 范式完成功能
    
    * 支持回调次数 nr_to_call
    
    * 移动至 libs 目录
    
    * 完善通知链相关方法
    
    * 修正相关格式
    
    * 文档编写
    
    * 更改文档路径

commit be63f3b2b6b472daa3ee17180aa607409cb9d182
Author: houmkh <1119644616@qq.com>
Date:   Sat Aug 5 18:52:46 2023 +0800

    实现系统调用Fstat (#295)
    
    * fstat
    
    * 修改syscall.rs中的verify_area

commit 9550910ae1de900e0291a84d268e8873fa142902
Author: Chiichen <39649411+Chiichen@users.noreply.github.com>
Date:   Sat Aug 5 18:30:55 2023 +0800

     替换 local_irq_save 为 IrqFlagsGuard 实现 (#317)

commit abf3f634bf7e13e829556e962e7c73a85d163335
Author: LoGin <longjin@DragonOS.org>
Date:   Sat Aug 5 15:30:06 2023 +0800

    自动安装、更新dadk (#319)
    
    * auto install/auto update dadk

commit d6fd9c1e8025dd679339f9156477cb7d26d3db0d
Author: LoGin <longjin@DragonOS.org>
Date:   Sat Aug 5 15:04:08 2023 +0800

    设置内核、relibc的远程为dragonos的git镜像站,防止国内网络问题导致编译失败 (#318)

commit 1a62e7767c1215f9668915b42de770e7993711bf
Author: LoGin <longjin@DragonOS.org>
Date:   Wed Aug 2 18:11:05 2023 +0800

    解决relibc的头文件没能识别__dragonos__定义的问题 (#315)

commit 06500303303ec14711b4f995e2058e12703f0f2c
Author: LoGin <longjin@DragonOS.org>
Date:   Wed Aug 2 17:33:16 2023 +0800

    修正null设备以及zero设备无法open、行为不符合预期的问题 (#314)

commit 4da3758acf0327d429dfce3d313b50c2e0fc7723
Author: Chiichen <39649411+Chiichen@users.noreply.github.com>
Date:   Wed Aug 2 14:29:59 2023 +0800

    修复了wait4的异常报错 (#312)
    
    * 修复了wait4的异常报错

commit 821bb9a2dcfd28f9878d53ba722bdf164cf00f69
Author: Xshine <caijiaxin@dragonos.org>
Date:   Fri Jul 28 17:51:05 2023 +0800

    增加 ListenTable 来检测端口占用 (#291)
    
    * 增加 ListenTable 来检测端口占用
    
    
    * 使用Arc封装GlobalSocketHandle
    
    * 删除 listen 处的端口检测逻辑,延至实现端口复用时完成
    
    * 设立两张表,分别记录TCP和UDP的端口占用
    
    * 实现 meatadata 相关逻辑
    
    * 实现socket关闭时,端口在表中移除
    
    * 使用端口管理器重构端口记录表
    
    * 修正与RawSocket相关的端口管理逻辑
    
    * 补充测试文件
    
    * 修正 unbind_port 在逻辑错误
    
    * 修正格式问题
    
    ---------
    
    Co-authored-by: longjin <longjin@RinGoTek.cn>

commit 7cc4a02c7ff7bafd798b185beb7b0c2986b9f32f
Author: zhaoyao73 <zhaoyao73@users.noreply.github.com>
Date:   Fri Jul 28 03:44:45 2023 -0400

    fix arch_try_cmpxchg macro declaration (#307)
    
    fix arch_try_cmpxchg in atomic_cmpxchg
    
    Co-authored-by: Yao Zhao <dragonlinux@gmail.com>

commit a30434f5201ca4c60b9515c8c23444fea3b5a8c6
Author: zhaoyao73 <zhaoyao73@users.noreply.github.com>
Date:   Tue Jul 25 10:02:42 2023 -0400

    fix some script bugs (#304)
    
    add arch linux prerequisited packages
    
    Co-authored-by: Yao Zhao <dragonlinux@gmail.com>

commit 40fe15e0953f989ccfeb74826d61621d43dea6bb
Author: LoGin <longjin@DragonOS.org>
Date:   Sat Jul 22 16:27:02 2023 +0800

    新的内存管理模块 (#303)
    
    &emsp;&emsp;实现了具有优秀架构设计的新的内存管理模块,对内核空间和用户空间的内存映射、分配、释放、管理等操作进行了封装,使得内核开发者可以更加方便地进行内存管理。
    
    &emsp;&emsp;内存管理模块主要由以下类型的组件组成:
    
    - **硬件抽象层(MemoryManagementArch)** - 提供对具体处理器架构的抽象,使得内存管理模块可以在不同的处理器架构上运行
    - **页面映射器(PageMapper)**- 提供对虚拟地址和物理地址的映射,以及页表的创建、填写、销毁、权限管理等操作。分为两种类型:内核页表映射器(KernelMapper)和用户页表映射器(位于具体的用户地址空间结构中)
    - **页面刷新器(PageFlusher)** - 提供对页表的刷新操作(整表刷新、单页刷新、跨核心刷新)
    - **页帧分配器(FrameAllocator)** - 提供对页帧的分配、释放、管理等操作。具体来说,包括BumpAllocator、BuddyAllocator
    - **小对象分配器** - 提供对小内存对象的分配、释放、管理等操作。指的是内核里面的SlabAllocator (SlabAllocator的实现目前还没有完成)
    - **MMIO空间管理器** - 提供对MMIO地址空间的分配、管理操作。(目前这个模块待进一步重构)
    - **用户地址空间管理机制** - 提供对用户地址空间的管理。
        - VMA机制 - 提供对用户地址空间的管理,包括VMA的创建、销毁、权限管理等操作
        - 用户映射管理 - 与VMA机制共同作用,管理用户地址空间的映射
    - **系统调用层** - 提供对用户空间的内存管理系统调用,包括mmap、munmap、mprotect、mremap等
    - **C接口兼容层** - 提供对原有的C代码的接口,是的C代码能够正常运行。
    
    
    除上面的新增内容以外,其它的更改内容:
    - 新增二进制加载器,以及elf的解析器
    - 解决由于local_irq_save、local_irq_restore函数的汇编不规范导致影响栈行为的bug。
    - 解决local_irq_save未关中断的错误。
    - 修复sys_gettimeofday对timezone参数的处理的bug
    
    ---------
    
    Co-authored-by: kong <kongweichao@dragonos.org>

commit bb5f098a864cee36b7d2c1ab9c029c0280d94a8a
Author: LoGin <longjin@DragonOS.org>
Date:   Sat Jul 22 16:24:55 2023 +0800

    Revert "新的内存管理模块 (#301)" (#302)
    
    This reverts commit d8ad0a5e7724469abd5cc3cf271993538878033e.

commit d8ad0a5e7724469abd5cc3cf271993538878033e
Author: LoGin <longjin@DragonOS.org>
Date:   Sat Jul 22 16:22:17 2023 +0800

    新的内存管理模块 (#301)
    
    &emsp;&emsp;实现了具有优秀架构设计的新的内存管理模块,对内核空间和用户空间的内存映射、分配、释放、管理等操作进行了封装,使得内核开发者可以更加方便地进行内存管理。
    
    &emsp;&emsp;内存管理模块主要由以下类型的组件组成:
    
    - **硬件抽象层(MemoryManagementArch)** - 提供对具体处理器架构的抽象,使得内存管理模块可以在不同的处理器架构上运行
    - **页面映射器(PageMapper)**- 提供对虚拟地址和物理地址的映射,以及页表的创建、填写、销毁、权限管理等操作。分为两种类型:内核页表映射器(KernelMapper)和用户页表映射器(位于具体的用户地址空间结构中)
    - **页面刷新器(PageFlusher)** - 提供对页表的刷新操作(整表刷新、单页刷新、跨核心刷新)
    - **页帧分配器(FrameAllocator)** - 提供对页帧的分配、释放、管理等操作。具体来说,包括BumpAllocator、BuddyAllocator
    - **小对象分配器** - 提供对小内存对象的分配、释放、管理等操作。指的是内核里面的SlabAllocator (SlabAllocator的实现目前还没有完成)
    - **MMIO空间管理器** - 提供对MMIO地址空间的分配、管理操作。(目前这个模块待进一步重构)
    - **用户地址空间管理机制** - 提供对用户地址空间的管理。
        - VMA机制 - 提供对用户地址空间的管理,包括VMA的创建、销毁、权限管理等操作
        - 用户映射管理 - 与VMA机制共同作用,管理用户地址空间的映射
    - **系统调用层** - 提供对用户空间的内存管理系统调用,包括mmap、munmap、mprotect、mremap等
    - **C接口兼容层** - 提供对原有的C代码的接口,是的C代码能够正常运行。
    
    
    除上面的新增内容以外,其它的更改内容:
    - 新增二进制加载器,以及elf的解析器
    - 解决由于local_irq_save、local_irq_restore函数的汇编不规范导致影响栈行为的bug。
    - 解决local_irq_save未关中断的错误。
    - 修复sys_gettimeofday对timezone参数的处理的bug

commit 0663027b111ffb6ff93becd60ffef1e2b8fbd4c6
Author: TingHuang <92705854+TingSHub@users.noreply.github.com>
Date:   Wed Jul 12 12:49:45 2023 +0800

    注册串口设备,创建字符设备框架(#290)
    
    * 按照rust规范修改两个函数名称
    
    * 修改一些函数句柄以符合rust规范
    
    * 添加字符设备相关
    
    * 添加字符设备相关文件
    
    * 添加字符设备驱动框架代码
    
    * 将串口注册
    
    * 规范代码

commit cc36cf4a186be834e6c2ab857b9b9501ddb8b1eb
Author: YJwu2023 <yujianwu2019@gmail.com>
Date:   Sat Jul 8 17:22:42 2023 +0800

    PCI设备中断重构,删去USB相关代码 (#285)
    
    * 修复ecam无法获取MCFG table的问题
    
    * 完善pcie
    
    * 完善irq的错误检测机制

commit 2311e2f30048d09250afc3e2e4e7029627996655
Author: 櫻井桃華 <89176634+TihayaKousaka@users.noreply.github.com>
Date:   Fri Jul 7 22:50:46 2023 +0800

    修改makefile通过编译 (#287)

commit 36fd013004ee0bd5fc7cfb452ba22531a83a859c
Author: houmkh <1119644616@qq.com>
Date:   Sat Jun 17 22:48:15 2023 +0800

    实现gettimeofday()系统调用和clocksource+timekeeping子模块 (#278)
    
    - 实现gettimeofday()系统调用
    - 实现clocksource+timekeeping子模块部分功能
    - 实现了timespec转换成日期时间

commit a55ac7b928a6ca08483bbb3355bea55f1446ccab
Author: LoGin <longjin@DragonOS.org>
Date:   Tue Jun 6 17:44:54 2023 +0800

    Update issue templates (#277)

commit 5f57834372f6cb720ba14103effa4799e195a963
Author: Tptogiar <2528891112@qq.com>
Date:   Tue Jun 6 16:41:02 2023 +0800

    Makefile: 根目录下添加make help命令 (#271)
    
    * Makefile: 根目录下添加make help命令
    
    * Makefile: 补充根目录Makefile的help命令

commit aa0367d69e15989684109c5b454e85da9ecb1975
Author: LoGin <longjin@DragonOS.org>
Date:   Tue May 30 10:21:11 2023 +0800

    新的ipi功能&kick_cpu功能的重写 (#274)

commit bb24249faabc5006784aa98ca17b4cbdcb788c65
Author: LoGin <longjin@DragonOS.org>
Date:   Sun May 28 23:00:37 2023 +0800

    添加AlignBox和int_like宏 (#272)

commit ab5c8ca46db8e7d4793a9791292122b0b9684274
Author: login <longjin@DragonOS.org>
Date:   Wed May 24 17:05:33 2023 +0800

    重构系统调用模块 (#267)
    
    * 完成系统调用模块重构
    
    * 更新github workflow

commit 660a04cef803fd73e9b294b30a96421b021a4b9b
Author: login <longjin@DragonOS.org>
Date:   Sat May 13 21:17:12 2023 +0800

    新增http server (#265)
    
    * 1.修复了当传入ahci驱动的缓冲区地址为用户缓冲区时,产生的内存越界问题.(采用分配内核缓冲区的方式临时解决)
    2.新增http server
    
    * 把libssl-dev添加到bootstrap.sh
    
    * http_server增加对父级相对路径的安全检查,防止访问系统内的其他文件
    
    * 检查空指针情况
    
    * 解决由于链接时,crt*.o未按照升序排列导致init段链接错误的问题

commit 49249f4ec94fad7baf923aed68d9a7b2da3de3d4
Author: Bullet <93781792+GP-Bullet@users.noreply.github.com>
Date:   Sat May 13 09:55:24 2023 +0800

    把调度器实例的裸指针改为Option (#262)

commit bfafc102798ab1968ccf6b04315d8d3359a70ca8
Author: login <longjin@DragonOS.org>
Date:   Thu May 11 17:41:42 2023 +0800

    修复读取stdin时,无法正常读取的问题。 (#264)

commit 7285c927d95bb4b5c692c51a8f86c47009d07667
Author: login <longjin@DragonOS.org>
Date:   Thu May 11 16:17:58 2023 +0800

    添加dadk支持 (#263)
    
    * 引入dadk,使用dadk0.1.1来编译test-relibc程序
    
    * 由于gitee仓库体积限制导致无法继续使用gitee上的rust索引,因此更换为清华源
    
    * github workflow的环境中,安装dadk
    
    * Auto configure dragonos rust toolchain

commit b11bb1b25676f528ec1b0e1da0af82b4652f70c4
Author: login <longjin@DragonOS.org>
Date:   Sun May 7 22:20:33 2023 +0800

    固定编译工具链、修复由于新版rust编译器问题导致的报错。 (#258)
    
    * 固定编译工具链、修复由于新版rust编译器问题导致的报错。
    
    * 完善github workflow环境配置

V0.1.7

备注

本文作者:龙进 longjin@DragonOS.org

2023年4月24日

贡献者名单

DragonOS V0.1.7版本由以下小伙伴贡献代码:

赞助者名单

感谢以下同学的赞赏,我们将不断努力!

更新内容-内核

  • scheduler: 修改CFSqueue从Vec变成红黑树 (#229)

  • new: lazy_init (#230) (#236)

  • pci: pci重构+pcie支持 (#235)

  • net: 增加网络子系统,且能在用户态进行编程 (#237) (#247)

  • mm: 调整brk系统调用,使得参数、返回值与Linux一致 (#238)

  • 修改errno,使其与relibc的保持一致 (#234)

  • pci: 修复ecam无法获取MCFG table的问题 (#241)

  • libs: DowncastArc and its docs (#244)

  • softirq: 增加定时器和软中断文档,修改了softirq面向c的接口 (#245)

  • spinlock: 修复spinlock忘记恢复rflags的问题 (#247)

  • waitqueue: 增加wakeup_all和sleep_without_schedule的功能 (#247)(#253)

  • filesystem: 把PollStatus结构体改为使用bitflags库来实现 (#247)

  • filesystem: 增加iovec的支持(暴力实现) (#247)

  • filesystem: 新增SysFS (#250) (#254)

  • driver: 根据sysfs,完善设备驱动模型 (#254)

  • pipe: 匿名管道重构 (#253)

  • irq: 新增IrqArch抽象。以及IrqFlagsGuard。以简化关中断-恢复中断的过程 (#253)

更新内容-用户环境

新增仓库
  • 新增子项目:dsc

  • 新增子项目:DADK DragonOS Application Development Kit

DragonOS-relibc
  • Add sys_dup and sys_dup2 support (#2)

  • 添加原本的libc的内存分配器,修复对齐问题。 (#6) (#7)

  • 配置网络相关的系统调用 (#8)

  • 修复由于DragonOS不支持TLS(thread local storage)导致errno变量无法正常工作的问题. (#8)

更新内容-其他

  • build: 修复Issue#220;vnc的端口号恢复5900 (#243)

  • bootstrap: 解决使用zsh在构建DragonOS时,无法直接使用一键初始化脚本进行安装的问题 (#252)

更新内容-软件移植

源码、发布版镜像下载

  您可以通过以下方式获得源代码:

通过Git获取
通过DragonOS软件镜像站获取

  为解决国内访问GitHub慢、不稳定的问题,同时为了方便开发者们下载DragonOS的每个版本的代码,我们特意搭建了镜像站,您可以通过以下地址访问镜像站:

  您可以通过镜像站获取到DragonOS的代码压缩包,以及编译好的可运行的磁盘镜像。

开放源代码声明

备注

为促进DragonOS项目的健康发展,DragonOS以GPLv2开源协议进行发布。所有能获得到DragonOS源代码以及相应的软件制品(包括但不限于二进制副本、文档)的人,都能享有我们通过GPLv2协议授予您的权利,同时您也需要遵守协议中规定的义务。

这是一个相当严格的,保护开源软件健康发展,不被侵占的协议。

对于大部分的善意的人们而言,您不会违反我们的开源协议。

我们鼓励DragonOS的自由传播、推广,但是请确保所有行为没有侵犯他人的合法权益,也没有违反GPLv2协议。

请特别注意,对于违反开源协议的,尤其是商业闭源使用以及任何剽窃、学术不端行为将会受到严肃的追责。(这是最容易违反我们的开源协议的场景)。

并且,请注意,按照GPLv2协议的要求,基于DragonOS修改或二次开发的软件,必须同样采用GPLv2协议开源,并标明其基于DragonOS进行了修改。亦需保证这些修改版本的用户能方便的获取到DragonOS的原始版本。

您必须使得DragonOS的开发者们,能够以同样的方式,从公开渠道获取到您二次开发的版本的源代码,否则您将违反GPLv2协议。

关于协议详细内容,还敬请您请阅读项目根目录下的LICENSE文件。请注意,按照GPLv2协议的要求,只有英文原版才具有法律效力。任何翻译版本都仅供参考。

开源软件使用情况

  DragonOS在开发的过程中,参考了一些开源项目的设计,或者引入了他们的部分代码,亦或是受到了他们的启发。现将他们列在下面。我们对这些开源项目的贡献者们致以最衷心的感谢!

格式:<项目名> - <链接> - <开源协议>

  • Linux - https://git.kernel.org/ - GPLv2

  • skiftOS - https://github.com/skiftOS/skift - MIT

  • FYSOS - https://github.com/fysnet/FYSOS - FYSOS’ License

  • LemonOS - https://github.com/LemonOSProject/LemonOS.git - BSD 2-Clause License

  • LZ4 - https://github.com/lz4/lz4 - BSD 2-Clause license

  • SerenityOS - https://github.com/SerenityOS/serenity.git - BSD 2-Clause license

  • MINE - 《一个64位操作系统的设计与实现》田宇; 人民邮电出版社

  • chcore - 《现代操作系统:设计与实现》陈海波,夏虞斌; 机械工业出版社

  • SimpleKernel - https://github.com/Simple-XX/SimpleKernel - MIT

  • rcore-fs - https://github.com/rcore-os/rcore-fs.git - MIT

  • redox - https://gitlab.redox-os.org/redox-os/redox - MIT

当前版本的所有提交记录

commit e0de0fd6a52199753a3127cfbb5d12f0a1555aae
Author: TingHuang <92705854+TingSHub@users.noreply.github.com>
Date:   Sun Apr 23 22:55:57 2023 +0800

    根据sysfs完善设备驱动模型 & 添加sysfs官方文档 (#254)
    
    * 根据sysfs完善设备驱动模型
    
    * 添加sysfs官方文档

commit f678331a3315b7847f08ab32b42d5bf49a9f3a6a
Author: hanjiezhou <zhouhanjie@dragonos.org>
Date:   Sun Apr 23 21:05:10 2023 +0800

    匿名管道重构&增加IrqArch trait以及IrqFlags及其守卫 (#253)
    
    * 实现匿名管道
    
    * 增加IrqArch trait以及IrqFlags及其守卫
    
    ---------
    
    Co-authored-by: longjin <longjin@RinGoTek.cn>

commit 8a1e95abb5e4df5e872bb452efc26c9e9631157d
Author: Bullet <93781792+GP-Bullet@users.noreply.github.com>
Date:   Fri Apr 21 23:36:54 2023 +0800

    解决使用zsh在构建DragonOS时,无法直接使用一键初始化脚本进行安装的问题  (#252)

commit dd9f1fc1a42406461e6f0d38cce1e56e22a1a15f
Author: TingHuang <92705854+TingSHub@users.noreply.github.com>
Date:   Fri Apr 21 16:03:42 2023 +0800

    新增SysFS (#250)
    
    * 添加sysfs
    
    * 注册sysfs
    
    * 添加sysfs相关
    
    * 添加rust-anlyzer辅助配置
    
    * 将设备与sysfs相关联
    
    * 添加单独的文件管理sysfs下的文件夹

commit cde5492f725681ed89abe1e6eb088e05d943d793
Author: login <longjin@DragonOS.org>
Date:   Wed Apr 19 18:05:02 2023 +0800

    新增网络socket的系统调用接口 (#247)
    
    1.修复spinlock忘记恢复rflags的问题
    2.WaitQueue增加wakeup_all的功能
    3.完善tcp,udp,raw socket
    4.把PollStatus结构体改为使用bitflags
    5.新增iovec结构体
    6.完成网络的系统调用
    7.在bootstrap里面添加dnsmasq bridge-utils iptables
    
    ---------
    
    Co-authored-by: guanjinquan <1666320330@qq.com>

commit 8fd71f277271ae68e648f290c67f187b030feae0
Author: houmkh <1119644616@qq.com>
Date:   Mon Apr 17 17:17:06 2023 +0800

    增加定时器和软中断文档,修改了softirq面向c的接口 (#245)
    
    * 增加定时器和软中断文档
    
    * 修改softirq对c的接口和文档
    
    * 修改文档格式

commit 77c928f6ce3192c79ea42ab7bcba2713e289f73b
Author: login <longjin@DragonOS.org>
Date:   Sun Apr 16 20:29:04 2023 +0800

    new: DowncastArc and its docs (#244)

commit 7149abaa49a4ca70f0e42ad3b61fdfd6a941a092
Author: HoshuChiu <129569557+HoshuChiu@users.noreply.github.com>
Date:   Sun Apr 16 14:47:51 2023 +0800

    修复Issue#220;vnc的端口号恢复5900 (#243)
    
    
    * 修复Issue#220
    
    * qemu-vnc端口号恢复为5900

commit 5c1e552cc7f0a6ad75c8a1fa2928e3b9cc619657
Author: YJwu2023 <yujianwu2019@gmail.com>
Date:   Fri Apr 14 12:21:08 2023 +0800

    修复ecam无法获取MCFG table的问题 (#241)

commit 79a452ce8f27ad9c7283ac0bcf4078ed6fa018d7
Author: houmkh <1119644616@qq.com>
Date:   Tue Apr 11 17:05:33 2023 +0800

    修改errno,使其与relibc的保持一致 (#234)
    
    修改errno,使其与relibc的保持一致

commit ac48398d3f17f24ff9b5da5e400ce912d05f0ba2
Author: login <longjin@DragonOS.org>
Date:   Tue Apr 11 16:54:14 2023 +0800

    调整brk系统调用,使得参数、返回值与Linux一致 (#238)
    
    * 新增用于测试relibc的app
    
    * 为适配relibc,修改do_execve中关于用户栈的内容的设置
    
    * 调整brk系统调用,使得参数、返回值与Linux一致

commit 13776c114b15c406b1e0aaeeb71812ea6e471d2e
Author: login <longjin@DragonOS.org>
Date:   Mon Apr 10 20:22:39 2023 +0800

    增加对dhcpv4的支持(tcp、udp socket已写好,但由于缺少epoll机制,尚未完整测试) (#237)
    
    * 为virtio网卡完成smoltcp的phy层配置
    
    * raw socket
    
    * 初步写完udp和tcp socket
    
    * 能够正常通过dhcp获取ipv4地址(具有全局iface btree)
    
    ---------
    
    Co-authored-by: guanjinquan <1666320330@qq.com>

commit 78bf93f02f84bf5e024ddfb559f040e68ce39ccf
Author: YJwu2023 <yujianwu2019@gmail.com>
Date:   Sun Apr 9 12:30:02 2023 +0800

    pci重构+pcie支持 (#235)
    
    * pci重构+pcie支持
    
    * pci重构测试完成
    
    * 修正makefile的问题
    
    * 小修改
    
    * 修改函数名字

commit 5c9a63df836eedaca33c8c4c600b7aaeb2caf9a6
Author: login <longjin@DragonOS.org>
Date:   Sat Apr 8 23:53:53 2023 +0800

    Patch add lazy init (#236)
    
    * 修正并发安全问题

commit 766127209ee49465a8086cfd0bec90d8b79a96c0
Author: login <longjin@DragonOS.org>
Date:   Thu Apr 6 19:01:30 2023 +0800

    new: lazy_init (#230)

commit e0dfd4d5d70d1b50fc7ad3ed4bf84b7ba6dad19d
Author: hanjiezhou <zhouhanjie@dragonos.org>
Date:   Thu Apr 6 00:50:14 2023 +0800

    修改CFSqueue从Vec变成红黑树 (#229)
    
    使用了由tickbh编写的rbtree: https://github.com/tickbh/rbtree-rs/blob/master/src/lib.rs
    
    Co-authored-by: tickbh <tickdream125@hotmail.com>

commit 2a7d773d3d39f1cb3d59d6baa817c896c6fd52d1
Author: TingHuang <92705854+TingSHub@users.noreply.github.com>
Date:   Wed Apr 5 13:02:05 2023 +0800

    新增设备驱动模型,为设备和驱动提供高层视图 (#227)
    
    * 添加base mod
    
    * 添加设备驱动模型相关文件
    
    * 删除单独的mod文件,使用mod.rs,修改一些格式上的问题
    
    * 移动驱动错误类型到该文件
    
    * 修改一些格式上的问题

commit 5d00b1852818dd4b25952fd6a30deb20e7c7df53
Author: login <longjin@DragonOS.org>
Date:   Wed Apr 5 00:53:35 2023 +0800

    修复显示刷新线程的空指针问题 (#228)

V0.1.6

备注

本文作者:龙进 longjin@DragonOS.org

2023年4月2日

贡献者名单

DragonOS V0.1.6版本由以下小伙伴贡献代码:

赞助者名单

感谢以下同学的赞赏,我们将不断努力!

更新内容-内核

  • softirq: 重构了软中断 (#223)

  • timer: 重构了系统定时器 (#223)

  • stdio: 新增tty设备,用于标准输入输出 (#202) (#217)

  • lib: 第一套键盘扫描码的状态机 (#216) (#219)

  • syscall: 新增dup,dup2系统调用 (#224)

  • syscall: 新增SystemError枚举类型,使得错误处理更清晰 (#205)

  • driver: 新增x87浮点处理器支持 (#212)

  • driver: VirtIO网卡能够正常发送、接收数据 (#204)

  • filesystem: 修正了FAT32判断逻辑,解决了文件系统为FAT12/16时系统无法正常启动的问题。 (#211)

  • filesystem: 新增VFS文档,以及修改文档配置 (#209)

  • textui: 修复由于textui加锁,更改了preempt_count导致“进程长时间连续输出字符”的情况下,进程调度器不运行的问题。 (#203)

  • scheduler: 解决由于在中断上下文以外,sched_enqueue时,未关中断导致cpu_queue双重加锁的问题 (#201)

更新内容-用户环境

新增仓库

更新内容-其他

  • build: 添加了qemu使用VNC作为图像输出的选项 (#222)

更新内容-软件移植

源码、发布版镜像下载

  您可以通过以下方式获得源代码:

通过Git获取
通过DragonOS软件镜像站获取

  为解决国内访问GitHub慢、不稳定的问题,同时为了方便开发者们下载DragonOS的每个版本的代码,我们特意搭建了镜像站,您可以通过以下地址访问镜像站:

  您可以通过镜像站获取到DragonOS的代码压缩包,以及编译好的可运行的磁盘镜像。

开放源代码声明

备注

为促进DragonOS项目的健康发展,DragonOS以GPLv2开源协议进行发布。所有能获得到DragonOS源代码以及相应的软件制品(包括但不限于二进制副本、文档)的人,都能享有我们通过GPLv2协议授予您的权利,同时您也需要遵守协议中规定的义务。

这是一个相当严格的,保护开源软件健康发展,不被侵占的协议。

对于大部分的善意的人们而言,您不会违反我们的开源协议。

我们鼓励DragonOS的自由传播、推广,但是请确保所有行为没有侵犯他人的合法权益,也没有违反GPLv2协议。

请特别注意,对于违反开源协议的,尤其是商业闭源使用以及任何剽窃、学术不端行为将会受到严肃的追责。(这是最容易违反我们的开源协议的场景)。

并且,请注意,按照GPLv2协议的要求,基于DragonOS修改或二次开发的软件,必须同样采用GPLv2协议开源,并标明其基于DragonOS进行了修改。亦需保证这些修改版本的用户能方便的获取到DragonOS的原始版本。

您必须使得DragonOS的开发者们,能够以同样的方式,从公开渠道获取到您二次开发的版本的源代码,否则您将违反GPLv2协议。

关于协议详细内容,还敬请您请阅读项目根目录下的LICENSE文件。请注意,按照GPLv2协议的要求,只有英文原版才具有法律效力。任何翻译版本都仅供参考。

开源软件使用情况

  DragonOS在开发的过程中,参考了一些开源项目的设计,或者引入了他们的部分代码,亦或是受到了他们的启发。现将他们列在下面。我们对这些开源项目的贡献者们致以最衷心的感谢!

格式:<项目名> - <链接> - <开源协议>

  • Linux - https://git.kernel.org/ - GPLv2

  • skiftOS - https://github.com/skiftOS/skift - MIT

  • FYSOS - https://github.com/fysnet/FYSOS - FYSOS’ License

  • LemonOS - https://github.com/LemonOSProject/LemonOS.git - BSD 2-Clause License

  • LZ4 - https://github.com/lz4/lz4 - BSD 2-Clause license

  • SerenityOS - https://github.com/SerenityOS/serenity.git - BSD 2-Clause license

  • MINE - 《一个64位操作系统的设计与实现》田宇; 人民邮电出版社

  • chcore - 《现代操作系统:设计与实现》陈海波,夏虞斌; 机械工业出版社

  • SimpleKernel - https://github.com/Simple-XX/SimpleKernel - MIT

  • rcore-fs - https://github.com/rcore-os/rcore-fs.git - MIT

  • redox - https://gitlab.redox-os.org/redox-os/redox - MIT

当前版本的所有提交记录

commit bacd691c9ef0502b5cc618aad50517f9e59df5e0
Author: login <longjin@DragonOS.org>
Date:   Sun Apr 2 17:09:33 2023 +0800

    软中断&定时器重构 (#223)
    
    * 软中断&定时器重构
    
    Co-authored-by: houmkh<houjiaying@DragonOS.org>
    
    * 修改timer的clock()
    
    * 删除debug信息
    
    ---------
    
    Co-authored-by: houmkh <1119644616@qq.com>

commit 6d345b774223b0daaf0ee629c7fb595a1912a9e2
Author: HoshuChiu <129569557+HoshuChiu@users.noreply.github.com>
Date:   Sun Apr 2 15:55:24 2023 +0800

    添加了qemu使用VNC作为图像输出的选项 (#222)
    
    * 添加了qemu使用VNC作为图像输出的选项
    
    * 设置vnc端口为5900
    
    ---------
    
    Co-authored-by: longjin <longjin@RinGoTek.cn>

commit 2b771e32f5795e0fdda458e3bb2651ef6b9673ac
Author: Gou Ngai <sujintao@dragonos.org>
Date:   Sun Apr 2 15:43:53 2023 +0800

    Add dup,dup2 (#224)
    
    * dup,dup2
    
    * fix: sys_dup2语义与posix不一致的问题
    
    ---------
    
    Co-authored-by: longjin <longjin@RinGoTek.cn>

commit d7b31a969ff091224a4929496f0278d024f78c77
Author: Gou Ngai <sujintao@dragonos.org>
Date:   Fri Mar 31 18:23:58 2023 +0800

    Patch keyboard capslock alt (#219)
    
    * keyboard-alt-capslock
    
    * 解决键盘输入'%'字符的时候无法回显的bug
    
    ---------
    
    Co-authored-by: longjin <longjin@RinGoTek.cn>

commit 20e3152e1eea97f87d644c3023391e172bc83c93
Author: login <longjin@DragonOS.org>
Date:   Fri Mar 31 12:54:37 2023 +0800

    将TTY与stdio进行连接,实现基本的stdio功能 (#217)
    
    * 将stdio与tty接上

commit 5fb12ce447710edf8566f250655a06cb27519fca
Author: Gou Ngai <sujintao@dragonos.org>
Date:   Thu Mar 30 18:19:02 2023 +0800

    第一套键盘扫描码的状态机 (#216)
    
    第一套键盘扫描码的状态机
    ---------
    
    Co-authored-by: guanjinquan <1666320330@qq.com>
    Co-authored-by: longjin <longjin@RinGoTek.cn>

commit 676b8ef62e1a0a1e52d65b40c53c1636a2954040
Author: Mork <91721145+MorkCarpenter@users.noreply.github.com>
Date:   Wed Mar 29 21:24:11 2023 +0800

    部分函数从返回值为Result<<>,i32>修改为Result<<>,SystemError> (#210)
    
    * 将Result<<>,i32>替换为Result<<>,SystemError>
    * bugfix: 显示双缓冲区初始化的时候,连续注册了两次Video Softirq的问题。
    
    Co-authored-by: longjin <longjin@RinGoTek.cn>

commit 64aea4b3494bee7375e1c1ee5739c9fab0db0cb7
Author: Gou Ngai <sujintao@dragonos.org>
Date:   Tue Mar 28 20:44:26 2023 +0800

    增加x87FPU支持 (#212)
    
    * remove `ret_from_syscall`
    *修复ps2键盘驱动程序inode在进程fork的时候导致死锁的问题.
    *更新: VFS每次拷贝文件描述符的时候,都会去调用inode的open函数
    
    ---------
    
    Co-authored-by: longjin <longjin@RinGoTek.cn>

commit 2286eda6526ed1b46afd79b47dc701034b9e903d
Author: WaferJay <17383312+WaferJay@users.noreply.github.com>
Date:   Mon Mar 27 09:32:43 2023 +0800

    修正了FAT32判断逻辑,解决了文件系统为FAT12/16时系统无法正常启动的问题。 (#211)
    
    * fix(fat): fix determination of fat type casue crash if fs is fat12/16
    
    * refactor(fat): split BiosParameterBlock.validate() into BiosParameterBlockFAT32.validate() and BiosParameterBlockLegacy.validate()
    
    * 调整“最大允许的簇号”的常量放置的位置。
    
    ---------
    
    Co-authored-by: longjin <longjin@RinGoTek.cn>

commit 45b8371173b070028457f7ee64be33f68b4f9ada
Author: login <longjin@DragonOS.org>
Date:   Sat Mar 25 14:51:16 2023 +0800

    新增VFS文档,以及修改文档配置 (#209)
    
    * 1.新增vfs设计文档
    2.修改文档版权标志为"2022-2023, DragonOS Community"
    3.修改电脑版文档页面的宽度为90%
    
    * layout.html末尾加空行

commit 73c607aaddf6e4634cad179a81d3f1bc589f7220
Author: YJwu2023 <119829947+YJwu2023@users.noreply.github.com>
Date:   Sat Mar 18 20:43:37 2023 +0800

    VirtIO网卡能够正常发送、接收数据 (#204)
    
    * virtio-net小修改
    
    * 移动volatile.rs到libs文件夹
    
    * 使用virtio-drivers 0.3.0
    
    * bugfix: 初始化BAR之后,未正确设置command register的问题
    
    
    ---------
    
    Co-authored-by: longjin <longjin@dragonos.org>

commit 4454d1a2dd1f1078750151c028a794cfd9a04a1b
Author: login <longjin@DragonOS.org>
Date:   Sat Mar 18 20:26:05 2023 +0800

    新增SystemError枚举类型,使得错误处理更清晰 (#205)

commit 0d48c3c9c21a2dd470d0e1e58b507db60e0887bb
Author: login <longjin@DragonOS.org>
Date:   Thu Mar 16 19:48:59 2023 +0800

    new: tty设备(尚未与stdio接上) (#202)

commit 790d45764090bce3bbfb96b42b2818100a8cef9a
Author: login <longjin@DragonOS.org>
Date:   Wed Mar 15 11:42:41 2023 +0800

    修复由于textui加锁,更改了preempt_count导致“进程长时间连续输出字符”的情况下,进程调度器不运行的问题。 (#203)

commit c2e757d8cbeed01b16f48bea48ed8447685e6f1a
Author: login <longjin@DragonOS.org>
Date:   Mon Mar 13 22:22:23 2023 +0800

    解决由于在中断上下文以外,sched_enqueue时,未关中断导致cpu_queue双重加锁的问题 (#201)

V0.1.5

备注

本文作者:龙进 longjin@RinGoTek.cn

2023年3月13日

贡献者名单

DragonOS V0.1.5版本由以下小伙伴贡献代码:

赞助者名单

感谢以下同学的赞赏,我们将不断努力!

更新内容-内核

  • scheduler: doc: 实时进程调度器文档 (#163)

  • scheduler: rt: RTQueue改用双向链表存储 (#174)

  • scheduler: load balance: 多核负载均衡 (#193)

  • Semaphore: new: 新增了rust实现的信号量 (#183)

  • mm: refactor: 重构了MMIO地址分配器 (#184)

  • RwLock: new: 新增了rust实现的读写锁 (#186)

  • driver: update: 完善pci的功能 (#194)

  • driver: new: VirtIO网卡驱动(仍存在问题) (#194)

  • driver: refactor: Rust版本的AHCI驱动 (#198)

  • block io: delete: 移除Block IO 调度器. (#196)

  • filesystem: refactor: 新版的VFS (#198)

  • filesystem: refactor: 新版的ProcFS (#198)

  • filesystem: refactor: 新版的DevS (#198)

  • filesystem: new: RamFS内存文件系统 (#198)

  • filesystem: new: FAT12/FAT16/FAT32文件系统 (#198)

  • filesystem: new: 新的设备、块设备抽象 (#198)

更新内容-用户环境

  • libc: 调整,将所有的app直接链接到libc.a中,而不是都执行一遍”搜索.o”的过程 (#171)

更新内容-其他

  • bootstrap: 解决ubuntu2210版本无法正确编译grub,以及正确安装qemu的问题 (#176)

  • toolchain: 添加rust的bare bone工具链 (#197)

更新内容-软件移植

源码、发布版镜像下载

  您可以通过以下方式获得源代码:

通过Git获取
通过DragonOS软件镜像站获取

  为解决国内访问GitHub慢、不稳定的问题,同时为了方便开发者们下载DragonOS的每个版本的代码,我们特意搭建了镜像站,您可以通过以下地址访问镜像站:

  您可以通过镜像站获取到DragonOS的代码压缩包,以及编译好的可运行的磁盘镜像。

开放源代码声明

备注

为促进DragonOS项目的健康发展,DragonOS以GPLv2开源协议进行发布。所有能获得到DragonOS源代码以及相应的软件制品(包括但不限于二进制副本、文档)的人,都能享有我们通过GPLv2协议授予您的权利,同时您也需要遵守协议中规定的义务。

这是一个相当严格的,保护开源软件健康发展,不被侵占的协议。

对于大部分的善意的人们而言,您不会违反我们的开源协议。

我们鼓励DragonOS的自由传播、推广,但是请确保所有行为没有侵犯他人的合法权益,也没有违反GPLv2协议。

请特别注意,对于违反开源协议的,尤其是商业闭源使用以及任何剽窃、学术不端行为将会受到严肃的追责。(这是最容易违反我们的开源协议的场景)。

并且,请注意,按照GPLv2协议的要求,基于DragonOS修改或二次开发的软件,必须同样采用GPLv2协议开源,并标明其基于DragonOS进行了修改。亦需保证这些修改版本的用户能方便的获取到DragonOS的原始版本。

您必须使得DragonOS的开发者们,能够以同样的方式,从公开渠道获取到您二次开发的版本的源代码,否则您将违反GPLv2协议。

关于协议详细内容,还敬请您请阅读项目根目录下的LICENSE文件。请注意,按照GPLv2协议的要求,只有英文原版才具有法律效力。任何翻译版本都仅供参考。

开源软件使用情况

  DragonOS在开发的过程中,参考了一些开源项目的设计,或者引入了他们的部分代码,亦或是受到了他们的启发。现将他们列在下面。我们对这些开源项目的贡献者们致以最衷心的感谢!

格式:<项目名> - <链接> - <开源协议>

  • Linux - https://git.kernel.org/ - GPLv2

  • skiftOS - https://github.com/skiftOS/skift - MIT

  • FYSOS - https://github.com/fysnet/FYSOS - FYSOS’ License

  • LemonOS - https://github.com/LemonOSProject/LemonOS.git - BSD 2-Clause License

  • LZ4 - https://github.com/lz4/lz4 - BSD 2-Clause license

  • SerenityOS - https://github.com/SerenityOS/serenity.git - BSD 2-Clause license

  • MINE - 《一个64位操作系统的设计与实现》田宇; 人民邮电出版社

  • chcore - 《现代操作系统:设计与实现》陈海波,夏虞斌; 机械工业出版社

  • SimpleKernel - https://github.com/Simple-XX/SimpleKernel - MIT

  • rcore-fs - https://github.com/rcore-os/rcore-fs.git - MIT

  • redox - https://gitlab.redox-os.org/redox-os/redox - MIT

当前版本的所有提交记录

commit 84407d360511c7699938a0f245ae33ff76f16b17
Author: login <longjin@DragonOS.org>
Date:   Mon Mar 13 00:26:04 2023 +0800

    bugfix:解决touch命令失败的问题 (#199)
    
    * bug fix : 解决touch命令失败的问题

commit 004e86ff19727df303c23b42c7a271b9214c6898
Author: login <longjin@DragonOS.org>
Date:   Sun Mar 12 22:36:11 2023 +0800

    新版文件系统重构完成 (#198)
    
    1.重构:VFS
    2. 重构:ProcFS
    3. 重构:DevFS
    4. 重构:FAT32
    5. 重构:AHCI驱动
    6. 新增:RamFS
    7. 新增:MountFS
    8. 新增:FAT12
    9. 新增:FAT16
    10. 重构:设备抽象
    
    Co-authored-by: guanjinquan <1666320330@qq.com>
    Co-authored-by: DaJiYuQia <88259094+DaJiYuQia@users.noreply.github.com>

commit 17041e0e307eaf9e8d8ddbddfa186cd1f10f1bc0
Author: login <longjin@DragonOS.org>
Date:   Sun Mar 12 21:04:37 2023 +0800

    添加rust的bare bone工具链 (#197)

commit 26d84a31393c50063ff416bc509316e8d342028c
Author: YJwu2023 <119829947+YJwu2023@users.noreply.github.com>
Date:   Sat Mar 11 21:09:50 2023 +0800

    新增VirtIO网卡驱动 (#194)
    
    * 修复内存bug与grub安装脚本的错误
    
    * 修改小bug
    
    * PCI增加功能与virtio-net驱动
    
    * little fix
    
    * virtio-net小修改

commit 1d48996375149279a721777b2c600e1b5c3ee1b5
Author: kong <45937622+kkkkkong@users.noreply.github.com>
Date:   Sat Mar 11 18:17:35 2023 +0800

    多核负载均衡(#193)
    
    * feat(sched):CPU负载检测初步实现
    
    * fix(smp):调整smp中的apic的头文件声明
    
    * fix(smp):简单的负载均衡算法实现
    
    * fix(sched):抽离负载均衡方法
    
    * fix(sched):修改rt中的运行队列bug,调整负载均衡逻辑
    
    * fix(process):移除无用测试代码
    
    * reformat code

commit ef9f9732b09f78d7192f1d0dd3b41be655fb0914
Author: houmkh <100781004+houmkh@users.noreply.github.com>
Date:   Thu Mar 9 23:31:25 2023 +0800

    修复了mmio buddy的bug (#189)
    
    * 修改buddy_query

commit c1396d277115b371d09ad6d39a1c419f9224ffd0
Author: Gou Ngai <sujintao@dragonos.org>
Date:   Mon Mar 6 11:28:32 2023 +0800

    Rwlock文档 (#186)
    
    * Rwlock文档

commit a7eb62a47a8d701b90a14f83cc9028cfed07c268
Author: houmkh <100781004+houmkh@users.noreply.github.com>
Date:   Mon Mar 6 11:21:29 2023 +0800

    修改mmio-buddy代码结构和函数名 (#184)
    
    * 修改mmio-buddy结构和函数名

commit c2481452f81750ec02adec627ab2edbc93d9cd9c
Author: houmkh <100781004+houmkh@users.noreply.github.com>
Date:   Sat Mar 4 18:36:55 2023 +0800

    rust重构mmio_buddy和mmio (#178)
    
    * rust重构mmio_buddy和mmio
    
    * mmio-buddy文档
    
    ---------
    
    Co-authored-by: longjin <longjin@RinGoTek.cn>

commit f1284c35717a2f9f8cee7cecfc835ba1d23a1161
Author: Gou Ngai <sujintao@dragonos.org>
Date:   Sat Mar 4 17:47:17 2023 +0800

    新增了rust实现的信号量 (#181)
    
    * 新增了rust实现的信号量
    
    ---------
    
    Co-authored-by: longjin <longjin@RinGoTek.cn>

commit 83b9512c1c1e8289000084adcafddebee6a23f16
Author: Gou Ngai <sujintao@dragonos.org>
Date:   Sat Mar 4 16:54:42 2023 +0800

    新增了rust实现的信号量 (#183)
    
    * 新增了rust实现的信号量

commit e532a536a0b244f4590e6eb7910084bd63049704
Author: login <longjin@ringotek.cn>
Date:   Thu Mar 2 22:50:07 2023 +0800

    添加赞助者:FengWangHao (#179)

commit b66beefd4e9ead61ee55f335246ebeb8277d3011
Author: login <longjin@ringotek.cn>
Date:   Mon Feb 27 01:00:35 2023 +0800

    解决ubuntu2210版本无法正确编译grub,以及正确安装qemu的问题 (#176)

commit 4177d0327c3eacdc606f0b22f99f208fd48cfff3
Author: kong <45937622+kkkkkong@users.noreply.github.com>
Date:   Mon Feb 20 17:03:37 2023 +0800

    RTQueue改用双向链表存储(#174)
    
    * RTQueue改用双向链表存储

commit 2bf5ee0e3cac3a91dee6a13b71c86a9477c07d9b
Author: login <longjin@ringotek.cn>
Date:   Sat Feb 11 13:04:24 2023 +0800

    修改libc的编译相关内容(#171)
    
    1.将libc的include文件夹分为export和internal
    2.将所有app都直接链接libc.a,而不是都执行一遍"搜索.o"的过程

commit 90b077f9d3ecd48ca46f8bbb32363620db6ddbe6
Author: kong <45937622+kkkkkong@users.noreply.github.com>
Date:   Thu Feb 9 15:24:37 2023 +0800

    Sched rt doc (#163)
    
    * update
    
    * 完善调度器文档
    
    * 更新RT调度器文档
    
    * 更新实时调度文档

commit 009f92d50fe2e52e425bce397801d3fa204daecd
Author: Satin Wuker <74630829+SatinWuker@users.noreply.github.com>
Date:   Tue Feb 7 19:29:09 2023 -0800

    fix typos 改正README_EN的错别字和语法错误 (#167)

V0.1.4

备注

本文作者:龙进 longjin@RinGoTek.cn

2023年2月4日

贡献者名单

DragonOS V0.1.4版本由以下小伙伴贡献代码:

赞助者名单

感谢以下同学的赞赏,我们将不断努力!

  • David Wen (人民币2000元)

  • Seele.Clover (人民币500元)

  • 叶锦毅 (人民币100元)

  • 林 (人民币50元)

  • Albert (人民币9.99元)

  • TerryLeeSCUT (人民币6.66元)

  • slientbard (人民币6.66元)

  • 悟 (人民币2.00元)

  • 【其他匿名的热心人士】(人民币1.00元)

更新内容-内核

  • Spinlock: new: 新增具有守卫的自旋锁SpinLock,支持编译期对锁的使用进行检查。 (#148)

  • Spinlock: feature: Raw spin lock 增加lock_irqsave、unlock_irqrestore(#151)

  • Mutex: new: Rust版本的Mutex (#157)

  • doc: new: Rust代码风格文档 (#161)

  • WaitQueue: new: Rust版本的WaitQueue (#162)

  • WaitQueue: update: C版本的wait_queue的唤醒,改为立即唤醒 (#158)

  • block io: new: Block IO 调度器. 当具有多核时,io调度器在核心1上运行。 (#158)

  • smp: bugfix: 为AP核启动apic_timer,使其能够运行调度 (#158)

  • smp: new: 增加kick_cpu功能,支持让某个特定核心立即运行调度器 (#158)

  • smp: new: 增加进程在核心间迁移的功能 (#158)

  • scheduler: new: 增加实时进程调度器(支持FIFO、RR策略) (#139)

  • scheduler: update: CFS调度器为每个核心设置单独的IDLE进程pcb(pid均为0) (#158)

  • scheduler: bugfix: process_wakeup时,对cfs的进程,重设虚拟运行时间。解决由于休眠的进程,其虚拟运行时间过小,导致其他进程饥饿的问题 (#158)

  • process: new: pcb中增加migrate_to字段 (#158)

更新内容-用户环境

更新内容-其他

更新内容-软件移植

源码、发布版镜像下载

  您可以通过以下方式获得源代码:

通过Git获取
通过DragonOS软件镜像站获取

  为解决国内访问GitHub慢、不稳定的问题,同时为了方便开发者们下载DragonOS的每个版本的代码,我们特意搭建了镜像站,您可以通过以下地址访问镜像站:

  您可以通过镜像站获取到DragonOS的代码压缩包,以及编译好的可运行的磁盘镜像。

开放源代码声明

备注

为促进DragonOS项目的健康发展,DragonOS以GPLv2开源协议进行发布。所有能获得到DragonOS源代码以及相应的软件制品(包括但不限于二进制副本、文档)的人,都能享有我们通过GPLv2协议授予您的权利,同时您也需要遵守协议中规定的义务。

这是一个相当严格的,保护开源软件健康发展,不被侵占的协议。

对于大部分的善意的人们而言,您不会违反我们的开源协议。

我们鼓励DragonOS的自由传播、推广,但是请确保所有行为没有侵犯他人的合法权益,也没有违反GPLv2协议。

请特别注意,对于违反开源协议的,尤其是商业闭源使用以及任何剽窃、学术不端行为将会受到严肃的追责。(这是最容易违反我们的开源协议的场景)。

并且,请注意,按照GPLv2协议的要求,基于DragonOS修改或二次开发的软件,必须同样采用GPLv2协议开源,并标明其基于DragonOS进行了修改。亦需保证这些修改版本的用户能方便的获取到DragonOS的原始版本。

您必须使得DragonOS的开发者们,能够以同样的方式,从公开渠道获取到您二次开发的版本的源代码,否则您将违反GPLv2协议。

关于协议详细内容,还敬请您请阅读项目根目录下的LICENSE文件。请注意,按照GPLv2协议的要求,只有英文原版才具有法律效力。任何翻译版本都仅供参考。

开源软件使用情况

  DragonOS在开发的过程中,参考了一些开源项目的设计,或者引入了他们的部分代码,亦或是受到了他们的启发。现将他们列在下面。我们对这些开源项目的贡献者们致以最衷心的感谢!

格式:<项目名> - <链接> - <开源协议>

  • Linux - https://git.kernel.org/ - GPLv2

  • skiftOS - https://github.com/skiftOS/skift - MIT

  • FYSOS - https://github.com/fysnet/FYSOS - FYSOS’ License

  • LemonOS - https://github.com/LemonOSProject/LemonOS.git - BSD 2-Clause License

  • LZ4 - https://github.com/lz4/lz4 - BSD 2-Clause license

  • SerenityOS - https://github.com/SerenityOS/serenity.git - BSD 2-Clause license

  • MINE - 《一个64位操作系统的设计与实现》田宇; 人民邮电出版社

  • chcore - 《现代操作系统:设计与实现》陈海波,夏虞斌; 机械工业出版社

  • SimpleKernel - https://github.com/Simple-XX/SimpleKernel - MIT

  • rcore-fs - https://github.com/rcore-os/rcore-fs.git - MIT

当前版本的所有提交记录

commit f6ba114bb0420e848ef7fc844c96c0d7a0552d93
Author: houmkh <100781004+houmkh@users.noreply.github.com>
Date:   Sat Feb 4 12:31:15 2023 +0800

    Block IO Scheduler (#158)
    
    * Block io调度器
    * process_wakeup时,对cfs的进程,重设虚拟运行时间。解决由于休眠的进程,其虚拟运行时间过小,导致其他进程饥饿的问题
    
    * 1、为AP核启动apic_timer,使其能够运行调度
    2、增加kick_cpu功能,支持让某个特定核心立即运行调度器
    3、wait_queue的唤醒,改为立即唤醒。
    4、增加进程在核心间迁移的功能
    5、CFS调度器为每个核心设置单独的IDLE进程pcb(pid均为0)
    6、pcb中增加migrate_to字段
    7、当具有多核时,io调度器在核心1上运行。
    
    * io调度器文件位置修改
    
    * 修改io的makefile
    
    * 更新makefile中的变量名
    
    * 修改io调度器函数名
    
    ---------
    
    Co-authored-by: login <longjin@ringotek.cn>

commit 151251b50b7ed55596edd32ffec49a4041010e2a
Author: login <longjin@ringotek.cn>
Date:   Tue Jan 31 19:27:02 2023 +0800

    Patch add rust waitqueue (#162)
    
    * new: rust版本的waitqueue
    
    * new:等待队列的文档

commit 3c369b1430e8d571bcc74a8ef7fefc1c4cae5dd2
Author: login <longjin@ringotek.cn>
Date:   Mon Jan 30 15:43:42 2023 +0800

    new:新增rust代码风格 (#161)

commit c28bd540ac856cd9d8d5597852af8f2588a660e4
Author: login <longjin@ringotek.cn>
Date:   Mon Jan 30 15:10:24 2023 +0800

    更新赞助者名单 (#160)
    
    * 更新赞赏者列表

commit 935f40ec174fec217aed4553d45996327443bc0e
Author: login <longjin@ringotek.cn>
Date:   Tue Jan 17 21:30:16 2023 +0800

    new: Rust版本的Mutex (#157)

commit d8a064128a8a06b90ff4c7b87c193518d9572641
Author: Gou Ngai <94795048+AlbertSanoe@users.noreply.github.com>
Date:   Mon Jan 16 19:58:50 2023 +0800

    Raw spin lock 增加lock_irqsave、unlock_irqrestore(#151)
    
    Raw spin lock 增加lock_irqsave、unlock_irqrestore

commit 06b09f34ed64a006a80ae8df383e3c8b176f02e0
Author: kong <45937622+kkkkkong@users.noreply.github.com>
Date:   Sat Jan 14 22:38:05 2023 +0800

    Patch sched rust (#139)
    
    * update
    
    * 添加rt调度器的rust初步实现
    
    * 完善rt调度逻辑
    
    * 调试rt调度器
    
    * 修改sched的返回值
    
    * cargo fmt 格式化
    
    * 删除无用代码,修补rt bug
    
    * 删除无用的代码,和重复的逻辑
    
    * 软中断bugfix
    
    * 删除一些代码
    
    * 添加kthread_run_rt文档
    
    * 解决sphinix警告_static目录不存在的问题
    
    Co-authored-by: longjin <longjin@RinGoTek.cn>

commit ec53d23ed03347854189d92b7e175f309779321b
Author: login <longjin@ringotek.cn>
Date:   Sat Jan 14 10:35:49 2023 +0800

    new: 新增具有守卫的自旋锁SpinLock,支持编译期对锁的使用进行检查。 (#148)

commit 41474ba3df99b6822ce452dc94dc53a4da62cba1
Author: login <longjin@ringotek.cn>
Date:   Tue Jan 10 22:07:41 2023 +0800

    更新Readme中关于DragonOS的介绍部分 (#146)

commit 8ad2e358fd3b05eed2919de50640682e51687fb5
Author: login <longjin@ringotek.cn>
Date:   Sun Jan 8 15:51:59 2023 +0800

    更新about app中的版本号 (#145)
    
    * 更新about app中的版本号

commit a8b621c8d1fe77251b8e4eafe258dc0ee7366dd5
Author: login <longjin@ringotek.cn>
Date:   Sun Jan 8 15:47:44 2023 +0800

    修正由于libc中具有crti.S和crtn.S,造成的与x86_64-elf-gcc不兼容的问题 (#144)

commit 9358ff0f6f7daa18d6fab4497de025736b3d6725
Author: login <longjin@ringotek.cn>
Date:   Sun Jan 8 15:06:52 2023 +0800

    Add v0.1.3 changelog (#143)
    
    * new: 0.1.3发行日志
    
    * 新增输出指定时间范围内的贡献者名单的脚本
    
    * 更新bootloader文档
    
    * update: 简介文档
    
    * new: 镜像站文档
    
    * update: 功能特性文档

V0.1.3

备注

本文作者:龙进 longjin@RinGoTek.cn

2023年1月8日

贡献者名单

DragonOS V0.1.3版本由以下小伙伴贡献代码:

赞助者名单

感谢以下同学的赞赏,我们将不断努力!

更新内容-内核

  • syscall: new: 增加getpid系统调用 (#120)

  • signal: update: 对于除了SIGKILL以外的信号,也将他们加入SigQueue (#120)

  • rtc: refactor: 使用Rust重构RTC驱动 (#118)

  • doc: new: 新增signal的文档 (#126)

  • Spinlock: new: 新增rust写的RawSpinlock (#127)

  • arch: update: 在lib.rs中,将arch模块的路径进行更改,使得其他模块使用arch的代码时,不需要指定arch::x86_64 (#128)

  • mm: bugfix: 修复页面分配器在初始化时,ZONE_NORMAL_INDEX始终为0的bug (#129)

  • scheduler: new: 使用Rust重构CFS调度器 (#131)

  • smp: 删除已经在smp中废弃的HPET中断转发函数 (#131)

  • process: bugfix: 修复init进程忘记设定fs gs寄存器的问题。 (#132)

  • vfs: update: 将VFS文件夹重命名为vfs (#133)

  • lockref: new: 新增rust版本的lockref (#135)

  • cpu: new: new:Rust封装cpu_relax(),通过pause指令,让cpu休息一会儿,降低空转功耗. (#135)

  • 使用rust重构softirq机制 (#138)

更新内容-用户环境

  • libc: bugfix: 注册信号处理函数时,总是注册sigkill的问题 (#120)

  • libc: new: 增加了raise、kill、abort (#120)

  • libc: new: 新增arch文件夹,在下面新增crt0 crti crtn文件 (#134)

  • libc: new: 新增fflush(), fprintf(), stdin, stdout, stderr, ferror(), fopen(), fclose(), putchar(), puts() (#136)

  • libc: new: 简单添加了fopen()对mode参数的处理。请注意,它没有完全遵循posix,也与Linux的不一致,将来使用Rust的时候完善它。 (#141)

  • 移植: new: 新增了gmp, mpfr, mpc的移植构建脚本 (#136)

  • 移植: new: 新增了gcc、binutils的交叉编译构建脚本以及gcc-11.3.0, binutils-2.38的补丁(在DragonOS-community下的仓库中)(#136)

  • compile: update: 更改编译器的Include路径,使得include时不需要加<libc/src/include/>前缀 (#124)

更新内容-其他

  • bugfix: 修复docker安装时异常退出的bug (#116)

  • new: 新增目标为x86_64-elf的GCC裸机编译器,并使用它来编译DragonOS (#111)

  • update: 更新Docker编译镜像至版本dragonos/dragonos-dev:v1.2, 并支持从Dockerfile构建这个编译镜像 (#111)

  • bugfix: 修复MBR磁盘镜像未设置启动标志的bug (#111)

  • update: 更新github workflow,增加cache,加快build check的速度

  • bugfix: 修复下载grub2.06时的提示错误 (#125)

更新内容-软件移植

源码、发布版镜像下载

  您可以通过以下方式获得源代码:

通过Git获取
通过DragonOS软件镜像站获取

  为解决国内访问GitHub慢、不稳定的问题,同时为了方便开发者们下载DragonOS的每个版本的代码,我们特意搭建了镜像站,您可以通过以下地址访问镜像站:

  您可以通过镜像站获取到DragonOS的代码压缩包,以及编译好的可运行的磁盘镜像。

开放源代码声明

备注

为促进DragonOS项目的健康发展,DragonOS以GPLv2开源协议进行发布。所有能获得到DragonOS源代码以及相应的软件制品(包括但不限于二进制副本、文档)的人,都能享有我们通过GPLv2协议授予您的权利,同时您也需要遵守协议中规定的义务。

这是一个相当严格的,保护开源软件健康发展,不被侵占的协议。

对于大部分的善意的人们而言,您不会违反我们的开源协议。

我们鼓励DragonOS的自由传播、推广,但是请确保所有行为没有侵犯他人的合法权益,也没有违反GPLv2协议。

请特别注意,对于违反开源协议的,尤其是商业闭源使用以及任何剽窃、学术不端行为将会受到严肃的追责。(这是最容易违反我们的开源协议的场景)。

并且,请注意,按照GPLv2协议的要求,基于DragonOS修改或二次开发的软件,必须同样采用GPLv2协议开源,并标明其基于DragonOS进行了修改。亦需保证这些修改版本的用户能方便的获取到DragonOS的原始版本。

您必须使得DragonOS的开发者们,能够以同样的方式,从公开渠道获取到您二次开发的版本的源代码,否则您将违反GPLv2协议。

关于协议详细内容,还敬请您请阅读项目根目录下的LICENSE文件。请注意,按照GPLv2协议的要求,只有英文原版才具有法律效力。任何翻译版本都仅供参考。

开源软件使用情况

  DragonOS在开发的过程中,参考了一些开源项目的设计,或者引入了他们的部分代码,亦或是受到了他们的启发。现将他们列在下面。我们对这些开源项目的贡献者们致以最衷心的感谢!

格式:<项目名> - <链接> - <开源协议>

  • Linux - https://git.kernel.org/ - GPLv2

  • skiftOS - https://github.com/skiftOS/skift - MIT

  • FYSOS - https://github.com/fysnet/FYSOS - FYSOS’ License

  • LemonOS - https://github.com/LemonOSProject/LemonOS.git - BSD 2-Clause License

  • LZ4 - https://github.com/lz4/lz4 - BSD 2-Clause license

  • SerenityOS - https://github.com/SerenityOS/serenity.git - BSD 2-Clause license

  • MINE - 《一个64位操作系统的设计与实现》田宇; 人民邮电出版社

  • chcore - 《现代操作系统:设计与实现》陈海波,夏虞斌; 机械工业出版社

  • SimpleKernel - https://github.com/Simple-XX/SimpleKernel - MIT

  • rcore-fs - https://github.com/rcore-os/rcore-fs.git - MIT

当前版本的所有提交记录

commit a8b621c8d1fe77251b8e4eafe258dc0ee7366dd5
Author: login <longjin@ringotek.cn>
Date:   Sun Jan 8 15:47:44 2023 +0800

    修正由于libc中具有crti.S和crtn.S,造成的与x86_64-elf-gcc不兼容的问题 (#144)

commit 9358ff0f6f7daa18d6fab4497de025736b3d6725
Author: login <longjin@ringotek.cn>
Date:   Sun Jan 8 15:06:52 2023 +0800

    Add v0.1.3 changelog (#143)
    
    * new: 0.1.3发行日志
    
    * 新增输出指定时间范围内的贡献者名单的脚本
    
    * 更新bootloader文档
    
    * update: 简介文档
    
    * new: 镜像站文档
    
    * update: 功能特性文档

commit fd91905f022b3ceaa59e666d1ff42d91fb8d40ef
Author: login <longjin@ringotek.cn>
Date:   Sun Jan 8 11:38:59 2023 +0800

    解决编译gcc、binutils的脚本中,变量名称错误的问题 (#142)

commit 62e4613978193aaf5d949a331df0398f2d085a30
Author: Gou Ngai <94795048+AlbertSanoe@users.noreply.github.com>
Date:   Sat Jan 7 23:15:37 2023 +0800

    使用rust重构softirq机制;解决Rtc驱动的编译警告问题 (#138)
    
    * 使用rust重构softirq机制
    * 解决Rtc驱动的编译警告问题
    
    Co-authored-by: longjin <longjin@RinGoTek.cn>

commit e9fdc57bf878f1bc5cc5743dfaeeaef743439291
Author: login <longjin@ringotek.cn>
Date:   Sat Jan 7 22:36:49 2023 +0800

    简单添加了fopen对mode参数的处理。请注意,它没有完全遵循posix,也与Linux的不一致,将来使用Rust的时候完善它。 (#141)

commit 2224c93ea968bc74621f7e124b4aca04875b3e6a
Author: guanjinquan <1666320330@qq.com>
Date:   Fri Jan 6 21:29:23 2023 +0800

    完善libc,构建了OS-specific工具链,编译了基于gcc-11.3.0的DragonOS userland compiler,移植了mpfr,gmp,mpc库 (#134)
    
    * 修改include路径
    
    * 添加了创建libsysapi.a和/bin/sysroot/usr/include/+lib/的代码
    
    * 修补.gitignore
    
    * 删除多余项
    
    * 优化脚本可读性
    
    * 新增crt0 crti crtn
    
    * 编译binutils所需的东西
    
    * fflush()和fprintf()的简单实现
    
    * 应用程序启动前,调用初始化libc的函数
    
    * 自动创建sysroot
    
    * 添加了stderr的初始化
    
    * 修改了stderr的初始化
    
    * 内核添加对stdio的简略处理
    
    * 格式化代码
    
    * 修正打开stdio文件描述符的问题
    
    * bugfix: 修复fprintf忘记释放buf的问题
    
    * 修复shell错误地把入口设置为main而不是_start的问题
    
    * 新增__cxa_atexit  (gcc要求libc提供这个)
    
    * 增加putchar puts
    
    * 更新写入磁盘镜像的脚本,默认无参数时,使用legacy方式安装
    
    * 更新编译脚本
    
    * stdio增加eof的定义
    
    * 新增extern cplusplus
    
    * mpfr gmp mpc 构建脚本
    
    * 更新libsysapi.a为libc.a
    
    * 加上ferror fopen fclose
    
    * 更新移植的软件的构建脚本
    
    * 更改build_gcc_toolchain.sh中的-save参数名为-save-cache
    
    Co-authored-by: longjin <longjin@RinGoTek.cn>

commit 61de2cdc3f29cdc6c441f128119e01e003e6f3ca
Author: login <longjin@ringotek.cn>
Date:   Tue Jan 3 23:09:25 2023 +0800

    新增rust版本的lockref (#135)
    
    * new:Rust封装cpu_relax(),通过pause指令,让cpu休息一会儿。降低空转功耗
    
    * new: Rust版本的lockref
    
    * Rust的RawSpinlock新增is_locked()和set_value()方法。
    
    * lockref文档

commit 2726f101b4cc787bbd36a69afffb0112f3a6567f
Author: login <longjin@ringotek.cn>
Date:   Tue Jan 3 21:01:56 2023 +0800

    删除无用的cfs.h (#136)

commit 587086d3f299f7394559d547c828191be20cfc11
Author: login <longjin@ringotek.cn>
Date:   Sun Jan 1 16:53:57 2023 +0800

    1、在文件系统目录下增加mod.rs 2、将VFS的路径改为vfs(#133)
    
    2、将VFS的路径改为vfs

commit 843e442971a47693f37a5f8d3452c383f7325359
Author: login <longjin@ringotek.cn>
Date:   Sat Dec 31 18:43:05 2022 +0800

    修复init进程忘记设定fs gs寄存器的问题。 (#132)

commit 74bde36e014ff501241bf40dd83653db47a2c8e4
Author: guanjinquan <1666320330@qq.com>
Date:   Sat Dec 31 17:35:39 2022 +0800

    Patch porting gcc v2 (#124)
    
    * 更改编译器的Include路径,使得include时不需要加`<libc/src/include/>`前缀
    
    * 修改include路径
    
    Co-authored-by: longjin <longjin@RinGoTek.cn>

commit d4f3de93a23e4bd4f000a3663768d47d094bf188
Author: login <longjin@ringotek.cn>
Date:   Sat Dec 31 17:26:12 2022 +0800

    使用Rust重构CFS调度器 (#131)
    
    * 新建调度器的文件
    
    * 把softirq vector移动到c文件中(原来在.h)
    
    * 将进程切换方式改为“中断返回时切换”
    
    * new:使用rust重构CFS
    
    * 删除已经在smp中废弃的HPET中断转发函数
    
    * 代码格式化
    
    * 删除多余的dunce依赖

commit 156949680c83f2d7e3b21ed68b11698b88eaf396
Author: login <longjin@ringotek.cn>
Date:   Sat Dec 31 13:47:49 2022 +0800

    bugfix:修复当使用sched()运行调度器,在切换进程的时候,由于不在中断上下文内,导致当前进程的上下文丢失的问题。 (#130)
    
    bugfix:修复当使用sched()运行调度器,在切换进程的时候,由于不在中断上下文内,导致当前进程的上下文丢失的问题。
    bugfix:修复切换进程的宏的汇编代码的损坏部分,未声明rax寄存器,从而导致的编译器未定义行为问题。

commit 882f0b7e7498dbff8de527b2b9159b7f6e6359c9
Author: YJwu2023 <119829947+YJwu2023@users.noreply.github.com>
Date:   Wed Dec 28 19:35:17 2022 +0800

    修复内存bug与grub安装脚本的错误 (#129)
    
    * 修复内存bug与grub安装脚本的错误
    
    * 修改小bug

commit adc1846b06fb862caed049f435fc0061488a6ff9
Author: login <longjin@ringotek.cn>
Date:   Mon Dec 26 13:13:12 2022 +0800

    内核:在lib.rs中,将arch模块的路径进行更改,使得其他模块使用arch的代码时,不需要指定arch::x86_64 (#128)

commit ac643d420b22f9d454ecefccd51ed34a9664586b
Author: login <longjin@ringotek.cn>
Date:   Sun Dec 25 23:53:35 2022 +0800

    new:新增rust写的RawSpinlock (#127)

commit 998390210549b47e6bdcc3fdab49eff4086ad18b
Author: login <longjin@ringotek.cn>
Date:   Sat Dec 24 23:30:26 2022 +0800

    新增signal文档 (#126)
    
    * 新增signal文档

commit a7f5ca7b67160557abf84a1169dd60093220aeb0
Author: YJwu2023 <119829947+YJwu2023@users.noreply.github.com>
Date:   Sat Dec 24 23:29:36 2022 +0800

    修复下载grub2.06时的提示错误 (#125)
    
    * 修复grub下载显示提示显示错误

commit 82762007da41148e1ed1df465211eb5c8ba2c15e
Author: login <longjin@ringotek.cn>
Date:   Fri Dec 23 18:11:47 2022 +0800

    Update makefile.yml

commit b975025ec8854ca232152f4ee44cc2226891a34c
Author: login <longjin@ringotek.cn>
Date:   Fri Dec 23 11:45:19 2022 +0800

    Update makefile.yml

commit ad2bb74d949bfcb2935e43ac7b261d7ecce23389
Author: login <longjin@ringotek.cn>
Date:   Fri Dec 23 11:21:22 2022 +0800

    Update makefile.yml

commit 6b7776d189ab5f19fbab20d6c5c9ed3ab20c7ab6
Author: login <longjin@ringotek.cn>
Date:   Fri Dec 23 10:59:15 2022 +0800

    修正smp的makefile中没有替换AS的问题

commit beb12a188b6c6bc4196796ac2ae1ecd7d8ed8223
Author: login <longjin@ringotek.cn>
Date:   Fri Dec 23 10:57:39 2022 +0800

    Update makefile.yml

commit d65c527730e5c8a75f6dad0f996c093040699ee3
Author: login <longjin@ringotek.cn>
Date:   Thu Dec 22 22:58:28 2022 +0800

    Update makefile.yml (#121)

commit 5ed4cd460200cb19aae8c3c67dfd77e1e9f0e105
Author: guanjinquan <75822481+guanjinquan@users.noreply.github.com>
Date:   Thu Dec 22 21:09:12 2022 +0800

    Patch gcc toolchain (#111)
    
    * 添加了GCC_cross_compile——tool_chain
    
    * - 解决环境变量路径拼接时,多了`/`的问题
    - apt安装时增加-y,不需用户确认
    
    * 解决添加环境变量的命令有误的问题
    
    * 修正编译错误时,还会执行下一步的问题
    
    * new: 编译完成后清理临时文件
    
    * 更新makefile
    
    * 调整:把grub安装在 $HOME/opt/dragonos-grub下
    
    * new: 新增dockerfile
    
    * 将镜像源换成中科大的(原因是清华的总是ban掉用于构建镜像的服务器的ip)
    
    * 修改为基于debian bullseye构建
    
    * 取消指定版本
    
    * 修复MBR磁盘镜像未设置启动标志的bug
    
    * 取消在docker中安装grub
    
    * 安装grub的过程改到客户机上进行
    
    * bootstrap.sh 添加--no-docker
    
    * 使用新版的docker编译镜像
    
    * 修补, 添加了一些关于gcc的check
    
    Co-authored-by: longjin <longjin@RinGoTek.cn>

commit ba0d93d8b26034abc54bcaf3f0ff04863bbd076e
Author: Gou Ngai <94795048+AlbertSanoe@users.noreply.github.com>
Date:   Mon Dec 19 15:04:37 2022 +0800

    refactor rtc module in rust (#118)
    
    * 用rust重构rtc模块
    
    * refactor the rtc module by rust
    
    * rtc-updated
    
    * rtc-updated-4
    
    * rtc

commit c588d6f77f4b38939701b946228218ea81a7c8dc
Author: login <longjin@ringotek.cn>
Date:   Mon Dec 19 15:03:44 2022 +0800

    Patch add abort func (#120)
    
    * 对于除了sigkill以外的信号,也加入队列
    
    * bugfix:libc中,注册信号处理函数时,总是注册sigkill的问题
    
    * 增加getpid系统调用
    
    * 增加了raise、kill、abort

commit 47f0d12a1f1a1aa11be8e751ecdbf76f0cb596d9
Author: YJwu2023 <119829947+YJwu2023@users.noreply.github.com>
Date:   Mon Dec 19 14:53:51 2022 +0800

    修复docker安装时异常退出的bug (#119)
    
    * 修复docker安装时异常退出的bug
    
    * 修复grub编译脚本的小bug

commit 978043e47d1143ca2d5cf22b20793f032e8eb5a5
Author: login <longjin@ringotek.cn>
Date:   Sun Dec 18 15:09:15 2022 +0800

    修复当系统中不存在dosfstools时,无法正确格式化磁盘镜像的问题 (#117)
    
    * 修复当系统中不存在dosfstools时,无法正确格式化磁盘镜像的问题

commit f9127772dc372a2e607388fdd6818d3f9c4c6d28
Author: YJwu2023 <119829947+YJwu2023@users.noreply.github.com>
Date:   Sat Dec 17 23:43:23 2022 +0800

    修复docker安装时异常退出的bug (#116)

V0.1.2

备注

本文作者:龙进 longjin@RinGoTek.cn

2022年12月17日

贡献者名单

DragonOS V0.1.2版本由以下小伙伴贡献代码:

赞助者名单

感谢以下同学的赞赏,我们将不断努力!

其中,非常感谢Seele.Clover给予DragonOS项目人民币500元的赞助与支持!我们对于每一笔赞助款项,将仔细登记,并确保其能被妥善的使用。

更新内容-内核

  • 删除rust_helloworld文件 (#113)

  • Signal: 允许用户注册信号处理函数,能够进入自定义的handler。(#112)

    • 支持kill命令

    • 允许用户自定义信号处理函数

    • 新增2个系统调用:SYS_SIGACTION , SYS_RT_SIGRETURN

    • libc增加signal(),sigaction()函数。

    • 暂时只支持旧版的sighandler,即:只有1个参数的void handler(int signum)类型的信号处理函数。对于另一种信号处理函数void handler(int signum, siginfo_t *info, void* data),尚不支持传递第三个参数。

  • 在内核代码中加入自定义的stdint.h文件 (#109)

  • 调整编译grub的脚本的部分 (#108)

  • 新增32、64位uefi启动 (#105)(#101)

  • 使用编译安装的grub-2.06,解决客户机上grub版本不对导致的编译无法运行的问题。

  • 增加了timekeeping模块 (#106)

  • bugfix: 修复rtc时钟对BCD码进行转换的时候,忘了处理day字段的问题 (#104)

  • new: 开发过程文档(完成了一半)

  • bootstrap.sh解决下载rust慢的问题

  • 更新“构建系统”文档

  • procfs->status增加显示preempt和虚拟运行时间 (#100)

  • ffz函数:获取u64中的第一个值为0的bit (#100)

  • 解决由于编译器优化导致local_irq_restore无法获取到正确的rflags的值的问题

  • 使用Rust重构串口驱动 (#99)

更新内容-用户环境

  • about app: 显示当前构建的git commit sha1以及构建时间(#114)

  • shell: 修复shell的exec命令对绝对路径的拼接错误问题(#114)

  • shell: exec命令增加”&”后台运行选项 (#100)

  • new: 测试signal用的app

  • 将libc目录进行调整,加入cargo作为rust的包管理器

源码、发布版镜像下载

  您可以通过以下方式获得源代码:

通过Git获取
通过DragonOS软件镜像站获取

  为解决国内访问GitHub慢、不稳定的问题,同时为了方便开发者们下载DragonOS的每个版本的代码,我们特意搭建了镜像站,您可以通过以下地址访问镜像站:

  您可以通过镜像站获取到DragonOS的代码压缩包,以及编译好的可运行的磁盘镜像。

开放源代码声明

备注

为促进DragonOS项目的健康发展,DragonOS以GPLv2开源协议进行发布。所有能获得到DragonOS源代码以及相应的软件制品(包括但不限于二进制副本、文档)的人,都能享有我们通过GPLv2协议授予您的权利,同时您也需要遵守协议中规定的义务。

这是一个相当严格的,保护开源软件健康发展,不被侵占的协议。

对于大部分的善意的人们而言,您不会违反我们的开源协议。

我们鼓励DragonOS的自由传播、推广,但是请确保所有行为没有侵犯他人的合法权益,也没有违反GPLv2协议。

请特别注意,对于违反开源协议的,尤其是商业闭源使用以及任何剽窃、学术不端行为将会受到严肃的追责。(这是最容易违反我们的开源协议的场景)。

并且,请注意,按照GPLv2协议的要求,基于DragonOS修改或二次开发的软件,必须同样采用GPLv2协议开源,并标明其基于DragonOS进行了修改。亦需保证这些修改版本的用户能方便的获取到DragonOS的原始版本。

您必须使得DragonOS的开发者们,能够以同样的方式,从公开渠道获取到您二次开发的版本的源代码,否则您将违反GPLv2协议。

关于协议详细内容,还敬请您请阅读项目根目录下的LICENSE文件。请注意,按照GPLv2协议的要求,只有英文原版才具有法律效力。任何翻译版本都仅供参考。

开源软件使用情况

  DragonOS在开发的过程中,参考了一些开源项目的设计,或者引入了他们的部分代码,亦或是受到了他们的启发。现将他们列在下面。我们对这些开源项目的贡献者们致以最衷心的感谢!

格式:<项目名> - <链接> - <开源协议>

  • Linux - https://git.kernel.org/ - GPLv2

  • skiftOS - https://github.com/skiftOS/skift - MIT

  • FYSOS - https://github.com/fysnet/FYSOS - FYSOS’ License

  • LemonOS - https://github.com/LemonOSProject/LemonOS.git - BSD 2-Clause License

  • LZ4 - https://github.com/lz4/lz4 - BSD 2-Clause license

  • SerenityOS - https://github.com/SerenityOS/serenity.git - BSD 2-Clause license

  • MINE - 《一个64位操作系统的设计与实现》田宇; 人民邮电出版社

  • chcore - 《现代操作系统:设计与实现》陈海波,夏虞斌; 机械工业出版社

  • SimpleKernel - https://github.com/Simple-XX/SimpleKernel - MIT

当前版本的所有提交记录

commit 7a818da88a1c7a1760de7671141b0ce1ca4e3dde
Author: login <longjin@ringotek.cn>
Date:   Sat Dec 17 17:49:12 2022 +0800

    Patch about auto gen version string (#114)
    
    * new: about app中,显示当前构建的git commit sha1以及构建时间
    
    * bugfix: 修复shell的exec命令对绝对路径的拼接错误问题

commit 83a7aaa46bbc411c43d4fc099c6c8884efbe4771
Author: login <longjin@ringotek.cn>
Date:   Sat Dec 17 16:31:50 2022 +0800

    删除rust_helloworld文件 (#113)

commit 6efd4740336205c9bfdd8b164e667cee2f38781e
Author: login <longjin@ringotek.cn>
Date:   Sat Dec 17 16:27:50 2022 +0800

    允许用户自定义信号处理函数 (#112)
    
    * new: 用户注册信号处理函数,能够进入自定义的handler
    
    * 修复忘了传信号的数字给用户的处理函数的bug
    
    * new:sigreturn
    
    * 删除注释

commit 0e0c187484281768391e131495f0655e40d70cf7
Author: login <longjin@ringotek.cn>
Date:   Fri Dec 16 16:20:09 2022 +0800

    在内核代码中加入自定义的stdint.h文件 (#109)

commit d02e6ea4112ad520aa4090ff73cdf592e14c0a82
Author: login <longjin@ringotek.cn>
Date:   Wed Dec 14 20:01:55 2022 +0800

    调整编译grub的脚本的部分 (#108)
    
    1、bugfix: 修复编译grub的脚本的部分错误
    2、将grub下载源替换为tuna
    3、优化写入磁盘镜像的脚本
    4、将bios文件夹改名为legacy

commit 38b341b8aa671f75ac26d05059aa2e9a09e653b7
Author: YJwu2023 <119829947+YJwu2023@users.noreply.github.com>
Date:   Wed Dec 14 16:58:49 2022 +0800

    新增32位uefi启动 (#105)
    
    * 新增32位uefi启动
    
    * 修复小bug
    
    * 增加grub本地编译安装
    
    * 增加本地grub编译安装脚本
    
    * 修正小错误
    
    * 修复空文件夹不上传的bug

commit 01876902fbf6ed43992cc7d153bd8c505cb5224b
Author: Gou Ngai <94795048+AlbertSanoe@users.noreply.github.com>
Date:   Wed Dec 14 15:13:54 2022 +0800

    增加了timekeeping模块 (#106)
    
    * 增加了timekeeping模块
    
    * 格式化文档和细节更改
    
    Co-authored-by: longjin <longjin@RinGoTek.cn>

commit 728aca308917a7d4d0ba10fe8174e9408d77a9a6
Author: login <longjin@ringotek.cn>
Date:   Sun Dec 11 22:59:47 2022 +0800

    bugfix: 修复rtc时钟对BCD码进行转换的时候,忘了处理day字段的问题 (#104)

commit 237e95c6ddce72d72ae7fedfeca412fab82b3622
Author: wwc-15172310230 <78997674+wwc-15172310230@users.noreply.github.com>
Date:   Sun Dec 11 22:22:10 2022 +0800

    调整user下libs的libc目录结构 (#103)
    
    * 调整user下libs的libc目录结构
    
    * 修正.gitignore文件的问题
    
    * 修复无法编译的问题
    
    Co-authored-by: longjin <longjin@RinGoTek.cn>

commit 2291ffdece1dc5a703602f79f74df8a4854d215b
Author: login <longjin@ringotek.cn>
Date:   Sun Dec 11 20:09:58 2022 +0800

    文档更新 (#102)
    
    * new: 开发过程文档(完成了一半)
    
    * bootstrap.sh解决下载rust慢的问题
    
    * 更新“构建系统”文档

commit 7f439c5ddbd2ecffc112149d16983975f523052c
Author: YJwu2023 <119829947+YJwu2023@users.noreply.github.com>
Date:   Fri Dec 9 16:08:54 2022 +0800

    增加uefi启动 (#101)
    
    * 增加uefi启动
    
    * 修改脚本
    
    * uefi修改
    
    * 删除错误的注释
    
    * 修正写入磁盘镜像的脚本
    
    * 修改X86_64为x86_64
    
    Co-authored-by: longjin <longjin@RinGoTek.cn>

commit 1a2eaa402f05f82aaeebe1e03824534a0a425d4d
Author: login <longjin@ringotek.cn>
Date:   Thu Dec 8 22:59:51 2022 +0800

    signal的处理(kill命令)以及一些其他的改进 (#100)
    
    * 将entry.S中冗余的ret_from_syscall代码删除,改为jmp Restore_all
    
    * new: 增加判断pt_regs是否来自用户态的函数
    
    * new: rust的cli和sti封装
    
    * 将原有的判断pt_regs是否来自用户态的代码,统一改为调用user_mode函数
    
    * ffz函数:获取u64中的第一个值为0的bit
    
    * spinlock增加 spinlock irq spin_unlock_irq
    
    * 临时解决显示刷新线程迟迟不运行的问题
    
    * 更改ffi_convert的生命周期标签
    
    * new: 测试signal用的app
    
    * 解决由于编译器优化导致local_irq_restore无法获取到正确的rflags的值的问题
    
    * new: exec命令增加"&"后台运行选项
    
    * procfs->status增加显示preempt和虚拟运行时间
    
    * 更改引用计数的FFIBind2Rust trait中的生命周期标签
    
    * new: signal处理(kill)
    
    * 更正在review中发现的一些细节问题

commit f8b55f6d3fcbf152a1cb6d6fc722bf1607418b28
Author: TingHuang <92705854+TingSHub@users.noreply.github.com>
Date:   Tue Dec 6 22:15:03 2022 +0800

    Patch uart (#99)
    
    * 添加UART驱动相关文件
    
    * 添加驱动核心文件,将rust编写的驱动代码加入Package中
    
    * 添加glib.h文件生成rust代码,添加uart驱动代码
    
    * 添加串口发送及接收相关代码
    
    * 添加字符串发送函数,未实现具体功能
    
    * 为调用uart驱动的代码添加rust接口
    
    * 添加字符串发送函数,修改C语言调用接口
    
    * 添加rust串口驱动
    
    * 添加uart.h头文件,将串口端口类型改为enum
    
    * 添加注释,规范代码

commit 036acc52ce9d0fb9e7d92768ff74939a29c07f32
Author: login <longjin@ringotek.cn>
Date:   Tue Nov 29 21:46:13 2022 +0800

    将entry.S中冗余的ret_from_syscall代码删除,改为jmp Restore_all (#98)
    
    * 将entry.S中冗余的ret_from_syscall代码删除,改为jmp Restore_all

V0.1.1

备注

本文作者:龙进 longjin@RinGoTek.cn

2022年11月27日

贡献者名单

DragonOS V0.1.1版本由以下小伙伴贡献代码:

赞助者名单

感谢以下同学的赞赏,我们将不断努力!

  • David Wen

  • TerryLeeSCUT

  • slientbard

其中,非常感谢David Wen给予DragonOS项目人民币1000元的赞助与支持!我们对于每一笔赞助款项,将仔细登记,并确保其能被妥善的使用。

更新内容-内核

  • 新增rust ffi (#77)

  • port kmalloc and printk to rust

  • rust下的kdebug kinfo kwarn kBUG kerror宏

  • bugfix: 修复进程pcb被回收时,未将其从链表中删除的问题

  • 目录结构优化:移动asm.h和cmpxchg.h

  • signal的发送

  • procfs:查看进程的status

  • 解决第一次编译时磁盘镜像权限错误的问题

  • 将fork相关代码移动到fork.c

更新内容-用户环境

  • shell:增加kill命令,可向目标进程发送信号。但由于仍未完善signal机制,因此目标进程暂时不能响应这个信号。

源码、发布版镜像下载

  您可以通过以下方式获得源代码:

通过Git获取
通过DragonOS软件镜像站获取

  为解决国内访问GitHub慢、不稳定的问题,同时为了方便开发者们下载DragonOS的每个版本的代码,我们特意搭建了镜像站,您可以通过以下地址访问镜像站:

  您可以通过镜像站获取到DragonOS的代码压缩包,以及编译好的可运行的磁盘镜像。

开放源代码声明

备注

为促进DragonOS项目的健康发展,DragonOS以GPLv2开源协议进行发布。所有能获得到DragonOS源代码以及相应的软件制品(包括但不限于二进制副本、文档)的人,都能享有我们通过GPLv2协议授予您的权利,同时您也需要遵守协议中规定的义务。

这是一个相当严格的,保护开源软件健康发展,不被侵占的协议。

对于大部分的善意的人们而言,您不会违反我们的开源协议。

我们鼓励DragonOS的自由传播、推广,但是请确保所有行为没有侵犯他人的合法权益,也没有违反GPLv2协议。

请特别注意,对于违反开源协议的,尤其是商业闭源使用以及任何剽窃、学术不端行为将会受到严肃的追责。(这是最容易违反我们的开源协议的场景)。

并且,请注意,按照GPLv2协议的要求,基于DragonOS修改或二次开发的软件,必须同样采用GPLv2协议开源,并标明其基于DragonOS进行了修改。亦需保证这些修改版本的用户能方便的获取到DragonOS的原始版本。

您必须使得DragonOS的开发者们,能够以同样的方式,从公开渠道获取到您二次开发的版本的源代码,否则您将违反GPLv2协议。

关于协议详细内容,还敬请您请阅读项目根目录下的LICENSE文件。请注意,按照GPLv2协议的要求,只有英文原版才具有法律效力。任何翻译版本都仅供参考。

开源软件使用情况

  DragonOS在开发的过程中,参考了一些开源项目的设计,或者引入了他们的部分代码,亦或是受到了他们的启发。现将他们列在下面。我们对这些开源项目的贡献者们致以最衷心的感谢!

格式:<项目名> - <链接> - <开源协议>

  • Linux - https://git.kernel.org/ - GPLv2

  • skiftOS - https://github.com/skiftOS/skift - MIT

  • FYSOS - https://github.com/fysnet/FYSOS - FYSOS’ License

  • LemonOS - https://github.com/LemonOSProject/LemonOS.git - BSD 2-Clause License

  • LZ4 - https://github.com/lz4/lz4 - BSD 2-Clause license

  • SerenityOS - https://github.com/SerenityOS/serenity.git - BSD 2-Clause license

  • MINE - 《一个64位操作系统的设计与实现》田宇; 人民邮电出版社

  • chcore - 《现代操作系统:设计与实现》陈海波,夏虞斌; 机械工业出版社

  • SimpleKernel - https://github.com/Simple-XX/SimpleKernel - MIT

当前版本的所有提交记录

commit d65ade9c5909076747bd00966a398fe27fbd290d
Author: DaJiYuQia <88259094+DaJiYuQia@users.noreply.github.com>
Date:   Sun Nov 27 14:21:31 2022 +0800

    Patch procf (#95)
    
    * debug color problem
    
    Co-authored-by: longjin <longjin@RinGoTek.cn>

commit 6cb769c423b09e88fea1763210200a716477be0a
Author: login <longjin@ringotek.cn>
Date:   Sun Nov 27 14:17:36 2022 +0800

    将include目录下的rust代码转移到他们应当属于的模块中 (#96)
    
    * 将include目录下的rust代码转移到他们应当属于的模块下。

commit 27a97abd2474b03ad09b562e5ed11e1fdae8eb32
Author: DaJiYuQia <88259094+DaJiYuQia@users.noreply.github.com>
Date:   Sat Nov 26 17:34:00 2022 +0800

    Patch procf (#90)
    
    * 1234
    
    * 123
    
    * 合并master
    
    * procfs
    
    * 1
    
    * procfs展示进程基本信息
    
    * modified code
    
    * 恢复权限
    
    * 恢复权限
    
     #恢复权限
    
    * modify permission
    
    * 删除run.sh
    
    * 解决第一次编译时磁盘镜像权限错误的问题
    
    * 恢复.vscode/c_cpp_properties.json
    
    * 删除process.c中错误的do_fork
    
    * remake procfs
    
    * 修改一些变量名
    
    * 修改类型
    
    * modified
    
    * data_puts缓冲区溢出后return
    
    Co-authored-by: longjin <longjin@RinGoTek.cn>

commit ad23fcddf893d7f92d2bf3efdb66e969416d2852
Author: login <longjin@ringotek.cn>
Date:   Wed Nov 23 21:34:35 2022 +0800

    bugfix: 修复进程退出时未释放signal和sighand && 增加赞赏者名单:David Wen (#93)
    
    * bugfix: 修复进程退出时未释放signal和sighand的bug
    
    * 增加赞赏者名单:David Wen

commit 0274cd6eeec01885232e7418a501857cb76da69e
Author: login <longjin@ringotek.cn>
Date:   Wed Nov 23 20:43:18 2022 +0800

    修正drop signal结构体的box对象的的问题 (#92)
    
    * fix: exit signal and exit sighand

commit c8025a88798dc57ecc5d7f20ad69de695445638f
Author: login <longjin@ringotek.cn>
Date:   Wed Nov 23 20:18:22 2022 +0800

    new:在fork时拷贝signal和sighand (#91)
    
    * refcount初始化
    
    * new: 实现copy_sighand
    del: 删除sighand_struct的wqh, 待将来有需要时,替换成rust版本的
    
    * new: 拷贝signal
    bugfix: 解决拷贝sighand时的uaf问题

commit 66f67c6a95b8aad85cfd2146a86e5e3e6a3568e7
Author: login <longjin@ringotek.cn>
Date:   Wed Nov 23 11:38:20 2022 +0800

    signal的发送(暂时父子进程之间共享信号及相应的结构体) (#89)
    
    * 解决由于spinlock.h中包含preempt_enable()带来的循环include问题
    
    * new: 初步实现signal的数据结构
    
    * new:signal相关数据结构
    
    * fix: 解决bindings.rs报一堆警告的问题
    
    * new: rust下的kdebug kinfo kwarn kBUG kerror宏
    
    * 移动asm.h和cmpxchg.h
    
    * new: signal的发送(暂时只支持父子进程共享信号及处理函数)

commit 3d729e2069e01ee07525ff83167566dac5322a40
Author: login <longjin@ringotek.cn>
Date:   Fri Nov 18 17:59:33 2022 +0800

    bugfix: 修复进程pcb被回收时,未将其从链表中删除的问题 (#87)
    
    * bugfix: 修复进程pcb被回收时,未将其从链表中删除的问题
    new: pcb相关api文档
    
    * 将文档加入目录

commit 0bfe94f46be9bdde1ade81a20e803aa2aafd2964
Author: login <longjin@ringotek.cn>
Date:   Fri Nov 18 16:32:15 2022 +0800

    new: rust下的kdebug kinfo kwarn kBUG kerror宏 (#86)
    
    * new: rust下的kdebug kinfo kwarn kBUG kerror宏

commit c6174797dcf3427f38bfa0f4bd3e039c319f7c5b
Author: login <longjin@ringotek.cn>
Date:   Thu Nov 17 20:29:29 2022 +0800

    fix: 解决bindings.rs报了很多警告的问题 (#85)
    
    * fix: 解决bindings.rs报一堆警告的问题

commit cffd7144fbed84f9775e89d7b99602c6ccc5a510
Author: login <longjin@ringotek.cn>
Date:   Wed Nov 16 15:18:03 2022 +0800

    signal相关数据结构&代码结构优化 (#84)
    
    * 解决由于spinlock.h中包含preempt_enable()带来的循环include问题
    
    * new: 初步实现signal的数据结构

commit fb6c29d01d4cf92368efec08c01e419c2a941f7d
Author: login <longjin@ringotek.cn>
Date:   Sun Nov 13 16:43:58 2022 +0800

    port kmalloc and printk to rust (#83)
    
    * 暂时移除cbindgen
    
    * 将lib文件夹更名为libs文件夹(解决rust的冲突)
    
    * 实现了全局的allocator
    
    * 实现了printk宏
    
    * new: 完善了printk的颜色

commit 82d2e446a401e7eee57a847f48a6d162931170c3
Author: login <longjin@ringotek.cn>
Date:   Sat Nov 12 15:25:54 2022 +0800

    new: 暂时移除cbindgen (#82)

commit 2aaf7808efe44ecfaadd51ae4f8892e667108578
Author: login <longjin@ringotek.cn>
Date:   Fri Nov 11 22:21:44 2022 +0800

    在内核中引入cbindgen,生成rust-C的FFI (#81)
    
    
    * 解决codeql失败问题
    
    * new: 为内核引入cbindgen

commit 2813126e3190c9b3c1a836a647b259a7adbe0cf3
Author: login <longjin@ringotek.cn>
Date:   Fri Nov 11 15:35:37 2022 +0800

    新增rust ffi (#77)
    
    * 引入cargo
    
    * 取消对Cargo.lock的跟踪
    
    * 解决vscode报错问题
    
    * new: rust的代码能够调用c语言的printk_color
    
    * 1、将原本run.sh的工作拆解,变为几个不同的make命令
    2、在docker镜像中编译rust
    
    * 更改workflow
    
    * update workflow
    
    * new: 解决workflow无法通过编译的问题

commit 5e023cf7911333eb05bfe65704dce4b01fa4d0a7
Author: login <longjin@ringotek.cn>
Date:   Fri Nov 11 15:21:45 2022 +0800

    Update makefile.yml

commit e44795008f7e34d2068cf28dcedbcb91f5ccd66b
Author: login <longjin@ringotek.cn>
Date:   Fri Nov 11 15:18:13 2022 +0800

    Update makefile.yml (#80)

commit ec5fb84b61c313824cc2199ab64e3af4b7e5f895
Author: login <longjin@ringotek.cn>
Date:   Fri Nov 11 15:08:09 2022 +0800

    Update makefile.yml

commit 6d9dff5f1ff347ea780a0249e54eef356cdcaaea
Author: login <longjin@ringotek.cn>
Date:   Fri Nov 11 15:07:48 2022 +0800

    Revert "Update makefile.yml (#78)" (#79)
    
    This reverts commit badc7d238f2341e844a90be3e357e5dd77a447fc.

commit badc7d238f2341e844a90be3e357e5dd77a447fc
Author: login <longjin@ringotek.cn>
Date:   Fri Nov 11 15:05:52 2022 +0800

    Update makefile.yml (#78)

V0.1.0

备注

本文作者:龙进 longjin@RinGoTek.cn

2022年11月6日

前言

  DragonOS从2022年1月15日开始开发,到如今已经经历了将近300天。在这么多个日夜里,已经数不清到底花了多少时间在DragonOS的开发之中, 我基本上把所有的空闲时间都给了DragonOS,保守估计总工时已经在1000小时以上。能够发布第一个版本,我感到十分有成就感。

  在2022年7月以来,陆陆续续的,有来自6所高校或企业的小伙伴/大佬加入了DragonOS的开发。我当时非常的欣喜,我想,也许在大家的一同努力下,我们能创造出 一个真正具有实用性的操作系统呢!我们累计召开了14次交流讨论会。我相信,在大家的共同努力下,将来,我们一定能创造出真正独立自主的、开放的、面向服务器领域应用的开源操作系统,并在生产环境中得到应用。

  尽管DragonOS目前只是一个玩具水平的操作系统,只是“比本科生毕业设计难度略高的”操作系统。但是,请不要小看它,它的内在的架构设计,瞄准了Linux5.18及以后的发行版, 虽尚未能达到Linux的水平,但我们正努力追赶。得益于Linux的相关资料,DragonOS在架构设计之时,学习了Linux的很多设计思想,相关组件都尽量考虑了可扩展性与可移植性。

  千里之行,始于足下。DragonOS V0.1.0版本的发布,是一个全新的开始。希望在未来的十年里,我们能与众多伙伴们一同努力,在2032年,将DragonOS建设成为具有实用意义的,能够在服务器领域取得广泛应用的开源操作系统!

  百舸争流,奋楫者先;中流击水,勇进者胜。 我相信,在接下来的时间里,在社区开发者们的不断努力下,我们的目标,终将成为现实!

特别鸣谢

  在DragonOS V0.1.0版本的发布之际,我想对我的老师、前辈以及学校表示衷心的感谢!

  • 佛山市南海区大沥镇中心小学 姚志城老师: 您是带领我接触计算机,学会编程的领路人。十年前,与您交谈时,您说过:“我们国家目前还没有自主的、成熟的操作系统”。这句话,为我的梦想埋下了种子。您培养了我对计算机的热爱,因此我选择了软件工程这个专业。感谢当年您的教导,师恩难忘!

  • 佛山市南海区石门实验学校: 在石实就读的三年里,非常感谢石实的“扬长教育”理念,在老师们的培养下,让我充分发挥了自己的个性和特长,也取得了不错的成绩。在石实的三年里,我学会了C++、Java以及简单的算法,也自己开发了几个安卓app,积累了将近6千行的代码量。

  • 佛山市南海区石门中学:“任重道远,毋忘奋斗”是石中的校训,我想,这句校训,也应当成为我们每个新时代青年人的座右铭。在石门中学的三年,家国情怀教育对我产生了很大的影响。我想,我们作为新时代的青年,更应当肩负起时代的重任,奋勇拼搏,为祖国的发展,为民族的自强,为人类的未来,努力奋斗!

  • 华南理工大学:“博学慎思,明辨笃行”,在华工,我得到了进一步的学习与发展。开拓了自己的视野,学会了跟很多人打交道。并且,在软件学院,我遇到了一群认真负责的老师。非常感谢学院对我的支持,支持我们成立项目组。我相信,在学院的支持下,能让DragonOS取得更好的发展,走的更远!

  • 华南理工大学软件学院 王国华老师:王老师是我《操作系统》课程的老师,在她的指导下,我对操作系统的原理有了更深的理解,并参加了“泛珠三角+大学生计算机作品赛“,在2022年6月的广东省选拔赛中,DragonOS取得了一等奖、最佳创新奖的好成绩。

  • 华南理工大学软件学院 汤峰老师: 汤老师是我们在校内的项目组的指导老师。在她的悉心指导下,我们将不断前行,保持我们前进的方向,持续构建开源社区。我由衷地感谢汤老师的指导!

  • Yaotian Feng: 在Bilibili上认识了这位非常厉害的老哥,解答了我的很多问题,很多次在我毫无头绪的debug了几天之后,几句话点醒了我,让我找到解决问题的路径。并且,他也跟我分享了容易踩坑的地方,让我在将要踩坑的时候能够有个心理预期,不至于那么难受哈哈哈哈。

贡献者名单

DragonOS V0.1.0版本的发布,离不开以下小伙伴们的共同努力:

赞助者名单

感谢以下同学的赞赏,我们将不断努力!

  • TerryLeeSCUT

  • slientbard

内核

遵循的一些标准规范
  • 启动引导:Multiboot2

  • 系统接口:posix 2008

硬件架构
  • 目前支持在x86-64架构的处理器上运行

Bootloader
  • 使用Grub 2.06作为bootloader

内存管理
  • 实现了基于bitmap的页分配器

  • 实现了slab分配器,用来分配小块的、具有对齐要求的内存

  • 抽象出VMA(虚拟内存区域)

  • 实现VMA反向映射机制

  • 实现MMIO地址空间自动映射机制

多核
  • 支持多核引导。也就是说,在DragonOS启动后,将会启动AP处理器。但是,为了简化其他内核模块的实现,目前AP处理器上,暂时没有任务在运行。

  • 粗略实现了IPI(处理器核间通信)框架

进程管理
  • 支持进程的创建、回收

  • 内核线程

  • kthread机制

  • 用户态、内核态进程/线程的fork/vfork(注意,用户态的fork和内核态的有一定的区别,内核态的fork更复杂)

  • exec 让进程去执行一个新的可执行文件

  • 进程的定时睡眠(sleep)(支持spin/rdtsc高精度睡眠、支持上下文切换方式的睡眠)

同步原语
  • spinlock 自旋锁

  • mutex 互斥量

  • atomic 原子变量

  • wait_queue 等待队列

  • semaphore 信号量

调度相关
  • CFS调度器

  • 单核调度(暂时不支持多核负载均衡)

  • completion “完成”机制,让一个进程能等待某个任务的完成。

IPC进程间通信
  • 匿名管道

文件系统
  • VFS虚拟文件系统的基本功能

  • FAT32文件系统(尚不支持删除文件夹)

  • devfs设备文件系统。目前只将键盘文件注册到其中。

  • rootfs根文件系统,在真实的磁盘文件系统被挂载前,为其他的伪文件系统提供支持。

  • 挂载点抽象。目前实现了文件系统的挂载,使用类似于栈的方式管理所有的挂载点。(将来需要优化这部分)

异常及中断处理
  • 处理器异常的捕获

  • 对APIC的支持

  • softirq软中断机制

  • 能够对内核栈进行traceback

内核数据结构
  • 普通的二叉树

  • kfifo先进先出缓冲区

  • 循环链表

  • IDR 映射数据结构

  • IDA ID分配数据组件

屏幕显示
  • VESA VBE显示芯片驱动

  • 实现了屏幕管理器,支持多个显示框架注册到屏幕管理器中。

  • 实现了TextUI文本界面框架,能够渲染文本到屏幕上。并且预留了上下滚动翻页、多显示窗口的支持。

  • printk

内核实用库
  • 字符串操作库

  • ELF可执行文件支持组件

  • 基础数学库

  • CRC函数库

软件移植
  • 移植了LZ4压缩库(V1.9.3),为将来实现页面压缩机制打下基础。

内核测试
  • ktest单元测试框架

  • 支持使用串口(COM1)输出屏幕内容到文件之中。

驱动程序支持
  • IDE硬盘

  • AHCI硬盘(SATA Native)

  • ACPI 高级电源配置模块

  • PCI总线驱动

  • XHCI主机控制器驱动(usb3.0)

  • ps/2键盘

  • ps/2鼠标

  • HPET高精度定时器

  • RTC时钟

  • local APIC定时器

  • UART串口(支持RS-232)

  • VBE显示

  • 虚拟tty设备

系统调用

DragonOS目前一共有22个有效的系统调用。

  • SYS_PUT_STRING 往屏幕上打印字符

  • SYS_OPEN 打开文件

  • SYS_CLOSE 关闭文件

  • SYS_READ 读取文件

  • SYS_WRITE 写入文件

  • SYS_LSEEK 调整文件指针

  • SYS_FORK fork系统调用

  • SYS_VFORK vfork系统调用

  • SYS_BRK 调整堆大小为指定值

  • SYS_SBRK 调整堆大小为相对值

  • SYS_REBOOT 重启 (将来sysfs完善后,将删除这个系统调用,请勿过度依赖这个系统调用)

  • SYS_CHDIR 切换进程的工作目录

  • SYS_GET_DENTS 获取目录中的目录项的元数据

  • SYS_EXECVE 让当前进程执行新的程序文件

  • SYS_WAIT4 等待进程退出

  • SYS_EXIT 退出当前进程

  • SYS_MKDIR 创建文件夹

  • SYS_NANOSLEEP 纳秒级睡眠(最长1秒)在小于500ns时,能够进行高精度睡眠

  • SYS_CLOCK 获取当前cpu时间

  • SYS_PIPE 创建管道

  • SYS_MSTAT 获取系统当前的内存状态信息

  • SYS_UNLINK_AT 删除文件夹或删除文件链接

Rust支持
  • 实现了一个简单的rust语言的hello world,计划在接下来的版本中,逐步转向使用rust进行开发。

用户环境

LibC

  LibC是应用程序与操作系统交互的纽带。DragonOS的LibC实现了一些简单的功能。

  • malloc堆内存分配器

  • 基础数学库

  • 简单的几个与文件相关的函数

  • pipe

  • fork/vfork

  • clock

  • sleep

  • printf

Shell命令行程序
  • 基于简单的字符串匹配的解析(不是通过编译课程学的的那一套东西做的,因此比较简单,粗暴)

  • 支持的命令:ls,cd,mkdir,exec,about,rmdir,rm,cat,touch,reboot

用户态驱动程序
  • 用户态键盘驱动程序

源码、发布版镜像下载

  您可以通过以下方式获得源代码:

通过Git获取
通过DragonOS软件镜像站获取

  为解决国内访问GitHub慢、不稳定的问题,同时为了方便开发者们下载DragonOS的每个版本的代码,我们特意搭建了镜像站,您可以通过以下地址访问镜像站:

  您可以通过镜像站获取到DragonOS的代码压缩包,以及编译好的可运行的磁盘镜像。

开放源代码声明

备注

为促进DragonOS项目的健康发展,DragonOS以GPLv2开源协议进行发布。所有能获得到DragonOS源代码以及相应的软件制品(包括但不限于二进制副本、文档)的人,都能享有我们通过GPLv2协议授予您的权利,同时您也需要遵守协议中规定的义务。

这是一个相当严格的,保护开源软件健康发展,不被侵占的协议。

对于大部分的善意的人们而言,您不会违反我们的开源协议。

我们鼓励DragonOS的自由传播、推广,但是请确保所有行为没有侵犯他人的合法权益,也没有违反GPLv2协议。

请特别注意,对于违反开源协议的,尤其是商业闭源使用以及任何剽窃、学术不端行为将会受到严肃的追责。(这是最容易违反我们的开源协议的场景)。

并且,请注意,按照GPLv2协议的要求,基于DragonOS修改或二次开发的软件,必须同样采用GPLv2协议开源,并标明其基于DragonOS进行了修改。亦需保证这些修改版本的用户能方便的获取到DragonOS的原始版本。

您必须使得DragonOS的开发者们,能够以同样的方式,从公开渠道获取到您二次开发的版本的源代码,否则您将违反GPLv2协议。

关于协议详细内容,还敬请您请阅读项目根目录下的LICENSE文件。请注意,按照GPLv2协议的要求,只有英文原版才具有法律效力。任何翻译版本都仅供参考。

开源软件使用情况

  DragonOS在开发的过程中,参考了一些开源项目的设计,或者引入了他们的部分代码,亦或是受到了他们的启发。现将他们列在下面。我们对这些开源项目的贡献者们致以最衷心的感谢!

格式:<项目名> - <链接> - <开源协议>

  • Linux - https://git.kernel.org/ - GPLv2

  • skiftOS - https://github.com/skiftOS/skift - MIT

  • FYSOS - https://github.com/fysnet/FYSOS - FYSOS’ License

  • LemonOS - https://github.com/LemonOSProject/LemonOS.git - BSD 2-Clause License

  • LZ4 - https://github.com/lz4/lz4 - BSD 2-Clause license

  • SerenityOS - https://github.com/SerenityOS/serenity.git - BSD 2-Clause license

  • MINE - 《一个64位操作系统的设计与实现》田宇; 人民邮电出版社

  • chcore - 《现代操作系统:设计与实现》陈海波,夏虞斌; 机械工业出版社

  • SimpleKernel - https://github.com/Simple-XX/SimpleKernel - MIT

Indices and tables