mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-19 04:56:30 +00:00
doc: Add ai doc translate tool and add English doc. (#1168)
- add tools/doc_translator.py - translated docs into English Signed-off-by: longjin <longjin@DragonOS.org>
This commit is contained in:
48
docs/locales/en/kernel/boot/bootloader.md
Normal file
48
docs/locales/en/kernel/boot/bootloader.md
Normal file
@ -0,0 +1,48 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/boot/bootloader.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:31
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# Bootloader
|
||||
|
||||
## X86_64
|
||||
|
||||
- [x] multiboot2
|
||||
- [x] HVM/PVH
|
||||
|
||||
### HVM/PVH Boot on x86_64
|
||||
|
||||
In the DragonOS note segment, there is a PVH header that allows QEMU to boot the DragonOS kernel using the ``-kernel`` parameter.
|
||||
|
||||
## RISC-V 64
|
||||
|
||||
DragonOS's boot process on RISC-V 64 is as follows:
|
||||
|
||||
opensbi --> uboot --> DragonStub --> kernel
|
||||
|
||||
This boot process decouples the DragonOS kernel from specific hardware boards, enabling the same binary file to boot and run on different hardware boards.
|
||||
|
||||
## Kernel Boot Callbacks
|
||||
|
||||
DragonOS abstracts the kernel bootloader, which is represented by the trait ``BootCallbacks``.
|
||||
Different bootloaders implement the corresponding callback to initialize the kernel's bootParams or other data structures.
|
||||
|
||||
When the kernel boots, it automatically registers callbacks based on the type of bootloader. And at the appropriate time, it calls these callback functions.
|
||||
|
||||
## References
|
||||
|
||||
- [Multiboot2 Specification](http://git.savannah.gnu.org/cgit/grub.git/tree/doc/multiboot.texi?h=multiboot2)
|
||||
|
||||
- [GNU GRUB Manual 2.06](https://www.gnu.org/software/grub/manual/grub/grub.html)
|
||||
|
||||
- [UEFI/Legacy Boot - yujianwu - DragonOS Community](https://bbs.dragonos.org/forum.php?mod=viewthread&tid=46)
|
123
docs/locales/en/kernel/boot/cmdline.md
Normal file
123
docs/locales/en/kernel/boot/cmdline.md
Normal file
@ -0,0 +1,123 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/boot/cmdline.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:48
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# Kernel Boot Command Line Parameters
|
||||
|
||||
:::{note}
|
||||
Author:
|
||||
- Longjin <longjin@DragonOS.org>
|
||||
:::
|
||||
|
||||
## Overview
|
||||
|
||||
  The DragonOS kernel boot command line parameter parsing module aims to provide support for parsing kernel boot command line parameters similar to Linux, enabling more flexible behavior for the kernel. This module allows the kernel to receive and parse command line parameters at boot time, and execute corresponding callback functions or set environment variables based on the type of parameters.
|
||||
|
||||
:::{note}
|
||||
Callback functions are not supported temporarily.
|
||||
:::
|
||||
|
||||
## Design
|
||||
|
||||
### Parameter Types
|
||||
|
||||
Kernel boot command line parameters are divided into three types:
|
||||
|
||||
- Arg type
|
||||
- KV type
|
||||
- EarlyKV type
|
||||
|
||||
#### Arg Type
|
||||
|
||||
Arg type parameters have only a name and no value. They are divided into the following two types:
|
||||
|
||||
- ArgNormal: The default value is `false`. If the parameter is present in the command line, it will be set to `true`.
|
||||
- ArgInv: The default value is `true`. If the parameter is present in the command line, it will be set to `false`.
|
||||
|
||||
#### KV Type
|
||||
|
||||
KV type parameters are represented in the command line as `name=value`, `value` separated by commas. Kernel modules can provide default values for these parameters.
|
||||
|
||||
#### EarlyKV Type
|
||||
|
||||
EarlyKV type parameters are similar to KV type parameters, but they are parsed before memory management initialization.
|
||||
|
||||
### Module Flags
|
||||
|
||||
Module flags are similar to `usbprobe.xxxx`.
|
||||
|
||||
### Parameter Declaration
|
||||
|
||||
Provides macros to declare kernel command line parameters.
|
||||
### procfs Support
|
||||
|
||||
:::{note}
|
||||
TODO: Display the current kernel's boot command line parameters under `/proc/cmdline`.
|
||||
:::
|
||||
|
||||
## Macros for Declaring Kernel Boot Command Line Parameters
|
||||
|
||||
### Arg Type Parameter Declaration
|
||||
```rust
|
||||
kernel_cmdline_param_arg!(varname, name, default_bool, inv);
|
||||
```
|
||||
- `varname`: The variable name of the parameter
|
||||
- `name`: The name of the parameter
|
||||
- `default_bool`: The default value
|
||||
- `inv`: Whether to invert
|
||||
|
||||
### KV Type Parameter Declaration
|
||||
|
||||
```rust
|
||||
kernel_cmdline_param_kv!(varname, name, default_str);
|
||||
```
|
||||
|
||||
- `varname`: The variable name of the parameter
|
||||
- `name`: The name of the parameter
|
||||
- `default_str`: The default value
|
||||
|
||||
### KV Type Parameter Declaration Before Memory Management Initialization
|
||||
|
||||
```rust
|
||||
kernel_cmdline_param_early_kv!(varname, name, default_str);
|
||||
```
|
||||
|
||||
- `varname`: The variable name of the parameter
|
||||
- `name`: The name of the parameter
|
||||
- `default_str`: The default value
|
||||
|
||||
## Example
|
||||
|
||||
The following example demonstrates how to declare and use KV type parameters:
|
||||
```rust
|
||||
kernel_cmdline_param_kv!(ROOTFS_PATH_PARAM, root, "");
|
||||
if let Some(rootfs_dev_path) = ROOTFS_PATH_PARAM.value_str() {
|
||||
.......
|
||||
} else {
|
||||
.......
|
||||
};
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
1. In the kernel code, use the `kernel_cmdline_param_kv!` macro to declare the required KV type parameters.
|
||||
2. During kernel initialization, retrieve the parameter value through the `value_str()` or `value_bool()` method of the parameter.
|
||||
3. Execute corresponding operations based on the parameter value.
|
||||
|
||||
By following these steps, developers can flexibly use kernel boot command line parameters to control kernel behavior.
|
||||
|
||||
## TODO
|
||||
|
||||
- Support displaying the current kernel's boot command line parameters under `/proc/cmdline` (requires procfs refactoring)
|
||||
- Support setting callback functions to set parameter values
|
22
docs/locales/en/kernel/boot/index.rst
Normal file
22
docs/locales/en/kernel/boot/index.rst
Normal file
@ -0,0 +1,22 @@
|
||||
.. note:: AI Translation Notice
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/boot/index.rst
|
||||
|
||||
- Translation time: 2025-05-19 01:41:19
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
|
||||
Please report issues via `Community Channel <https://github.com/DragonOS-Community/DragonOS/issues>`_
|
||||
|
||||
Bootloader
|
||||
====================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Contents
|
||||
|
||||
bootloader
|
||||
cmdline
|
45
docs/locales/en/kernel/configuration/arch.md
Normal file
45
docs/locales/en/kernel/configuration/arch.md
Normal file
@ -0,0 +1,45 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/configuration/arch.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:24
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# Target Architecture Configuration
|
||||
|
||||
## Supported Architectures
|
||||
|
||||
- x86_64
|
||||
- riscv64
|
||||
|
||||
## Architecture-Specific Configuration
|
||||
|
||||
In order to support the debugging functionality of VSCode, we need to modify the following line in the `.vscode/settings.json` file:
|
||||
```
|
||||
"rust-analyzer.cargo.target": "riscv64gc-unknown-none-elf",
|
||||
// "rust-analyzer.cargo.target": "x86_64-unknown-none",
|
||||
```
|
||||
|
||||
If you want to compile for the x86_64 architecture, enable the x86_64 line and comment out the others.
|
||||
If you want to compile for the riscv64 architecture, enable the riscv64 line and comment out the others.
|
||||
|
||||
At the same time, we also need to modify the environment variable configuration in the makefile:
|
||||
|
||||
Please modify the following line in the `env.mk` file:
|
||||
```Makefile
|
||||
ifeq ($(ARCH), )
|
||||
# !!!!在这里设置ARCH,可选x86_64和riscv64
|
||||
# !!!!!!!如果不同时调整这里以及vscode的settings.json,那么自动补全和检查将会失效
|
||||
export ARCH=riscv64
|
||||
endif
|
||||
```
|
||||
|
||||
Please note that changing the architecture requires a recompilation, so please run `make clean` to clean up the compilation results. Then run `make run` to proceed.
|
114
docs/locales/en/kernel/configuration/config.md
Normal file
114
docs/locales/en/kernel/configuration/config.md
Normal file
@ -0,0 +1,114 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/configuration/config.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:17
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# Kernel Compilation Configuration Guide
|
||||
|
||||
## Principle
|
||||
|
||||
  Within the kernel directory, the kernel configuration is set using `kernel.config`. This file is parsed in a manner similar to a TOML file, and then the configuration of each module's `d.config` is parsed to determine the status of features.
|
||||
|
||||
## Example
|
||||
|
||||
**kernel.config**
|
||||
|
||||
```toml
|
||||
[[module.include]]
|
||||
name = "init"
|
||||
path = "src/init/"
|
||||
enable = "y"
|
||||
description = ""
|
||||
|
||||
[[module.include]]
|
||||
name = "mm"
|
||||
path = "src/mm/"
|
||||
enable = "y"
|
||||
description = ""
|
||||
```
|
||||
|
||||
- **[[module.include]]:** Adds the module to the include list
|
||||
- **name:** Module name
|
||||
- **path:** Module path, where the `d.config` file is located
|
||||
- **enable:**
|
||||
- **y:** Enabled, parse the `d.config` file of the module
|
||||
- **n:** Disabled, do not parse
|
||||
- **description:** Description of the module
|
||||
|
||||
**src/mm/d.config**
|
||||
|
||||
```toml
|
||||
[module]
|
||||
name = "mm"
|
||||
description = ""
|
||||
|
||||
[[module.include]]
|
||||
name = "allocator"
|
||||
path = "src/mm/allocator/"
|
||||
enable = "y"
|
||||
description = ""
|
||||
|
||||
[[module.features]]
|
||||
name = "mm_debug"
|
||||
enable = "y"
|
||||
description = ""
|
||||
```
|
||||
|
||||
- **[module]:** Current module
|
||||
- **name:** Name of the current module
|
||||
- **description:** Description of the module
|
||||
- **[[module.include]]:** Modules included in the current module, same as in `kernel.config`
|
||||
- **[[module.features]]:** Features in the current module
|
||||
- **name:** Feature name
|
||||
- **enable:** Whether the feature is enabled
|
||||
- **y:** Enabled
|
||||
- **n:** Disabled
|
||||
- **description:** Description of the feature
|
||||
|
||||
*The following are the `d.config` files of other modules:*
|
||||
|
||||
**src/mm/allocator/d.config**
|
||||
|
||||
```toml
|
||||
[module]
|
||||
name = "allocator"
|
||||
description = ""
|
||||
|
||||
[[module.features]]
|
||||
name = "allocator_debug"
|
||||
enable = "y"
|
||||
description = ""
|
||||
```
|
||||
|
||||
**src/init/d.config**
|
||||
|
||||
```toml
|
||||
[module]
|
||||
name = "init"
|
||||
description = ""
|
||||
|
||||
[[module.features]]
|
||||
name = "init_debug"
|
||||
enable = "y"
|
||||
description = ""
|
||||
```
|
||||
|
||||
All features enabled in the `d.config` files of the activated modules will be ultimately generated into the `D.config` file in the kernel directory. That is, `D.config` is the final kernel compilation configuration, as follows:
|
||||
|
||||
**D.config**
|
||||
|
||||
```
|
||||
init_debug = y
|
||||
allocator_debug = y
|
||||
mm_debug = y
|
||||
```
|
22
docs/locales/en/kernel/configuration/index.rst
Normal file
22
docs/locales/en/kernel/configuration/index.rst
Normal file
@ -0,0 +1,22 @@
|
||||
.. note:: AI Translation Notice
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/configuration/index.rst
|
||||
|
||||
- Translation time: 2025-05-19 01:41:17
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
|
||||
Please report issues via `Community Channel <https://github.com/DragonOS-Community/DragonOS/issues>`_
|
||||
|
||||
Kernel Compilation Configuration
|
||||
====================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Contents
|
||||
|
||||
config
|
||||
arch
|
26
docs/locales/en/kernel/container/index.rst
Normal file
26
docs/locales/en/kernel/container/index.rst
Normal file
@ -0,0 +1,26 @@
|
||||
.. note:: AI Translation Notice
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/container/index.rst
|
||||
|
||||
- Translation time: 2025-05-19 01:41:19
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
|
||||
Please report issues via `Community Channel <https://github.com/DragonOS-Community/DragonOS/issues>`_
|
||||
|
||||
====================================
|
||||
Containerization
|
||||
====================================
|
||||
|
||||
This is the documentation related to containerization in DragonOS.
|
||||
|
||||
It mainly includes namespace, overlayfs, and cgroup
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
namespaces/index
|
||||
../filesystem/unionfs/index
|
28
docs/locales/en/kernel/container/namespaces/index.rst
Normal file
28
docs/locales/en/kernel/container/namespaces/index.rst
Normal file
@ -0,0 +1,28 @@
|
||||
.. note:: AI Translation Notice
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/container/namespaces/index.rst
|
||||
|
||||
- Translation time: 2025-05-19 01:41:19
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
|
||||
Please report issues via `Community Channel <https://github.com/DragonOS-Community/DragonOS/issues>`_
|
||||
|
||||
====================================
|
||||
Namespaces
|
||||
====================================
|
||||
|
||||
DragonOS's namespaces currently support pid_namespace and mnt_namespace, and more features are expected to be added in the future.
|
||||
|
||||
Namespaces are an important component in the process of containerization.
|
||||
|
||||
Since the current OS is single-user, user_namespace is globally static.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
pid_namespace
|
||||
mnt_namespace
|
33
docs/locales/en/kernel/container/namespaces/mnt_namespace.md
Normal file
33
docs/locales/en/kernel/container/namespaces/mnt_namespace.md
Normal file
@ -0,0 +1,33 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/container/namespaces/mnt_namespace.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:19
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# Mount Namespace
|
||||
|
||||
## Underlying Architecture
|
||||
|
||||
pcb -> nsproxy -> mnt_namespace
|
||||
|
||||
Each mounted file system has its own independent mount point, which is represented in the data structure as a red-black tree of mounts. Each namespace has its own independent mounts, so mounting and unmounting file systems will not affect others.
|
||||
|
||||
## System Call Interface
|
||||
|
||||
- clone
|
||||
- CLONE_NEWNS is used to create a new MNT namespace. It provides an independent file system mount point.
|
||||
- unshare
|
||||
- After calling unshare() with the CLONE_NEWPID flag, all subsequent child processes will run in the new namespace.
|
||||
- setns
|
||||
- Adds the process to the specified namespace.
|
||||
- chroot
|
||||
- Changes the current process's root directory to the specified path, providing file system isolation.
|
38
docs/locales/en/kernel/container/namespaces/pid_namespace.md
Normal file
38
docs/locales/en/kernel/container/namespaces/pid_namespace.md
Normal file
@ -0,0 +1,38 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/container/namespaces/pid_namespace.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:31
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# Process Namespace
|
||||
:::{note} Author: Cao Fengyi 1553389239@qq.com
|
||||
|
||||
October 30, 2024
|
||||
:::
|
||||
|
||||
`pid_namespace` is a type of namespace in the kernel that is used to achieve process isolation. It allows processes running in different namespaces to have independent views of process IDs (PIDs).
|
||||
|
||||
## Underlying Architecture
|
||||
|
||||
pcb -> nsproxy -> pid_namespace
|
||||
- `pid_namespace` contains an independent set of process allocators and an orphan process reaper, which independently manages PIDs within the namespace.
|
||||
- Detailed information about processes is stored in the proc file system. The information corresponding to a specific PID is located within the `pid_namespace`, recording information related to the `pid_namespace`.
|
||||
- The limitations imposed by `pid_namespace` are controlled and managed by `ucount`.
|
||||
|
||||
## System Call Interface
|
||||
|
||||
- `clone`
|
||||
- `CLONE_NEWPID` is used to create a new PID namespace. When this flag is used, the child process will run in the new PID namespace, with the process ID starting from 1.
|
||||
- `unshare`
|
||||
- After calling `unshare()` with the `CLONE_NEWPID` flag, all subsequent child processes will run within the new namespace.
|
||||
- `getpid`
|
||||
- Calling `getpid()` within a namespace returns the process ID of the process within the current PID namespace.
|
112
docs/locales/en/kernel/core_api/atomic.md
Normal file
112
docs/locales/en/kernel/core_api/atomic.md
Normal file
@ -0,0 +1,112 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/core_api/atomic.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:25
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# Atomic Variables
|
||||
|
||||
## Introduction
|
||||
|
||||
  DragonOS implements atomic variables of type `atomic_t`. Atomic variables are implemented using architecture-specific atomic operation instructions. The specific implementation is located in `kernel/common/atomic.h`.
|
||||
|
||||
## API
|
||||
|
||||
   Note that all the following APIs are atomic operations.
|
||||
|
||||
### `inline void atomic_add(atomic_t *ato, long val)`
|
||||
|
||||
#### Description
|
||||
|
||||
   Atomically adds a specified value to the atomic variable.
|
||||
|
||||
#### Parameters
|
||||
|
||||
**ato**
|
||||
|
||||
   The atomic variable object.
|
||||
|
||||
**val**
|
||||
|
||||
   The value to be added to the variable.
|
||||
|
||||
### `inline void atomic_sub(atomic_t *ato, long val)`
|
||||
|
||||
#### Description
|
||||
|
||||
   Atomically subtracts a specified value from the atomic variable.
|
||||
|
||||
#### Parameters
|
||||
|
||||
**ato**
|
||||
|
||||
   The atomic variable object.
|
||||
|
||||
**val**
|
||||
|
||||
   The value to be subtracted from the variable.
|
||||
|
||||
### `void atomic_inc(atomic_t *ato)`
|
||||
|
||||
#### Description
|
||||
|
||||
   Atomically increments the atomic variable by 1.
|
||||
|
||||
#### Parameters
|
||||
|
||||
**ato**
|
||||
|
||||
   The atomic variable object.
|
||||
|
||||
### `void atomic_dec(atomic_t *ato)`
|
||||
|
||||
#### Description
|
||||
|
||||
   Atomically decrements the atomic variable by 1.
|
||||
|
||||
#### Parameters
|
||||
|
||||
**ato**
|
||||
|
||||
   The atomic variable object.
|
||||
|
||||
### `inline void atomic_set_mask(atomic_t *ato, long mask)`
|
||||
|
||||
#### Description
|
||||
|
||||
   Performs a bitwise OR operation between the atomic variable and the mask variable.
|
||||
|
||||
#### Parameters
|
||||
|
||||
**ato**
|
||||
|
||||
   The atomic variable object.
|
||||
|
||||
**mask**
|
||||
|
||||
   The variable used for the OR operation with the atomic variable.
|
||||
|
||||
### `inline void atomic_clear_mask(atomic_t *ato, long mask)`
|
||||
|
||||
#### Description
|
||||
|
||||
   Performs a bitwise AND operation between the atomic variable and the mask variable.
|
||||
|
||||
#### Parameters
|
||||
|
||||
**ato**
|
||||
|
||||
   The atomic variable object.
|
||||
|
||||
**mask**
|
||||
|
||||
   The variable used for the AND operation with the atomic variable.
|
77
docs/locales/en/kernel/core_api/casting.md
Normal file
77
docs/locales/en/kernel/core_api/casting.md
Normal file
@ -0,0 +1,77 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/core_api/casting.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:11
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# Type Conversion Library API
|
||||
|
||||
  The kernel provides some functions to help you convert between different types. These include the following types:
|
||||
|
||||
- Numeric type conversion (using the `num-traits` library)
|
||||
- Arc type conversion
|
||||
|
||||
  All functions not specially marked are implemented in `kernel/src/libs/casting.rs`.
|
||||
|
||||
## 1. Numeric Type Conversion
|
||||
|
||||
### 1.1 Conversion Between Integer Types and Enum Types
|
||||
|
||||
  You can use macros provided by the `num-traits` library to convert between enum types and integer types.
|
||||
The SystemError enum type uses this approach, and you can find its usage in `kernel/src/syscall/mod.rs`.
|
||||
|
||||
  It first inherits the `FromPrimitive, ToPrimitive` two traits, and then performs the conversion like this:
|
||||
|
||||
```rust
|
||||
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();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
  These two functions well illustrate how to use these two traits.
|
||||
|
||||
## 2. Arc Type Conversion
|
||||
|
||||
### 2.1 Conversion from Arc<dyn U> to Arc<T>
|
||||
|
||||
  When we need to convert an `Arc<dyn U>` to a specific type pointer of `Arc<T>`, we need to implement the `DowncastArc` trait for `U`. This trait is defined in `kernel/src/libs/casting.rs`. It requires `trait U` to implement the `Any + Sync + Send` trait.
|
||||
|
||||
  To implement the `DowncastArc` trait for `trait U: Any + Send + Sync`, you need to do the following:
|
||||
|
||||
```rust
|
||||
impl DowncastArc for dyn U {
|
||||
fn as_any_arc(self: Arc<Self>) -> Arc<dyn Any> {
|
||||
return self;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
  Using the `DowncastArc` trait, we can convert like this:
|
||||
|
||||
```rust
|
||||
let arc: Arc<dyn U> = ...;
|
||||
let arc_t: Arc<T> = arc.downcast_arc::<T>().unwrap();
|
||||
```
|
||||
|
||||
  If the specific type of `arc` is not `Arc<T>`, then `downcast_arc::<T>()` will return `None`.
|
28
docs/locales/en/kernel/core_api/index.rst
Normal file
28
docs/locales/en/kernel/core_api/index.rst
Normal file
@ -0,0 +1,28 @@
|
||||
.. note:: AI Translation Notice
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/core_api/index.rst
|
||||
|
||||
- Translation time: 2025-05-19 01:41:08
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
|
||||
Please report issues via `Community Channel <https://github.com/DragonOS-Community/DragonOS/issues>`_
|
||||
|
||||
====================================
|
||||
Core API Documentation
|
||||
====================================
|
||||
|
||||
This is the core API documentation for DragonOS.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Kernel Utility Library
|
||||
|
||||
kernel_api
|
||||
atomic
|
||||
casting
|
||||
notifier_chain
|
||||
softirq
|
630
docs/locales/en/kernel/core_api/kernel_api.md
Normal file
630
docs/locales/en/kernel/core_api/kernel_api.md
Normal file
@ -0,0 +1,630 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/core_api/kernel_api.md
|
||||
|
||||
- Translation time: 2025-05-19 01:43:39
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# DragonOS Kernel Core API
|
||||
|
||||
## Circular Linked List Management Functions
|
||||
|
||||
  Circular linked list is one of the important data structures in the kernel. It is included in `kernel/common/list.h`.
|
||||
|
||||
### `void list_init(struct List *list)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Initialize a List structure so that its prev and next pointers point to itself.
|
||||
|
||||
#### Parameters
|
||||
|
||||
**list**
|
||||
|
||||
  The List structure to be initialized.
|
||||
|
||||
### `void list_add(struct List *entry, struct List *node)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Insert the node after the entry.
|
||||
|
||||
#### Parameters
|
||||
|
||||
**entry**
|
||||
|
||||
  An existing node in the circular linked list.
|
||||
|
||||
**node**
|
||||
|
||||
  The node to be inserted.
|
||||
|
||||
### `void list_append(struct List *entry, struct List *node)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Insert the node before the entry.
|
||||
|
||||
#### Parameters
|
||||
|
||||
**entry**
|
||||
|
||||
  An existing node in the circular linked list.
|
||||
|
||||
**node**
|
||||
|
||||
  The node to be inserted.
|
||||
|
||||
### `void list_del(struct List *entry)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Remove the node from the list.
|
||||
|
||||
#### Parameters
|
||||
|
||||
**entry**
|
||||
|
||||
  The node to be removed.
|
||||
|
||||
### `list_del_init(struct List *entry)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Remove the node from the list and re-initialize the entry using list_init().
|
||||
|
||||
#### Parameters
|
||||
|
||||
**entry**
|
||||
|
||||
  The node to be removed.
|
||||
|
||||
### `bool list_empty(struct List *entry)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Check if the list is empty.
|
||||
|
||||
#### Parameters
|
||||
|
||||
**entry**
|
||||
|
||||
  A node in the list.
|
||||
|
||||
### `struct List *list_prev(struct List *entry)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Get the previous node of the entry.
|
||||
|
||||
#### Parameters
|
||||
|
||||
**entry**
|
||||
|
||||
  A node in the list.
|
||||
|
||||
### `struct List *list_next(struct List *entry)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Get the next node of the entry.
|
||||
|
||||
#### Parameters
|
||||
|
||||
**entry**
|
||||
|
||||
  A node in the list.
|
||||
|
||||
### `void list_replace(struct List *old, struct List *new)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Replace the old node in the list with the new node.
|
||||
|
||||
#### Parameters
|
||||
|
||||
**old**
|
||||
|
||||
  The node to be removed.
|
||||
|
||||
**new**
|
||||
|
||||
  The new node to be inserted into the list.
|
||||
|
||||
(_translated_label___list_entry_en)=
|
||||
|
||||
### `list_entry(ptr, type, member)`
|
||||
|
||||
#### Description
|
||||
|
||||
  This macro can get the address of the structure that contains the List pointed to by ptr.
|
||||
|
||||
#### Parameters
|
||||
|
||||
**ptr**
|
||||
|
||||
  Pointer to the List structure.
|
||||
|
||||
**type**
|
||||
|
||||
  The type of the structure that contains the List.
|
||||
|
||||
**member**
|
||||
|
||||
  The name of the List structure member in the structure that contains the List.
|
||||
|
||||
### `list_first_entry(ptr, type, member)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Get the first element in the list. Please note that this macro requires the list to be non-empty, otherwise it will cause an error.
|
||||
|
||||
#### Parameters
|
||||
|
||||
  Same as {ref}`list_entry() <_list_entry>`
|
||||
|
||||
### `list_first_entry_or_null(ptr, type, member)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Get the first element in the list. If the list is empty, return NULL.
|
||||
|
||||
#### Parameters
|
||||
|
||||
  Same as {ref}`list_entry() <_list_entry>`
|
||||
|
||||
### `list_last_entry(ptr, type, member)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Get the last element in the list. Please note that this macro requires the list to be non-empty, otherwise it will cause an error.
|
||||
|
||||
#### Parameters
|
||||
|
||||
  Same as {ref}`list_entry() <_list_entry>`
|
||||
|
||||
### `list_last_entry_or_full(ptr, type, member)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Get the last element in the list. If the list is empty, return NULL.
|
||||
|
||||
#### Parameters
|
||||
|
||||
  Same as {ref}`list_entry() <_list_entry>`
|
||||
|
||||
(_translated_label___list_next_entry_en)=
|
||||
### `list_next_entry(pos, member)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Get the next element in the list.
|
||||
|
||||
#### Parameters
|
||||
|
||||
**pos**
|
||||
|
||||
  Pointer to the outer structure.
|
||||
|
||||
**member**
|
||||
|
||||
  The name of the List structure member in the outer structure.
|
||||
|
||||
### `list_prev_entry(pos, member)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Get the previous element in the list.
|
||||
|
||||
#### Parameters
|
||||
|
||||
  Same as {ref}`list_next_entry() <_list_next_entry>`
|
||||
|
||||
(_translated_label___list_for_each_en)=
|
||||
### `list_for_each(ptr, head)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Traverse the entire list (from front to back).
|
||||
|
||||
#### Parameters
|
||||
|
||||
**ptr**
|
||||
|
||||
  Pointer to the List structure.
|
||||
|
||||
**head**
|
||||
|
||||
  Pointer to the head node of the list (struct List*).
|
||||
|
||||
### `list_for_each_prev(ptr, head)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Traverse the entire list (from back to front).
|
||||
|
||||
#### Parameters
|
||||
|
||||
  Same as {ref}`list_for_each() <_list_for_each>`
|
||||
|
||||
(_translated_label___list_for_each_safe_en)=
|
||||
### `list_for_each_safe(ptr, n, head)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Traverse the entire list from front to back (supports deletion of the current list node).
|
||||
|
||||
  This macro uses a temporary variable to prevent errors that may occur during iteration if the current ptr node is deleted.
|
||||
|
||||
#### Parameters
|
||||
|
||||
**ptr**
|
||||
|
||||
  Pointer to the List structure.
|
||||
|
||||
**n**
|
||||
|
||||
  Pointer to store the temporary value (List type).
|
||||
|
||||
**head**
|
||||
|
||||
  Pointer to the head node of the list (struct List*).
|
||||
|
||||
### `list_for_each_prev_safe(ptr, n, head)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Traverse the entire list from back to front (supports deletion of the current list node).
|
||||
|
||||
  This macro uses a temporary variable to prevent errors that may occur during iteration if the current ptr node is deleted.
|
||||
|
||||
#### Parameters
|
||||
|
||||
  Same as {ref}`list_for_each_safe() <_list_for_each_safe>`
|
||||
|
||||
(_translated_label___list_for_each_entry_en)=
|
||||
### `list_for_each_entry(pos, head, member)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Iterate through the list of a given type from the beginning.
|
||||
|
||||
#### Parameters
|
||||
|
||||
**pos**
|
||||
|
||||
  Pointer to a structure of the specific type.
|
||||
|
||||
**head**
|
||||
|
||||
  Pointer to the head node of the list (struct List*).
|
||||
|
||||
**member**
|
||||
|
||||
  The name of the List member in the structure pointed to by pos.
|
||||
|
||||
### `list_for_each_entry_reverse(pos, head, member)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Iterate through the list of a given type in reverse order.
|
||||
|
||||
#### Parameters
|
||||
|
||||
  Same as {ref}`list_for_each_entry() <_list_for_each_entry>`
|
||||
|
||||
### `list_for_each_entry_safe(pos, n, head, member)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Iterate through the list of a given type from the beginning (supports deletion of the current list node).
|
||||
|
||||
#### Parameters
|
||||
|
||||
**pos**
|
||||
|
||||
  Pointer to a structure of the specific type.
|
||||
|
||||
**n**
|
||||
|
||||
  Pointer to store the temporary value (same type as pos).
|
||||
|
||||
**head**
|
||||
|
||||
  Pointer to the head node of the list (struct List*).
|
||||
|
||||
**member**
|
||||
|
||||
  The name of the List member in the structure pointed to by pos.
|
||||
|
||||
### `list_prepare_entry(pos, head, member)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Prepare a 'pos' structure for {ref}`list_for_each_entry_continue() <_list_for_each_entry_continue>`.
|
||||
|
||||
#### Parameters
|
||||
|
||||
**pos**
|
||||
|
||||
  Pointer to a structure of the specific type, used as the starting point for iteration.
|
||||
|
||||
**head**
|
||||
|
||||
  Pointer to the struct List structure to start iteration from.
|
||||
|
||||
**member**
|
||||
|
||||
  The name of the List member in the structure pointed to by pos.
|
||||
|
||||
(_translated_label___list_for_each_entry_continue_en)=
|
||||
### `list_for_each_entry_continue(pos, head, member)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Continue iterating through the list from the next element of the specified position.
|
||||
|
||||
#### Parameters
|
||||
|
||||
**pos**
|
||||
|
||||
  Pointer to a structure of the specific type. This pointer is used as the iteration pointer.
|
||||
|
||||
**head**
|
||||
|
||||
  Pointer to the struct List structure to start iteration from.
|
||||
|
||||
**member**
|
||||
|
||||
  The name of the List member in the structure pointed to by pos.
|
||||
|
||||
### `list_for_each_entry_continue_reverse(pos, head, member)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Iterate through the list in reverse order, starting from the previous element of the specified position.
|
||||
|
||||
#### Parameters
|
||||
|
||||
  Same as {ref}`list_for_each_entry_continue() <_list_for_each_entry_continue>`
|
||||
|
||||
### `list_for_each_entry_from(pos, head, member)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Continue iterating through the list from the specified position.
|
||||
|
||||
#### Parameters
|
||||
|
||||
  Same as {ref}`list_for_each_entry_continue() <_list_for_each_entry_continue>`
|
||||
|
||||
(_translated_label___list_for_each_entry_safe_continue_en)=
|
||||
### `list_for_each_entry_safe_continue(pos, n, head, member)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Continue iterating through the list from the next element of the specified position (supports deletion of the current list node).
|
||||
|
||||
#### Parameters
|
||||
|
||||
**pos**
|
||||
|
||||
  Pointer to a structure of the specific type. This pointer is used as the iteration pointer.
|
||||
|
||||
**n**
|
||||
|
||||
  Pointer to store the temporary value (same type as pos).
|
||||
|
||||
**head**
|
||||
|
||||
  Pointer to the struct List structure to start iteration from.
|
||||
|
||||
**member**
|
||||
|
||||
  The name of the List member in the structure pointed to by pos.
|
||||
|
||||
### `list_for_each_entry_safe_continue_reverse(pos, n, head, member)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Iterate through the list in reverse order, starting from the previous element of the specified position (supports deletion of the current list node).
|
||||
|
||||
#### Parameters
|
||||
|
||||
  Same as {ref}`list_for_each_entry_safe_continue() <_list_for_each_entry_safe_continue>`
|
||||
|
||||
### `list_for_each_entry_safe_from(pos, n, head, member)`
|
||||
|
||||
#### Description
|
||||
|
||||
  Continue iterating through the list from the specified position (supports deletion of the current list node).
|
||||
|
||||
#### Parameters
|
||||
|
||||
  Same as {ref}`list_for_each_entry_safe_continue() <_list_for_each_entry_safe_continue>`
|
||||
|
||||
---
|
||||
|
||||
## Basic C Function Library
|
||||
|
||||
  Kernel programming differs from application layer programming; you will not be able to use functions from LibC. To address this, the kernel implements some commonly used C language functions, trying to make their behavior as close as possible to standard C library functions. It is important to note that the behavior of these functions may differ from standard C library functions, so it is recommended to carefully read the following documentation, which will be helpful to you.
|
||||
|
||||
### String Operations
|
||||
|
||||
#### `int strlen(const char *s)`
|
||||
|
||||
##### Description
|
||||
|
||||
  Measure and return the length of the string.
|
||||
|
||||
##### Parameters
|
||||
|
||||
**src**
|
||||
|
||||
  Source string.
|
||||
|
||||
#### `long strnlen(const char *src, unsigned long maxlen)`
|
||||
|
||||
##### Description
|
||||
|
||||
  Measure and return the length of the string. If the string length is greater than maxlen, return maxlen.
|
||||
|
||||
##### Parameters
|
||||
|
||||
**src**
|
||||
|
||||
  Source string.
|
||||
|
||||
**maxlen**
|
||||
|
||||
  Maximum length.
|
||||
|
||||
#### `long strnlen_user(const char *src, unsigned long maxlen)`
|
||||
|
||||
##### Description
|
||||
|
||||
  Measure and return the length of the string. If the string length is greater than maxlen, return maxlen.
|
||||
|
||||
  This function performs address space validation, requiring the src string to be from user space. If the source string is from kernel space, it will return 0.
|
||||
|
||||
##### Parameters
|
||||
|
||||
**src**
|
||||
|
||||
  Source string, located in user space.
|
||||
|
||||
**maxlen**
|
||||
|
||||
  Maximum length.
|
||||
|
||||
#### `char *strncpy(char *dst, const char *src, long count)`
|
||||
|
||||
##### Description
|
||||
|
||||
  Copy a string of count bytes and return the dst string.
|
||||
|
||||
##### Parameters
|
||||
|
||||
**src**
|
||||
|
||||
  Source string.
|
||||
|
||||
**dst**
|
||||
|
||||
  Destination string.
|
||||
|
||||
**count**
|
||||
|
||||
  Length of the source string to copy.
|
||||
|
||||
#### `char *strcpy(char *dst, const char *src)`
|
||||
|
||||
##### Description
|
||||
|
||||
  Copy the source string and return the dst string.
|
||||
|
||||
##### Parameters
|
||||
|
||||
**src**
|
||||
|
||||
  Source string.
|
||||
|
||||
**dst**
|
||||
|
||||
  Destination string.
|
||||
|
||||
#### `long strncpy_from_user(char *dst, const char *src, unsigned long size)`
|
||||
|
||||
##### Description
|
||||
|
||||
  Copy a string of count bytes from user space to kernel space, and return the size of the copied string.
|
||||
|
||||
  This function performs address space validation to prevent address space overflow issues.
|
||||
|
||||
##### Parameters
|
||||
|
||||
**src**
|
||||
|
||||
  Source string.
|
||||
|
||||
**dst**
|
||||
|
||||
  Destination string.
|
||||
|
||||
**size**
|
||||
|
||||
  Length of the source string to copy.
|
||||
|
||||
#### `int strcmp(char *FirstPart, char *SecondPart)`
|
||||
|
||||
##### Description
|
||||
|
||||
Compare the sizes of two strings.
|
||||
|
||||
***Return Value***
|
||||
|
||||
| Situation | Return Value |
|
||||
| ----------------------- | --- |
|
||||
| FirstPart == SecondPart | 0 |
|
||||
| FirstPart > SecondPart | 1 |
|
||||
| FirstPart < SecondPart | -1 |
|
||||
|
||||
##### Parameters
|
||||
|
||||
**FirstPart**
|
||||
|
||||
  First string.
|
||||
|
||||
**SecondPart**
|
||||
|
||||
  Second string.
|
||||
|
||||
### Memory Operations
|
||||
|
||||
#### `void *memcpy(void *dst, const void *src, uint64_t size)`
|
||||
|
||||
##### Description
|
||||
|
||||
  Copy memory from src to dst.
|
||||
|
||||
##### Parameters
|
||||
|
||||
**dst**
|
||||
|
||||
  Pointer to the destination address.
|
||||
|
||||
**src**
|
||||
|
||||
  Pointer to the source address.
|
||||
|
||||
**size**
|
||||
|
||||
  Size of data to be copied.
|
||||
|
||||
#### `void *memmove(void *dst, const void *src, uint64_t size)`
|
||||
|
||||
##### Description
|
||||
|
||||
  Similar to `memcpy()`, but this function prevents data from being incorrectly overwritten when the source and destination memory regions overlap.
|
||||
|
||||
##### Parameters
|
||||
|
||||
**dst**
|
||||
|
||||
  Pointer to the destination address.
|
||||
|
||||
**src**
|
||||
|
||||
  Pointer to the source address.
|
||||
|
||||
**size**
|
||||
|
||||
  Size of data to be copied.
|
48
docs/locales/en/kernel/core_api/notifier_chain.md
Normal file
48
docs/locales/en/kernel/core_api/notifier_chain.md
Normal file
@ -0,0 +1,48 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/core_api/notifier_chain.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:30
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# Notifier Chain Notification Chain
|
||||
|
||||
## 1. Overview of Principles
|
||||
|
||||
  The notification chain is an event notification mechanism between subsystems within the kernel or between modules within a subsystem. Essentially, a notification chain is a list of event handling functions. Each notification chain is associated with a specific type of event (e.g., reboot event). When a specific event occurs, the corresponding callback functions in the event's notification chain are called, allowing the subsystem/module to respond to the event and perform the appropriate processing.
|
||||
|
||||
  The notification chain is somewhat similar to the subscription mechanism. It can be understood as: there is a "notifier" that maintains a list, and the "subscriber" registers its callback function into this list ("subscriber" can also unregister its callback function). When an event occurs that needs to be notified, the "notifier" traverses all the callback functions in the list and calls them, allowing all registered "subscribers" to respond and handle the event accordingly.
|
||||
|
||||
## 2. Core Features
|
||||
|
||||
### 2.1 Registering Callback Functions
|
||||
|
||||
  The callback function is encapsulated into a specific structure and registered into the designated notification chain. The related method is `register`, which is used by the "subscriber".
|
||||
|
||||
### 2.2 Unregistering Callback Functions
|
||||
|
||||
  The callback function is removed from the designated notification chain, i.e., it is deleted from the notification chain. The related method is `unregister`, which is used by the "subscriber".
|
||||
|
||||
### 2.3 Event Notification
|
||||
|
||||
  When an event occurs, the notification chain related to that event performs the event notification through this method. `call_chain` This method traverses all elements in the notification chain and calls the registered callback functions in sequence. This method is used by the "notifier".
|
||||
|
||||
## 3. Types of Notification Chains
|
||||
|
||||
  Each type of notification chain has corresponding `register`, `unregister`, and `call_chain` interfaces, with functions as described in the core features above.
|
||||
|
||||
- `AtomicNotifierChain`: Atomic notification chain, cannot sleep, recommended for use in interrupt context.
|
||||
- `BlockingNotifierChain`: Blocking notification chain, can sleep, recommended for use in process context.
|
||||
- `RawNotifierChain`: Raw notification chain, the caller is responsible for thread safety.
|
||||
|
||||
## 4. Other Issues
|
||||
|
||||
  `BlockingNotifierChain` does not currently support the sleeping functionality.
|
178
docs/locales/en/kernel/core_api/softirq.md
Normal file
178
docs/locales/en/kernel/core_api/softirq.md
Normal file
@ -0,0 +1,178 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/core_api/softirq.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:38
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# Soft Interrupt
|
||||
|
||||
  Software interrupt, also known as the bottom half of an interrupt, is used to delay the processing of work that was not completed by the hard interrupt (the top half of the interrupt). Dividing the interrupt into two stages can effectively solve the problems of long interrupt handling time and interrupt loss.
|
||||
|
||||
## 1. Design Philosophy
|
||||
|
||||
  Each CPU has its own pending status. Soft interrupts are "initiated by which CPU, executed by which CPU", and the pending status of each CPU is not shared. The same soft interrupt vector can run concurrently on multiple cores.
|
||||
|
||||
  When we need to register a new soft interrupt, we need to implement the `SoftirqVec` feature for the soft interrupt handler, and then call the `register_softirq` function to register the soft interrupt handler within the soft interrupt mechanism.
|
||||
|
||||
  Please note that due to the reentrancy and concurrency of soft interrupts, the soft interrupt handler must ensure thread safety itself.
|
||||
|
||||
## 2. Soft Interrupt Vector Number
|
||||
|
||||
```rust
|
||||
pub enum SoftirqNumber {
|
||||
/// 时钟软中断信号
|
||||
TIMER = 0,
|
||||
/// 帧缓冲区刷新软中断
|
||||
VideoRefresh = 1,
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Soft Interrupt API
|
||||
|
||||
### 3.1. SoftirqVec Feature
|
||||
|
||||
```rust
|
||||
pub trait SoftirqVec: Send + Sync + Debug {
|
||||
fn run(&self);
|
||||
}
|
||||
```
|
||||
|
||||
  The feature that the soft interrupt handler needs to implement. It needs to implement the `run` function to handle the soft interrupt. When the soft interrupt is executed, the `run` function will be called.
|
||||
|
||||
### 3.2. Softirq API
|
||||
|
||||
#### 3.2.1. Register Soft Interrupt Vector
|
||||
|
||||
```rust
|
||||
pub fn register_softirq(&self,
|
||||
softirq_num: SoftirqNumber,
|
||||
handler: Arc<dyn SoftirqVec>,
|
||||
) -> Result<i32, SystemError>
|
||||
```
|
||||
|
||||
- Parameters:
|
||||
|
||||
- softirq_num: Interrupt vector number
|
||||
|
||||
- handler: The structure corresponding to the interrupt function, which needs to point to a structure variable that implements the `SoftirqVec` feature
|
||||
|
||||
- Return:
|
||||
|
||||
- Ok(i32): 0
|
||||
|
||||
- Err(SystemError): Error code
|
||||
|
||||
#### 3.2.2. Unregister Soft Interrupt Vector
|
||||
|
||||
```rust
|
||||
pub fn unregister_softirq(&self, softirq_num: SoftirqNumber)
|
||||
```
|
||||
|
||||
- Parameters:
|
||||
|
||||
- softirq_num: Interrupt vector number
|
||||
|
||||
#### 3.2.3. Execute Soft Interrupt
|
||||
|
||||
```rust
|
||||
pub fn do_softirq(&self)
|
||||
```
|
||||
|
||||
- Purpose: Execute the soft interrupt function (**only called after hard interrupt execution**)
|
||||
|
||||
#### 3.2.4. Clear the Pending Flag of Soft Interrupt
|
||||
|
||||
```rust
|
||||
pub unsafe fn clear_softirq_pending(&self, softirq_num: SoftirqNumber)
|
||||
```
|
||||
|
||||
- Purpose: Clear the pending flag of the specified soft interrupt on the current CPU. Please note that this function is unsafe because it directly modifies the pending flag without locking.
|
||||
|
||||
- Parameters:
|
||||
|
||||
- softirq_num: Interrupt vector number
|
||||
|
||||
#### 3.2.5. Mark Soft Interrupt as to be Executed
|
||||
|
||||
```rust
|
||||
pub fn raise_softirq(&self, softirq_num: SoftirqNumber)
|
||||
```
|
||||
|
||||
- Purpose: Mark the specified soft interrupt as to be executed on the current CPU
|
||||
|
||||
- Parameters:
|
||||
|
||||
- softirq_num: Interrupt vector number
|
||||
|
||||
### 3.3. Usage Example
|
||||
|
||||
```rust
|
||||
#[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));
|
||||
}
|
||||
```
|
23
docs/locales/en/kernel/cpu_arch/index.rst
Normal file
23
docs/locales/en/kernel/cpu_arch/index.rst
Normal file
@ -0,0 +1,23 @@
|
||||
.. note:: AI Translation Notice
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/cpu_arch/index.rst
|
||||
|
||||
- Translation time: 2025-05-19 01:41:20
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
|
||||
Please report issues via `Community Channel <https://github.com/DragonOS-Community/DragonOS/issues>`_
|
||||
|
||||
Processor Architecture
|
||||
====================================
|
||||
|
||||
This section of the documentation provides descriptions of some programming implementation details related to the processor architecture.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Contents
|
||||
|
||||
x86_64/index
|
21
docs/locales/en/kernel/cpu_arch/x86_64/index.rst
Normal file
21
docs/locales/en/kernel/cpu_arch/x86_64/index.rst
Normal file
@ -0,0 +1,21 @@
|
||||
.. note:: AI Translation Notice
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/cpu_arch/x86_64/index.rst
|
||||
|
||||
- Translation time: 2025-05-19 01:41:18
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
|
||||
Please report issues via `Community Channel <https://github.com/DragonOS-Community/DragonOS/issues>`_
|
||||
|
||||
x86-64 Related Documentation
|
||||
====================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Contents
|
||||
|
||||
usb_legacy_support
|
24
docs/locales/en/kernel/cpu_arch/x86_64/usb_legacy_support.md
Normal file
24
docs/locales/en/kernel/cpu_arch/x86_64/usb_legacy_support.md
Normal file
@ -0,0 +1,24 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/cpu_arch/x86_64/usb_legacy_support.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:19
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# USB Legacy Support
|
||||
|
||||
## Introduction
|
||||
|
||||
  USB Legacy Support refers to the support provided by the BIOS for USB mice and USB keyboards. On computers that support and enable USB Legacy Support, the USB mouse and keyboard are simulated by the BIOS, making them appear to the operating system as if they were PS/2 mice and keyboards.
|
||||
|
||||
## Related
|
||||
|
||||
- When initializing the USB controller, its USB Legacy Support should be disabled.
|
375
docs/locales/en/kernel/debug/debug-kernel-with-gdb.md
Normal file
375
docs/locales/en/kernel/debug/debug-kernel-with-gdb.md
Normal file
@ -0,0 +1,375 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/debug/debug-kernel-with-gdb.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:41
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# How to Use GDB to Debug the Kernel
|
||||
|
||||
## Introduction
|
||||
  GDB is a powerful open-source debugging tool that can help you better diagnose and fix errors in programs.
|
||||
|
||||
  It provides a rich set of features that allow you to check the execution status of a program, track the execution flow of code, view and modify the values of variables, analyze memory states, and more. It can be used in conjunction with a compiler to allow you to access debugging information during the debugging process.
|
||||
|
||||
  This tutorial will guide you on how to use `rust-gdb` to debug the kernel in DragonOS, including how to start debugging and the corresponding debugging commands.
|
||||
|
||||
:::{note}
|
||||
If you are already familiar with the various commands of `rust-gdb`, you only need to read the first part of this tutorial.
|
||||
:::
|
||||
|
||||
---
|
||||
## 1. Getting Started
|
||||
|
||||
### 1.1 Preparation
|
||||
|
||||
  Before you start debugging the kernel, you need to enable debug mode in /Kernel/Cargo.toml by changing `debug = false` to `debug = true` in the Cargo.toml file.
|
||||
|
||||
```shell
|
||||
debug = false
|
||||
```
|
||||
  **Change to**
|
||||
```shell
|
||||
debug = true
|
||||
```
|
||||
|
||||
### 1.2 Running DragonOS
|
||||
|
||||
  After the preparation is complete, you can compile and run DragonOS to proceed with the subsequent debugging work.
|
||||
|
||||
  Open a terminal in the root directory of DragonOS and use `make run` to start compiling and running DragonOS. For more help with compilation commands, see
|
||||
> [Building DragonOS](https://docs.dragonos.org/zh_CN/latest/introduction/build_system.html).
|
||||
|
||||
### 1.3 Running GDB
|
||||
  Once DragonOS has started running, you can start debugging with GDB.
|
||||
|
||||
  **You only need to open a new terminal and run `make gdb` to start the GDB debugger.**
|
||||
|
||||
```shell
|
||||
❯ make gdb
|
||||
rust-gdb -n -x tools/.gdbinit
|
||||
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
|
||||
Copyright (C) 2022 Free Software Foundation, Inc.
|
||||
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
|
||||
This is free software: you are free to change and redistribute it.
|
||||
There is NO WARRANTY, to the extent permitted by law.
|
||||
Type "show copying" and "show warranty" for details.
|
||||
This GDB was configured as "x86_64-linux-gnu".
|
||||
Type "show configuration" for configuration details.
|
||||
For bug reporting instructions, please see:
|
||||
<https://www.gnu.org/software/gdb/bugs/>.
|
||||
Find the GDB manual and other documentation resources online at:
|
||||
<http://www.gnu.org/software/gdb/documentation/>.
|
||||
|
||||
--Type <RET> for more, q to quit, c to continue without paging--
|
||||
```
|
||||
|
||||
:::{note}
|
||||
If you see the above information, input `c` and press Enter.
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 2. Debugging
|
||||
|
||||
### 2.1 Start
|
||||
|
||||
  After completing the above steps, you can start debugging.
|
||||
|
||||
```shell
|
||||
For help, type "help".
|
||||
Type "apropos word" to search for commands related to "word".
|
||||
warning: No executable has been specified and target does not support
|
||||
determining executable automatically. Try using the "file" command.
|
||||
0xffff8000001f8f63 in ?? ()
|
||||
(gdb)
|
||||
```
|
||||
|
||||
:::{note}
|
||||
The output information from GDB, `0xffff8000001f8f63 in ?? ()`, indicates that DragonOS is still in the process of booting.
|
||||
:::
|
||||
|
||||
  **Input `continue` or `c` to continue the program execution.**
|
||||
|
||||
```shell
|
||||
For help, type "help".
|
||||
Type "apropos word" to search for commands related to "word".
|
||||
warning: No executable has been specified and target does not support
|
||||
determining executable automatically. Try using the "file" command.
|
||||
0xffff8000001f8f63 in ?? ()
|
||||
(gdb) continue
|
||||
Continuing.
|
||||
```
|
||||
|
||||
  While DragonOS is running, you can press `Ctrl+C` at any time to send an interrupt signal to view the current state of the kernel.
|
||||
|
||||
```shell
|
||||
(gdb) continue
|
||||
Continuing.
|
||||
^C
|
||||
Thread 1 received signal SIGINT, Interrupt.
|
||||
0xffff800000140c21 in io_in8 (port=113) at common/glib.h:136
|
||||
136 __asm__ __volatile__("inb %%dx, %0 \n\t"
|
||||
(gdb)
|
||||
```
|
||||
|
||||
### 2.2 Setting Breakpoints and Watchpoints
|
||||
|
||||
  Setting breakpoints and watchpoints is the most fundamental step in program debugging.
|
||||
|
||||
- **Setting Breakpoints**
|
||||
|
||||
  You can use the `break` or `b` command to set a breakpoint.
|
||||
|
||||
  Regarding the usage of `break` or `b` commands:
|
||||
|
||||
```shell
|
||||
b <line_number> #在当前活动源文件的相应行号打断点
|
||||
|
||||
b <file>:<line_number> #在对应文件的相应行号打断点
|
||||
|
||||
b <function_name> #为一个命名函数打断点
|
||||
```
|
||||
|
||||
- **Setting Watchpoints**
|
||||
|
||||
  You can use the `watch` command to set a watchpoint.
|
||||
|
||||
```shell
|
||||
watch <variable> # 设置对特定变量的监视点,将在特定变量发生变化的时候触发断点
|
||||
|
||||
watch <expression> # 设置对特定表达式的监视点,比如watch *(int*)0x12345678会在内存地址0x12345678处
|
||||
# 的整数值发生更改时触发断点。
|
||||
```
|
||||
|
||||
- **Managing Breakpoints and Watchpoints**
|
||||
|
||||
  Once we have set breakpoints, how can we view all the breakpoint information?
|
||||
|
||||
  You can use `info b`, `info break`, or `info breakpoints` to view all breakpoint information:
|
||||
|
||||
```shell
|
||||
(gdb) b 309
|
||||
Breakpoint 12 at 0xffff8000001f8f16: file /home/heyicong/.cargo/registry/src/mirrors.tuna.tsinghua.edu.cn-df7c3c540f42cdbd/thingbuf-0.1.4/src/lib.rs, line 315.
|
||||
(gdb) watch slots
|
||||
Watchpoint 13: slots
|
||||
(gdb) info b
|
||||
Num Type Disp Enb Address What
|
||||
12 breakpoint keep y 0xffff8000001f8f16 in thingbuf::Core::pop_ref<u8>
|
||||
at /home/heyicong/.cargo/registry/src/mirrors.tuna.tsinghua.edu.cn-df7c3c540f42cdbd/thingbuf-0.1.4/src/lib.rs:315
|
||||
13 watchpoint keep y slots
|
||||
(gdb)
|
||||
```
|
||||
|
||||
  In the above information, the breakpoint with number 12 is the one we set in line 309 of the active source file. If its `Address` is `<MULTIPLE>`, it indicates that there are identical breakpoints at multiple addresses. This is very common in loops. The breakpoint with number 13 is the watchpoint we set for the `slots` variable.
|
||||
|
||||
  We can perform operations on breakpoints or watchpoints using the following commands:
|
||||
|
||||
```shell
|
||||
delete <breakpoint#> # 或 d <breakpoint#> 删除对应编号的断点,在您不再需要使用这个断点的时候可以通过此命令删除断点
|
||||
delete <watchpoint#> # 或 d <watchpoint##> 删除对应编号的监视点,在您不再需要使用这个监视点的时候可以通过此命令删除监视点
|
||||
|
||||
disable <breakpoint#> # 禁用对应编号的断点,这适合于您只是暂时不需要使用这个断点时使用,当您禁用一个断点,下
|
||||
# 次程序运行到该断点处将不会停下来
|
||||
disable <watchpoint#> # 禁用对应编号的监视点,这适合于您只是暂时不需要使用这个监视点时使用
|
||||
|
||||
enable <breakpoint#> # 启用对应编号的断点
|
||||
enable <watchpoint#> # 启用对应编号的监视点
|
||||
|
||||
#clear命令
|
||||
clear # 清除当前活动源文件的断点以及监视点
|
||||
clear <point_number> # 清除对应编号的所有断点或监视点,这与delete行为是一致的
|
||||
clear <file> # 清除指定文件的所有断点与监视点
|
||||
```
|
||||
|
||||
## 2.3 Viewing Variables and Memory
|
||||
|
||||
- **print and display**
|
||||
|
||||
  You can use `print` or `p` to print variable values.
|
||||
|
||||
  The `print` command is used to print the value of a variable or expression. It allows you to view the data in the program during debugging.
|
||||
|
||||
```shell
|
||||
print <variable> # 打印对应变量名的值,例如:print my_variable 或者 p my_variable
|
||||
|
||||
print <expression> # 打印合法表达式的值,例如:print a+b 或者 p a+b
|
||||
|
||||
# 示例输出
|
||||
(gdb) print order
|
||||
$3 = core::sync::atomic::Ordering::SeqCst
|
||||
```
|
||||
|
||||
```{note}
|
||||
如果您不仅想打印值,还想显示更多详细信息(例如类型信息),可以使用ptype命令。
|
||||
```
|
||||
|
||||
  You can use the `display` command to continuously track variables or expressions. The `display` command is used to set expressions that need to be tracked and displayed every time the program stops. It is similar to the print command, but unlike print, the display command automatically prints the value of the specified expression every time the program stops, without requiring manual input of a command.
|
||||
|
||||
```shell
|
||||
display <variable> # 打印对应变量名的值,例如:display my_variable
|
||||
|
||||
display <expression> # 打印合法表达式的值,例如:display a+b
|
||||
|
||||
# 示例输出
|
||||
(gdb) display order
|
||||
1: order = core::sync::atomic::Ordering::SeqCst #其中1表示display编号,
|
||||
#您可以通过info display命令来查看所有display编号
|
||||
```
|
||||
|
||||
```{note}
|
||||
一旦您设置了display命令,每当程序停止(例如,在断点处停止)时,GDB将自动打印指定表达式的值。
|
||||
|
||||
display命令非常有用,因为它允许您在调试过程中持续监视表达式的值,而无需每次都手动输入print命令。它特别适用于那些您希望持续跟踪的变量或表达式。
|
||||
```
|
||||
|
||||
  **To cancel an already set display command and stop automatically displaying the value of an expression, you can use the undisplay command:**
|
||||
|
||||
```shell
|
||||
undisplay <display编号> # 如果不指定<display编号>,则将取消所有已设置的display命令,
|
||||
# 您可以通过info display命令来查看所有display编号
|
||||
```
|
||||
|
||||
```{note}
|
||||
请注意,print和display命令只会在程序暂停执行时评估变量或表达式的值。如果程序正在运行,您需要通过设置断点或使用其他调试命令来暂停程序,然后才能使用print命令查看数据的值,display命令设置的值将会在程序暂停时自动输出。
|
||||
```
|
||||
|
||||
- **Output Format**
|
||||
|
||||
  You can set the output format to get more information you need, for example: `print /a var`
|
||||
> Refer to [GDB Cheat Sheet](https://darkdust.net/files/GDB%20Cheat%20Sheet.pdf)
|
||||
|
||||
```shell
|
||||
Format
|
||||
a Pointer.
|
||||
c Read as integer, print as character.
|
||||
d Integer, signed decimal.
|
||||
f Floating point number.
|
||||
o Integer, print as octal.
|
||||
s Try to treat as C string.
|
||||
t Integer, print as binary (t = „two“).
|
||||
u Integer, unsigned decimal.
|
||||
x Integer, print as hexadecimal.
|
||||
```
|
||||
|
||||
### 2.4 Viewing the Call Stack
|
||||
|
||||
- **Viewing the Call Stack**
|
||||
|
||||
  When the program is paused at a breakpoint, how should you trace the program's behavior?
|
||||
|
||||
  You can use the `backtarce` command to view the call stack. The `backtrace` command is used to print the backtrace information of the current call stack. It displays all the active function call chains during program execution, including the function names, parameters, and line numbers in the source files.
|
||||
|
||||
```shell
|
||||
# 示例输出
|
||||
(gdb) backtrace
|
||||
#0 function1 (arg1=10, arg2=20) at file1.c:15
|
||||
#1 function2 () at file2.c:25
|
||||
#2 xx () at xx.c:8
|
||||
```
|
||||
|
||||
  Each line of backtrace information starts with #<frame_number>, indicating the frame number. Then comes the function name and parameter list, followed by the source file name and line number.
|
||||
By viewing the backtrace information, you can understand in which functions the program is executing and the position of each function in the call stack. This is very useful for debugging the program and locating problems.
|
||||
|
||||
- **Switching the Stack**
|
||||
|
||||
  You can use the `frame` or `f` command to switch to the corresponding stack frame to get more information and perform operations.
|
||||
|
||||
```shell
|
||||
frame <frame_number>
|
||||
f <frame_number>
|
||||
```
|
||||
|
||||
  In addition to simply executing the backtrace command, you can also use some options to customize the output of the backtrace information. For example:
|
||||
```shell
|
||||
backtrace full #显示完整的符号信息,包括函数参数和局部变量。
|
||||
backtrace <frame_count> #限制回溯信息的帧数,只显示指定数量的帧。
|
||||
backtrace <frame_start>-<frame_end> #指定要显示的帧范围。
|
||||
backtrace thread <thread_id> #显示指定线程的回溯信息。
|
||||
```
|
||||
|
||||
### 2.5 Multi-core
|
||||
|
||||
  When debugging the kernel, you may need to view the running status of each core.
|
||||
|
||||
  You can use the `info threads` command to view the running status of each core.
|
||||
|
||||
```shell
|
||||
(gdb) info threads
|
||||
Id Target Id Frame
|
||||
1 Thread 1.1 (CPU#0 [halted ]) 0xffff800000140a3e in Start_Kernel () at main.c:227
|
||||
* 2 Thread 1.2 (CPU#1 [running]) thingbuf::Core::pop_ref<u8> ()
|
||||
at /home/heyicong/.cargo/registry/src/mirrors.tuna.tsinghua.edu.cn-df7c3c540f42cdbd/thingbuf-0.1.4/src/lib.rs:315
|
||||
(gdb)
|
||||
```
|
||||
|
||||
  You can use the `thread <thread_id>` command to switch to the context of a specific core to view and debug the status of that core. For example:
|
||||
|
||||
```shell
|
||||
(gdb) thread 1
|
||||
[Switching to thread 1 (Thread 1.1)]
|
||||
#0 0xffff800000140a3e in Start_Kernel () at main.c:227
|
||||
227 hlt();
|
||||
```
|
||||
|
||||
### 2.6 More
|
||||
|
||||
  Next, I will introduce more commands that you may find useful during debugging:
|
||||
|
||||
```shell
|
||||
step #或者s,逐行执行程序,并进入到函数调用中。可以在step命令后加执行次数,例:step 3 表示要连续执行3个步骤
|
||||
step <function> #进入指定的函数,并停止在函数内的第一行。
|
||||
|
||||
next #或者n,逐行执行程序,但跳过函数调用,直接执行函数调用后的下一行代码。
|
||||
#它允许你在不进入函数内部的情况下执行代码,从而快速跳过函数调用的细节。
|
||||
#同样,next也可以在命令后加执行次数
|
||||
|
||||
finish #用于从当前函数中一直执行到函数返回为止,并停在调用该函数的地方。
|
||||
#它允许你快速执行完当前函数的剩余部分,并返回到调用函数的上下文中。
|
||||
|
||||
continue #用于继续程序的执行,直到遇到下一个断点或
|
||||
#程序正常结束或者程序暂停。
|
||||
|
||||
quit #退出调试
|
||||
|
||||
list #或者l,显示当前活动源文件源代码的片段,以及当前执行的位置。
|
||||
list <filename>:<function> #显示<filename>文件里面的<funtion>函数的源代码片段
|
||||
list <filename>:<line_number> #显示<filename>文件里面的<line_number>附近的源代码片段
|
||||
list <first>,<last> #显示当前活动源文件的<first>至<last>之间的源代码片段
|
||||
set listsize <count> #设置list命令显示的源代码行数。默认情况下,list命令显示当前行和其周围的几行代码。
|
||||
|
||||
info args #显示当前函数的参数及其值
|
||||
info breakpoints #显示断点以及监视点信息
|
||||
info display #显示当前设置的display列表
|
||||
info locals #显示当前函数/栈帧中的局部变量及其值
|
||||
info sharedlibrary #显示当前已加载的共享库(shared library)信息
|
||||
info signals #显示当前程序所支持的信号信息。它可以列出程序可以接收和处理的不同信号的列表。
|
||||
info threads #显示各个核心/线程信息,它可以列出当前正在运行的核心/线程以及它们的状态。
|
||||
|
||||
show directories #显示当前源代码文件的搜索路径列表。这些搜索路径决定了GDB在查找源代码文件时的搜索范围。
|
||||
show listsize #显示打印源代码时的上下文行数。它确定了在使用list命令(或其简写形式l)时显示的源代码行数。
|
||||
|
||||
whatis variable_name #查看给定变量或表达式的类型信息。它可以帮助你了解变量的数据类型。
|
||||
ptype #显示给定类型或变量的详细类型信息。它可以帮助你了解类型的结构和成员。
|
||||
#相较于whatis命令,ptype命令更加详细。
|
||||
|
||||
set var <variable_name>=<value> #设置变量值
|
||||
|
||||
return <expression> #强制使当前函数返回设定值
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
  Now, you can use rust-gdb to debug the DragonOS kernel code.
|
||||
|
||||
> You can refer to the GDB command documentation for more help: [GDB Cheat Sheet](https://darkdust.net/files/GDB%20Cheat%20Sheet.pdf)
|
25
docs/locales/en/kernel/debug/index.rst
Normal file
25
docs/locales/en/kernel/debug/index.rst
Normal file
@ -0,0 +1,25 @@
|
||||
.. note:: AI Translation Notice
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/debug/index.rst
|
||||
|
||||
- Translation time: 2025-05-19 01:41:09
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
|
||||
Please report issues via `Community Channel <https://github.com/DragonOS-Community/DragonOS/issues>`_
|
||||
|
||||
Kernel Debug Module
|
||||
====================================
|
||||
|
||||
This is the documentation for the kernel debug module of DragonOS.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Contents
|
||||
|
||||
traceback
|
||||
debug-kernel-with-gdb
|
||||
profiling-kernel-with-dadk
|
109
docs/locales/en/kernel/debug/profiling-kernel-with-dadk.md
Normal file
109
docs/locales/en/kernel/debug/profiling-kernel-with-dadk.md
Normal file
@ -0,0 +1,109 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/debug/profiling-kernel-with-dadk.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:49
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# Performance Analysis of the Kernel Using DADK
|
||||
|
||||
## 1. Overview
|
||||
|
||||
This document will teach you how to use DADK to perform performance analysis on the DragonOS kernel, in order to identify and resolve potential performance bottlenecks.
|
||||
|
||||
### 1.1 Preparation
|
||||
|
||||
::: {note}
|
||||
Before you start, please ensure that you have installed DADK and have set up the compilation environment for the DragonOS kernel.
|
||||
:::
|
||||
|
||||
### 1.2 What is a Flame Graph?
|
||||
|
||||
If you haven't heard of flame graphs before, you can read this article: [How to Read Flame Graphs? - Ruanyifeng](https://www.ruanyifeng.com/blog/2017/09/flame-graph.html)
|
||||
|
||||
In simple terms, a flame graph is an SVG image generated from performance sampling results, used to display the call stack of the CPU.
|
||||
|
||||

|
||||
|
||||
The x-axis represents the number of samples. If a function occupies a wider width on the x-axis, it means it was sampled more frequently, indicating longer execution time. Note that the x-axis does not represent time, but rather all call stacks are merged and sorted alphabetically.
|
||||
|
||||
A flame graph is used to identify which function at the top level occupies the largest width. If there is a "plateau" (flat area), it indicates that the function may have performance issues.
|
||||
|
||||
Colors have no special meaning, as flame graphs represent the CPU's busy level, so warm tones are generally chosen.
|
||||
|
||||
## 2. Configuring the DragonOS Kernel
|
||||
|
||||
Since performance analysis requires detailed symbol table data, we need to configure the kernel compilation as follows:
|
||||
|
||||
In `kernel/Cargo.toml`'s `[profile.release]` section, set the following two options:
|
||||
|
||||
```toml
|
||||
[profile.release]
|
||||
debug = true
|
||||
opt-level = 1
|
||||
```
|
||||
|
||||
This will ensure that the compiled kernel includes symbol table data, making it easier for us to perform performance analysis.
|
||||
|
||||
## 3. Using DADK for Performance Analysis
|
||||
|
||||
### 3.1 Booting the Kernel
|
||||
|
||||
First, we need to boot the DragonOS kernel.
|
||||
|
||||
```shell
|
||||
# 使用你喜欢的方式启动内核,例如:
|
||||
make run
|
||||
# 或者
|
||||
make build && make qemu-nographic
|
||||
```
|
||||
|
||||
### 3.2 Running Your Workload
|
||||
|
||||
After booting the kernel, we need to run some workloads in order to perform performance analysis.
|
||||
|
||||
This can be an application or something else. Even you can choose to do nothing and simply observe the call stack of the DragonOS kernel when it is idle.
|
||||
|
||||
### 3.3 Starting DADK for Performance Analysis
|
||||
|
||||
In the DragonOS project directory, run the following command:
|
||||
|
||||
```shell
|
||||
dadk profile sample --format flamegraph --output flame.svg --interval 200ms --duration 20s --cpu-mask 0x1
|
||||
```
|
||||
|
||||
The above command will perform performance analysis on the DragonOS kernel and generate a flame graph.
|
||||
|
||||
Detailed explanation:
|
||||
|
||||
- `--format flamegraph`: Specifies the output format as a flame graph.
|
||||
- `--output flame.svg`: Specifies the output filename as `flame.svg`.
|
||||
- `--interval 200ms`: Specifies the sampling interval as 200ms.
|
||||
- `--duration 20s`: Specifies the sampling duration as 20 seconds.
|
||||
- `--cpu-mask 0x1`: Specifies the CPU to be sampled as CPU 0. (This is a bitmask, meaning if you want to sample CPU 0 and 1, the cpu-mask would be 0x3)
|
||||
|
||||
*For more parameters, please refer to `dadk profile sample --help`.*
|
||||
|
||||
::: {note}
|
||||
Since sampling will pause vCPU, the sampling time should not be too short, otherwise it may affect the normal operation of the system.
|
||||
:::
|
||||
|
||||
After waiting for a while, you will get a `flame.svg` file.
|
||||
|
||||
### 3.4 Analyzing the Flame Graph
|
||||
|
||||
Open the `flame.svg` file in a browser, and you will see a flame graph.
|
||||
|
||||
You can click on a function in the flame graph to view its call stack.
|
||||
|
||||
**You can right-click the image below and open it in a new tab to experience the interactive effects.**
|
||||
|
||||

|
50
docs/locales/en/kernel/debug/traceback.md
Normal file
50
docs/locales/en/kernel/debug/traceback.md
Normal file
@ -0,0 +1,50 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/debug/traceback.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:10
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# Kernel Stack Traceback
|
||||
|
||||
## Introduction
|
||||
|
||||
  The functionality of the kernel stack traceback is located in the `kernel/debug/traceback/` folder. It provides traceback capabilities for the kernel mode, printing the call stack to the screen.
|
||||
|
||||
---
|
||||
|
||||
## API
|
||||
|
||||
### `void traceback(struct pt_regs * regs)`
|
||||
|
||||
#### Purpose
|
||||
|
||||
  This interface is defined in `kernel/debug/traceback/traceback.h`, which will perform a traceback on the given kernel stack and print the trace results to the screen.
|
||||
|
||||
#### Parameters
|
||||
|
||||
##### regs
|
||||
|
||||
  The first stack frame of the kernel stack to start the tracing (i.e., the bottom of the stack)
|
||||
|
||||
---
|
||||
|
||||
## Implementation Principle
|
||||
|
||||
  After the kernel is linked for the first time, the Makefile will run the `kernel/debug/kallsyms` program to extract the symbol table of the kernel file, and then generate `kernel/debug/kallsyms.S`. The rodata segment of this file stores the symbol table of the functions in the text segment. Then, this file will be compiled into `kallsyms.o`. Finally, the Makefile will again call the `ld` command to link the kallsyms.o into the kernel file.
|
||||
|
||||
  When the `traceback` function is called, it will traverse the symbol table to find the corresponding symbols and output them.
|
||||
|
||||
---
|
||||
|
||||
## Future Development Directions
|
||||
|
||||
- Add the capability to write to a log file
|
29
docs/locales/en/kernel/filesystem/index.rst
Normal file
29
docs/locales/en/kernel/filesystem/index.rst
Normal file
@ -0,0 +1,29 @@
|
||||
.. note:: AI Translation Notice
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/filesystem/index.rst
|
||||
|
||||
- Translation time: 2025-05-19 01:41:15
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
|
||||
Please report issues via `Community Channel <https://github.com/DragonOS-Community/DragonOS/issues>`_
|
||||
|
||||
File System
|
||||
====================================
|
||||
|
||||
The file system module of DragonOS consists of VFS (Virtual File System) and specific file systems.
|
||||
|
||||
todo: Due to the refactoring of the file system module, the documentation is temporarily unavailable and will be completed by April 10, 2023.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Contents
|
||||
|
||||
overview
|
||||
vfs/index
|
||||
sysfs
|
||||
kernfs
|
||||
unionfs/index
|
37
docs/locales/en/kernel/filesystem/kernfs.md
Normal file
37
docs/locales/en/kernel/filesystem/kernfs.md
Normal file
@ -0,0 +1,37 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/filesystem/kernfs.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:15
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# KernFS
|
||||
|
||||
:::{note}
|
||||
|
||||
Maintainer:
|
||||
- Long Jin <longjin@dragonos.org>
|
||||
:::
|
||||
|
||||
## 1. Introduction
|
||||
  KernFS is a pseudo file system that acts as a container for other kernel file systems, providing a file interface to users. Its core functionality is that when files in KernFS are read/written or trigger callback points, the predefined callback functions will be invoked, triggering operations on other kernel file systems.
|
||||
|
||||
  This design decouples the basic operations of SysFS and file systems. KernFS serves as the carrier of SysFS, allowing SysFS to focus more on the management of KObjects, resulting in more elegant code.
|
||||
|
||||
  In the future, the kernel subsystem of DragonOS or other kernel file systems can use KernFS as a carrier for file system operations, decoupling the system management logic from specific file system operations.
|
||||
|
||||
## 2. Usage
|
||||
|
||||
  Taking SysFS as an example, a new KernFS instance is created as the file system interface for SysFS, and then it is mounted under the directory `/sys`. Then, sysfs implements the upper-layer logic to manage KObjects. Each upper-layer KObject must include a KernFSInode. By setting the PrivateData of KernFSInode, KernFS can retrieve the corresponding KObject or sysfs attribute based on the Inode. Furthermore, when creating a KernFSInode, different callbacks are passed to the specific Inode, enabling "different Inodes to trigger different callback behaviors when read or written."
|
||||
|
||||
  When a callback occurs, KernFS passes the callback information and private information to the callback function, allowing the callback function to retrieve the corresponding KObject or sysfs attribute based on the input information, thus achieving the high-level functionality provided by sysfs.
|
||||
|
||||
  From the above description, we can see that KernFS achieves the purpose of "decoupling specific file operations from high-level management logic" by storing the callback functions and callback information of the upper-layer file systems.
|
107
docs/locales/en/kernel/filesystem/overview.md
Normal file
107
docs/locales/en/kernel/filesystem/overview.md
Normal file
@ -0,0 +1,107 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/filesystem/overview.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:36
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
:::{note}
|
||||
Author of this article: Long Jin
|
||||
|
||||
Email: <longjin@DragonOS.org>
|
||||
:::
|
||||
|
||||
# Overview
|
||||
|
||||
  In this article, we will introduce the architecture design of the DragonOS file system.
|
||||
|
||||
## Overview
|
||||
|
||||
  As shown in the following diagram, the file system-related mechanisms of DragonOS mainly include the following parts:
|
||||
|
||||
- System call interface
|
||||
- Virtual File System (VFS)
|
||||
- File abstraction (File)
|
||||
- Mount file system (MountFS)
|
||||
- Specific file systems
|
||||
|
||||
```text
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ │
|
||||
Syscall: │ sys_open, sys_read, sys_write, sys_close, │
|
||||
│ │
|
||||
│ sys_lseek, etc.. │
|
||||
│ │
|
||||
└───────────────────────┬─────────────────────────┘
|
||||
│
|
||||
│
|
||||
VFS: ┌──────▼─────┐
|
||||
│ │
|
||||
│ File │
|
||||
│ │
|
||||
└──────┬─────┘
|
||||
│
|
||||
┌────────▼────────┐
|
||||
│ │
|
||||
│ MountFS │
|
||||
│ │
|
||||
└────┬────────────┘
|
||||
│
|
||||
Filesystems: ┌─────────────┼─────────────┬────────────┐
|
||||
│ │ │ │
|
||||
┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼────┐ ┌─────▼─────┐
|
||||
│ │ │ │ │ │ │ │
|
||||
│ FAT │ │ DevFS │ │ ProcFS │ │ RamFS │
|
||||
│ │ │ │ │ │ │ │
|
||||
└───────────┘ └───────────┘ └──────────┘ └───────────┘
|
||||
```
|
||||
|
||||
## System Call Interface
|
||||
|
||||
  The file system-related system call interfaces of DragonOS mainly include the following:
|
||||
|
||||
- `sys_open`: Open file
|
||||
- `sys_read`: Read file
|
||||
- `sys_write`: Write file
|
||||
- `sys_close`: Close file
|
||||
- `sys_lseek`: Set file pointer position
|
||||
- `sys_mkdir`: Create directory
|
||||
- `sys_unlink_at`: Delete file or directory (distinguish between file and directory by parameter `flag`)
|
||||
- `sys_ioctl`: Control device (not implemented)
|
||||
- `sys_fstat`: Get file status (not implemented)
|
||||
- `sys_fsync`: Synchronize file (not implemented)
|
||||
- `sys_ftruncate`: Truncate file (not implemented)
|
||||
- `sys_fchmod`: Modify file permissions (not implemented)
|
||||
- Other system call interfaces (not implemented)
|
||||
|
||||
  For the specific meaning of the interfaces, you can refer to the relevant documentation of Linux.
|
||||
|
||||
## Virtual File System (VFS)
|
||||
|
||||
  VFS is the core of the DragonOS file system, providing a unified set of file system interfaces, allowing DragonOS to support various different file systems. The main functions of VFS include:
|
||||
|
||||
- Provide a unified file system interface
|
||||
- Provide file system mounting and unmounting mechanism (MountFS)
|
||||
- Provide file abstraction (File)
|
||||
- Provide file system abstraction (FileSystem)
|
||||
- Provide IndexNode abstraction
|
||||
- Provide file system caching and synchronization mechanism (not implemented yet)
|
||||
|
||||
  For detailed introduction of VFS, please see [DragonOS Virtual File System](vfs/index.rst).
|
||||
|
||||
## Specific File Systems
|
||||
|
||||
  The file systems currently supported by DragonOS include:
|
||||
|
||||
- FAT file system (FAT12, FAT16, FAT32)
|
||||
- DevFS
|
||||
- ProcFS
|
||||
- RamFS
|
124
docs/locales/en/kernel/filesystem/sysfs.md
Normal file
124
docs/locales/en/kernel/filesystem/sysfs.md
Normal file
@ -0,0 +1,124 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/filesystem/sysfs.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:50
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# SysFS
|
||||
|
||||
:::{note}
|
||||
Author: Huang Ting
|
||||
|
||||
Email: <huangting@DragonOS.org>
|
||||
:::
|
||||
|
||||
## 1. SysFS and Device Driver Model
|
||||
|
||||
### 1.1. The relationship between devices, drivers, buses, etc., is complex
|
||||
|
||||
  If you want the kernel to run smoothly, you must code these functionalities for each module. This will make the kernel very bloated and redundant. The idea of the device model is to abstract these codes into a shared framework for all modules. This not only makes the code concise, but also allows device driver developers to avoid the headache of this essential but burdensome task, and focus their limited energy on implementing the differences of the devices.
|
||||
|
||||
  The device model provides a template, an optimal approach and process that has been proven. This reduces unnecessary errors during the development process and clears the way for future maintenance.
|
||||
|
||||
### 1.2. sysfs is a memory-based file system, its role is to provide kernel information in the form of files for user programs to use.
|
||||
|
||||
  sysfs can be seen as a file system similar to proc, devfs, and devpty. This file system is virtual and can make it easier to manage system devices. It can generate a hierarchical view of all system hardware, similar to the proc file system that provides process and status information. sysfs organizes the devices and buses connected to the system into a hierarchical file structure, which can be accessed from user space, exporting kernel data structures and their attributes to user space.
|
||||
|
||||
## 2. Device Driver Model in DragonOS
|
||||
|
||||
### 2.1. The basic elements are composed of devices and drivers
|
||||
|
||||
#### 2.1.1. Device
|
||||
|
||||
```rust
|
||||
/// @brief: 所有设备都应该实现该trait
|
||||
pub trait Device: Any + Send + Sync + Debug {}
|
||||
```
|
||||
|
||||
  DragonOS uses a global device manager to manage all devices in the system.
|
||||
|
||||
```rust
|
||||
/// @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. Driver
|
||||
|
||||
```rust
|
||||
/// @brief: 所有驱动驱动都应该实现该trait
|
||||
pub trait Driver: Any + Send + Sync + Debug {}
|
||||
```
|
||||
|
||||
  Similarly, drivers also use a global driver manager for management.
|
||||
|
||||
```rust
|
||||
/// @brief: 驱动管理器
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DriverManager {
|
||||
drivers: BTreeMap<IdTable, Arc<dyn Driver>>, // 所有驱动
|
||||
sys_info: Option<Arc<dyn IndexNode>>, // sys information
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2. Bus
|
||||
|
||||
  Bus is a type of device, and it also needs a driver to initialize. Due to the special nature of buses, a global bus manager is used for management.
|
||||
|
||||
```rust
|
||||
/// @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
|
||||
}
|
||||
```
|
||||
|
||||
  As can be seen, each manager contains a sys_info. The device model establishes a connection with sysfs through this member, and sys_info points to the unique inode in sysfs. For a device, it corresponds to the devices folder under sysfs, and the same applies to other components.
|
||||
|
||||
## 3. How to Develop Drivers
|
||||
|
||||
  Taking the platform bus as an example, the platform bus is a virtual bus that can match devices and drivers mounted on it and drive the devices. This bus is a type of device and also a type of bus. When programming, you need to create an instance of this device and implement the Device trait and Bus trait for the device instance to indicate that this structure is a bus device. At the same time, the matching rules on the bus should be implemented. Different buses have different matching rules. This bus uses a matching table for matching, and both devices and drivers should have a matching table, indicating the devices supported by the driver and the drivers supported by the device.
|
||||
|
||||
```rust
|
||||
pub struct CompatibleTable(BTreeSet<&'static str>);
|
||||
```
|
||||
|
||||
  For a bus device, you need to call bus_register to register the bus into the system and visualize it in sysfs.
|
||||
|
||||
```rust
|
||||
/// @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),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
  From the source code of bus_register, we can see that this function not only generates a bus folder under sysfs/bus, but also internally calls device_register. This function adds the bus to the device manager and generates a device folder under sys/devices.
|
23
docs/locales/en/kernel/filesystem/unionfs/index.rst
Normal file
23
docs/locales/en/kernel/filesystem/unionfs/index.rst
Normal file
@ -0,0 +1,23 @@
|
||||
.. note:: AI Translation Notice
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/filesystem/unionfs/index.rst
|
||||
|
||||
- Translation time: 2025-05-19 01:41:16
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
|
||||
Please report issues via `Community Channel <https://github.com/DragonOS-Community/DragonOS/issues>`_
|
||||
|
||||
====================================
|
||||
Union Filesystem
|
||||
====================================
|
||||
Union Filesystem:
|
||||
OverlayFS merges multiple filesystems (referred to as "layers") into a single logical filesystem, allowing users to see a unified directory structure.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
overlayfs
|
46
docs/locales/en/kernel/filesystem/unionfs/overlayfs.md
Normal file
46
docs/locales/en/kernel/filesystem/unionfs/overlayfs.md
Normal file
@ -0,0 +1,46 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/filesystem/unionfs/overlayfs.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:18
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# overlayfs
|
||||
|
||||
OverlayFS is currently the most widely used union file system, with a simple principle and convenient usage, mainly used in containers.
|
||||
|
||||
In Docker, OverlayFS is one of the default storage drivers. Docker creates an independent upper directory for each container, while all containers share the same lower image file. This design makes resource sharing between containers more efficient and reduces storage requirements.
|
||||
|
||||
## Architecture Design
|
||||
|
||||
OverlayFS has two layers and a virtual merged layer.
|
||||
|
||||
- **Lower Layer (Lower Layer)**: Usually a read-only file system. It can contain multiple layers.
|
||||
- **Upper Layer (Upper Layer)**: A writable layer. All write operations are performed on this layer.
|
||||
- **Merged Layer (Merged Layer)**: The logical view of the upper and lower layers is merged, and the final file system presented to the user is shown.
|
||||
|
||||
## Working Principle
|
||||
|
||||
- **Read Operation**:
|
||||
- OverlayFS will first read the file from the Upper Layer. If the file does not exist in the upper layer, it will read the content from the Lower Layer.
|
||||
- **Write Operation**:
|
||||
- If a file is located in the Lower Layer and an attempt is made to write to it, the system will copy it up to the Upper Layer and then write to it in the upper layer. If the file already exists in the Upper Layer, it will be directly written to that layer.
|
||||
- **Delete Operation**:
|
||||
- When deleting a file, OverlayFS creates a whiteout entry in the upper layer, which hides the file in the lower layer.
|
||||
|
||||
## Copy-up
|
||||
|
||||
- **Copy-on-Write (Write-time Copy)**
|
||||
When a file in the lower layer is modified, it is copied to the upper layer (called copy-up). All subsequent modifications will be performed on the copied file in the upper layer.
|
||||
|
||||
## Implementation Logic
|
||||
|
||||
The implementation is achieved by building `ovlInode` to implement the `indexnode` trait to represent the inode of the upper or lower layer. Specific operations related to files and directories are handled accordingly.
|
16
docs/locales/en/kernel/filesystem/vfs/api.md
Normal file
16
docs/locales/en/kernel/filesystem/vfs/api.md
Normal file
@ -0,0 +1,16 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/filesystem/vfs/api.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:12
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# VFS API Documentation
|
72
docs/locales/en/kernel/filesystem/vfs/design.md
Normal file
72
docs/locales/en/kernel/filesystem/vfs/design.md
Normal file
@ -0,0 +1,72 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/filesystem/vfs/design.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:33
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
:::{note}
|
||||
Author of this article: Long Jin
|
||||
|
||||
Email: <longjin@DragonOS.org>
|
||||
:::
|
||||
|
||||
# Design
|
||||
|
||||
  The architecture design of VFS is shown in the following diagram:
|
||||
|
||||
```text
|
||||
┌─────────┐
|
||||
│ │
|
||||
│ 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
|
||||
  The File structure is the most basic abstraction in VFS, representing an opened file. Whenever a process opens a file, a File structure is created to maintain the state information of that file.
|
||||
|
||||
## 2. Traits
|
||||
|
||||
  For each specific file system, the following traits must be implemented:
|
||||
|
||||
- FileSystem: Indicates that a struct is a file system
|
||||
- IndexNode: Indicates that a struct is an index node
|
||||
|
||||
  Generally, there is a one-to-one relationship between FileSystem and IndexNode, meaning that one file system corresponds to one type of IndexNode. However, for some special file systems, such as DevFS, different IndexNodes may exist based on different device types. Therefore, there is a one-to-many relationship between FileSystem and IndexNode.
|
||||
|
||||
## 3. MountFS
|
||||
|
||||
  Although MountFS implements the FileSystem and IndexNode traits, it is not itself a "file system," but rather a mechanism used to mount different file systems onto the same file system tree.
|
||||
All file systems that need to be mounted onto the file system tree must go through MountFS to complete the mounting process. In other words, each file system structure in the mount tree is wrapped with a MountFS structure.
|
||||
|
||||
  For most operations, MountFS simply forwards the operation to the specific file system without any processing. At the same time, to support cross-file system operations, such as searching in a directory tree, each lookup or find operation will go through the corresponding method of MountFSInode to determine whether the current inode is a mount point and handle it specially. If the operation is found to cross the boundary of a specific file system, MountFS will forward the operation to the next file system and perform an inode replacement. This functionality is implemented by wrapping a regular Inode structure with a MountFSInode structure.
|
33
docs/locales/en/kernel/filesystem/vfs/index.rst
Normal file
33
docs/locales/en/kernel/filesystem/vfs/index.rst
Normal file
@ -0,0 +1,33 @@
|
||||
.. note:: AI Translation Notice
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/filesystem/vfs/index.rst
|
||||
|
||||
- Translation time: 2025-05-19 01:41:14
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
|
||||
Please report issues via `Community Channel <https://github.com/DragonOS-Community/DragonOS/issues>`_
|
||||
|
||||
VFS Virtual File System
|
||||
====================================
|
||||
|
||||
In DragonOS, VFS acts as an adapter, hiding the differences between specific file systems and providing a unified file operation interface abstraction to the outside.
|
||||
|
||||
VFS is the core of the file system in DragonOS. It provides a set of unified file system interfaces, enabling DragonOS to support various different file systems. The main functions of VFS include:
|
||||
|
||||
- Providing a unified file system interface
|
||||
- Providing mount and unmount mechanisms for file systems (MountFS)
|
||||
- Providing file abstraction (File)
|
||||
- Providing file system abstraction (FileSystem)
|
||||
- Providing IndexNode abstraction
|
||||
- Providing caching and synchronization mechanisms for file systems (not yet implemented)
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Directory
|
||||
|
||||
design
|
||||
api
|
23
docs/locales/en/kernel/ipc/index.rst
Normal file
23
docs/locales/en/kernel/ipc/index.rst
Normal file
@ -0,0 +1,23 @@
|
||||
.. note:: AI Translation Notice
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/ipc/index.rst
|
||||
|
||||
- Translation time: 2025-05-19 01:41:19
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
|
||||
Please report issues via `Community Channel <https://github.com/DragonOS-Community/DragonOS/issues>`_
|
||||
|
||||
====================================
|
||||
Inter-Process Communication
|
||||
====================================
|
||||
|
||||
This is the documentation for DragonOS Inter-Process Communication (IPC).
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
signal
|
97
docs/locales/en/kernel/ipc/signal.md
Normal file
97
docs/locales/en/kernel/ipc/signal.md
Normal file
@ -0,0 +1,97 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/ipc/signal.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:43
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# Signal Signal
|
||||
|
||||
:::{note}
|
||||
This document Maintainer: Longjin
|
||||
|
||||
Email: <longjin@RinGoTek.cn>
|
||||
:::
|
||||
|
||||
  Signals are a mechanism for inter-process communication. When a signal is sent to a specific process, it can trigger a specific behavior (such as exiting the program or running a signal handler). Signals are asynchronous notifications sent to a process or a specific thread within the same process, used to notify it that an event has occurred. Common uses of signals include interrupting, suspending, terminating, or ending a process. When sending a signal, the operating system interrupts the normal execution flow of the target process to deliver the signal. Execution can be interrupted at any non-atomic instruction. If the process has previously registered a signal handler, the handler routine is executed. Otherwise, the default signal handler is executed.
|
||||
|
||||
  Signals are similar to interrupts, with the difference being that interrupts are mediated by the CPU and handled by the kernel, while signals are generated within the kernel (and can also be generated through system calls) and are handled by the default handlers of individual processes or the kernel.
|
||||
|
||||
## 1. Overview of Signal Handling
|
||||
|
||||
### 1.1 Signal Sending
|
||||
|
||||
  When process A wants to send a signal to process B, it uses the `kill(pid, signal)` interface to send the signal. Then, it enters the `sys_kill()` function in the kernel for processing. The kernel will then add the signal to the `sigpending` in the target process's PCB.
|
||||
|
||||
Illustration:
|
||||
|
||||
```text
|
||||
┌────────────┐
|
||||
│ Process A: │
|
||||
│ │
|
||||
│ sys_kill │
|
||||
└──────┬─────┘
|
||||
│
|
||||
│
|
||||
┌──────▼──────┐ ┌────────────────────┐
|
||||
│ Send Signal ├────►Add to sigpending of│
|
||||
└─────────────┘ │ process B. │
|
||||
└────────────────────┘
|
||||
|
||||
```
|
||||
|
||||
### 1.2 Signal Handling
|
||||
|
||||
  When a process exits the kernel mode, it jumps into the `do_signal()` function to check if there are any signals that need to be handled. If there are, the signal handling process is initiated.
|
||||
|
||||
Signal handling process illustration:
|
||||
|
||||
```text
|
||||
|
||||
┌───────────────────────┐
|
||||
│ 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. │
|
||||
└──────────────┘ └─────────────────┘
|
||||
|
||||
```
|
||||
|
||||
- If the kernel checks and finds that the process has not specified a signal handler and the signal handling action is not "ignore", the process will be terminated.
|
||||
- If the kernel finds that the signal is not ignored, it will:
|
||||
- Save the current kernel stack
|
||||
- Set up the user-mode stack frame for signal handling
|
||||
- Return to user mode and execute the signal handler
|
||||
- After the signal handler finishes, it will enter the __sa_restorer__ provided by libc, initiating the `sys_sigreturn()` system call to return to kernel mode
|
||||
- The kernel restores the kernel stack before handling the signal.
|
||||
- The signal handling process ends, and the kernel continues with the process of returning to user mode.
|
||||
- If the kernel finds that the current signal is being ignored, it checks the next signal.
|
||||
- If no signals need to be handled, it returns to user mode.
|
||||
|
||||
## 2. Other Issues
|
||||
|
||||
  None at present.
|
26
docs/locales/en/kernel/ktest/index.rst
Normal file
26
docs/locales/en/kernel/ktest/index.rst
Normal file
@ -0,0 +1,26 @@
|
||||
.. note:: AI Translation Notice
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/ktest/index.rst
|
||||
|
||||
- Translation time: 2025-05-19 01:41:16
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
|
||||
Please report issues via `Community Channel <https://github.com/DragonOS-Community/DragonOS/issues>`_
|
||||
|
||||
====================================
|
||||
Kernel Testing
|
||||
====================================
|
||||
|
||||
This chapter will introduce how to test the kernel, including manual testing and automated testing.
|
||||
|
||||
We need to perform thorough testing on the kernel as much as possible, so that we can better ensure the stability of the kernel and reduce the difficulty of debugging other modules.
|
||||
|
||||
Setting up comprehensive test cases can help us detect problems as much as possible, preventing us from being "stabbed" by hidden bugs in existing modules when writing new modules.
|
||||
|
||||
Since it is difficult to debug using tools like GDB, manual testing in the kernel is more challenging compared to testing applications.
|
||||
|
||||
For some modules, we can write code for unit testing and output error messages. Unfortunately, not all modules can be unit tested. For example, common modules such as memory management and process management cannot be unit tested.
|
26
docs/locales/en/kernel/libs/id-allocation.md
Normal file
26
docs/locales/en/kernel/libs/id-allocation.md
Normal file
@ -0,0 +1,26 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/libs/id-allocation.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:12
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# ID Allocation
|
||||
|
||||
:::{note}
|
||||
Author: Longjin <longjin@DragonOS.org>
|
||||
|
||||
September 25, 2024
|
||||
:::
|
||||
|
||||
The kernel provides an ID allocator named `IdAllocator`, located in `kernel/crates/ida`.
|
||||
|
||||
It is capable of allocating and releasing IDs. By default, it increments to allocate IDs. If the ID exceeds the set maximum value, it will search for an available ID starting from the minimum value. If there are no available IDs, the allocation will fail.
|
26
docs/locales/en/kernel/libs/index.rst
Normal file
26
docs/locales/en/kernel/libs/index.rst
Normal file
@ -0,0 +1,26 @@
|
||||
.. note:: AI Translation Notice
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/libs/index.rst
|
||||
|
||||
- Translation time: 2025-05-19 01:41:11
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
|
||||
Please report issues via `Community Channel <https://github.com/DragonOS-Community/DragonOS/issues>`_
|
||||
|
||||
====================================
|
||||
Other Kernel Libraries
|
||||
====================================
|
||||
|
||||
This section contains documentation for some libraries in the kernel that do not belong to any subsystem.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
lib_ui/scm
|
||||
lib_ui/textui
|
||||
unified-init
|
||||
id-allocation
|
97
docs/locales/en/kernel/libs/lib_ui/scm.md
Normal file
97
docs/locales/en/kernel/libs/lib_ui/scm.md
Normal file
@ -0,0 +1,97 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/libs/lib_ui/scm.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:31
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# Screen Manager (SCM)
|
||||
|
||||
:::{note}
|
||||
Author: Zhou Hanjie <2625553453@qq.com>
|
||||
:::
|
||||
  The Screen Manager is used to control all UI frameworks. All frameworks must be registered with the Screen Manager before they can be used. Then, SCM controls which UI framework is currently in use.
|
||||
|
||||
## traits
|
||||
|
||||
### ScmUiFramework
|
||||
  Each UI framework that is to be registered with SCM must implement the methods defined in this trait, as follows:
|
||||
```rust
|
||||
pub trait ScmUiFramework: Sync + Send + Debug {
|
||||
// 安装ui框架的回调函数
|
||||
fn install(&self) -> Result<i32, SystemError> {
|
||||
return Err(SystemError::ENOSYS);
|
||||
}
|
||||
// 卸载ui框架的回调函数
|
||||
fn uninstall(&self) -> Result<i32, SystemError> {
|
||||
return Err(SystemError::ENOSYS);
|
||||
}
|
||||
// 启用ui框架的回调函数
|
||||
fn enable(&self) -> Result<i32, SystemError> {
|
||||
return Err(SystemError::ENOSYS);
|
||||
}
|
||||
// 禁用ui框架的回调函数
|
||||
fn disable(&self) -> Result<i32, SystemError> {
|
||||
return Err(SystemError::ENOSYS);
|
||||
}
|
||||
// 改变ui框架的帧缓冲区的回调函数
|
||||
fn change(&self, _buf: ScmBufferInfo) -> Result<i32, SystemError> {
|
||||
return Err(SystemError::ENOSYS);
|
||||
}
|
||||
/// @brief 获取ScmUiFramework的元数据
|
||||
/// @return 成功:Ok(ScmUiFramework的元数据)
|
||||
/// 失败:Err(错误码)
|
||||
fn metadata(&self) -> Result<ScmUiFrameworkMetadata, SystemError> {
|
||||
// 若文件系统没有实现此方法,则返回“不支持”
|
||||
return Err(SystemError::ENOSYS);
|
||||
}
|
||||
}
|
||||
```
|
||||
## Main APIs
|
||||
### scm_init() - Initialize the screen management module
|
||||
#### Prototype
|
||||
```rust
|
||||
pub extern "C" fn scm_init()
|
||||
```
|
||||
#### Description
|
||||
  scm_init() is mainly used to initialize some global variables used by SCM, such as the flag indicating whether double buffering is used, and some global variables used by textui when it is not initialized.
|
||||
|
||||
### scm_reinit() - Reinitialize the screen management module after the memory management unit is initialized
|
||||
#### Prototype
|
||||
```rust
|
||||
pub extern "C" fn scm_reinit() -> i32
|
||||
```
|
||||
#### Description
|
||||
  scm_reinit() is used to reprocess the frame buffer issues after the memory management unit has been initialized.
|
||||
|
||||
### scm_enable_double_buffer() - Enable double buffering
|
||||
#### Prototype
|
||||
```rust
|
||||
pub extern "C" fn scm_enable_double_buffer() -> i32
|
||||
```
|
||||
#### Description
|
||||
  scm_enable_double_buffer() is used to enable double buffering for outputting information to the window. After enabling, the information output to the window is temporarily stored in a buffer, and then this buffer's content is output to the window's frame buffer at regular intervals, rendering it to the window.
|
||||
|
||||
### scm_framework_enable() - Enable a specific UI framework and render its frame buffer to the screen
|
||||
#### Prototype
|
||||
```rust
|
||||
pub fn scm_framework_enable(framework: Arc<dyn ScmUiFramework>) -> Result<i32, SystemError>
|
||||
```
|
||||
#### Description
|
||||
  scm_framework_enable is used to enable a specific UI framework and render its frame buffer to the screen.
|
||||
|
||||
### scm_register() - Register a UI framework with the screen manager
|
||||
#### Prototype
|
||||
```rust
|
||||
pub fn scm_register(framework: Arc<dyn ScmUiFramework>) -> Result<i32, SystemError>
|
||||
```
|
||||
#### Description
|
||||
  scm_register is used to register a UI framework with SCM. It mainly calls the callback functions of the UI framework to install and activate it.
|
44
docs/locales/en/kernel/libs/lib_ui/textui.md
Normal file
44
docs/locales/en/kernel/libs/lib_ui/textui.md
Normal file
@ -0,0 +1,44 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/libs/lib_ui/textui.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:14
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# Text UI Framework (textui)
|
||||
|
||||
:::{note}
|
||||
Author: Zhou Hanjie <2625553453@qq.com>
|
||||
:::
|
||||
  The text framework is primarily used for rendering and displaying text windows in DragonOS. It outputs printed text information to the screen window. Displaying text in the window is divided into two scenarios: one is when the Memory Management Unit (MM) has not been initialized, which prevents dynamic memory allocation, imposing many restrictions (for example, not being able to use vec, mpsc, etc.), so it directly outputs the printed information to the window's frame buffer without using complex structures like virtual lines; the other is when the Memory Management Unit (MM) has been initialized, allowing dynamic memory allocation, which enables the use of more complex structures to handle the text information to be printed.
|
||||
|
||||
## Main APIs
|
||||
### rs_textui_init() - Text UI framework initialization
|
||||
#### Prototype
|
||||
```rust
|
||||
pub extern "C" fn rs_textui_init() -> i32
|
||||
```
|
||||
#### Description
|
||||
  rs_textui_init() is mainly used to initialize some global variable information that the textui framework needs (such as TEXTUIFRAMEWORK, TEXTUI_PRIVATE_INFO, etc.), and to register the textui framework with scm.
|
||||
|
||||
### textui_putchar() - Print text information to the currently used window in the textui framework
|
||||
#### Prototype
|
||||
```rust
|
||||
pub extern "C" fn rs_textui_putchar(character: u8, fr_color: u32, bk_color: u32) -> i32
|
||||
|
||||
pub fn textui_putchar(
|
||||
character: char,
|
||||
fr_color: FontColor,
|
||||
bk_color: FontColor,
|
||||
) -> Result<(), SystemError>
|
||||
```
|
||||
#### Description
|
||||
  textui_putchar() needs to handle two scenarios: one is when the Memory Management Unit (MM) has not been initialized, which prevents dynamic memory allocation, imposing many restrictions (for example, not being able to use vec, mpsc, etc.), so it directly outputs the printed information to the window's frame buffer without using complex structures like virtual lines; the other is when the Memory Management Unit (MM) has been initialized, allowing dynamic memory allocation, which enables the use of more complex structures to handle the text information to be printed.
|
64
docs/locales/en/kernel/libs/unified-init.md
Normal file
64
docs/locales/en/kernel/libs/unified-init.md
Normal file
@ -0,0 +1,64 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/libs/unified-init.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:09
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# unified-init Unified Initialization Library
|
||||
|
||||
:::{note}
|
||||
Author: Longjin <longjin@DragonOS.org>
|
||||
|
||||
December 25, 2023
|
||||
:::
|
||||
|
||||
## 1. Introduction
|
||||
|
||||
This library is located in `kernel/crates/unified-init`.
|
||||
It provides unified initialization macros to register functions into a unified initialization list. It facilitates unified initialization.
|
||||
|
||||
It is important to note that the array of initializers is no_mangle, so its naming should follow the rules of `模块_初始化器` to prevent naming conflicts that could lead to unexpected errors.
|
||||
|
||||
## 2. Usage
|
||||
|
||||
```rust
|
||||
use system_error::SystemError;
|
||||
use unified_init::define_unified_initializer_slice;
|
||||
use unified_init_macros::unified_init;
|
||||
|
||||
/// 初始化函数都将会被放到这个列表中
|
||||
define_unified_initializer_slice!(INITIALIZER_LIST);
|
||||
|
||||
#[unified_init(INITIALIZER_LIST)]
|
||||
fn init1() -> Result<(), SystemError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[unified_init(INITIALIZER_LIST)]
|
||||
fn init2() -> Result<(), SystemError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(INITIALIZER_LIST.len(), 2);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## 3. Development
|
||||
|
||||
When testing, you can write test code in `main.rs`,
|
||||
and then run `cargo expand --bin unified-init-expand` in the current directory to see the code after the proc macro has been expanded.
|
||||
|
||||
## 4. Maintainer
|
||||
|
||||
Longjin <longjin@DragonOS.org>
|
26
docs/locales/en/kernel/locking/index.rst
Normal file
26
docs/locales/en/kernel/locking/index.rst
Normal file
@ -0,0 +1,26 @@
|
||||
.. note:: AI Translation Notice
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/locking/index.rst
|
||||
|
||||
- Translation time: 2025-05-19 01:41:08
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
|
||||
Please report issues via `Community Channel <https://github.com/DragonOS-Community/DragonOS/issues>`_
|
||||
|
||||
====================================
|
||||
Locks
|
||||
====================================
|
||||
|
||||
This is the documentation explaining the lock variables in DragonOS.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
locks
|
||||
spinlock
|
||||
mutex
|
||||
rwlock
|
63
docs/locales/en/kernel/locking/locks.md
Normal file
63
docs/locales/en/kernel/locking/locks.md
Normal file
@ -0,0 +1,63 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/locking/locks.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:26
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# Types of Locks and Their Rules
|
||||
|
||||
## Introduction
|
||||
|
||||
  The DragonOS kernel implements several types of locks, which can be broadly categorized into two types:
|
||||
|
||||
- Sleepable locks
|
||||
- Spin locks
|
||||
|
||||
## Types of Locks
|
||||
|
||||
### Sleepable Locks
|
||||
|
||||
  Sleepable locks can only be acquired in a context that is preemptible.
|
||||
|
||||
  In DragonOS, the following sleepable locks are implemented:
|
||||
|
||||
- semaphore
|
||||
- mutex_t
|
||||
|
||||
### Spin Locks
|
||||
|
||||
- spinlock_t
|
||||
- {ref}`RawSpinLock <_spinlock_doc_rawspinlock>` (Rust version of spinlock_t, but incompatible with spinlock_t)
|
||||
- {ref}`SpinLock <_spinlock_doc_spinlock>` —— Built on top of RawSpinLock, it wraps a guard, binding the lock and the data it protects into a single structure. This allows for compile-time checks to prevent accessing data without holding the lock.
|
||||
|
||||
  When a process acquires a spin lock, it changes the lock count in the PCB, thereby implicitly disabling preemption. To provide more flexible operations, spinlock also provides the following methods:
|
||||
|
||||
| Suffix | Description |
|
||||
|----------------------|--------------------------------------------------------|
|
||||
| _irq() | Disable interrupts when acquiring the lock, enable them when releasing |
|
||||
| _irqsave()/_irqrestore() | Save the interrupt state when acquiring the lock, and restore it when releasing |
|
||||
|
||||
## Detailed Introduction
|
||||
|
||||
### Detailed Introduction to Spin Locks
|
||||
|
||||
  For a detailed introduction to spin locks, please refer to the document: {ref}`自旋锁 <_spinlock_doc>`
|
||||
|
||||
### Semaphore
|
||||
|
||||
  A semaphore is implemented based on a counter.
|
||||
|
||||
  When the available resources are insufficient, a process attempting to perform a down operation on the semaphore will be put to sleep until the resources become available.
|
||||
|
||||
### Mutex
|
||||
|
||||
  Please refer to {ref}`Mutex文档 <_mutex_doc>`
|
185
docs/locales/en/kernel/locking/mutex.md
Normal file
185
docs/locales/en/kernel/locking/mutex.md
Normal file
@ -0,0 +1,185 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/locking/mutex.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:16
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
(_translated_label___mutex_doc_en)=
|
||||
|
||||
:::{note}
|
||||
Author: Longjin <longjin@RinGoTek.cn>
|
||||
:::
|
||||
|
||||
# Mutex (Mutual Exclusion)
|
||||
|
||||
  A mutex is a lightweight synchronization primitive, with only two states: locked and idle.
|
||||
|
||||
  When a mutex is occupied, any process attempting to lock it will be put to sleep until the resource becomes available.
|
||||
|
||||
## 1. Features
|
||||
|
||||
- Only one task can hold the mutex at a time.
|
||||
- Recursive locking and unlocking are not allowed.
|
||||
- Mutex can only be operated through its API.
|
||||
- Mutex cannot be used in hard interrupts or soft interrupts.
|
||||
|
||||
## 2. Definition
|
||||
|
||||
  The mutex is defined in `lib/mutex.rs`, as shown below:
|
||||
|
||||
```rust
|
||||
/// @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. Usage
|
||||
|
||||
  Similar to SpinLock, the Rust version of Mutex has a guard. When using it, you need to transfer the ownership of the data to be protected to the Mutex. Moreover, the guard can only be generated after a successful lock, so at any moment, each Mutex can have at most one guard.
|
||||
|
||||
  When you need to read or modify the data protected by the Mutex, you should first use the `lock()` method of the Mutex. This method returns a `MutexGuard`. You can use the member functions of the protected data to perform some operations, or directly read or write the protected data. (This is equivalent to obtaining a mutable reference to the protected data.)
|
||||
|
||||
  A complete example is shown in the code below:
|
||||
|
||||
```rust
|
||||
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是加锁的状态
|
||||
debug!("x={:?}", x);
|
||||
}
|
||||
// 由于上方的变量`g`,也就是Mutex守卫的生命周期结束,自动释放了Mutex。因此,在此处,Mutex是放锁的状态
|
||||
debug!("x={:?}", x);
|
||||
```
|
||||
|
||||
  For variables inside a structure, we can use Mutex to perform fine-grained locking, that is, wrap the member variables that need to be locked in detail with Mutex, for example:
|
||||
|
||||
```rust
|
||||
pub struct a {
|
||||
pub data: Mutex<data_struct>,
|
||||
}
|
||||
```
|
||||
|
||||
  Of course, we can also lock the entire structure:
|
||||
|
||||
```rust
|
||||
struct MyStruct {
|
||||
pub data: data_struct,
|
||||
}
|
||||
/// 被全局加锁的结构体
|
||||
pub struct LockedMyStruct(Mutex<MyStruct>);
|
||||
```
|
||||
|
||||
## 4. API
|
||||
|
||||
### 4.1. new - Initialize Mutex
|
||||
|
||||
#### Prototype
|
||||
|
||||
```rust
|
||||
pub const fn new(value: T) -> Self
|
||||
```
|
||||
|
||||
#### Description
|
||||
|
||||
  The `new()` method is used to initialize a Mutex. This method requires a protected data as a parameter. It returns a Mutex.
|
||||
|
||||
### 4.2. lock - Lock
|
||||
|
||||
#### Prototype
|
||||
|
||||
```rust
|
||||
pub fn lock(&self) -> MutexGuard<T>
|
||||
```
|
||||
|
||||
#### Description
|
||||
|
||||
  Lock the Mutex, returns the guard of the Mutex. You can use this guard to operate the protected data.
|
||||
|
||||
  If the Mutex is already locked, this method will block the current process until the Mutex is released.
|
||||
|
||||
### 4.3. try_lock - Try to Lock
|
||||
|
||||
#### Prototype
|
||||
|
||||
```rust
|
||||
pub fn try_lock(&self) -> Result<MutexGuard<T>, i32>
|
||||
```
|
||||
|
||||
#### Description
|
||||
|
||||
  Try to lock the Mutex. If the lock fails, the current process will not be added to the waiting queue. If the lock is successful, it returns the guard of the Mutex; if the Mutex is already locked, it returns `Err(错误码)`.
|
||||
|
||||
## 5. C Version of Mutex (Will be deprecated in the future)
|
||||
|
||||
  The mutex is defined in `common/mutex.h`. Its data type is as follows:
|
||||
|
||||
```c
|
||||
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)`**
|
||||
|
||||
  Initialize a mutex object.
|
||||
|
||||
#### mutex_lock
|
||||
|
||||
**`void mutex_lock(mutex_t *lock)`**
|
||||
|
||||
  Lock a mutex object. If the mutex is currently held by another process, the current process will enter a sleep state.
|
||||
|
||||
#### mutex_unlock
|
||||
|
||||
**`void mutex_unlock(mutex_t *lock)`**
|
||||
|
||||
  Unlock a mutex object. If there are other processes in the mutex's waiting queue, the next process will be awakened.
|
||||
|
||||
#### mutex_trylock
|
||||
|
||||
**`void mutex_trylock(mutex_t *lock)`**
|
||||
|
||||
  Try to lock a mutex object. If the mutex is currently held by another process, it returns 0. Otherwise, the lock is successful and returns 1.
|
||||
|
||||
#### mutex_is_locked
|
||||
|
||||
**`void mutex_is_locked(mutex_t *lock)`**
|
||||
|
||||
  Determine if the mutex is already locked. If the given mutex is in a locked state, it returns 1; otherwise, it returns 0.
|
216
docs/locales/en/kernel/locking/rwlock.md
Normal file
216
docs/locales/en/kernel/locking/rwlock.md
Normal file
@ -0,0 +1,216 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/locking/rwlock.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:57
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# RwLock Read-Write Lock
|
||||
:::{note}
|
||||
Author: sujintao
|
||||
|
||||
Email: <sujintao@dragonos.org>
|
||||
:::
|
||||
|
||||
## 1. Introduction
|
||||
  A read-write lock is a mechanism used in a concurrent environment to protect shared data among multiple processes. Compared to a regular spinlock, a read-write lock divides access to shared data into two types: read access and write access. Read access to shared data is controlled by a read lock, while write access to shared data is controlled by a write lock. The design of a read-write lock allows for multiple "readers" (read-only access) and a single "writer" (write access) to coexist simultaneously. For shared data that is mostly read-only, using a read-write lock to control access can improve performance to some extent.
|
||||
|
||||
## 2. Implementation of Read-Write Lock in DragonOS
|
||||
### 2.1 Mechanism of Read-Write Lock
|
||||
  The purpose of a read-write lock is to maintain the consistency of shared variables in a multi-threaded system. Data is wrapped in an RwLock data structure, and all access and modification must be done through this structure. Each process that accesses shared data will obtain a guard. A read-only process obtains a READER (reader guard), while a process that needs to modify a shared variable obtains a WRITER (writer guard). As a "shadow" of the RwLock, threads perform access and modification operations based on the guard.
|
||||
|
||||
  In practice, in addition to READER and WRITER, a read-write lock also introduces an UPGRADER. This is a guard that lies between READER and WRITER. The role of the UPGRADER is to prevent WRITER starvation. When a process obtains an UPGRADER, it treats it as a READER. However, the UPGRADER can be upgraded, and after upgrading, it becomes a WRITER guard, allowing write operations on shared data.
|
||||
|
||||
  All guards satisfy the RAII mechanism native to Rust. When the scope of a guard ends, the guard will automatically release.
|
||||
|
||||
### 2.2 Relationship Between Read-Write Lock Guards
|
||||
  At any given time, multiple READERS can exist, meaning that multiple processes can access shared data simultaneously. However, only one WRITER can exist at a time, and when a process obtains a WRITER, no READERS or UPGRADERS can exist. A process can obtain an UPGRADER only if there are no existing UPGRADERS or WRITERS. However, once a process obtains an UPGRADER, it cannot successfully apply for a READER.
|
||||
|
||||
### 2.3 Design Details
|
||||
|
||||
#### 2.3.1 RwLock Data Structure
|
||||
```rust
|
||||
pub struct RwLock<T> {
|
||||
lock: AtomicU32,//原子变量
|
||||
data: UnsafeCell<T>,
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3.2 READER Guard Data Structure
|
||||
```rust
|
||||
pub struct RwLockReadGuard<'a, T: 'a> {
|
||||
data: *const T,
|
||||
lock: &'a AtomicU32,
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3.3 UPGRADER Guard Data Structure
|
||||
```rust
|
||||
pub struct RwLockUpgradableGuard<'a, T: 'a> {
|
||||
data: *const T,
|
||||
inner: &'a RwLock<T>,
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3.4 WRITER Guard Data Structure
|
||||
```rust
|
||||
pub struct RwLockWriteGuard<'a, T: 'a> {
|
||||
data: *mut T,
|
||||
inner: &'a RwLock<T>,
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3.5 Introduction to the lock Structure in RwLock
|
||||
The lock is a 32-bit atomic variable AtomicU32, and its bit allocation is as follows:
|
||||
```
|
||||
UPGRADER_BIT WRITER_BIT
|
||||
^ ^
|
||||
OVERFLOW_BIT +------+ +-------+
|
||||
^ | |
|
||||
| | |
|
||||
+-+--+--------------------------------------------------------+-+--+-+--+
|
||||
| | | | |
|
||||
| | | | |
|
||||
| | The number of the readers | | |
|
||||
| | | | |
|
||||
+----+--------------------------------------------------------+----+----+
|
||||
31 30 2 1 0
|
||||
```
|
||||
|
||||
  (From right to left) The 0th bit represents whether WRITER is valid. If WRITER_BIT = 1, it indicates that a process has obtained a WRITER guard. If UPGRADER_BIT = 1, it indicates that a process has obtained an UPGRADER guard. Bits 2 to 30 are used to represent the number of processes that have obtained READER guards in binary form. The 31st bit is an overflow detection bit. If OVERFLOW_BIT = 1, new requests for obtaining READER guards will be rejected.
|
||||
|
||||
## 3. Main APIs of Read-Write Lock
|
||||
### 3.1 Main APIs of RwLock
|
||||
```rust
|
||||
///功能: 输入需要保护的数据类型data,返回一个新的RwLock类型.
|
||||
pub const fn new(data: T) -> Self
|
||||
```
|
||||
```rust
|
||||
///功能: 获得READER守卫
|
||||
pub fn read(&self) -> RwLockReadGuard<T>
|
||||
```
|
||||
```rust
|
||||
///功能: 尝试获得READER守卫
|
||||
pub fn try_read(&self) -> Option<RwLockReadGuard<T>>
|
||||
```
|
||||
```rust
|
||||
///功能: 获得WRITER守卫
|
||||
pub fn write(&self) -> RwLockWriteGuard<T>
|
||||
```
|
||||
```rust
|
||||
///功能: 尝试获得WRITER守卫
|
||||
pub fn try_write(&self) -> Option<RwLockWriteGuard<T>>
|
||||
```
|
||||
```rust
|
||||
///功能: 获得UPGRADER守卫
|
||||
pub fn upgradeable_read(&self) -> RwLockUpgradableGuard<T>
|
||||
```
|
||||
```rust
|
||||
///功能: 尝试获得UPGRADER守卫
|
||||
pub fn try_upgradeable_read(&self) -> Option<RwLockUpgradableGuard<T>>
|
||||
```
|
||||
### 3.2 Main APIs of the WRITER Guard RwLockWriteGuard
|
||||
```rust
|
||||
///功能: 将WRITER降级为READER
|
||||
pub fn downgrade(self) -> RwLockReadGuard<'rwlock, T>
|
||||
```
|
||||
```rust
|
||||
///功能: 将WRITER降级为UPGRADER
|
||||
pub fn downgrade_to_upgradeable(self) -> RwLockUpgradableGuard<'rwlock, T>
|
||||
```
|
||||
### 3.3 Main APIs of the UPGRADER Guard RwLockUpgradableGuard
|
||||
```rust
|
||||
///功能: 将UPGRADER升级为WRITER
|
||||
pub fn upgrade(mut self) -> RwLockWriteGuard<'rwlock, T>
|
||||
```
|
||||
```rust
|
||||
///功能: 将UPGRADER降级为READER
|
||||
pub fn downgrade(self) -> RwLockReadGuard<'rwlock, T>
|
||||
```
|
||||
|
||||
## 4. Usage Examples
|
||||
```rust
|
||||
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");
|
||||
}
|
||||
```
|
118
docs/locales/en/kernel/locking/spinlock.md
Normal file
118
docs/locales/en/kernel/locking/spinlock.md
Normal file
@ -0,0 +1,118 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/locking/spinlock.md
|
||||
|
||||
- Translation time: 2025-05-19 01:43:03
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
(_translated_label___spinlock_doc_en)=
|
||||
|
||||
:::{note}
|
||||
Author: Longjin <longjin@RinGoTek.cn>
|
||||
:::
|
||||
|
||||
# Spinlock
|
||||
|
||||
## 1. Introduction
|
||||
|
||||
  A spinlock is a type of lock used for synchronization in multi-threaded environments. Threads repeatedly check if the lock variable is available. Since the thread remains in a running state during this process, it is a form of busy waiting. Once a spinlock is acquired, the thread will hold onto it until it is explicitly released.
|
||||
|
||||
  DragonOS implements spinlocks in the `kernel/src/lib/spinlock.rs` file. Based on slight differences in functional characteristics, two types of spinlocks, `RawSpinLock` and `SpinLock`, are provided.
|
||||
|
||||
(_translated_label___spinlock_doc_rawspinlock_en)=
|
||||
## 2. RawSpinLock - Raw Spinlock
|
||||
|
||||
  `RawSpinLock` is a raw spinlock, whose data part contains an AtomicBool, implementing the basic functionality of a spinlock. Its locking and unlocking require manual determination of the corresponding timing, meaning that, like spinlocks used in other languages, you need to first call the `lock()` method, and then manually call the `unlock()` method when leaving the critical section. We do not explicitly inform the compiler of which data the spinlock is protecting.
|
||||
|
||||
  RawSpinLock provides programmers with very flexible control over locking and unlocking. However, due to its excessive flexibility, it is easy to make mistakes when using it. Common issues include "accessing critical section data without locking", "forgetting to unlock", and "double unlocking". The compiler cannot check for these issues, and they can only be discovered at runtime.
|
||||
|
||||
:::{warning}
|
||||
`RawSpinLock` is not binary compatible with the C version of `spinlock_t`. If you need to operate on the C version of `spinlock_t` for temporary compatibility reasons, please use the operation functions for the C version of spinlock_t provided in `spinlock.rs`.
|
||||
|
||||
However, for newly developed features, please do not use the C version of `spinlock_t`, as it will be removed as code refactoring progresses.
|
||||
:::
|
||||
|
||||
(_translated_label___spinlock_doc_spinlock_en)=
|
||||
## 3. SpinLock - Spinlock with Guard
|
||||
|
||||
  `SpinLock` is an encapsulation of `RawSpinLock`, enabling compile-time checks for issues such as "accessing critical section data without locking", "forgetting to unlock", and "double unlocking"; it also supports internal mutability of data.
|
||||
|
||||
  Its struct prototype is as follows:
|
||||
|
||||
```rust
|
||||
#[derive(Debug)]
|
||||
pub struct SpinLock<T> {
|
||||
lock: RawSpinlock,
|
||||
/// 自旋锁保护的数据
|
||||
data: UnsafeCell<T>,
|
||||
}
|
||||
```
|
||||
|
||||
### 3.1. Usage
|
||||
|
||||
  You can initialize a SpinLock like this:
|
||||
|
||||
```rust
|
||||
let x = SpinLock::new(Vec::new());
|
||||
```
|
||||
|
||||
  When initializing this SpinLock, you must pass the data you want to protect into the SpinLock, which will then manage it.
|
||||
|
||||
  When you need to read or modify data protected by SpinLock, please first use the `lock()` method of SpinLock. This method will return a `SpinLockGuard`. You can use the member functions of the protected data to perform some operations, or directly read and write the protected data. (This is equivalent to obtaining a mutable reference to the protected data.)
|
||||
|
||||
  The complete example is shown in the code below:
|
||||
|
||||
```rust
|
||||
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是加锁的状态
|
||||
debug!("x={:?}", x);
|
||||
}
|
||||
// 由于上方的变量`g`,也就是SpinLock守卫的生命周期结束,自动释放了SpinLock。因此,在此处,SpinLock是放锁的状态
|
||||
debug!("x={:?}", x);
|
||||
```
|
||||
|
||||
  For variables inside a struct, we can use SpinLock to perform fine-grained locking, that is, wrap the member variables that need to be locked in SpinLock, for example:
|
||||
|
||||
```rust
|
||||
pub struct a {
|
||||
pub data: SpinLock<data_struct>,
|
||||
}
|
||||
```
|
||||
|
||||
  Of course, we can also lock the entire struct:
|
||||
|
||||
```rust
|
||||
struct MyStruct {
|
||||
pub data: data_struct,
|
||||
}
|
||||
/// 被全局加锁的结构体
|
||||
pub struct LockedMyStruct(SpinLock<MyStruct>);
|
||||
```
|
||||
|
||||
### 3.2. Principle
|
||||
|
||||
  `SpinLock` can achieve compile-time checking because it introduces a `SpinLockGuard` as a guard. When writing code, we ensure that only after calling the `lock()` method of `SpinLock` to acquire the lock can a `SpinLockGuard` be generated. Moreover, whenever we want to access protected data, we must obtain a guard. We also implement the `Drop` trait for `SpinLockGuard`; when the guard's lifetime ends, the lock will be automatically released. There is no other way to release the lock. Therefore, we can know that, in a context, as long as the `SpinLockGuard`'s lifetime has not ended, it has the right to access the critical section data, and the data access is safe.
|
||||
|
||||
### 3.3. Existing Issues
|
||||
|
||||
#### 3.3.1. Double Locking
|
||||
|
||||
  Please note that the compile-time checks supported by `SpinLock` are not omnipotent. It currently cannot detect the issue of "double locking" at compile time. Consider this scenario: function A acquires the lock, and then function B attempts to lock again, which results in a "double locking" issue. This kind of problem cannot be detected at compile time.
|
||||
|
||||
  To address this issue, we recommend the following programming approach:
|
||||
|
||||
- If function B needs to access data within the critical section, function B should receive a parameter of type `&SpinLockGuard`, which is obtained by function A. In this way, function B can access the data within the critical section.
|
42
docs/locales/en/kernel/memory_management/allocate-memory.md
Normal file
42
docs/locales/en/kernel/memory_management/allocate-memory.md
Normal file
@ -0,0 +1,42 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/memory_management/allocate-memory.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:13
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# Memory Allocation Guide
|
||||
|
||||
  This document will explain how to perform memory allocation within the kernel. Before starting, please understand a basic point: DragonOS's kernel manages memory using 4KB pages and has a buddy allocator and a slab allocator. It also has specific management mechanisms for both user space and kernel space.
|
||||
|
||||
## 1. Safe Memory Allocation
|
||||
|
||||
  By default, KernelAllocator is bound as the global memory allocator. It automatically selects between using the slab allocator or the buddy allocator based on the size of the memory requested. Therefore, in the kernel, using Rust's native memory allocation functions or creating an `Box` object, etc., is safe.
|
||||
|
||||
## 2. Manual Management of Page Frames
|
||||
|
||||
:::{warning}
|
||||
**Please be extremely cautious!** Manually managing page frames bypasses Rust's memory safety mechanisms, which may lead to memory leaks or memory errors.
|
||||
:::
|
||||
|
||||
  In some cases, we need to manually allocate page frames. For example, when we need to create a new page table or a new address space within the kernel. In such situations, we need to manually allocate page frames. Using the `LockedFrameAllocator`'s `allocate()` function can allocate contiguous page frames in physical address space. Please note that since the underlying implementation uses the buddy allocator, the number of page frames must be a power of two, and the maximum size should not exceed 1GB.
|
||||
|
||||
  When you need to release page frames, you can use the `LockedFrameAllocator`'s `deallocate()` function or the `deallocate_page_frames()` function to release contiguous page frames in physical address space.
|
||||
|
||||
  When you need to map page frames, you can use the `KernelMapper::lock()` function to obtain a kernel mapper object and then perform the mapping. Since KernelMapper is an encapsulation of PageMapper, once you obtain a KernelMapper, you can use the PageMapper-related interfaces to manage the mapping in the kernel space.
|
||||
|
||||
:::{warning}
|
||||
**Never** use KernelMapper to map memory in user address space. This would cause that part of memory to be detached from the user address space management, leading to memory errors.
|
||||
:::
|
||||
|
||||
## 3. Allocating Memory for User Programs
|
||||
|
||||
  In the kernel, you can use the user address space structure (`AddressSpace`) and its functions such as `mmap()`, `map_anonymous()`, etc., to allocate memory for user programs. These functions will automatically map the user program's memory into the user address space and automatically create VMA structures. You can use the `AddressSpace`'s `munmap()` function to unmap the user program's memory from the user address space and destroy the VMA structure. Functions such as `mprotect()` can be used for adjusting permissions.
|
27
docs/locales/en/kernel/memory_management/index.rst
Normal file
27
docs/locales/en/kernel/memory_management/index.rst
Normal file
@ -0,0 +1,27 @@
|
||||
.. note:: AI Translation Notice
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/memory_management/index.rst
|
||||
|
||||
- Translation time: 2025-05-19 01:41:10
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
|
||||
Please report issues via `Community Channel <https://github.com/DragonOS-Community/DragonOS/issues>`_
|
||||
|
||||
.. _translated_label__memory_management_module_en:
|
||||
|
||||
====================================
|
||||
Memory Management
|
||||
====================================
|
||||
|
||||
This section explains the design and implementation principles of the memory management module, along with the corresponding interfaces.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
intro
|
||||
allocate-memory
|
||||
mmio
|
34
docs/locales/en/kernel/memory_management/intro.md
Normal file
34
docs/locales/en/kernel/memory_management/intro.md
Normal file
@ -0,0 +1,34 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/memory_management/intro.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:11
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# Introduction to the Memory Management Module
|
||||
|
||||
## 1. Overview
|
||||
|
||||
  DragonOS implements a memory management module with excellent architectural design, encapsulating operations such as memory mapping, allocation, release, and management for both kernel space and user space. This allows kernel developers to more conveniently perform memory management tasks.
|
||||
|
||||
  The memory management module of DragonOS is mainly composed of the following types of components:
|
||||
|
||||
- **Hardware Abstraction Layer (MemoryManagementArch)** - Provides abstraction for specific processor architectures, enabling the memory management module to run on different processor architectures.
|
||||
- **Page Mapper (PageMapper)** - Provides mapping between virtual addresses and physical addresses, as well as operations for creating, filling, destroying, and managing page tables. It is divided into two types: Kernel Page Table Mapper (KernelMapper) and User Page Table Mapper (located within the specific user address space structure).
|
||||
- **Page Flusher (PageFlusher)** - Provides operations for flushing page tables (full table flush, single page flush, cross-core flush).
|
||||
- **Frame Allocator (FrameAllocator)** - Provides operations for allocating, releasing, and managing page frames. Specifically, it includes BumpAllocator and BuddyAllocator.
|
||||
- **Small Object Allocator** - Provides operations for allocating, releasing, and managing small memory objects. This refers to the SlabAllocator within the kernel (the implementation of SlabAllocator is currently not completed).
|
||||
- **MMIO Space Manager** - Provides operations for allocating and managing MMIO address spaces. (This module is pending further refactoring.)
|
||||
- **User Address Space Management Mechanism** - Provides management of user address spaces.
|
||||
- **VMA Mechanism** - Provides management of user address spaces, including creation, destruction, and permission management of VMA.
|
||||
- **User Mapping Management** - Works together with the VMA mechanism to manage mappings in user address spaces.
|
||||
- **System Call Layer** - Provides system calls for the user-space memory management system, including mmap, munmap, mprotect, mremap, etc.
|
||||
- **C Interface Compatibility Layer** - Provides interfaces for existing C code, enabling C code to run normally.
|
204
docs/locales/en/kernel/memory_management/mmio.md
Normal file
204
docs/locales/en/kernel/memory_management/mmio.md
Normal file
@ -0,0 +1,204 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/memory_management/mmio.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:34
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# MMIO
|
||||
|
||||
MMIO stands for "Memory-Mapped I/O," and it is widely used for interaction with hardware devices.
|
||||
|
||||
## Address Space Management
|
||||
|
||||
DragonOS implements a mechanism for managing MMIO address spaces. This section will introduce them.
|
||||
|
||||
### Why is Automatic Allocation of MMIO Address Space Needed?
|
||||
|
||||
  Since many devices on a computer require MMIO address space, and the demand for MMIO address space varies among different devices connected to each computer, manually specifying an MMIO address for each device type would lead to significant waste of virtual address space and increase system complexity. Moreover, we will need to handle exception handling functions for different virtual memory regions in the future. Therefore, we need a mechanism that can automatically allocate MMIO address space.
|
||||
|
||||
### What Features Does This Mechanism Provide?
|
||||
|
||||
- Allocates MMIO virtual address space ranging from 4K to 1GB for drivers
|
||||
- Adds these virtual address spaces to VMA for unified management
|
||||
- Allows batch release of these address spaces
|
||||
|
||||
### How Is This Mechanism Implemented?
|
||||
|
||||
  This mechanism essentially uses the buddy system to maintain MMIO virtual address space. In `mm/mm.h`, the MMIO virtual address space range is specified, which starts at `0xffffa10000000000` and covers a 1TB space. In other words, the buddy system maintains this 1TB virtual address space for MMIO.
|
||||
|
||||
### Address Space Allocation Process
|
||||
|
||||
1. Initialize the MMIO-mapping module, creating 512 1GB `__mmio_buddy_addr_region` blocks in the MMIO buddy system.
|
||||
2. The driver uses `mmio_create` to request address space allocation.
|
||||
3. `mmio_create` aligns the requested address space size to the nearest power of 2 and allocates memory address space from the buddy.
|
||||
4. Create a VMA and mark it as `VM_IO|VM_DONTCOPY`. MMIO VMA is only bound under `initial_mm` and will not be copied.
|
||||
5. Allocation is complete.
|
||||
|
||||
Once the MMIO address space is allocated, it behaves like a regular VMA and can be operated using mmap series functions.
|
||||
|
||||
### MMIO Mapping Process
|
||||
|
||||
  After obtaining the virtual address space, when we attempt to map memory into this address space, we can call the `mm_map` function to map this region.
|
||||
|
||||
  This function performs special handling for MMIO VMA mapping. That is: it creates a `Page` structure and the corresponding `anon_vma`. Then, it fills the corresponding physical address into the page table.
|
||||
|
||||
### Releasing MMIO Virtual Address Space
|
||||
|
||||
  When a device is unmounted, the driver can call the `mmio_release` function to release the specified MMIO address space.
|
||||
|
||||
  During the release process, `mmio_release` performs the following steps:
|
||||
|
||||
1. Remove the MMIO region's mapping from the page table.
|
||||
2. Release the MMIO region's VMA.
|
||||
3. Return the address space back to the MMIO buddy system.
|
||||
|
||||
## Buddy Algorithm for MMIO
|
||||
|
||||
### Definition of Buddy
|
||||
|
||||
  Two memory blocks are considered buddy memory blocks if they satisfy the following three conditions:
|
||||
|
||||
1. The sizes of the two memory blocks are the same.
|
||||
2. The memory addresses of the two memory blocks are contiguous.
|
||||
3. The two memory blocks are derived from the same larger block of memory.
|
||||
|
||||
### Buddy Algorithm
|
||||
|
||||
  The buddy algorithm is used to manage and organize the allocation and recycling of large contiguous memory blocks to reduce external fragmentation during system runtime. In the buddy system, each memory block size is $2^n$ bytes. In DragonOS, the buddy system memory pool maintains a total of 1TB of contiguous storage space, with the largest memory block size being $1G$ (i.e., $2^{30}B$) and the smallest memory block size being $4K$ (i.e., $2^{12}B$).
|
||||
|
||||
  The core idea of the buddy algorithm is that when an application requests memory, it always allocates the smallest memory block larger than the requested size, and the allocated memory block size is $2^nB$. (e.g., if an application requests $3B$ of memory, there is no integer $n$ such that $2^n = 3$, and $3 \in [2^1, 2^2]$, so the system will allocate a $2^2B$ memory block to the application, and the memory request is successfully completed.)
|
||||
|
||||
  What if there is no such "suitable" memory block in the buddy system? The system will first look for a larger memory block. If found, it will split the larger memory block into suitable memory blocks to allocate to the application. (e.g., if an application requests $3B$ of memory, and the system's smallest memory block larger than $3B$ is $16B$, then the $16B$ block will be split into two $8B$ blocks. One is placed back into the memory pool, and the other is further split into two $4B$ blocks. One of the $4B$ blocks is placed back into the memory pool, and the other is allocated to the application. Thus, the memory request is successfully completed.)
|
||||
|
||||
  If the system cannot find a larger memory block, it will attempt to merge smaller memory blocks until the required size is met. (e.g., if an application requests $3B$ of memory, and the system checks the memory pool and finds only two $2B$ memory blocks, the system will merge these two $2B$ blocks into a $4B$ block and allocate it to the application. Thus, the memory request is successfully completed.)
|
||||
|
||||
  Finally, if the system cannot find a large enough memory block and is unable to successfully merge smaller blocks, it will notify the application that there is not enough memory to allocate.
|
||||
|
||||
### Data Structures of the Buddy Algorithm
|
||||
|
||||
```
|
||||
|
||||
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 │ │
|
||||
│ └────────────┘ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
```rust
|
||||
|
||||
/// 最大的内存块为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,
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Design Philosophy
|
||||
|
||||
  In DragonOS, the `MmioBuddyMemPool` structure is used as the data structure for the buddy (for convenience of expression, the buddy algorithm is referred to as buddy) memory pool. It records the starting address (pool_start_addr) of the memory pool and the total size of memory blocks in the memory pool (pool_size). It also maintains a bidirectional linked list array (free_regions) of size `MMIO_BUDDY_REGION_COUNT`. Each linked list in `free_regions` maintains several free memory blocks (MmioBuddyAddrRegion).
|
||||
|
||||
  The index of `free_regions` is related to the size of the memory block. Since each memory block size is $2^{n}$ bytes, we can let $exp = n$. The conversion formula between index and exp is as follows: $index = exp - 12$. For example, a memory block of size $2^{12}$ bytes has $exp = 12$, and using the above formula, we get $index = 12 - 12 = 0$, so this memory block is stored in `free_regions[0].list`. Through this conversion formula, each time we take or release a memory block of size $2^n$, we only need to operate on `free_regions[n -12]`. In DragonOS, the largest memory block size in the buddy memory pool is $1G = 2^{30} bytes$, and the smallest is $4K = 2^{12} bytes$, so $index \in [0, 18]$.
|
||||
|
||||
  As a memory allocation mechanism, the buddy serves all processes. To solve the problem of synchronizing the linked list data in free_regions among different processes, the linked list type in `free_regions` uses a SpinLock (denoted as {ref}`自旋锁 <_spinlock_doc_spinlock>`) to protect the free memory block linked list (MmioFreeRegionList). `MmioFreeRegionList` encapsulates a linked list (list) that actually stores information about free memory blocks and the corresponding list length (num_free). With the use of a spinlock, only one process can modify a particular linked list at the same time, such as taking elements from the list (memory allocation) or inserting elements into the list (memory release).
|
||||
|
||||
  The element type in `MmioFreeRegionList` is the `MmioBuddyAddrRegion` structure, which records the starting address (vaddr) of the memory block.
|
||||
|
||||
### Internal APIs of the Buddy Algorithm
|
||||
|
||||
**P.S. The following functions are all members of the MmioBuddyMemPool structure. A global reference of type MmioBuddyMemPool has already been created in the system, denoted as `MMIO_POOL`. To use the following functions, please use them in the form of `MMIO_POOL.xxx()`, which does not require passing self.**
|
||||
|
||||
| **Function Name** | **Description** |
|
||||
|:----------------------------------------------------------------- |:--------------------------------------------------------- |
|
||||
| __create_region(&self, vaddr) | Pass the virtual address to create a new memory block address structure |
|
||||
| __give_back_block(&self, vaddr, exp) | Return the memory block at address vaddr with exponent exp back to the buddy |
|
||||
| __buddy_split(&self, region, exp, list_guard) | Split the given memory block of size $2^{exp}$ into two and insert the memory blocks of size $2^{exp-1}$ into the list |
|
||||
| __query_addr_region(&self, exp, list_guard) | Request a memory block of size $2^{exp}$ from the buddy |
|
||||
| mmio_buddy_query_addr_region(&self, exp) | A wrapper for query_addr_region, **please use this function instead of __query_addr_region** |
|
||||
| __buddy_add_region_obj(&self, region, list_guard) | Add a memory block to the specified address space list |
|
||||
| __buddy_block_vaddr(&self, vaddr, exp) | Calculate the virtual memory address of the buddy block based on the address and memory block size |
|
||||
| __pop_buddy_block( &self, vaddr, exp, list_guard) | Find and pop the buddy block of the specified memory block |
|
||||
| __buddy_pop_region( &self, list_guard) | Retrieve a memory region from the specified free list |
|
||||
| __buddy_merge(&self, exp, list_guard, high_list_guard) | Merge all memory blocks of size $2^{exp}$ |
|
||||
| __buddy_merge_blocks(&self, region_1, region_2, exp, high_list_guard) | Merge two **already removed from the list** memory blocks |
|
||||
|
||||
### External APIs of the Buddy Algorithm
|
||||
|
||||
| **Function Name** | **Description** |
|
||||
| ----------------------------------------------- | ------------------------------------------- |
|
||||
| __mmio_buddy_init() | Initialize the buddy system, **called in mmio_init(), do not call it arbitrarily** |
|
||||
| __exp2index(exp) | Convert the exponent $exp$ of $2^{exp}$ to the index in the memory pool's array |
|
||||
| mmio_create(size, vm_flags, res_vaddr, res_length) | Create an MMIO region with size aligned to the requested size, and bind its VMA to initial_mm |
|
||||
| mmio_release(vaddr, length) | Cancel the mapping of MMIO at address vaddr with size length and return it to the buddy |
|
21
docs/locales/en/kernel/process_management/index.rst
Normal file
21
docs/locales/en/kernel/process_management/index.rst
Normal file
@ -0,0 +1,21 @@
|
||||
.. note:: AI Translation Notice
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/process_management/index.rst
|
||||
|
||||
- Translation time: 2025-05-19 01:41:09
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
|
||||
Please report issues via `Community Channel <https://github.com/DragonOS-Community/DragonOS/issues>`_
|
||||
|
||||
Process Management Module
|
||||
====================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
kthread
|
||||
load_binary
|
30
docs/locales/en/kernel/process_management/kthread.md
Normal file
30
docs/locales/en/kernel/process_management/kthread.md
Normal file
@ -0,0 +1,30 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/process_management/kthread.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:23
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# kthread Kernel Threads
|
||||
|
||||
  The kernel thread module is implemented in `process/kthread.rs`, providing support for kernel threads and related functionalities. Kernel threads act as the "avatars" of the kernel, enhancing the system's parallelism and fault tolerance capabilities.
|
||||
|
||||
## Principles
|
||||
|
||||
  Each kernel thread runs in kernel mode, executing its specific tasks.
|
||||
|
||||
  The creation of a kernel thread is achieved by calling the `KernelThreadMechanism::create()` or `KernelThreadMechanism::create_and_run()` function, which sends a creation task to the `kthreadd` daemon thread. In other words, the creation of a kernel thread is ultimately handled by `kthread_daemon`.
|
||||
|
||||
  After a kernel thread is created, it defaults to a sleeping state. To wake it up, the `ProcessManager::wakeup` function should be used.
|
||||
|
||||
  When other kernel modules wish to stop a kernel thread, they can call the `KernelThreadMechanism::stop()` function to wait for the thread to exit, then obtain the return value and clean up the thread's PCB (Process Control Block).
|
||||
|
||||
  Kernel threads should frequently check the result of `KernelThreadMechanism::should_stop()` to determine whether they should exit. When it is detected that the thread needs to exit, it can return a return code to terminate itself. (Note: resource cleanup is important)
|
30
docs/locales/en/kernel/process_management/load_binary.md
Normal file
30
docs/locales/en/kernel/process_management/load_binary.md
Normal file
@ -0,0 +1,30 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/process_management/load_binary.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:18
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# Loader
|
||||
|
||||
## 1. Binary Program Loading
|
||||
|
||||
  In this section, you will learn about the principles of the binary loader in DragonOS.
|
||||
|
||||
  When DragonOS loads a binary program, it performs a "probe-load" process.
|
||||
|
||||
  During the probe phase, DragonOS reads the file header and sequentially calls the probe functions of each binary loader to determine whether the binary program is suitable for that loader. If it is suitable, the loader will be used to load the program.
|
||||
|
||||
  During the load phase, DragonOS uses the aforementioned loader to load the program. The loader will map the various segments of the binary program into memory and obtain the entry address of the binary program.
|
||||
|
||||
:::{note}
|
||||
Currently, DragonOS does not support dynamic linking, so all binary programs are statically linked. And only the ELF loader is temporarily supported.
|
||||
:::
|
202
docs/locales/en/kernel/sched/c_waiting.md
Normal file
202
docs/locales/en/kernel/sched/c_waiting.md
Normal file
@ -0,0 +1,202 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/sched/c_waiting.md
|
||||
|
||||
- Translation time: 2025-05-19 01:43:09
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# APIs Related to "Waiting" (C Language)
|
||||
|
||||
:::{warning}
|
||||
|
||||
As the kernel evolves, we will gradually replace the C language waiting mechanism with the Rust language waiting mechanism. During this process, we will retain both the C and Rust waiting mechanisms to allow for comparison during development.
|
||||
Once the timing is ripe, we will gradually remove the C language waiting mechanism.
|
||||
:::
|
||||
|
||||
   If several processes need to wait for an event to occur before they can be executed, a "waiting" mechanism is required to achieve process synchronization.
|
||||
|
||||
## I. wait_queue Waiting Queue
|
||||
|
||||
   wait_queue is a process synchronization mechanism, known as "waiting queue" in Chinese. It can suspend the current process and wake them up when the time is ripe, by another process.
|
||||
|
||||
   When you need to wait for an event to complete, using the wait_queue mechanism can reduce the overhead of process synchronization. Compared to overusing spinlocks and semaphores, or repeatedly calling usleep(1000) functions for synchronization, wait_queue is an efficient solution.
|
||||
|
||||
:::{warning}
|
||||
The implementation of the wait_queue in `wait_queue.h` does not have an independent queue head and does not consider locking the wait_queue. Therefore, in later development, the queue head implementation in `wait_queue_head_t` was added, which is essentially a linked list plus a spinlock. It is compatible with the queue `wait_queue_node_t` in `wait_queue.h`. When you use `struct wait_queue_head` as the queue head, you can still use the functions to add nodes to the waiting queue.
|
||||
:::
|
||||
|
||||
### Simple Usage
|
||||
|
||||
   The usage of a waiting queue mainly includes the following parts:
|
||||
|
||||
- Creating and initializing a waiting queue
|
||||
- Using the `wait_queue_sleep_on_` series of functions to suspend the current process. Processes that are suspended later will be placed at the end of the queue.
|
||||
- Using the `wait_queue_wakeup()` function to wake up processes waiting in the waiting queue and add them to the scheduling queue
|
||||
|
||||
   To use wait_queue, you need to `#include<common/wait_queue.h>`, and create a `wait_queue_node_t` type variable as the head of the waiting queue. This structure contains only two member variables:
|
||||
|
||||
```c
|
||||
typedef struct
|
||||
{
|
||||
struct List wait_list;
|
||||
struct process_control_block *pcb;
|
||||
} wait_queue_node_t;
|
||||
```
|
||||
|
||||
   For the waiting queue, there is a good naming method:
|
||||
|
||||
```c
|
||||
wait_queue_node_t wq_keyboard_interrupt_received;
|
||||
```
|
||||
|
||||
   This naming convention increases code readability and makes it easier to understand what the code is waiting for.
|
||||
|
||||
### Initializing the Waiting Queue
|
||||
|
||||
   The function `wait_queue_init(wait_queue_node_t *wait_queue, struct process_control_block *pcb)` provides the functionality to initialize a wait_queue node.
|
||||
|
||||
   When you initialize the queue head, you only need to pass the pointer to the wait_queue head node, and set the second parameter to NULL.
|
||||
|
||||
### Inserting a Node into the Waiting Queue
|
||||
|
||||
   You can use the following functions to suspend the current process and insert it into the specified waiting queue. These functions have similar overall functions, but differ in some details.
|
||||
|
||||
| Function Name | Explanation |
|
||||
| ----------------------------------- | ---------------------------------------------------------------- |
|
||||
| wait_queue_sleep_on() | Suspends the current process and sets the suspension state to PROC_UNINTERRUPTIBLE |
|
||||
| wait_queue_sleep_on_unlock() | Suspends the current process and sets the suspension state to PROC_UNINTERRUPTIBLE. After the current process is inserted into the waiting queue, it unlocks the given spinlock |
|
||||
| wait_queue_sleep_on_interriptible() | Suspends the current process and sets the suspension state to PROC_INTERRUPTIBLE |
|
||||
|
||||
### Waking Up a Process from the Waiting Queue
|
||||
|
||||
   You can use the `void wait_queue_wakeup(wait_queue_node_t * wait_queue_head, int64_t state);` function to wake up the first process in the specified waiting queue that has a suspension state matching the specified `state`.
|
||||
|
||||
   If there are no matching processes, no process will be woken up, as if nothing happened.
|
||||
|
||||
------------------------------------------------------------
|
||||
  
|
||||
  
|
||||
  
|
||||
|
||||
## II. wait_queue_head Waiting Queue Head
|
||||
|
||||
   The data structure is defined as follows:
|
||||
|
||||
```c
|
||||
typedef struct
|
||||
{
|
||||
struct List wait_list;
|
||||
spinlock_t lock; // 队列需要有一个自旋锁,虽然目前内部并没有使用,但是以后可能会用.
|
||||
} wait_queue_head_t;
|
||||
```
|
||||
|
||||
   The usage logic of the waiting queue head is the same as the waiting queue itself, because it is also a node of the waiting queue (just with an additional lock). The functions of wait_queue_head are basically the same as those of wait_queue, except that they include the string \*\*\*\_with\_node\_\*\*\*.
|
||||
|
||||
   Meanwhile, the wait_queue.h file provides many macros that can make your work easier.
|
||||
|
||||
### Provided Macros
|
||||
| Macro | Explanation |
|
||||
| ----------------------------------- | ---------------------------------------------------------------- |
|
||||
| DECLARE_WAIT_ON_STACK(name, pcb) | Declare a wait_queue node on the stack, and bind the pcb-represented process to this node |
|
||||
| DECLARE_WAIT_ON_STACK_SELF(name) | Declare a wait_queue node on the stack, and bind the current process (i.e., the process itself) to this node |
|
||||
| DECLARE_WAIT_ALLOC(name, pcb) | Use `kzalloc` to declare a wait_queue node, and bind the pcb-represented process to this node. Remember to use kfree to release the space |
|
||||
| DECLARE_WAIT_ALLOC_SELF(name) | Use `kzalloc` to declare a wait_queue node, and bind the current process (i.e., the process itself) to this node. Remember to use kfree to release the space |
|
||||
|
||||
### Creating a Waiting Queue Head
|
||||
   You can directly call the macro
|
||||
```c
|
||||
DECLARE_WAIT_QUEUE_HEAD(m_wait_queue_head); // 在栈上声明一个队列头变量
|
||||
```
|
||||
   Or manually declare
|
||||
```c
|
||||
struct wait_queue_head_t m_wait_queue_head = {0};
|
||||
wait_queue_head_init(&m_wait_queue_head);
|
||||
```
|
||||
|
||||
### Inserting a Node into the Waiting Queue
|
||||
|
||||
| Function Name | Explanation |
|
||||
| ----------------------------------- | ---------------------------------------------------------------- |
|
||||
| wait_queue_sleep_with_node(wait_queue_head_t *head, wait_queue_node_t *wait_node) | Pass in a waiting queue node, and set the suspension state of the node to PROC_UNINTERRUPTIBLE |
|
||||
| wait_queue_sleep_with_node_unlock(wait_queue_head_t *q, wait_queue_node_t *wait, void *lock) | Pass in a waiting queue node, suspend the process pointed to by the node's pcb, and set the suspension state to PROC_UNINTERRUPTIBLE. After the current process is inserted into the waiting queue, unlock the given spinlock |
|
||||
| wait_queue_sleep_with_node_interriptible(wait_queue_head_t *q, wait_queue_node_t *wait) | Pass in a waiting queue node, suspend the process pointed to by the node's pcb, and set the suspension state to PROC_INTERRUPTIBLE |
|
||||
|
||||
### Waking Up a Process from the Waiting Queue
|
||||
   The `wait_queue_wakeup` function in `wait_queue.h` directly kfree's the wait_node node. For stack-based wait_node, you can choose `wait_queue_wakeup_on_stack(wait_queue_head_t *q, int64_t state)` to wake up the queue head node in the queue.
|
||||
|
||||
------------------------------------------------------------
|
||||
  
|
||||
  
|
||||
  
|
||||
|
||||
## III. completion Completion Count
|
||||
|
||||
### Simple Usage
|
||||
   The usage of completion mainly includes the following parts:
|
||||
|
||||
- Declare a completion (can be on the stack, using kmalloc, or using an array)
|
||||
- Use wait_for_completion to wait for the event to complete
|
||||
- Use complete to wake up the waiting processes
|
||||
|
||||
   Waiting operation
|
||||
```c
|
||||
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);
|
||||
}
|
||||
```
|
||||
|
||||
   Completion operation
|
||||
```c
|
||||
void kthread_fun(struct completion *comp) {
|
||||
// ...... 做一些事 .......
|
||||
// 这里你确定你完成了目标事件
|
||||
|
||||
complete(&comp);
|
||||
// 或者你使用complete_all
|
||||
complete_all(&comp);
|
||||
}
|
||||
```
|
||||
|
||||
### More Usage
|
||||
   In the kernel/sched/completion.c folder, you can see several functions starting with __test__, which are test code for the completion module and cover most of the completion functions. You can refer to these functions to learn how to use them.
|
||||
|
||||
### Initializing Completion
|
||||
   The function `completion_init(struct completion *x)` provides the functionality to initialize a completion. When you use `DECLARE_COMPLETION_ON_STACK` to create (on the stack), it will be automatically initialized.
|
||||
|
||||
### Completion-related wait series functions
|
||||
|
||||
| Function Name | Explanation |
|
||||
| ----------------------------------- | ---------------------------------------------------------------- |
|
||||
| wait_for_completion(struct completion *x) | Suspends the current process and sets the suspension state to PROC_UNINTERRUPTIBLE. |
|
||||
| wait_for_completion_timeout(struct completion *x, long timeout) | Suspends the current process and sets the suspension state to PROC_UNINTERRUPTIBLE. After waiting for timeout time (jiffies time slice), the process is automatically awakened. |
|
||||
| wait_for_completion_interruptible(struct completion *x) | Suspends the current process and sets the suspension state to PROC_INTERRUPTIBLE. |
|
||||
| wait_for_completion_interruptible_timeout(struct completion *x, long timeout) | Suspends the current process and sets the suspension state to PROC_INTERRUPTIBLE. After waiting for timeout time (jiffies time slice), the process is automatically awakened. |
|
||||
| wait_for_multicompletion(struct completion x[], int n)| Suspends the current process and sets the suspension state to PROC_UNINTERRUPTIBLE. (Waiting for the completion of the array's completions) |
|
||||
|
||||
### Completion-related complete series functions
|
||||
|
||||
| Function Name | Explanation |
|
||||
| ----------------------------------- | ---------------------------------------------------------------- |
|
||||
| complete(struct completion *x) | Indicates that an event has been completed, and wakes up one process from the waiting queue |
|
||||
| complete_all(struct completion *x) | Indicates that the events related to this completion are marked as permanently completed, and wakes up all processes in the waiting queue |
|
||||
|
||||
### Other Functions for Querying Information
|
||||
| Function Name | Explanation |
|
||||
| ----------------------------------- | ---------------------------------------------------------------- |
|
||||
| completion_done(struct completion *x) | Checks if the completion's done variable is greater than 0. If it is, returns true; otherwise, returns false. Adding this function before waiting may accelerate the process? (Not tested experimentally yet, needs further verification) |
|
||||
| try_wait_for_completion(struct completion *x) | Checks if the completion's done variable is greater than 0. If it is, returns true (and decrements done by 1); otherwise, returns false. Adding this function before waiting may accelerate the process? (This function has the same logic as `completion_done`, but actively decrements the completion's done variable by 1) |
|
43
docs/locales/en/kernel/sched/cfs.md
Normal file
43
docs/locales/en/kernel/sched/cfs.md
Normal file
@ -0,0 +1,43 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/sched/cfs.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:13
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# APIs Related to the Completely Fair Scheduler
|
||||
|
||||
   CFS (Completely Fair Scheduler), as the name suggests, is a fully fair scheduler. CFS is one of the mainline schedulers and is also one of the most typical O(1) schedulers.
|
||||
|
||||
## Structure Introduction
|
||||
|
||||
- `_translated_label__`CompletelyFairScheduler``
|
||||
   ``CompletelyFairScheduler``实现了``Scheduler``trait,他是完全调度算法逻辑的主要实施者。
|
||||
|
||||
- ``FairSchedEntity``
|
||||
- **重要字段**
|
||||
- ``cfs_rq``: 它指向了自己所在的完全公平调度队列。
|
||||
- ``my_cfs_rq``: 为一个``Option``变量,当该实体作为一个单独进程时,这个值为``None``,但是若这个实体为一个组,那这个变量必需为这个组内的私有调度队列。这个``cfs_rq``还可以继续往下深入,就构成了上述的树型结构。
|
||||
- ``pcb``: 它指向了当前实体对应的``PCB``,同样,若当前实体为一个组,则这个``Weak``指针不指向任何值。
|
||||
|
||||
  ``FairSchedEntity``是完全公平调度器中最重要的结构体,他代表一个实体单位,它不止表示一个进程,它还可以是一个组或者一个用户,但是它在cfs队列中所表示的就单单是一个调度实体。这样的设计可以为上层提供更多的思路,比如上层可以把不同的进程归纳到一个调度实体从而实现组调度等功能而不需要改变调度算法。
|
||||
|
||||
  在cfs中,整体的结构是**一棵树**,每一个调度实体作为``cfs_rq``中的一个节点,若该调度实体不是单个进程(它可能是一个进程组),则在该调度实体中还需要维护一个自己的``cfs_rq``,这样的嵌套展开后,每一个叶子节点就是一个单独的进程。需要理解这样一棵树,**在后续文档中会以这棵树为核心讲解**。
|
||||
|
||||
  该结构体具体的字段意义请查阅源代码。这里提及几个重要的字段:
|
||||
|
||||
|
||||
- ``CfsRunQueue``
|
||||
  ``CfsRunQueue``完全公平调度算法中管理``FairSchedEntity``的队列,它可以挂在总的``CpuRunQueue``下,也可以作为子节点挂在``FairSchedEntity``上,详见上文``FairSchedEntity``。
|
||||
|
||||
- **重要字段**
|
||||
- ``entities``: 存储调度实体的红黑树
|
||||
- ``current`_en`: The entity currently running
|
79
docs/locales/en/kernel/sched/core.md
Normal file
79
docs/locales/en/kernel/sched/core.md
Normal file
@ -0,0 +1,79 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/sched/core.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:31
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# APIs Related to Process Scheduler
|
||||
|
||||
   This section defines the APIs related to process scheduling in DragonOS, which are the interfaces for the system to perform process scheduling. It also abstracts the Scheduler trait to allow specific scheduler implementations.
|
||||
|
||||
## Introduction to Scheduler
|
||||
|
||||
   Generally, a system handles multiple requests at the same time, but its resources are limited and prioritized. Scheduling is the method used to coordinate each request's usage of these resources.
|
||||
|
||||
## Overall Architecture
|
||||
   The entire scheduling subsystem is organized in a **tree structure**, with each CPU managing such a tree. Each CPU's ``CpuRunQueue``即可以理解为树的根节点。每个``CpuRunQueue``下会管理着不同调度策略的子树,根据不同的调度策略深入到对应子树中实施调度。大体结构如下:
|
||||
|
||||
- CpuRunQueue
|
||||
- Cfs
|
||||
- CfsRunQueue
|
||||
- FairSchedEntity
|
||||
- CfsRunQueue
|
||||
- ...(嵌套)
|
||||
- Rt
|
||||
- ...
|
||||
- Idle
|
||||
- ...
|
||||
- RR
|
||||
- ...
|
||||
- ...
|
||||
|
||||
  基于这个结构,调度子系统能够更轻松地解耦以及添加其他调度策略。
|
||||
  
|
||||
|
||||
## 重要结构
|
||||
- ``Scheduler:``
|
||||
  ``Scheduler``是各个调度算法提供给上层的接口,实现不同的调度算法,只需要向外提供这样一组接口即可。
|
||||
|
||||
- ``CpuRunQueue:``
|
||||
  ``CpuRunQueue``为总的CPU运行队列,他会根据不同的调度策略来进行调度。他作为调度子系统的根节点来组织调度。
|
||||
- **重要字段**
|
||||
- ``lock``: 过程锁,因为在深入到具体调度策略后的调度过程中还会需要访问``CpuRunQueue``中的信息,在cfs中保存了``CpuRunQueue``对象,我们需要确保在整体过程上锁后,子对象中不需要二次加锁即可访问,所以过程锁比较适合这个场景,若使用对象锁,则在对应调度策略中想要访问``CpuRunQueue``中的信息时需要加锁,但是最外层已经将``CpuRunQueue``对象上锁,会导致内层永远拿不到锁。对于该字段,详见[CpuRunQueue的self_lock方法及其注释](https://code.dragonos.org.cn/xref/DragonOS/kernel/src/sched/mod.rs?r=dd8e74ef0d7f91a141bd217736bef4fe7dc6df3d#360)。
|
||||
- ``cfs``: Cfs调度器的根节点,往下伸展为一棵子树,详见完全公平调度文档。
|
||||
- ``current``: 当前在CPU上运行的进程。
|
||||
- ``idle``: 当前CPU的Idle进程。
|
||||
|
||||
|
||||
## 调度流程
|
||||
  一次有效的调度分两种情况,第一是主动调用``__schedule``或者``schedule``函数进行调度,第二是通过时钟中断,判断当前运行的任务时间是否到期。
|
||||
|
||||
- **主动调度**
|
||||
- ``__schedule``和``schedule``函数:
|
||||
- ``__schedule``:真正执行调度。会按照当前调度策略来选择下一个任务执行。
|
||||
- ``schedule``: ``__schedule``的上层封装,它需要该任务在内核中的所有资源释放干净才能进行调度,即判断当前进程的``preempt_count``是否为0,若不为0则会**panic**。
|
||||
- 参数:这两个函数都需要提供一个参数:``SchedMode``。用于控制此次调度的行为,可选参数主要有以下两个:
|
||||
- ``SchedMode::SM_NONE``: 标志当前进程没有被抢占而是主动让出,他**不会**被再次加入队列,直到有其他进程主动唤醒它,这个标志位主要用于信号量、等待队列以及一些主动唤醒场景的实现。
|
||||
- ``SchedMode::SM_PREEMPT``:标志当前是被**抢占**运行的,他**会**再次被加入调度队列等待下次调度,通俗来说:它是被别的进程抢占了运行时间,有机会运行时他会继续执行。
|
||||
|
||||
- **时钟调度**
|
||||
  时钟中断到来的时候,调度系统会进行更新,包括判断是否需要下一次调度。以下为主要的函数调用栈:
|
||||
- ``LocalApicTimer::handle_irq``: 中断处理函数
|
||||
- ``ProcessManager::update_process_times``: 更新当前进程的时钟信息(统计运行时等)
|
||||
- ``scheduler_tick``: 调度子系统tick入口
|
||||
- ``CompletelyFairScheduler::tick``: 以cfs为例,此为cfs调度算法的tick入口
|
||||
- ``CfsRunQueue::entity_tick``: 对所有调度实体进行tick
|
||||
- ``CfsRunQueue::update_current``: 更新当前运行任务的运行时间及判断是否到期
|
||||
- ``CfsRunQueue::account_cfs_rq_runtime``: 计算当前队列的运行时间
|
||||
- ``CpuRunQueue::resched_current``: 若上一步计算的时间超时则到这一步,这里会设置进程标志为``NEED_SCHEDULE``.
|
||||
|
||||
- 退出中断:退出中断时检查当前进程是否存在标志位``NEED_SCHEDULE``,若存在则调用``__schedule`` performs scheduling.
|
28
docs/locales/en/kernel/sched/index.rst
Normal file
28
docs/locales/en/kernel/sched/index.rst
Normal file
@ -0,0 +1,28 @@
|
||||
.. note:: AI Translation Notice
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/sched/index.rst
|
||||
|
||||
- Translation time: 2025-05-19 01:41:10
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
|
||||
Please report issues via `Community Channel <https://github.com/DragonOS-Community/DragonOS/issues>`_
|
||||
|
||||
====================================
|
||||
DragonOS Scheduling
|
||||
====================================
|
||||
|
||||
This is the documentation related to process scheduling in DragonOS.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
c_waiting
|
||||
rust_waiting
|
||||
core
|
||||
cfs
|
||||
rt
|
||||
kernel_timer
|
184
docs/locales/en/kernel/sched/kernel_timer.md
Normal file
184
docs/locales/en/kernel/sched/kernel_timer.md
Normal file
@ -0,0 +1,184 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/sched/kernel_timer.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:48
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# Kernel Timer
|
||||
|
||||
## 1. Introduction
|
||||
|
||||
  The kernel timer is a type of timer within the kernel. The working mechanism of the kernel timer is: adding the timer to a queue and setting the expiration time for each timer. When the timer expires, the function corresponding to the timer is executed.
|
||||
|
||||
## 2. Design Concept
|
||||
|
||||
  The timer type is a structure of `Timer`, and `Timer` is composed of `SpinLock<InnerTimer>`. A global queue `TIMER_LIST` with element type `Arc<Timer>` is used to store the timers created by the system. When creating a timer, you should call `Timer::new(timer_func,expire_jiffies)`, where timer_func is the function to be executed by the timer, expire_jiffies is the expiration time of the timer, and the type of `timer_func` parameter is a structure that implements the `TimerFunction` characteristic. After creating the timer, you should use `Timer::activate()` to insert the timer into `TIMER_LIST`.
|
||||
|
||||
  **If you only want the current PCB to sleep for a certain period, you should call `schedule_timeout(timeout)`, and timeout specifies the duration of the PCB sleep.**
|
||||
|
||||
## 3. Features That the Timer Should Implement
|
||||
|
||||
  The function to be executed by the timer should implement the `TimerFunction` characteristic, and its definition is as follows:
|
||||
|
||||
```rust
|
||||
/// 定时器要执行的函数的特征
|
||||
pub trait TimerFunction: Send + Sync {
|
||||
fn run(&mut self);
|
||||
}
|
||||
```
|
||||
|
||||
  A typical implementation method is: creating a zero-length structure, implementing the `TimerFunction` characteristic, and then implementing the operation to be performed by the timer in the `run` function.
|
||||
|
||||
## 4. Timer API
|
||||
|
||||
### 4.1. Timer API
|
||||
|
||||
#### 4.1.1. Create a Timer
|
||||
```rust
|
||||
pub fn new(timer_func: Box<dyn TimerFunction>, expire_jiffies: u64) -> Arc<Self>
|
||||
```
|
||||
|
||||
**Parameters**
|
||||
|
||||
- timer_func: A structure corresponding to the function that the timer needs to execute, which implements the `TimerFunction` characteristic
|
||||
|
||||
- expire_jiffies: The expiration time of the timer (unit: **jiffies**)
|
||||
|
||||
**Return**
|
||||
|
||||
- Pointer to the timer structure
|
||||
|
||||
#### 4.1.2. Insert the Timer into the Timer List
|
||||
|
||||
```rust
|
||||
pub fn activate(&self)
|
||||
```
|
||||
|
||||
### 4.2. Other APIs
|
||||
|
||||
  **If you want to use the following functions in a .c module, please add rs_ before the function name.**
|
||||
|
||||
#### 4.2.1. Make the Process Sleep for a Certain Period
|
||||
|
||||
```rust
|
||||
pub fn schedule_timeout(mut timeout: i64) -> Result<i64, SystemError>
|
||||
```
|
||||
|
||||
**Function**
|
||||
|
||||
  Make the process sleep for timeout jiffies
|
||||
|
||||
**Parameters**
|
||||
|
||||
- timeout: The time to sleep (unit: **jiffies**)
|
||||
|
||||
**Return Value**
|
||||
|
||||
- Ok(i64): Remaining time to sleep (unit: **jiffies**)
|
||||
- Err(SystemError): Error code
|
||||
|
||||
#### 4.2.2. Get the Expiration Time of the First Timer in the Queue
|
||||
|
||||
```rust
|
||||
pub fn timer_get_first_expire() -> Result<u64, SystemError>
|
||||
```
|
||||
|
||||
**Function**
|
||||
|
||||
  Get the expiration time of the first timer in the queue, i.e., the expiration time of the earliest expiring timer
|
||||
|
||||
**Return Value**
|
||||
|
||||
- Ok(i64): Expiration time of the earliest expiring timer (unit: **jiffies**)
|
||||
- Err(SystemError): Error code
|
||||
|
||||
#### 4.2.3. Get the Current System Time
|
||||
|
||||
```rust
|
||||
pub fn clock() -> u64
|
||||
```
|
||||
|
||||
**Function**
|
||||
|
||||
  Get the current system time (unit: **jiffies**)
|
||||
|
||||
#### 4.2.4. Calculate the Timer Time Slice Corresponding to the Next n Milliseconds or Microseconds
|
||||
|
||||
##### 4.2.4.1. Milliseconds
|
||||
|
||||
```rust
|
||||
pub fn next_n_ms_timer_jiffies(expire_ms: u64) -> u64
|
||||
```
|
||||
|
||||
**Function**
|
||||
|
||||
  Calculate the timer time slice corresponding to the next n **milliseconds**
|
||||
|
||||
**Parameters**
|
||||
|
||||
- expire_ms: n milliseconds
|
||||
|
||||
**Return Value**
|
||||
|
||||
  The corresponding timer time slice (unit: **milliseconds**)
|
||||
|
||||
##### 4.2.4.2. Microseconds
|
||||
|
||||
```rust
|
||||
pub fn next_n_us_timer_jiffies(expire_us: u64) -> u64
|
||||
```
|
||||
|
||||
**Function**
|
||||
|
||||
  Calculate the timer time slice corresponding to the next n **microseconds**
|
||||
|
||||
**Parameters**
|
||||
|
||||
- expire_ms: n microseconds
|
||||
|
||||
**Return Value**
|
||||
|
||||
  The corresponding timer time slice (unit: **microseconds**)
|
||||
|
||||
## 5. Creating a Timer Instance
|
||||
|
||||
```rust
|
||||
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();
|
||||
}
|
||||
```
|
71
docs/locales/en/kernel/sched/rt.md
Normal file
71
docs/locales/en/kernel/sched/rt.md
Normal file
@ -0,0 +1,71 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/sched/rt.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:17
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# APIs Related to Real-Time Process Scheduler
|
||||
|
||||
   RT (Real-Time Scheduler), real-time scheduler. Real-time scheduling is a method of allocating CPU resources to complete real-time processing tasks.
|
||||
|
||||
   In DragonOS, processes are divided into two categories: "real-time processes" and "normal processes". Real-time processes have higher priority than normal processes. If there are real-time processes in the current system's execution queue, the RT scheduler will prioritize selecting a real-time process. If there are multiple real-time processes in the queue, the scheduler will select the one with the highest priority to execute.
|
||||
|
||||
## 1. Introduction to RTQueue
|
||||
|
||||
   RTQueue is a scheduling queue used to store real-time processes with a state of running. Each CPU maintains its own RTQueue, and it primarily uses Vec as the main storage structure to implement this.
|
||||
|
||||
### 1.1 Main Functions
|
||||
1. enqueue(): Add the PCB to the queue
|
||||
2. dequeue(): Remove the PCB from the queue
|
||||
|
||||
## 2. Introduction to SchedulerRT
|
||||
|
||||
   RT Scheduler class, which mainly implements the initialization and scheduling function of the RT scheduler.
|
||||
|
||||
### 2.1 Main Functions
|
||||
1. pick_next_task_rt(): Get the first PCB that needs to be executed on the current CPU
|
||||
2. sched(): Implementation of the sched() function for the Scheduler trait. This function handles the logic for scheduling real-time processes and returns the PCB to be executed next. If no suitable PCB is found, it returns None.
|
||||
3. enqueue(): Also an implementation of the sched() function for the Scheduler trait, which adds a PCB to the scheduler's scheduling queue
|
||||
|
||||
### 2.2 Kernel Scheduling Policies
|
||||
   Currently, the main scheduling policies in DragonOS are SCHED_NORMAL policy | SCHED_FIFO policy | SCHED_RT policy. The specific scheduling policies are as follows:
|
||||
1. SCHED_NORMAL policy:
|
||||
SCHED_NORMAL is an "absolute fair scheduling policy", and processes using this policy are scheduled using CFS.
|
||||
|
||||
2. SCHED_FIFO policy:
|
||||
SCHED_FIFO is a "real-time process scheduling policy", which is a first-in-first-out scheduling strategy. This policy does not involve the CPU time slice mechanism. Without a higher-priority process, the process can only wait for other processes to release CPU resources. In the SCHED_FIFO policy, the running time of the process scheduled by the scheduler is not limited and can run for an arbitrary length of time.
|
||||
|
||||
3. SCHED_RR policy:
|
||||
SCHED_RR is a "real-time process scheduling policy", which uses a time-slice rotation mechanism. The time_slice of the corresponding process will decrease during execution. Once the process uses up its CPU time slice, it will be added to the execution queue of the same priority on the same CPU. At the same time, the CPU resource is released, and the CPU usage will be allocated to the next process to execute.
|
||||
|
||||
## 3. Q&A
|
||||
   Several commonly used methods
|
||||
1. How to create a real-time process
|
||||
|
||||
```c
|
||||
struct process_control_block *pcb_name = kthread_run_rt(&fn_name, NULL, "test create rt pcb");
|
||||
```
|
||||
Where kthread_run_rt is a macro for creating a kernel real-time thread
|
||||
|
||||
2. Meaning of fields related to real-time processes in the PCB
|
||||
1. policy: The scheduling policy of the real-time process, currently SCHED_FIFO and SCHED_RR
|
||||
2. priority: The priority of the real-time process, ranging from 0 to 99. The larger the number, the higher the priority
|
||||
3. rt_time_slice: The time slice of the real-time process. The default is 100, which decreases as the CPU runs. When rt_time_slice reaches 0, the time slice is reset to its initial value, and the process is added to the execution queue
|
||||
|
||||
3. How real-time processes are stored in the queue
|
||||
- Currently, Vec is used to store the processes. Due to the specific implementation logic, the enqueue and dequeue operations are performed at the end of the queue. Therefore, the following phenomenon may occur: when there are multiple real-time processes with the same priority waiting to run, a starvation phenomenon may occur. That is, the process that ran out of its time slice will be executed next, causing starvation among the processes with the same priority waiting.
|
||||
|
||||
4. Todo
|
||||
1. Use a doubly linked list (or other methods) to store the queue of real-time processes to solve the above starvation issue
|
||||
2. Currently, the real-time scheduling is for a single CPU. It needs to be implemented for multiple CPUs
|
||||
3. Implement the bandwidth allocation ratio between real-time processes and normal processes
|
||||
4. Achieve load balancing between multiple CPUs
|
94
docs/locales/en/kernel/sched/rust_waiting.md
Normal file
94
docs/locales/en/kernel/sched/rust_waiting.md
Normal file
@ -0,0 +1,94 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/sched/rust_waiting.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:21
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# APIs Related to "Waiting" (Rust Language)
|
||||
|
||||
   If several processes need to wait for a certain event to occur before they can be executed, a "waiting" mechanism is required to achieve process synchronization.
|
||||
|
||||
## 1. WaitQueue Waiting Queue
|
||||
|
||||
   WaitQueue is a process synchronization mechanism, known as "Waiting Queue" in Chinese. It can suspend the current process and, when the time is ripe, another process can wake them up.
|
||||
|
||||
   When you need to wait for an event to complete, using the WaitQueue mechanism can reduce the overhead of process synchronization. Compared to abusing spinlocks and semaphores, or repeatedly using functions like usleep(1000), WaitQueue is an efficient solution.
|
||||
|
||||
### 1.1 Using WaitQueue
|
||||
|
||||
   Using WaitQueue is very simple, requiring just three steps:
|
||||
|
||||
1. Initialize a WaitQueue object.
|
||||
2. Call the API related to suspending the current process and suspend it.
|
||||
3. When the event occurs, another process calls the API related to waking up the WaitQueue to wake up a process.
|
||||
|
||||
   Here is a simple example:
|
||||
|
||||
### 1.1.1 Initializing a WaitQueue Object
|
||||
|
||||
   Initializing a WaitQueue object is very simple; you just need to call `WaitQueue::INIT`.
|
||||
|
||||
```rust
|
||||
let mut wq = WaitQueue::INIT;
|
||||
```
|
||||
|
||||
### 1.1.2 Suspending the Process
|
||||
|
||||
   You can suspend the current process as follows:
|
||||
|
||||
```rust
|
||||
wq.sleep();
|
||||
```
|
||||
|
||||
   The current process will be suspended until another process calls `wq.wakeup()`.
|
||||
|
||||
### 1.1.3 Waking Up the Process
|
||||
|
||||
   You can wake up a process as follows:
|
||||
|
||||
```rust
|
||||
// 唤醒等待队列头部的进程(如果它的state & PROC_INTERRUPTIBLE 不为0)
|
||||
wq.wakeup(PROC_INTERRUPTIBLE);
|
||||
|
||||
// 唤醒等待队列头部的进程(如果它的state & PROC_UNINTERRUPTIBLE 不为0)
|
||||
wq.wakeup(PROC_UNINTERRUPTIBLE);
|
||||
|
||||
// 唤醒等待队列头部的进程(无论它的state是什么)
|
||||
wq.wakeup((-1) as u64);
|
||||
```
|
||||
|
||||
### 1.2 APIs
|
||||
|
||||
### 1.2.1 Suspending the Process
|
||||
|
||||
   You can use the following functions to suspend the current process and insert it into the specified waiting queue. These functions have similar overall functionality, but differ in some details.
|
||||
|
||||
| Function Name | Explanation |
|
||||
| --------------------------------------- | ---------------------------------------------------------------- |
|
||||
| sleep() | Suspend the current process and set its state to `PROC_INTERRUPTIBLE` |
|
||||
| sleep_uninterruptible() | Suspend the current process and set its state to `PROC_UNINTERRUPTIBLE` |
|
||||
| sleep_unlock_spinlock() | Suspend the current process and set its state to `PROC_INTERRUPTIBLE`. After inserting the current process into the waiting queue, unlock the given spinlock |
|
||||
| sleep_unlock_mutex() | Suspend the current process and set its state to `PROC_INTERRUPTIBLE`. After inserting the current process into the waiting queue, unlock the given Mutex |
|
||||
| sleep_uninterruptible_unlock_spinlock() | Suspend the current process and set its state to `PROC_UNINTERRUPTIBLE`. After inserting the current process into the waiting queue, unlock the given spinlock |
|
||||
| sleep_uninterruptible_unlock_mutex() | Suspend the current process and set its state to `PROC_UNINTERRUPTIBLE`. After inserting the current process into the waiting queue, unlock the given Mutex |
|
||||
|
||||
### 1.2.2 Waking Up the Process
|
||||
|
||||
   You can use the `wakeup(state)` function to wake up the first process in the waiting queue. If the process's state, after performing a bitwise AND operation with the given state, results in a non-zero value, it will be woken up.
|
||||
|
||||
   Return value: Returns `true` if a process is woken up, otherwise returns `false`.
|
||||
|
||||
### 1.2.3 Other APIs
|
||||
|
||||
| Function Name | Explanation |
|
||||
| ------------- | ------------------ |
|
||||
| len() | Returns the number of processes in the waiting queue |
|
23
docs/locales/en/kernel/syscall/index.rst
Normal file
23
docs/locales/en/kernel/syscall/index.rst
Normal file
@ -0,0 +1,23 @@
|
||||
.. note:: AI Translation Notice
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/syscall/index.rst
|
||||
|
||||
- Translation time: 2025-05-19 01:41:08
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
|
||||
Please report issues via `Community Channel <https://github.com/DragonOS-Community/DragonOS/issues>`_
|
||||
|
||||
.. _translated_label__syscall_en:
|
||||
|
||||
System Calls
|
||||
====================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Contents
|
||||
|
||||
syscall_table
|
107
docs/locales/en/kernel/syscall/syscall_table.rst
Normal file
107
docs/locales/en/kernel/syscall/syscall_table.rst
Normal file
@ -0,0 +1,107 @@
|
||||
.. note:: AI Translation Notice
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/syscall/syscall_table.rst
|
||||
|
||||
- Translation time: 2025-05-19 01:41:32
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
|
||||
Please report issues via `Community Channel <https://github.com/DragonOS-Community/DragonOS/issues>`_
|
||||
|
||||
System Call Table Implementation Plan
|
||||
=====================================
|
||||
|
||||
.. note::
|
||||
Author: longjin <longjin@dragonos.org>
|
||||
|
||||
Date: 2025/05/13
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
.. mermaid::
|
||||
:align: center
|
||||
:caption: System Call Table Architecture
|
||||
|
||||
classDiagram
|
||||
class Syscall {
|
||||
<<trait>>
|
||||
+num_args() usize
|
||||
+handle(args, from_user) Result<usize, SystemError>
|
||||
+entry_format(args) Vec<FormattedSyscallParam>
|
||||
}
|
||||
|
||||
class SyscallHandle {
|
||||
+nr: usize
|
||||
+inner_handle: &dyn Syscall
|
||||
}
|
||||
|
||||
class SyscallTable {
|
||||
-entries: [Option<&SyscallHandle>; 512]
|
||||
+get(nr) Option<&dyn Syscall>
|
||||
}
|
||||
|
||||
Syscall <|.. SysXXXXXXHandle
|
||||
SyscallHandle "1" *-- "1" Syscall
|
||||
SyscallTable "1" *-- "512" SyscallHandle
|
||||
|
||||
Compared to the original approach of dispatching system calls in a single large match statement, this approach uses a trait-based and system call table-based implementation. The main advantages include:
|
||||
|
||||
- Reduced stack memory usage: Avoids a single large function consuming excessive stack space
|
||||
- Support for parameter printing: Through a unified parameter formatting interface
|
||||
- Better extensibility: Adding new system calls does not require modifying the dispatch logic
|
||||
|
||||
Core Design
|
||||
-----------
|
||||
|
||||
Syscall Trait
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
All system call handler functions must implement the `Syscall` trait:
|
||||
|
||||
.. code-block:: rust
|
||||
|
||||
pub trait Syscall: Send + Sync + 'static {
|
||||
fn num_args(&self) -> usize;
|
||||
fn handle(&self, args: &[usize], from_user: bool) -> Result<usize, SystemError>;
|
||||
fn entry_format(&self, args: &[usize]) -> Vec<FormattedSyscallParam>;
|
||||
}
|
||||
|
||||
- `num_args()`: Returns the number of arguments required by the system call
|
||||
- `handle()`: Executes the actual system call handling
|
||||
- `entry_format()`: Formats the parameters for debugging printing
|
||||
|
||||
SyscallHandle
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
The `SyscallHandle` struct associates a system call number with its handler:
|
||||
|
||||
.. code-block:: rust
|
||||
|
||||
pub struct SyscallHandle {
|
||||
pub nr: usize, // System call number
|
||||
pub inner_handle: &'static dyn Syscall, // Handler function
|
||||
pub name: &'static str,
|
||||
}
|
||||
|
||||
SyscallTable
|
||||
~~~~~~~~~~~~
|
||||
|
||||
The `SyscallTable` manages all system calls:
|
||||
|
||||
- Fixed size of 512 entries
|
||||
- Initialized at compile time
|
||||
- Allows quick lookup of the handler function by system call number
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Implementing a System Call
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
1. Define the implementation of `Syscall` for the specific system call
|
||||
2. Register the system call in the system call table
|
||||
3. Load all registered system calls during system initialization
|
332
docs/locales/en/kernel/trace/eBPF.md
Normal file
332
docs/locales/en/kernel/trace/eBPF.md
Normal file
@ -0,0 +1,332 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/trace/eBPF.md
|
||||
|
||||
- Translation time: 2025-05-19 01:41:41
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# eBPF
|
||||
|
||||
> Author: Chen Linfeng
|
||||
>
|
||||
> Email: chenlinfeng25@outlook.com
|
||||
|
||||
## Overview
|
||||
|
||||
eBPF is a revolutionary technology that originated from the Linux kernel. It allows sandboxed programs to run in privileged contexts (such as the operating system kernel). It is used to extend the kernel's functionality in a secure and efficient manner without modifying the kernel source code or loading kernel modules.
|
||||
|
||||
Historically, due to the kernel's privileged position in supervising and controlling the entire system, the operating system has been an ideal place for implementing observability, security, and networking features. At the same time, due to the kernel's core position and high requirements for stability and security, the kernel has been difficult to iterate quickly. Therefore, traditionally, the innovation speed at the operating system level has been slower compared to features implemented outside the operating system itself.
|
||||
|
||||
eBPF fundamentally changes this approach. By allowing sandboxed programs to run within the operating system, application developers can run eBPF programs to add additional functionality to the operating system at runtime. Then, with the help of JIT compilers and verification engines, the operating system ensures that these programs are as secure and efficient as natively compiled programs. This has sparked a wave of eBPF-based projects covering a wide range of use cases, including next-generation network implementations, observability, and security features.
|
||||
|
||||
## eBPF in DragonOS
|
||||
|
||||
Adding eBPF support to a new OS requires understanding the eBPF runtime process. Typically, eBPF needs user-space tools and kernel-related infrastructure to function properly. Since a new OS usually is compatible with Linux applications, this can further simplify the porting work of user-space tools. As long as the kernel implements the relevant system calls and features, it can work with existing tools to support eBPF.
|
||||
|
||||
## eBPF Execution Process
|
||||
|
||||

|
||||
|
||||
As shown in the figure, the execution process of an eBPF program is divided into three main steps:
|
||||
|
||||
1. Source code -> Binary
|
||||
1. Users can write eBPF programs in Python/C/Rust and compile the source code into a binary program using the relevant toolchain.
|
||||
2. In this step, users need to reasonably use helper functions to enrich the functionality of the eBPF program.
|
||||
2. Loading eBPF program
|
||||
1. User-space tool libraries encapsulate the system call interfaces provided by the kernel to simplify the user's work. After preprocessing, user-space tools make system calls to request the kernel to load the eBPF program.
|
||||
1. The kernel first verifies the eBPF program to check its correctness and legality, and also performs further processing on the program.
|
||||
1. The kernel attaches the eBPF program to the kernel's mount points (kprobe/uprobe/trace_point) based on the user's request.
|
||||
1. During kernel operation, when these mount points are triggered by specific events, the eBPF program is executed.
|
||||
3. Data Interaction
|
||||
1. eBPF programs can collect information from the kernel, and user tools can selectively retrieve this information.
|
||||
2. eBPF programs can directly output information to a file, and user tools can read and parse the content of the file to obtain the information.
|
||||
3. eBPF programs share and exchange data between the kernel and user space through Maps.
|
||||
|
||||
## User-space Support
|
||||
|
||||
There are many user-space eBPF tool libraries, such as C's libbpf, Python's bcc, and Rust's Aya. Overall, the processing flow of these tools is similar. DragonOS currently supports eBPF programs written with the [Aya](https://github.com/aya-rs/aya) framework. As an example, the user-space tool processing flow for Aya is as follows:
|
||||
|
||||
1. Provide helper functions and Map abstractions for eBPF usage, making it easier to implement eBPF programs.
|
||||
2. Process the compiled eBPF program, call system calls to create Maps, and obtain corresponding file descriptors.
|
||||
3. Update the values of Maps (.data) as needed.
|
||||
4. Modify the relevant instructions of the eBPF program based on relocation information.
|
||||
5. Handle bpf to bpf calls in the eBPF program according to the kernel version.
|
||||
6. Load the eBPF program into the kernel.
|
||||
7. Package system calls and provide a large number of functions to help access eBPF information and interact with the kernel.
|
||||
|
||||
DragonOS's support for the Aya library is not complete. By trimming the Aya library, we have implemented a smaller [tiny-aya](https://github.com/DragonOS-Community/tiny-aya). To ensure future compatibility with Aya, tiny-aya only modifies the core tool aya in Aya. Some functions have been disabled because the system calls or files they require are not yet implemented in DragonOS.
|
||||
|
||||
### Tokio
|
||||
|
||||
Aya requires an asynchronous runtime. With the addition of some system calls and fixes for some errors, DragonOS now supports a basic Tokio runtime.
|
||||
|
||||
### Using Aya to Create an eBPF Program
|
||||
|
||||
As described in the [official documentation](https://aya-rs.dev/book/start/development/) provided by Aya, users only need to install the corresponding Rust toolchain according to its process to create an eBPF project based on a template. Taking the current implementation of `syscall_ebf` as an example, this program counts the number of system calls and stores them in a HashMap.
|
||||
|
||||
```
|
||||
├── Cargo.toml
|
||||
├── README.md
|
||||
├── syscall_ebpf
|
||||
├── syscall_ebpf-common
|
||||
├── syscall_ebpf-ebpf
|
||||
└── xtask
|
||||
```
|
||||
|
||||
The project structure in the user/app directory is as follows:
|
||||
|
||||
- `syscall_ebpf-ebpf` is the directory for implementing eBPF code, which will be compiled into bytecode.
|
||||
- `syscall_ebpf-common` is a common library, convenient for information exchange between the kernel and user space.
|
||||
- `syscall_ebpf` is the user-space program, responsible for loading the eBPF program and retrieving data generated by the eBPF program.
|
||||
- `xtask` is a command-line tool, convenient for users to compile and run user-space programs.
|
||||
|
||||
To run user-space programs in DragonOS, the project created using the template cannot be used directly:
|
||||
|
||||
1. This project does not meet DragonOS's requirements for the structure of user programs, but this can be easily modified.
|
||||
2. Because DragonOS's support for the Tokio runtime is not yet complete, the usage method needs to be slightly modified.
|
||||
|
||||
```
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
```
|
||||
|
||||
3. Because the support for Aya is not complete, the project's dependencies on aya and aya-log need to be replaced with the implementations in tiny-aya.
|
||||
|
||||
```
|
||||
[dependencies]
|
||||
aya = { git = "https://github.com/DragonOS-Community/tiny-aya.git" }
|
||||
aya-log = { git = "https://github.com/DragonOS-Community/tiny-aya.git" }
|
||||
```
|
||||
|
||||
With slight modifications, eBPF programs can be implemented using the existing tools of Aya.
|
||||
|
||||
## Kernel-space Support
|
||||
|
||||
Kernel-space support mainly consists of three parts:
|
||||
|
||||
1. kprobe implementation: located in directory `kernel/crates/kprobe`
|
||||
2. rbpf runtime: located in directory `kernel/crates/rbpf`
|
||||
3. System call support
|
||||
4. Helper function support
|
||||
|
||||
### rbpf
|
||||
|
||||
Previously, rbpf was used to run some simple eBPF programs. To run more complex programs, it needs to be modified.
|
||||
|
||||
1. Add support for bpf to bpf calls: by adding new stack abstractions and saving and restoring necessary register data.
|
||||
2. Disable unnecessary internal memory checks, which are usually handled by the kernel's verifier.
|
||||
3. Add data structures with ownership to avoid limitations on lifetimes.
|
||||
|
||||
### System Calls
|
||||
|
||||
All eBPF-related system calls are concentrated in `bpf()`, and they are further distinguished by the parameter `cmd`. The current support is as follows:
|
||||
|
||||
```rust
|
||||
pub fn bpf(cmd: bpf_cmd, attr: &bpf_attr) -> Result<usize> {
|
||||
let res = match cmd {
|
||||
// Map related commands
|
||||
bpf_cmd::BPF_MAP_CREATE => map::bpf_map_create(attr),
|
||||
bpf_cmd::BPF_MAP_UPDATE_ELEM => map::bpf_map_update_elem(attr),
|
||||
bpf_cmd::BPF_MAP_LOOKUP_ELEM => map::bpf_lookup_elem(attr),
|
||||
bpf_cmd::BPF_MAP_GET_NEXT_KEY => map::bpf_map_get_next_key(attr),
|
||||
bpf_cmd::BPF_MAP_DELETE_ELEM => map::bpf_map_delete_elem(attr),
|
||||
bpf_cmd::BPF_MAP_LOOKUP_AND_DELETE_ELEM => map::bpf_map_lookup_and_delete_elem(attr),
|
||||
bpf_cmd::BPF_MAP_LOOKUP_BATCH => map::bpf_map_lookup_batch(attr),
|
||||
bpf_cmd::BPF_MAP_FREEZE => map::bpf_map_freeze(attr),
|
||||
// Program related commands
|
||||
bpf_cmd::BPF_PROG_LOAD => prog::bpf_prog_load(attr),
|
||||
// Object creation commands
|
||||
bpf_cmd::BPF_BTF_LOAD => {
|
||||
error!("bpf cmd {:?} not implemented", cmd);
|
||||
return Err(SystemError::ENOSYS);
|
||||
}
|
||||
ty => {
|
||||
unimplemented!("bpf cmd {:?} not implemented", ty)
|
||||
}
|
||||
};
|
||||
res
|
||||
}
|
||||
```
|
||||
|
||||
Among these, the command for creating a Map is further细分 to determine the specific Map type. Currently, we have added support for general Maps:
|
||||
|
||||
```rust
|
||||
bpf_map_type::BPF_MAP_TYPE_ARRAY
|
||||
bpf_map_type::BPF_MAP_TYPE_PERCPU_ARRAY
|
||||
bpf_map_type::BPF_MAP_TYPE_PERF_EVENT_ARRAY
|
||||
bpf_map_type::BPF_MAP_TYPE_HASH
|
||||
bpf_map_type::BPF_MAP_TYPE_PERCPU_HASH
|
||||
bpf_map_type::BPF_MAP_TYPE_QUEUE
|
||||
bpf_map_type::BPF_MAP_TYPE_STACK
|
||||
bpf_map_type::BPF_MAP_TYPE_LRU_HASH
|
||||
bpf_map_type::BPF_MAP_TYPE_LRU_PERCPU_HASH
|
||||
|
||||
bpf_map_type::BPF_MAP_TYPE_CPUMAP
|
||||
| bpf_map_type::BPF_MAP_TYPE_DEVMAP
|
||||
| bpf_map_type::BPF_MAP_TYPE_DEVMAP_HASH => {
|
||||
error!("bpf map type {:?} not implemented", map_meta.map_type);
|
||||
Err(SystemError::EINVAL)?
|
||||
}
|
||||
```
|
||||
|
||||
All Maps implement the defined interface, which is referenced from the Linux implementation:
|
||||
|
||||
```rust
|
||||
pub trait BpfMapCommonOps: Send + Sync + Debug + CastFromSync {
|
||||
/// Lookup an element in the map.
|
||||
///
|
||||
/// See https://ebpf-docs.dylanreimerink.nl/linux/helper-function/bpf_map_lookup_elem/
|
||||
fn lookup_elem(&mut self, _key: &[u8]) -> Result<Option<&[u8]>> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
/// Update an element in the map.
|
||||
///
|
||||
/// See https://ebpf-docs.dylanreimerink.nl/linux/helper-function/bpf_map_update_elem/
|
||||
fn update_elem(&mut self, _key: &[u8], _value: &[u8], _flags: u64) -> Result<()> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
/// Delete an element from the map.
|
||||
///
|
||||
/// See https://ebpf-docs.dylanreimerink.nl/linux/helper-function/bpf_map_delete_elem/
|
||||
fn delete_elem(&mut self, _key: &[u8]) -> Result<()> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
/// For each element in map, call callback_fn function with map,
|
||||
/// callback_ctx and other map-specific parameters.
|
||||
///
|
||||
/// See https://ebpf-docs.dylanreimerink.nl/linux/helper-function/bpf_for_each_map_elem/
|
||||
fn for_each_elem(&mut self, _cb: BpfCallBackFn, _ctx: *const u8, _flags: u64) -> Result<u32> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
/// Look up an element with the given key in the map referred to by the file descriptor fd,
|
||||
/// and if found, delete the element.
|
||||
fn lookup_and_delete_elem(&mut self, _key: &[u8], _value: &mut [u8]) -> Result<()> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
/// perform a lookup in percpu map for an entry associated to key on cpu.
|
||||
fn lookup_percpu_elem(&mut self, _key: &[u8], cpu: u32) -> Result<Option<&[u8]>> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
/// Get the next key in the map. If key is None, get the first key.
|
||||
///
|
||||
/// Called from syscall
|
||||
fn get_next_key(&self, _key: Option<&[u8]>, _next_key: &mut [u8]) -> Result<()> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
/// Push an element value in map.
|
||||
fn push_elem(&mut self, _value: &[u8], _flags: u64) -> Result<()> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
/// Pop an element value from map.
|
||||
fn pop_elem(&mut self, _value: &mut [u8]) -> Result<()> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
/// Peek an element value from map.
|
||||
fn peek_elem(&self, _value: &mut [u8]) -> Result<()> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
/// Freeze the map.
|
||||
///
|
||||
/// It's useful for .rodata maps.
|
||||
fn freeze(&self) -> Result<()> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
/// Get the first value pointer.
|
||||
fn first_value_ptr(&self) -> *const u8 {
|
||||
panic!("value_ptr not implemented")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The system call that connects eBPF and kprobe is [`perf_event_open`](https://man7.org/linux/man-pages/man2/perf_event_open.2.html). This system call is very complex in Linux, so DragonOS does not implement it according to Linux. Currently, only two functions are supported:
|
||||
|
||||
```rust
|
||||
match args.type_ {
|
||||
// Kprobe
|
||||
// See /sys/bus/event_source/devices/kprobe/type
|
||||
perf_type_id::PERF_TYPE_MAX => {
|
||||
let kprobe_event = kprobe::perf_event_open_kprobe(args);
|
||||
Box::new(kprobe_event)
|
||||
}
|
||||
perf_type_id::PERF_TYPE_SOFTWARE => {
|
||||
// For bpf prog output
|
||||
assert_eq!(args.config, perf_sw_ids::PERF_COUNT_SW_BPF_OUTPUT);
|
||||
assert_eq!(
|
||||
args.sample_type,
|
||||
Some(perf_event_sample_format::PERF_SAMPLE_RAW)
|
||||
);
|
||||
let bpf_event = bpf::perf_event_open_bpf(args);
|
||||
Box::new(bpf_event)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- One of them, `PERF_TYPE_SOFTWARE`, is used to create software-defined events, and `PERF_COUNT_SW_BPF_OUTPUT` ensures that this event is used to collect the output of bpf.
|
||||
- `PERF_TYPE_MAX` usually indicates the creation of kprobe/uprobe events, which is one of the ways users can use kprobe. Users can bind an eBPF program to this event.
|
||||
|
||||
Similarly, different events of perf also implement the defined interface:
|
||||
|
||||
```rust
|
||||
pub trait PerfEventOps: Send + Sync + Debug + CastFromSync + CastFrom {
|
||||
fn mmap(&self, _start: usize, _len: usize, _offset: usize) -> Result<()> {
|
||||
panic!("mmap not implemented for PerfEvent");
|
||||
}
|
||||
fn set_bpf_prog(&self, _bpf_prog: Arc<File>) -> Result<()> {
|
||||
panic!("set_bpf_prog not implemented for PerfEvent");
|
||||
}
|
||||
fn enable(&self) -> Result<()> {
|
||||
panic!("enable not implemented");
|
||||
}
|
||||
fn disable(&self) -> Result<()> {
|
||||
panic!("disable not implemented");
|
||||
}
|
||||
fn readable(&self) -> bool {
|
||||
panic!("readable not implemented");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This interface is currently not stable.
|
||||
|
||||
### Helper Function Support
|
||||
|
||||
User-space tools communicate with the kernel through system calls to set up and exchange eBPF data. In the kernel, the execution of eBPF programs also requires the help of the kernel. A standalone eBPF program is not very useful, so it calls the kernel-provided `helper` functions to access kernel resources.
|
||||
|
||||
Most of the currently supported `helper` functions are related to Map operations:
|
||||
|
||||
```rust
|
||||
/// Initialize the helper functions.
|
||||
pub fn init_helper_functions() {
|
||||
let mut map = BTreeMap::new();
|
||||
unsafe {
|
||||
// Map helpers::Generic map helpers
|
||||
map.insert(1, define_func!(raw_map_lookup_elem));
|
||||
map.insert(2, define_func!(raw_map_update_elem));
|
||||
map.insert(3, define_func!(raw_map_delete_elem));
|
||||
map.insert(164, define_func!(raw_map_for_each_elem));
|
||||
map.insert(195, define_func!(raw_map_lookup_percpu_elem));
|
||||
// map.insert(93,define_func!(raw_bpf_spin_lock);
|
||||
// map.insert(94,define_func!(raw_bpf_spin_unlock);
|
||||
// Map helpers::Perf event array helpers
|
||||
map.insert(25, define_func!(raw_perf_event_output));
|
||||
// Probe and trace helpers::Memory helpers
|
||||
map.insert(4, define_func!(raw_bpf_probe_read));
|
||||
// Print helpers
|
||||
map.insert(6, define_func!(trace_printf));
|
||||
|
||||
// Map helpers::Queue and stack helpers
|
||||
map.insert(87, define_func!(raw_map_push_elem));
|
||||
map.insert(88, define_func!(raw_map_pop_elem));
|
||||
map.insert(89, define_func!(raw_map_peek_elem));
|
||||
}
|
||||
BPF_HELPER_FUN_SET.init(map);
|
||||
}
|
||||
```
|
24
docs/locales/en/kernel/trace/index.rst
Normal file
24
docs/locales/en/kernel/trace/index.rst
Normal file
@ -0,0 +1,24 @@
|
||||
.. note:: AI Translation Notice
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/trace/index.rst
|
||||
|
||||
- Translation time: 2025-05-19 01:41:20
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
|
||||
Please report issues via `Community Channel <https://github.com/DragonOS-Community/DragonOS/issues>`_
|
||||
|
||||
Kernel Tracing Mechanism
|
||||
====================================
|
||||
|
||||
The kernel tracing mechanism consists of many functionalities, such as kprobe, uprobe, tracepoint, ftrace, and eBPF, which is used to extend the observability of the kernel. The kernel currently supports kprobe and eBPF. This chapter will introduce these two mechanisms.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Contents
|
||||
|
||||
eBPF
|
||||
kprobe
|
64
docs/locales/en/kernel/trace/kprobe.md
Normal file
64
docs/locales/en/kernel/trace/kprobe.md
Normal file
@ -0,0 +1,64 @@
|
||||
:::{note}
|
||||
**AI Translation Notice**
|
||||
|
||||
This document was automatically translated by `Qwen/Qwen3-8B` model, for reference only.
|
||||
|
||||
- Source document: kernel/trace/kprobe.md
|
||||
|
||||
- Translation time: 2025-05-19 01:54:55
|
||||
|
||||
- Translation model: `Qwen/Qwen3-8B`
|
||||
|
||||
Please report issues via [Community Channel](https://github.com/DragonOS-Community/DragonOS/issues)
|
||||
|
||||
:::
|
||||
|
||||
# kprobe
|
||||
|
||||
> Author: Chen Linfeng
|
||||
> Email: chenlinfeng25@outlook.com
|
||||
|
||||
## Overview
|
||||
|
||||
The Linux kprobes debugging technology is a lightweight kernel debugging technique specifically designed by kernel developers to facilitate tracking the execution status of kernel functions. Using kprobes technology, kernel developers can dynamically insert probe points in most specified functions of the kernel to collect the required debugging status information, with minimal impact on the original execution flow of the kernel.
|
||||
|
||||
The kprobes technology relies on hardware architecture support, mainly including CPU exception handling and single-step debugging mechanisms. The former is used to cause the program's execution flow to enter the user-registered callback function, while the latter is used for single-step execution of the probed instruction. It is worth noting that on some architectures, the hardware does not support the single-step debugging mechanism, which can be resolved through software simulation methods (such as RISC-V).
|
||||
|
||||
## kprobe Workflow
|
||||
|
||||
<img src="/kernel/trace/kprobe_flow.png" style="zoom: 67%;" alt="xxx"/>
|
||||
|
||||
1. After registering a kprobe, each registered kprobe corresponds to a kprobe structure, which records the location of the probe point and the original instruction at that location.
|
||||
2. The location of the probe point is replaced with an exception instruction. When the CPU executes to this location, it will enter an exception state. On x86_64, the instruction is int3 (if the kprobe is optimized, the instruction is jmp).
|
||||
3. When the exception instruction is executed, the system checks whether it is an exception installed by kprobe. If it is, the pre_handler of the kprobe is executed. Then, using the CPU's single-step debugging (single-step) feature, the relevant registers are set, and the next instruction is set to the original instruction at the probe point, returning from the exception state.
|
||||
4. The system enters the exception state again. The previous step has set the single-step related registers, so the original instruction is executed and the system will enter the exception state again. At this point, the single-step is cleared, and the post_handler is executed, and the system safely returns from the exception state.
|
||||
5. When unloading the kprobe, the original instruction at the probe point is restored.
|
||||
|
||||
The kernel currently supports x86 and riscv64. Since riscv64 does not have a single-step execution mode, we use the break exception to simulate it. When saving the probe point instruction, we additionally fill in a break instruction, allowing the execution of the original instruction to trigger the break exception again on the riscv64 architecture.
|
||||
|
||||
## kprobe Interfaces
|
||||
|
||||
```rust
|
||||
pub fn register_kprobe(kprobe_info: KprobeInfo) -> Result<LockKprobe, SystemError>;
|
||||
pub fn unregister_kprobe(kprobe: LockKprobe) -> Result<(), SystemError>;
|
||||
|
||||
impl KprobeBasic {
|
||||
pub fn call_pre_handler(&self, trap_frame: &dyn ProbeArgs)
|
||||
pub fn call_post_handler(&self, trap_frame: &dyn ProbeArgs)
|
||||
pub fn call_fault_handler(&self, trap_frame: &dyn ProbeArgs)
|
||||
pub fn call_event_callback(&self, trap_frame: &dyn ProbeArgs)
|
||||
pub fn update_event_callback(&mut self, callback: Box<dyn CallBackFunc>)
|
||||
pub fn disable(&mut self)
|
||||
pub fn enable(&mut self)
|
||||
pub fn is_enabled(&self) -> bool
|
||||
pub fn symbol(&self) -> Option<&str>
|
||||
}
|
||||
```
|
||||
|
||||
- `call_pre_handler` Calls the user-defined callback function before the probe point instruction is executed.
|
||||
- `call_post_handler` Calls the user-defined callback function after the probe point instruction has been executed in single-step mode.
|
||||
- `call_fault_handler` Calls the user-defined callback function if the first two callback functions fail.
|
||||
- `call_event_callback` Used to call eBPF-related callback functions, usually called in the same way as `call_post_handler` after the probe point instruction is executed in single-step mode.
|
||||
- `update_event_callback` Used to update the callback function during runtime.
|
||||
- `disable` and `enable` are used to dynamically disable the kprobe. After calling `disable`, the kprobe will not execute the callback function when triggered.
|
||||
- `symbol` Returns the function name of the probe point.
|
Reference in New Issue
Block a user