Compare commits

...

53 Commits

Author SHA1 Message Date
seewishnew
23daa3dabc Merge 128d456923 into 3527693aeb 2024-02-16 11:09:36 -08:00
Philipp Oppermann
3527693aeb Merge pull request #1296 from phil-opp/update-bootloader-version
Specify bootloader version without a patch version
2024-02-16 13:29:45 +01:00
Philipp Oppermann
4376233ec3 Update bootloader docs.rs links to always point to latest v0.9 version 2024-02-16 13:26:05 +01:00
Philipp Oppermann
1f6402f746 Specify bootloader version as v0.9 (without patch version) in all posts
Cargo automatically chooses the latest patch version, but beginners might not know that. So this hopefully avoids some confusion.
2024-02-16 13:25:04 +01:00
Philipp Oppermann
3556211904 Merge pull request #1295 from phil-opp/update-data-layout
Update data layouts of custom targets to LLVM 18
2024-02-16 13:15:03 +01:00
Philipp Oppermann
c31dcb48e5 Update data layouts of custom targets to LLVM 18 2024-02-16 13:11:03 +01:00
acyanbird
59f84c2a45 update (#1289) 2024-02-13 17:49:41 +01:00
Philipp Oppermann
6bc593be79 Merge pull request #1288 from acyanbird/main
fix typo and format
2024-02-13 09:39:40 +01:00
acyanbird
3d337e5f80 Update index.zh-CN.md 2024-02-13 00:44:58 +00:00
acyanbird
9691bd5dc8 Update index.zh-CN.md 2024-02-12 21:16:07 +00:00
acyanbird
c0fbb10907 Update index.zh-CN.md 2024-02-11 03:26:29 +00:00
acyanbird
f9f1b8e7b9 fix typo and format 2024-02-11 03:01:50 +00:00
Philipp Oppermann
bde9fd0262 Merge pull request #1287 from acyanbird/main
translate comment and support me to chinese
2024-02-10 12:39:59 +01:00
acyanbird
bd361ee25a translate comment and support me 2024-02-09 22:51:30 +00:00
Philipp Oppermann
f13ccee48b Merge pull request #1286 from phil-opp/translate-support-me
Enable translations of the 'Support Me' section and the comment note
2024-02-08 19:34:10 +01:00
Philipp Oppermann
e4316a8a16 Enable translations of the comment note 2024-02-08 19:31:34 +01:00
Philipp Oppermann
3eeb25c946 Enable translations of the 'Support Me' section 2024-02-08 19:27:16 +01:00
Philipp Oppermann
813f434ecd Merge pull request #1285 from phil-opp/fix-links
Fix links to Rust unstable book
2024-02-08 19:26:12 +01:00
Philipp Oppermann
71f5d220ee Fix links to Rust unstable book 2024-02-08 19:06:38 +01:00
Philipp Oppermann
71e5ea0268 Merge pull request #1283 from acyanbird/patch-1
fix formatting error
2024-02-08 10:47:51 +01:00
acyanbird
da82fe8a0f Update index.zh-CN.md 2024-02-08 00:54:14 +00:00
Philipp Oppermann
2b444d3262 Merge pull request #1274 from acyanbird/main
change some format errors for chinese edition minimal kernel
2024-01-30 18:03:55 +01:00
acyanbird
b118828956 change some format errors 2024-01-28 23:59:17 +00:00
Philipp Oppermann
4595872c6b Merge pull request #1271 from phil-opp/blog-update-data-layout
Update data layouts in target specifications
2024-01-28 11:52:43 +01:00
Philipp Oppermann
1dced13b29 Update data layouts in target specifications
The data layout changed in LLVM. Using the wrong data layout now leads to a build error.
2024-01-28 11:50:57 +01:00
Philipp Oppermann
4b023bb432 Merge pull request #1262 from acyanbird/main
fix testing and paging introduction chapter errors in zh-CN translation
2024-01-05 18:00:30 +01:00
acyanbird
b1b35833d6 fix zh-CN paging introduction 2024-01-05 16:27:14 +00:00
acyanbird
34120a0409 fix zh-CN testing code err 2024-01-05 16:16:08 +00:00
Philipp Oppermann
6367e931e5 Merge pull request #1082 from TornaxO7/post_04_pub_fn_test_runner
Adding fix to make the test_runner functions pub
2023-12-28 17:20:22 +01:00
Philipp Oppermann
02fe09d56f Merge pull request #1254 from swnakamura/change-translator-name
Change one of translator's github ID
2023-12-08 19:46:42 +01:00
woodyZootopia
f4ab296b8b Change one of translator's github id
woodyZootopia changed their github id to swnakamura. This commit changes
the translators section accordingly
2023-12-02 15:04:01 +09:00
Philipp Oppermann
db4068826b Merge pull request #1167 from swnakamura/translate-11allocatordesign-ja
Translate post 11 "allocator design" into Japanese
2023-11-27 14:40:47 +01:00
woodyZootopia
9b1791a48d Refine the translation of post 11 2023-11-26 13:22:08 +09:00
Philipp Oppermann
61d074cc6c Merge pull request #1253 from keith666666/main
Fix: broken link of generator
2023-11-25 18:16:44 +01:00
Your Name
417c22556d fix: broken link of generator 2023-11-25 18:15:03 +08:00
Philipp Oppermann
5b4d04e337 Fix datetime comparison error in before_build.py 2023-10-17 09:57:11 +02:00
Philipp Oppermann
684ef64767 Merge pull request #1242 from phil-opp/improve-comment-link
Add `in:title` qualifier for discussion links
2023-09-14 20:30:36 +02:00
Philipp Oppermann
87d0ce5fa2 Add in:title qualifier for discussion links
Reduces the number of false positives in search.
2023-09-14 20:23:53 +02:00
Philipp Oppermann
5b67cb05ff Merge pull request #1237 from xzmeng/issue-1199
minimal-rust-kernel: fix missing .toml in zh-CN translation
2023-09-06 20:04:10 +02:00
Meng Xiangzhuo
81b7829657 minimal-rust-kernel: fix missing .toml in zh-CN translation 2023-08-27 03:48:25 +08:00
Philipp Oppermann
1ddeb129ac Merge pull request #1235 from Connortsui20/post-02-beginner-cargo-tips
Grammar fix
2023-08-21 09:26:13 +02:00
woodyZootopia
53d181d57b Rebase to the latest main 2023-08-21 13:34:57 +09:00
woodyZootopia
b634a24f4b Finish translation 2023-08-21 13:33:14 +09:00
woodyZootopia
4ef59648be Add Japanese article 2023-08-21 13:33:14 +09:00
Connortsui20
73628c1d05 Grammar fix 2023-08-18 08:12:39 -04:00
Philipp Oppermann
2e3230eca2 Merge pull request #1234 from Connortsui20/post-02-beginner-cargo-tips
Update post 2 with beginner friendly cargo tips
2023-08-17 16:30:54 +02:00
Connortsui20
63dc179cc7 shorter .cargo explanation 2023-08-17 09:13:43 -04:00
Connortsui20
211544af00 Better clarification 2023-08-17 08:42:54 -04:00
Connortsui20
1ff26bb4b6 Update post 2 with beginner friendly cargo tips
As a relatively new person to Rust, I confused the `.cargo/config.toml` with the global cargo config in my home directory (`~/.cargo/config.toml). While this is pretty obviously wrong in hindsight, since I've never used the `[unstable]` options before, I didn't realize my mistake until this next thing that tripped me up.

For `cargo install bootimage`, I think it is reasonable to tell the reader to go into a different directory to execute the command, since it might be the case that the reader has never dealt with different targets before and would have no idea that running `cargo install` for the new target that they just made in a json would be wrong (at least this was me).

This could be worded differently than the changes I made, but I think that the addition of these could only benefit a confused reader.
2023-08-16 18:57:37 -04:00
Vishnu C
128d456923 Minor corrections 2022-12-28 01:48:13 -08:00
Vishnu C
0652ed79c3 Minor edits and formatting corrections 2022-12-28 01:40:54 -08:00
Vishnu C
7500cac640 Adds code and documentation to rectify potential leaky headers in linked list allocator 2022-12-28 01:23:09 -08:00
TornaxO7
7ce356f99d Adding fix to make the test_runner functions pub 2022-02-23 02:26:05 +01:00
32 changed files with 1503 additions and 121 deletions

View File

@@ -8,7 +8,7 @@ from github import Github
g = Github()
one_month_ago = datetime.datetime.now() - datetime.timedelta(days=32)
one_month_ago = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(days=32)
def filter_date(issue):
return issue.closed_at > one_month_ago

View File

@@ -49,6 +49,13 @@ translated_content_notice = "This is a community translation of the <strong><a h
translated_by = "Translation by"
translation_contributors = "With contributions from"
word_separator = "and"
support_me = """
<h2>Support Me</h2>
<p>Creating and maintaining this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance. The best way to support me is to <a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. Thank you!</p>
"""
comment_note = """
Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. This comment thread directly maps to a <a href="_discussion_url_"><em>discussion on GitHub</em></a>, so you can also comment there if you prefer.
"""
# Chinese (simplified)
[languages.zh-CN]
@@ -67,6 +74,13 @@ translated_content_notice = "这是对原文章 <strong><a href=\"_original.perm
translated_by = "翻译者:"
translation_contributors = "With contributions from"
word_separator = "和"
support_me = """
<h2>支持我</h2>
<p>创建和维护这个博客以及相关的库带来了十分庞大的工作量,即便我十分热爱它们,仍然需要你们的支持。通过赞助我,可以让我有能投入更多时间与精力在创造新内容,开发新功能上。赞助我最好的办法是通过<a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. 十分感谢各位!</p>
"""
comment_note = """
你有问题需要解决,想要分享反馈,或者讨论更多的想法吗?请随时在这里留下评论!请使用尽量使用英文并遵循 Rust 的 <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. 这个讨论串将与 <a href="_discussion_url_"><em>discussion on GitHub</em></a> 直接连接,所以你也可以直接在那边发表评论
"""
# Chinese (traditional)
[languages.zh-TW]
@@ -85,6 +99,13 @@ translated_content_notice = "這是對原文章 <strong><a href=\"_original.perm
translated_by = "翻譯者:"
translation_contributors = "With contributions from"
word_separator = "和"
support_me = """
<h2>Support Me</h2>
<p>Creating and maintaining this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance. The best way to support me is to <a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. Thank you!</p>
"""
comment_note = """
Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. This comment thread directly maps to a <a href="_discussion_url_"><em>discussion on GitHub</em></a>, so you can also comment there if you prefer.
"""
# Japanese
[languages.ja]
@@ -103,6 +124,13 @@ translated_content_notice = "この記事は<strong><a href=\"_original.permalin
translated_by = "翻訳者:"
translation_contributors = "With contributions from"
word_separator = "及び"
support_me = """
<h2>Support Me</h2>
<p>Creating and maintaining this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance. The best way to support me is to <a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. Thank you!</p>
"""
comment_note = """
Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. This comment thread directly maps to a <a href="_discussion_url_"><em>discussion on GitHub</em></a>, so you can also comment there if you prefer.
"""
# Persian
[languages.fa]
@@ -121,6 +149,13 @@ translated_content_notice = "این یک ترجمه از جامعه کاربرا
translated_by = "ترجمه توسط"
translation_contributors = "With contributions from"
word_separator = "و"
support_me = """
<h2>Support Me</h2>
<p>Creating and maintaining this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance. The best way to support me is to <a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. Thank you!</p>
"""
comment_note = """
Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. This comment thread directly maps to a <a href="_discussion_url_"><em>discussion on GitHub</em></a>, so you can also comment there if you prefer.
"""
# Russian
[languages.ru]
@@ -139,6 +174,13 @@ translated_content_notice = "Это перевод сообщества пост
translated_by = "Перевод сделан"
translation_contributors = "With contributions from"
word_separator = "и"
support_me = """
<h2>Support Me</h2>
<p>Creating and maintaining this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance. The best way to support me is to <a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. Thank you!</p>
"""
comment_note = """
Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. This comment thread directly maps to a <a href="_discussion_url_"><em>discussion on GitHub</em></a>, so you can also comment there if you prefer.
"""
# French
[languages.fr]
@@ -157,6 +199,13 @@ translated_content_notice = "Ceci est une traduction communautaire de l'article
translated_by = "Traduit par : "
translation_contributors = "With contributions from"
word_separator = "et"
support_me = """
<h2>Support Me</h2>
<p>Creating and maintaining this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance. The best way to support me is to <a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. Thank you!</p>
"""
comment_note = """
Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. This comment thread directly maps to a <a href="_discussion_url_"><em>discussion on GitHub</em></a>, so you can also comment there if you prefer.
"""
# Korean
[languages.ko]
@@ -175,3 +224,10 @@ translated_content_notice = "이것은 커뮤니티 멤버가 <strong><a href=\"
translated_by = "번역한 사람 : "
translation_contributors = "With contributions from"
word_separator = "와"
support_me = """
<h2>Support Me</h2>
<p>Creating and maintaining this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance. The best way to support me is to <a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>. Thank you!</p>
"""
comment_note = """
Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. This comment thread directly maps to a <a href="_discussion_url_"><em>discussion on GitHub</em></a>, so you can also comment there if you prefer.
"""

View File

@@ -505,7 +505,7 @@ A minimal target specification that describes the `x86_64-unknown-linux-gnu` tar
```json
{
"llvm-target": "x86_64-unknown-linux-gnu",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"target-endian": "little",
"target-pointer-width": "64",
"target-c-int-width": "32",
@@ -527,7 +527,7 @@ In order to disable the multimedia extensions, we create a new target named `x86
```json
{
"llvm-target": "x86_64-unknown-linux-gnu",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"target-endian": "little",
"target-pointer-width": "64",
"target-c-int-width": "32",

View File

@@ -98,7 +98,7 @@ Rust allows us to define [custom targets] through a JSON configuration file. A m
```json
{
"llvm-target": "x86_64-unknown-linux-gnu",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"linker-flavor": "gcc",
"target-endian": "little",
"target-pointer-width": "64",
@@ -133,7 +133,7 @@ For our target system, we define the following JSON configuration in a file name
```json
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"linker-flavor": "gcc",
"target-endian": "little",
"target-pointer-width": "64",

View File

@@ -122,7 +122,7 @@ rtl = true
```json
{
"llvm-target": "x86_64-unknown-linux-gnu",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
@@ -145,7 +145,7 @@ rtl = true
```json
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
@@ -204,7 +204,7 @@ For more information, see our post on [disabling SIMD](@/edition-2/posts/02-mini
```json
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
@@ -414,7 +414,7 @@ pub extern "C" fn _start() -> ! {
# in Cargo.toml
[dependencies]
bootloader = "0.9.23"
bootloader = "0.9"
```
افزودن بوت‌لودر به عنوان وابستگی برای ایجاد یک دیسک ایمیج قابل بوت کافی نیست. مشکل این است که ما باید هسته خود را با بوت لودر پیوند دهیم، اما کارگو از [اسکریپت های بعد از بیلد] پشتیبانی نمی‌کند.

View File

@@ -118,7 +118,7 @@ Pour notre système cible toutefois, nous avons besoin de paramètres de configu
```json
{
"llvm-target": "x86_64-unknown-linux-gnu",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
@@ -141,7 +141,7 @@ Nous pouvons aussi cibler les systèmes `x86_64` avec notre noyau, donc notre sp
```json
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
@@ -201,7 +201,7 @@ Notre fichier de spécification de cible ressemble maintenant à ceci :
```json
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",

View File

@@ -9,7 +9,7 @@ chapter = "Bare Bones"
# Please update this when updating the translation
translation_based_on_commit = "7212ffaa8383122b1eb07fe1854814f99d2e1af4"
# GitHub usernames of the people that translated this post
translators = ["woodyZootopia", "JohnTitor"]
translators = ["swnakamura", "JohnTitor"]
+++
この記事では、Rustで最小限の64bitカーネルを作ります。前の記事で作った[フリースタンディングなRustバイナリ][freestanding Rust binary]を下敷きにして、何かを画面に出力する、ブータブルディスクイメージを作ります。
@@ -116,7 +116,7 @@ Cargoは`--target`パラメータを使ってさまざまなターゲットを
```json
{
"llvm-target": "x86_64-unknown-linux-gnu",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
@@ -139,7 +139,7 @@ Cargoは`--target`パラメータを使ってさまざまなターゲットを
```json
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
@@ -198,7 +198,7 @@ SIMDを無効化することによる問題に、`x86_64`における浮動小
```json
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
@@ -411,7 +411,7 @@ pub extern "C" fn _start() -> ! {
# in Cargo.toml
[dependencies]
bootloader = "0.9.23"
bootloader = "0.9"
```
bootloaderを依存として加えることだけでブータブルディスクイメージが実際に作れるわけではなく、私達のカーネルをコンパイル後にブートローダーにリンクする必要があります。問題は、cargoが[<ruby>ビルド後<rp> (</rp><rt>post-build</rt><rp>) </rp></ruby>にスクリプトを走らせる機能][post-build scripts]を持っていないことです。

View File

@@ -124,7 +124,7 @@ Cargo는 `--target` 인자를 통해 여러 컴파일 대상 시스템들을 지
```json
{
"llvm-target": "x86_64-unknown-linux-gnu",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
@@ -148,7 +148,7 @@ Cargo는 `--target` 인자를 통해 여러 컴파일 대상 시스템들을 지
```json
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
@@ -209,7 +209,7 @@ SIMD 레지스터 값들을 메모리에 백업하고 또 다시 복구하는
```json
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
@@ -418,7 +418,7 @@ pub extern "C" fn _start() -> ! {
# Cargo.toml 에 들어갈 내용
[dependencies]
bootloader = "0.9.23"
bootloader = "0.9"
```
부트로더를 의존 크레이트로 추가하는 것만으로는 부팅 가능한 디스크 이미지를 만들 수 없습니다. 커널 컴파일이 끝난 후 커널을 부트로더와 함께 링크할 수 있어야 하는데, cargo는 현재 [빌드 직후 스크립트 실행][post-build scripts] 기능을 지원하지 않습니다.

View File

@@ -112,7 +112,7 @@ For our target system, however, we require some special configuration parameters
```json
{
"llvm-target": "x86_64-unknown-linux-gnu",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
@@ -135,7 +135,7 @@ We also target `x86_64` systems with our kernel, so our target specification wil
```json
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
@@ -195,7 +195,7 @@ Our target specification file now looks like this:
```json
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
@@ -260,7 +260,7 @@ That's where the [`build-std` feature] of cargo comes in. It allows to recompile
[`build-std` feature]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std
[nightly Rust compilers]: #installing-rust-nightly
To use the feature, we need to create a [cargo configuration] file at `.cargo/config.toml` with the following content:
To use the feature, we need to create a local [cargo configuration] file at `.cargo/config.toml` (the `.cargo` folder should be next to your `src` folder) with the following content:
```toml
# in .cargo/config.toml
@@ -403,14 +403,14 @@ Instead of writing our own bootloader, which is a project on its own, we use the
# in Cargo.toml
[dependencies]
bootloader = "0.9.23"
bootloader = "0.9"
```
Adding the bootloader as a dependency is not enough to actually create a bootable disk image. The problem is that we need to link our kernel with the bootloader after compilation, but cargo has no support for [post-build scripts].
[post-build scripts]: https://github.com/rust-lang/cargo/issues/545
To solve this problem, we created a tool named `bootimage` that first compiles the kernel and bootloader, and then links them together to create a bootable disk image. To install the tool, execute the following command in your terminal:
To solve this problem, we created a tool named `bootimage` that first compiles the kernel and bootloader, and then links them together to create a bootable disk image. To install the tool, go into your home directory (or any directory outside of your cargo project) and execute the following command in your terminal:
```
cargo install bootimage
@@ -418,7 +418,7 @@ cargo install bootimage
For running `bootimage` and building the bootloader, you need to have the `llvm-tools-preview` rustup component installed. You can do so by executing `rustup component add llvm-tools-preview`.
After installing `bootimage` and adding the `llvm-tools-preview` component, we can create a bootable disk image by executing:
After installing `bootimage` and adding the `llvm-tools-preview` component, you can create a bootable disk image by going back into your cargo project directory and executing:
```
> cargo bootimage

View File

@@ -119,7 +119,7 @@ Cargo поддерживает различные целевые системы
```json
{
"llvm-target": "x86_64-unknown-linux-gnu",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
@@ -142,7 +142,7 @@ Cargo поддерживает различные целевые системы
```json
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
@@ -202,7 +202,7 @@ Cargo поддерживает различные целевые системы
```json
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
@@ -411,7 +411,7 @@ pub extern "C" fn _start() -> ! {
# in Cargo.toml
[dependencies]
bootloader = "0.9.23"
bootloader = "0.9"
```
Добавление загрузчика в качестве зависимости недостаточно для создания загрузочного образа диска. Проблема в том, что нам нужно связать наше ядро с загрузчиком после компиляции, но в cargo нет поддержки [скриптов после сборки][post-build scripts].

View File

@@ -15,7 +15,7 @@ translation_contributors = ["JiangengDong"]
在这篇文章中,我们将基于 **x86架构**the x86 architecture使用 Rust 语言,编写一个最小化的 64 位内核。我们将从上一章中构建的[独立式可执行程序][freestanding-rust-binary]开始,构建自己的内核;它将向显示器打印字符串,并能被打包为一个能够引导启动的**磁盘映像**disk image
[freestanding Rust binary]: @/edition-2/posts/01-freestanding-rust-binary/index.md
[freestanding-rust-binary]: @/edition-2/posts/01-freestanding-rust-binary/index.md
<!-- more -->
@@ -92,7 +92,7 @@ Nightly 版本的编译器允许我们在源码的开头插入**特性标签**
```json
{
"llvm-target": "x86_64-unknown-linux-gnu",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
@@ -112,7 +112,7 @@ Nightly 版本的编译器允许我们在源码的开头插入**特性标签**
```json
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
@@ -157,14 +157,16 @@ Nightly 版本的编译器允许我们在源码的开头插入**特性标签**
禁用 SIMD 产生的一个问题是,`x86_64` 架构的浮点数指针运算默认依赖于 SIMD 寄存器。我们的解决方法是,启用 `soft-float` 特征,它将使用基于整数的软件功能,模拟浮点数指针运算。
为了让读者的印象更清晰,我们撰写了一篇关于 [禁用 SIMD][disabling SIMD](@/edition-2/posts/02-minimal-rust-kernel/disable-simd/index.zh-CN.md) 的短文。
为了让读者的印象更清晰,我们撰写了一篇关于 [禁用 SIMD][disabling SIMD] 的短文。
[disabling SIMD]: @/edition-2/posts/02-minimal-rust-kernel/disable-simd/index.zh-CN.md
现在,我们将各个配置项整合在一起。我们的目标配置清单应该长这样:
```json
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
@@ -225,10 +227,10 @@ error[E0463]: can't find crate for `core`
#### `build-std` 选项
此时就到了cargo中 [`build-std` 特性][`build-std` feature] 登场的时刻,该特性允许你按照自己的需要重编译 `core` 等标准crate而不需要使用Rust安装程序内置的预编译版本。 但是该特性是全新的功能,到目前为止尚未完全完成,所以它被标记为 "unstable" 且仅被允许在 [nightly Rust 编译器][nightly Rust compilers] 环境下调用。
此时就到了cargo中 [`build-std` 特性][`build-std` feature] 登场的时刻,该特性允许你按照自己的需要重编译 `core` 等标准crate而不需要使用Rust安装程序内置的预编译版本。 但是该特性是全新的功能,到目前为止尚未完全完成,所以它被标记为 "unstable" 且仅被允许在 [Nightly Rust 编译器][Nightly Rust compilers] 环境下调用。
[`build-std` feature]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std
[nightly Rust compilers]: #安装 Nightly Rust
[Nightly Rust compilers]:https://os.phil-opp.com/zh-CN/minimal-rust-kernel/#an-zhuang-nightly-rust
要启用该特性,你需要创建一个 [cargo 配置][cargo configuration] 文件,即 `.cargo/config.toml`,并写入以下语句:
@@ -347,7 +349,7 @@ pub extern "C" fn _start() -> ! {
使用 `unsafe` 语句块要求程序员有足够的自信,所以必须强调的一点是,**肆意使用 unsafe 语句块并不是 Rust 编程的一贯方式**。在缺乏足够经验的前提下,直接在 `unsafe` 语句块内操作裸指针,非常容易把事情弄得很糟糕;比如,在不注意的情况下,我们很可能会意外地操作缓冲区以外的内存。
在这样的前提下,我们希望最小化 `unsafe ` 语句块的使用。使用 Rust 语言,我们能够将不安全操作将包装为一个安全的抽象模块。举个例子,我们可以创建一个 VGA 缓冲区类型,把所有的不安全语句封装起来,来确保从类型外部操作时,无法写出不安全的代码:通过这种方式,我们只需要最少的 `unsafe` 语句块来确保我们不破坏**内存安全**[memory safety](https://en.wikipedia.org/wiki/Memory_safety))。在下一篇文章中,我们将会创建这样的 VGA 缓冲区封装。
在这样的前提下,我们希望最小化 `unsafe` 语句块的使用。使用 Rust 语言,我们能够将不安全操作将包装为一个安全的抽象模块。举个例子,我们可以创建一个 VGA 缓冲区类型,把所有的不安全语句封装起来,来确保从类型外部操作时,无法写出不安全的代码:通过这种方式,我们只需要最少的 `unsafe` 语句块来确保我们不破坏**内存安全**[memory safety](https://en.wikipedia.org/wiki/Memory_safety))。在下一篇文章中,我们将会创建这样的 VGA 缓冲区封装。
## 启动内核
@@ -363,7 +365,7 @@ pub extern "C" fn _start() -> ! {
# in Cargo.toml
[dependencies]
bootloader = "0.9.23"
bootloader = "0.9"
```
只添加引导程序为依赖项,并不足以创建一个可引导的磁盘映像;我们还需要内核编译完成之后,将内核和引导程序组合在一起。然而,截至目前,原生的 cargo 并不支持在编译完成后添加其它步骤(详见[这个 issue](https://github.com/rust-lang/cargo/issues/545))。
@@ -396,7 +398,7 @@ cargo install bootimage
### 在 QEMU 中启动内核
现在我们可以在虚拟机中启动内核了。为了在[ QEMU](https://www.qemu.org/) 中启动内核,我们使用下面的命令:
现在我们可以在虚拟机中启动内核了。为了在[QEMU](https://www.qemu.org/) 中启动内核,我们使用下面的命令:
```bash
> qemu-system-x86_64 -drive format=raw,file=target/x86_64-blog_os/debug/bootimage-blog_os.bin
@@ -426,7 +428,7 @@ warning: TCG doesn't support requested feature: CPUID.01H:ECX.vmx [bit 5]
要让在 QEMU 中运行内核更轻松,我们可以设置在 cargo 配置文件中设置 `runner` 配置项:
```toml
# in .cargo/config
# in .cargo/config.toml
[target.'cfg(target_os = "none")']
runner = "bootimage runner"

View File

@@ -9,7 +9,7 @@ chapter = "Bare Bones"
# Please update this when updating the translation
translation_based_on_commit = "bd6fbcb1c36705b2c474d7fcee387bfea1210851"
# GitHub usernames of the people that translated this post
translators = ["woodyZootopia", "JohnTitor"]
translators = ["swnakamura", "JohnTitor"]
+++
[VGAテキストモード][VGA text mode]は画面にテキストを出力するシンプルな方法です。この記事では、すべてのunsafeな要素を別のモジュールにカプセル化することで、それを安全かつシンプルに扱えるようにするインターフェースを作ります。また、Rustの[フォーマッティングマクロ][formatting macros]のサポートも実装します。

View File

@@ -169,7 +169,7 @@ pub struct Writer {
}
```
我们将让这个 `Writer` 类型将字符写入屏幕的最后一行,并在一行写满或接收到换行符 `\n` 的时候,将所有的字符向上位移一行。`column_position` 变量将跟踪光标在最后一行的位置。当前字符的前景和背景色将由 `color_code` 变量指定;另外,我们存入一个 VGA 字符缓冲区的可变借用到`buffer`变量中。需要注意的是,这里我们对借用使用**显式生命周期**[explicit lifetime](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#lifetime-annotation-syntax)),告诉编译器这个借用在何时有效:我们使用** `'static` 生命周期 **['static lifetime](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#the-static-lifetime)),意味着这个借用应该在整个程序的运行期间有效;这对一个全局有效的 VGA 字符缓冲区来说,是非常合理的。
我们将让这个 `Writer` 类型将字符写入屏幕的最后一行,并在一行写满或接收到换行符 `\n` 的时候,将所有的字符向上位移一行。`column_position` 变量将跟踪光标在最后一行的位置。当前字符的前景和背景色将由 `color_code` 变量指定;另外,我们存入一个 VGA 字符缓冲区的可变借用到`buffer`变量中。需要注意的是,这里我们对借用使用**显式生命周期**[explicit lifetime](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#lifetime-annotation-syntax)),告诉编译器这个借用在何时有效:我们使用 `'static` 生命周期(['static lifetime](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#the-static-lifetime)),意味着这个借用应该在整个程序的运行期间有效;这对一个全局有效的 VGA 字符缓冲区来说,是非常合理的。
### 打印字符

View File

@@ -77,7 +77,7 @@ error[E0463]: can't find crate for `test`
#![test_runner(crate::test_runner)]
#[cfg(test)]
fn test_runner(tests: &[&dyn Fn()]) {
pub fn test_runner(tests: &[&dyn Fn()]) {
println!("Running {} tests", tests.len());
for test in tests {
test();

View File

@@ -9,7 +9,7 @@ chapter = "Bare Bones"
# Please update this when updating the translation
translation_based_on_commit = "dce5c9825bd4e7ea6c9530e999c9d58f80c585cc"
# GitHub usernames of the people that translated this post
translators = ["woodyZootopia", "JohnTitor"]
translators = ["swnakamura", "JohnTitor"]
+++
この記事では、`no_std`な実行環境における<ruby>単体テスト<rp> (</rp><rt>unit test</rt><rp>) </rp></ruby>と<ruby>結合テスト<rp> (</rp><rt>integration test</rt><rp>) </rp></ruby>について学びます。Rustではカスタムテストフレームワークがサポートされているので、これを使ってカーネルの中でテスト関数を実行します。QEMUの外へとテストの結果を通知するため、QEMUと`bootimage`の様々な機能を使います。
@@ -81,7 +81,7 @@ error[E0463]: can't find crate for `test`
#![test_runner(crate::test_runner)]
#[cfg(test)]
fn test_runner(tests: &[&dyn Fn()]) {
pub fn test_runner(tests: &[&dyn Fn()]) {
println!("Running {} tests", tests.len());
for test in tests {
test();

View File

@@ -73,7 +73,7 @@ To implement a custom test framework for our kernel, we add the following to our
#![test_runner(crate::test_runner)]
#[cfg(test)]
fn test_runner(tests: &[&dyn Fn()]) {
pub fn test_runner(tests: &[&dyn Fn()]) {
println!("Running {} tests", tests.len());
for test in tests {
test();

View File

@@ -77,7 +77,7 @@ error[E0463]: can't find crate for `test`
#![test_runner(crate::test_runner)]
#[cfg(test)]
fn test_runner(tests: &[&dyn Fn()]) {
pub fn test_runner(tests: &[&dyn Fn()]) {
println!("Running {} tests", tests.len());
for test in tests {
test();
@@ -989,7 +989,7 @@ harness = false
#![no_main]
use core::panic::PanicInfo;
use blog_os::{QemuExitCode, exit_qemu, serial_println};
use blog_os::{QemuExitCode, exit_qemu, serial_println, serial_print};
#[no_mangle]
pub extern "C" fn _start() -> ! {

View File

@@ -9,7 +9,7 @@ chapter = "Interrupts"
# Please update this when updating the translation
translation_based_on_commit = "a8a6b725cff2e485bed76ff52ac1f18cec08cc7b"
# GitHub usernames of the people that translated this post
translators = ["woodyZootopia"]
translators = ["swnakamura"]
+++
CPU例外は、例えば無効なメモリアドレスにアクセスしたときやゼロ除算したときなど、様々なミスによって発生します。それらに対処するために、ハンドラ関数を提供する **<ruby>割り込み記述子表<rp> (</rp><rt>interrupt descriptor table</rt><rp>) </rp></ruby>** を設定しなくてはなりません。この記事を読み終わる頃には、私達のカーネルは[ブレークポイント例外][breakpoint exceptions]を捕捉し、その後通常の実行を継続できるようになっているでしょう。

View File

@@ -9,7 +9,7 @@ chapter = "Interrupts"
# Please update this when updating the translation
translation_based_on_commit = "81d4f49f153eb5f390681f5c13018dd2aa6be0b1"
# GitHub usernames of the people that translated this post
translators = ["shimomura1004", "woodyZootopia"]
translators = ["shimomura1004", "swnakamura"]
+++
この記事では、ハードウェア割り込みを正しく CPU に転送するためにプログラム可能な割り込みコントローラの設定を行います。これらの割り込みに対処するため、例外ハンドラのときに行ったのと同じように割り込み記述子表に新しいエントリを追加しなくてはいけません。ここでは周期タイマ割り込みの受け方と、キーボードからの入力の受け方を学びます。

View File

@@ -9,7 +9,7 @@ chapter = "Memory Management"
# Please update this when updating the translation
translation_based_on_commit = "3315bfe2f63571f5e6e924d58ed32afd8f39f892"
# GitHub usernames of the people that translated this post
translators = ["woodyZootopia", "JohnTitor"]
translators = ["swnakamura", "JohnTitor"]
+++
この記事では**ページング**を紹介します。これは、私達のオペレーティングシステムにも使う、とても一般的なメモリ管理方式です。なぜメモリの分離が必要なのか、**セグメンテーション**がどういう仕組みなのか、**仮想メモリ**とは何なのか、ページングがいかにしてメモリ<ruby>断片化<rp> (</rp><rt>フラグメンテーション</rt><rp>) </rp></ruby>の問題を解決するのかを説明します。また、x86_64アーキテクチャにおける、マルチレベルページテーブルのレイアウトについても説明します。

View File

@@ -149,7 +149,7 @@ x86_64 平台使用4级页表页大小为4KiB无论层级每个页表
![Bits 012 are the page offset, bits 1221 the level 1 index, bits 2130 the level 2 index, bits 3039 the level 3 index, and bits 3948 the level 4 index](x86_64-table-indices-from-address.svg)
我们可以看到,每个表索引号占据9个字节,这当然是有道理的,每个表都有 2^9 = 512 个条目低12位用来表示内存页的偏移量2^12 bytes = 4KiB而上文提到页大小为4KiB。第48-64位毫无用处这也就意味着 x86_64 并非真正的64位因为它实际上支持48位地址。
我们可以看到,每个表索引号占据 9 个比特,这当然是有道理的,每个表都有 2^9 = 512 个条目低12位用来表示内存页的偏移量2^12 bytes = 4KiB而上文提到页大小为4KiB。第 48-64 位毫无用处,这也就意味着 x86_64 并非真正的 64 位,因为它实际上支持 48 位地址。
[5-level page table]: https://en.wikipedia.org/wiki/Intel_5-level_paging
@@ -191,7 +191,7 @@ x86_64 平台使用4级页表页大小为4KiB无论层级每个页表
- 1个4级页表
- 512个3级页表因为4级页表可以有512个条目
- 512*512个2级页表因为每个3级页表可以有512个条目
- 512*512*512个1级页表因为每个2级页表可以有512个条目
- 512\*512\*512个1级页表因为每个2级页表可以有512个条目
### 页表格式
@@ -225,7 +225,7 @@ pub struct PageTable {
| 63 | no execute | 禁止在该页中运行代码EFER寄存器中的NXE比特位必须一同被设置 |
我们可以看到仅1251位会用于存储页帧地址或页表地址其余比特都用于存储标志位或由操作系统自由使用。
其原因就是该地址总是指向一个4096比特对齐的地址、页表或者页帧的起始地址。
其原因就是该地址总是指向一个4096字节对齐的地址、页表或者页帧的起始地址。
这也就意味着0-11位始终为0没有必要存储这些东西硬件层面在使用该地址之前也会将这12位比特设置为052-63位同理因为x86_64平台仅支持52位物理地址类似于上文中提到的仅支持48位虚拟地址的原因
进一步说明一下可用的标志位:

View File

@@ -7,7 +7,7 @@ date = 2019-03-14
[extra]
chapter = "Memory Management"
translation_based_on_commit = "27ab4518acbb132e327ed4f4f0508393e9d4d684"
translators = ["woodyZootopia", "garasubo"]
translators = ["swnakamura", "garasubo"]
+++
この記事では私達のカーネルをページングに対応させる方法についてお伝えします。まずページテーブルの物理フレームにカーネルがアクセスできるようにする様々な方法を示し、それらの利点と欠点について議論します。次にアドレス変換関数を、ついで新しい<ruby>対応付け<rp> (</rp><rt>マッピング</rt><rp>) </rp></ruby>を作るための関数を実装します。
@@ -281,7 +281,7 @@ frame.map(|frame| frame.start_address() + u64::from(addr.page_offset()))
```toml
[dependencies]
bootloader = { version = "0.9.23", features = ["map_physical_memory"]}
bootloader = { version = "0.9", features = ["map_physical_memory"]}
```
この機能を有効化すると、ブートローダは物理メモリの全体を、ある未使用の仮想アドレス空間にマッピングします。この仮想アドレスの範囲をカーネルに伝えるために、ブートローダは**boot information**構造体を渡します。
@@ -291,7 +291,7 @@ bootloader = { version = "0.9.23", features = ["map_physical_memory"]}
`bootloader`クレートは、カーネルに渡されるすべての情報を格納する[`BootInfo`]構造体を定義しています。この構造体はまだ開発の初期段階にあり、将来の[対応していないsemverの][semver-incompatible]ブートローダのバージョンに更新した際には、うまく動かなくなることが予想されます。`map_physical_memory` featureが有効化されているので、いまこれは`memory_map``physical_memory_offset`という2つのフィールドを持っています
[`BootInfo`]: https://docs.rs/bootloader/0.9.3/bootloader/bootinfo/struct.BootInfo.html
[`BootInfo`]: https://docs.rs/bootloader/0.9/bootloader/bootinfo/struct.BootInfo.html
[semver-incompatible]: https://doc.rust-lang.org/stable/cargo/reference/specifying-dependencies.html#caret-requirements
- `memory_map`フィールドは、利用可能な物理メモリの情報の概要を保持しています。システムの利用可能な物理メモリがどのくらいかや、どのメモリ領域がVGAハードウェアのようなデバイスのために予約されているかをカーネルに伝えます。これらのメモリマッピングはBIOSやUEFIファームウェアから取得できますが、それが可能なのはブートのごく初期に限られます。そのため、これらをカーネルが後で取得することはできないので、ブートローダによって提供する必要があるわけです。このメモリマッピングは後で必要となります。

View File

@@ -278,7 +278,7 @@ We choose the first approach for our kernel since it is simple, platform-indepen
```toml
[dependencies]
bootloader = { version = "0.9.23", features = ["map_physical_memory"]}
bootloader = { version = "0.9", features = ["map_physical_memory"]}
```
With this feature enabled, the bootloader maps the complete physical memory to some unused virtual address range. To communicate the virtual address range to our kernel, the bootloader passes a _boot information_ structure.
@@ -287,7 +287,7 @@ With this feature enabled, the bootloader maps the complete physical memory to s
The `bootloader` crate defines a [`BootInfo`] struct that contains all the information it passes to our kernel. The struct is still in an early stage, so expect some breakage when updating to future [semver-incompatible] bootloader versions. With the `map_physical_memory` feature enabled, it currently has the two fields `memory_map` and `physical_memory_offset`:
[`BootInfo`]: https://docs.rs/bootloader/0.9.3/bootloader/bootinfo/struct.BootInfo.html
[`BootInfo`]: https://docs.rs/bootloader/0.9/bootloader/bootinfo/struct.BootInfo.html
[semver-incompatible]: https://doc.rust-lang.org/stable/cargo/reference/specifying-dependencies.html#caret-requirements
- The `memory_map` field contains an overview of the available physical memory. This tells our kernel how much physical memory is available in the system and which memory regions are reserved for devices such as the VGA hardware. The memory map can be queried from the BIOS or UEFI firmware, but only very early in the boot process. For this reason, it must be provided by the bootloader because there is no way for the kernel to retrieve it later. We will need the memory map later in this post.

View File

@@ -62,7 +62,7 @@ translation_contributors = ["liuyuran"]
在这个例子中我们看到各种直接映射的页表框架。页表的物理地址也是有效的虚拟地址这样我们就可以很容易地访问从CR3寄存器开始的各级页表。
然而它使虚拟地址空间变得杂乱无章并使寻找较大尺寸的连续内存区域更加困难。例如想象一下我们想在上述图形中创建一个大小为1000&nbsp;KiB的虚拟内存区域例如 [memory-mapping a file] . 我们不能在`28 KiB`处开始区域,因为它将与`1004 KiB`处已经映射的页面相撞。所以我们必须进一步寻找,直到找到一个足够大的未映射区域,例如在`1008 KiB`。这是一个类似于[segmentation]的碎片化问题。
然而它使虚拟地址空间变得杂乱无章并使寻找较大尺寸的连续内存区域更加困难。例如想象一下我们想在上述图形中创建一个大小为1000&nbsp;KiB的虚拟内存区域例如 [memory-mapping a file]我们不能在`28 KiB`处开始区域,因为它将与`1004 KiB`处已经映射的页面相撞。所以我们必须进一步寻找,直到找到一个足够大的未映射区域,例如在`1008 KiB`。这是一个类似于[segmentation]的碎片化问题。
[memory-mapping a file]: https://en.wikipedia.org/wiki/Memory-mapped_file
[segmentation]: @/edition-2/posts/08-paging-introduction/index.md#fragmentation
@@ -277,18 +277,18 @@ frame.map(|frame| frame.start_address() + u64::from(addr.page_offset()))
所有这些方法的设置都需要对页表进行修改。例如需要创建物理内存的映射或者需要对4级表的一个条目进行递归映射。问题是如果没有访问页表的现有方法我们就无法创建这些所需的映射。
这意味着我们需要 bootloader 的帮助bootloader 创建了内核运行的页表。Bootloader 可以访问页表,所以它可以创建内核需要的任何映射。在目前的实现中,"bootloader "工具箱支持上述两种方法,通过 [cargo features] 进行控制。
这意味着我们需要 bootloader 的帮助bootloader 创建了内核运行的页表。Bootloader 可以访问页表,所以它可以创建内核需要的任何映射。在目前的实现中,bootloader 工具箱支持上述两种方法,通过 [cargo features] 进行控制。
[cargo features]: https://doc.rust-lang.org/cargo/reference/features.html#the-features-section
- `map_physical_memory` 功能将某处完整的物理内存映射到虚拟地址空间。因此,内核可以访问所有的物理内存,并且可以遵循[_映射完整物理内存_](#ying-she-wan-zheng-de-wu-li-nei-cun)的方法。
- 有了 "recursive_page_table "功能bootloader会递归地映射4级page table的一个条目。这允许内核访问页表如[_递归页表_](#di-gui-ye-biao)部分所述。
- 有了 recursive_page_table 功能bootloader会递归地映射4级page table的一个条目。这允许内核访问页表如[_递归页表_](#di-gui-ye-biao)部分所述。
我们为我们的内核选择了第一种方法,因为它很简单,与平台无关,而且更强大(它还允许访问非页表框架)。为了启用所需的引导程序支持,我们在 "引导程序 "的依赖中加入了 "map_physical_memory"功能。
我们为我们的内核选择了第一种方法,因为它很简单,与平台无关,而且更强大(它还允许访问非页表框架)。为了启用所需的引导程序支持,我们在 引导程序 的依赖中加入了 "map_physical_memory"功能。
```toml
[dependencies]
bootloader = { version = "0.9.23", features = ["map_physical_memory"]}
bootloader = { version = "0.9", features = ["map_physical_memory"]}
```
启用这个功能后bootloader 将整个物理内存映射到一些未使用的虚拟地址范围。为了将虚拟地址范围传达给我们的内核bootloader 传递了一个 _启动信息_ 结构。
@@ -296,16 +296,16 @@ bootloader = { version = "0.9.23", features = ["map_physical_memory"]}
### 启动信息
Bootloader "板块定义了一个[`BootInfo`]结构,包含了它传递给我们内核的所有信息。这个结构还处于早期阶段,所以在更新到未来的[semver-incompatible]bootloader版本时可能会出现一些故障。在启用 "map_physical_memory "功能后,它目前有两个字段 "memory_map "和 "physical_memory_offset"。
`Bootloader` 板块定义了一个[`BootInfo`]结构,包含了它传递给我们内核的所有信息。这个结构还处于早期阶段,所以在更新到未来的 [semver-incompatible] bootloader 版本时,可能会出现一些故障。在启用 "map_physical_memory" 功能后,它目前有两个字段 "memory_map" 和 "physical_memory_offset"。
[`BootInfo`]: https://docs.rs/bootloader/0.9.3/bootloader/bootinfo/struct.BootInfo.html
[`BootInfo`]: https://docs.rs/bootloader/0.9/bootloader/bootinfo/struct.BootInfo.html
[semver-incompatible]: https://doc.rust-lang.org/stable/cargo/reference/specifying-dependencies.html#caret-requirements
- `memory_map`字段包含了可用物理内存的概览。它告诉我们的内核系统中有多少物理内存可用哪些内存区域被保留给设备如VGA硬件。内存图可以从BIOS或UEFI固件中查询但只能在启动过程的早期查询。由于这个原因它必须由引导程序提供因为内核没有办法在以后检索到它。在这篇文章的后面我们将需要内存图。
- physical_memory_offset`告诉我们物理内存映射的虚拟起始地址。通过把这个偏移量加到物理地址上,我们得到相应的虚拟地址。这使得我们可以从我们的内核中访问任意的物理内存。
- `physical_memory_offset`告诉我们物理内存映射的虚拟起始地址。通过把这个偏移量加到物理地址上,我们得到相应的虚拟地址。这使得我们可以从我们的内核中访问任意的物理内存。
- 这个物理内存偏移可以通过在Cargo.toml中添加一个`[package.metadata.bootloader]`表并设置`physical-memory-offset = "0x0000f00000000000"`或任何其他值来定制。然而请注意如果bootloader遇到物理地址值开始与偏移量以外的空间重叠也就是说它以前会映射到其他早期的物理地址的区域就会出现恐慌。所以一般来说这个值越高>1 TiB越好。
Bootloader将 "BootInfo "结构以"&'static BootInfo "参数的形式传递给我们的内核,并传递给我们的"_start "函数。我们的函数中还没有声明这个参数,所以让我们添加它。
Bootloader将 `BootInfo` 结构以 `&'static BootInfo`参数的形式传递给我们的内核,并传递给我们的`_start`函数。我们的函数中还没有声明这个参数,所以让我们添加它。
```rust
// in src/main.rs
@@ -415,7 +415,7 @@ pub unsafe fn active_level_4_table(physical_memory_offset: VirtAddr)
首先,我们从`CR3`寄存器中读取活动的4级表的物理帧。然后我们取其物理起始地址将其转换为`u64`,并将其添加到`physical_memory_offset`中,得到页表框架映射的虚拟地址。最后,我们通过`as_mut_ptr`方法将虚拟地址转换为`*mut PageTable`原始指针,然后不安全地从它创建一个`&mut PageTable`引用。我们创建一个`&mut`引用,而不是`&`引用,因为我们将在本篇文章的后面对页表进行突变。
我们不需要在这里使用不安全块因为Rust把一个 "不安全 "fn的完整主体当作一个大的 "不安全 "块。这使得我们的代码更加危险,因为我们可能会在不知不觉中在前几行引入不安全操作。这也使得在安全操作之间发现不安全操作的难度大大增加。有一个[RFC]https://github.com/rust-lang/rfcs/pull/2585可以改变这种行为。
我们不需要在这里使用不安全块因为Rust把一个 `不安全 fn` 的完整主体当作一个大的 `不安全`块。这使得我们的代码更加危险,因为我们可能会在不知不觉中在前几行引入不安全操作。这也使得在安全操作之间发现不安全操作的难度大大增加。有一个[RFC](https://github.com/rust-lang/rfcs/pull/2585)可以改变这种行为。
现在我们可以用这个函数来打印第4级表格的条目。
@@ -621,11 +621,11 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! {
[`translate_addr`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/mapper/trait.Translate.html#method.translate_addr
[`translate`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/mapper/trait.Translate.html#tymethod.translate
特质只定义接口,不提供任何实现。`x86_64`板块目前提供了三种类型来实现不同要求的特征。[`OffsetPageTable`] 类型假设完整的物理内存被映射到虚拟地址空间的某个偏移处。[`MappedPageTable`]更灵活一些。它只要求每个页表帧在一个可计算的地址处被映射到虚拟地址空间。最后,[递归页表]类型可以用来通过[递归页表](#di-gui-ye-biao)访问页表框架。
特质只定义接口,不提供任何实现。`x86_64`板块目前提供了三种类型来实现不同要求的特征。[`OffsetPageTable`] 类型假设完整的物理内存被映射到虚拟地址空间的某个偏移处。[`MappedPageTable`]更灵活一些。它只要求每个页表帧在一个可计算的地址处被映射到虚拟地址空间。最后,[`递归页表`]类型可以用来通过[递归页表](#di-gui-ye-biao)访问页表框架。
[`OffsetPageTable`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/mapper/struct.OffsetPageTable.html
[`MappedPageTable`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/mapper/struct.MappedPageTable.html
[`RecursivePageTable`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/mapper/struct.RecursivePageTable.html
[`递归页表`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/mapper/struct.RecursivePageTable.html
在我们的例子中bootloader在`physical_memory_offset`变量指定的虚拟地址上映射完整的物理内存,所以我们可以使用`OffsetPageTable`类型。为了初始化它,我们在`memory`模块中创建一个新的`init`函数。
@@ -648,11 +648,11 @@ unsafe fn active_level_4_table(physical_memory_offset: VirtAddr)
{}
```
该函数接受 "physical_memory_offset "作为参数,并返回一个新的 "OffsetPageTable "实例,该实例具有 "静态 "寿命。这意味着该实例在我们内核的整个运行时间内保持有效。在函数体中,我们首先调用 "active_level_4_table "函数来获取4级页表的可变引用。然后我们用这个引用调用[`OffsetPageTable::new`] 函数。作为第二个参数,`new'函数希望得到物理内存映射开始的虚拟地址,该地址在`physical_memory_offset'变量中给出。
该函数接受 "physical_memory_offset "作为参数,并返回一个新的 "OffsetPageTable "实例,该实例具有 "静态 "寿命。这意味着该实例在我们内核的整个运行时间内保持有效。在函数体中,我们首先调用 "active_level_4_table "函数来获取4级页表的可变引用。然后我们用这个引用调用[`OffsetPageTable::new`] 函数。作为第二个参数,`new`函数希望得到物理内存映射开始的虚拟地址,该地址在`physical_memory_offset'变量中给出。
[`OffsetPageTable::new`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/mapper/struct.OffsetPageTable.html#method.new
从现在开始,`active_level_4_table'函数只能从`init'函数中调用,因为它在多次调用时很容易导致别名的可变引用,这可能导致未定义的行为。出于这个原因,我们通过删除`pub`指定符使该函数成为私有的。
从现在开始,`active_level_4_table`函数只能从`init`函数中调用,因为它在多次调用时很容易导致别名的可变引用,这可能导致未定义的行为。出于这个原因,我们通过删除`pub`指定符使该函数成为私有的。
我们现在可以使用`Translate::translate_addr`方法而不是我们自己的`memory::translate_addr`函数。我们只需要在`kernel_main`中修改几行。
@@ -760,9 +760,9 @@ pub fn create_example_mapping(
#### 一个假的 `FrameAllocator`
为了能够调用`create_example_mapping`,我们需要首先创建一个实现`FrameAllocator`特质的类型。如上所述,如果`map_to'需要新的页表,该特质负责为其分配框架。
为了能够调用`create_example_mapping`,我们需要首先创建一个实现`FrameAllocator`特质的类型。如上所述,如果`map_to`需要新的页表,该特质负责为其分配框架。
让我们从简单的情况开始,假设我们不需要创建新的页面表。对于这种情况,一个总是返回 "无 "的框架分配器就足够了。我们创建这样一个`空框架分配器'来测试我们的映射函数。
让我们从简单的情况开始,假设我们不需要创建新的页面表。对于这种情况,一个总是返回 "无 "的框架分配器就足够了。我们创建这样一个`空框架分配器`来测试我们的映射函数。
```rust
// in src/memory.rs
@@ -787,7 +787,7 @@ unsafe impl FrameAllocator<Size4KiB> for EmptyFrameAllocator {
图中左边是虚拟地址空间,右边是物理地址空间,中间是页表。页表被存储在物理内存框架中,用虚线表示。虚拟地址空间包含一个地址为`0x803fe00000`的单一映射页用蓝色标记。为了将这个页面转换到它的框架CPU在4级页表上行走直到到达地址为36&nbsp;KiB的框架。
此外该图用红色显示了VGA文本缓冲区的物理帧。我们的目标是使用`create_example_mapping`函数将一个先前未映射的虚拟页映射到这个帧。由于我们的`EmptyFrameAllocator'总是返回`None',我们想创建映射,这样就不需要分配器提供额外的帧。这取决于我们为映射选择的虚拟页。
此外该图用红色显示了VGA文本缓冲区的物理帧。我们的目标是使用`create_example_mapping`函数将一个先前未映射的虚拟页映射到这个帧。由于我们的`EmptyFrameAllocator`总是返回`None`,我们想创建映射,这样就不需要分配器提供额外的帧。这取决于我们为映射选择的虚拟页。
图中显示了虚拟地址空间中的两个候选页,都用黄色标记。一个页面在地址`0x803fdfd000`比映射的页面蓝色早3页。虽然4级和3级页表的索引与蓝色页相同但2级和1级的索引不同见[上一篇][页表-索引]。2级表的不同索引意味着这个页面使用了一个不同的1级表。由于这个1级表还不存在如果我们选择该页作为我们的例子映射我们就需要创建它这就需要一个额外的未使用的物理帧。相比之下地址为`0x803fe02000`的第二个候选页就没有这个问题因为它使用了与蓝色页面相同的1级页表。因此所有需要的页表都已经存在。
@@ -828,9 +828,9 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! {
我们首先通过调用 "create_example_mapping "函数为地址为0的页面创建映射并为 "mapper "和 "frame_allocator "实例提供一个可变的引用。这将页面映射到VGA文本缓冲区框架所以我们应该在屏幕上看到对它的任何写入。
然后我们将页面转换为原始指针,并写一个值到偏移量`400`。我们不写到页面的开始因为VGA缓冲区的顶行被下一个`println`直接移出了屏幕。我们写值`0x_f021_f077_f065_f04e`,表示白色背景上的字符串 _"New!"_ 。正如我们[在_"VGA文本模式"_帖子中]所学到的对VGA缓冲区的写入应该是不稳定的所以我们使用[`write_volatile`]方法。
然后我们将页面转换为原始指针,并写一个值到偏移量`400`。我们不写到页面的开始因为VGA缓冲区的顶行被下一个`println`直接移出了屏幕。我们写值`0x_f021_f077_f065_f04e`,表示白色背景上的字符串 _"New!"_ 。正如我们[在 _"VGA文本模式"_ 帖子中]所学到的对VGA缓冲区的写入应该是不稳定的所以我们使用[`write_volatile`]方法。
[在_"VGA文本模式"_帖子中]: @/edition-2/posts/03-vga-text-buffer/index.md#volatile
[在 _"VGA文本模式"_ 帖子中]: @/edition-2/posts/03-vga-text-buffer/index.md#volatile
[`write_volatile`]: https://doc.rust-lang.org/std/primitive.pointer.html#method.write_volatile
当我们在QEMU中运行它时我们看到以下输出。
@@ -923,7 +923,7 @@ impl BootInfoFrameAllocator {
这个函数使用迭代器组合方法将初始的`MemoryMap`转化为可用的物理帧的迭代器。
- 首先,我们调用`iter`方法,将内存映射转换为[`MemoryRegion`]s的迭代器。
- 首先,我们调用`iter`方法,将内存映射转换为多个[`MemoryRegion`]的迭代器。
- 然后我们使用[`filter`]方法跳过任何保留或其他不可用的区域。Bootloader为它创建的所有映射更新了内存地图所以被我们的内核使用的帧代码、数据或堆栈或存储启动信息的帧已经被标记为`InUse`或类似的。因此,我们可以确定 "可使用" 的帧没有在其他地方使用。
- 之后,我们使用[`map`]组合器和Rust的[range语法]将我们的内存区域迭代器转化为地址范围的迭代器。
- 接下来,我们使用[`flat_map`]将地址范围转化为帧起始地址的迭代器,使用[`step_by`]选择每4096个地址。由于4096字节=4&nbsp;KiB是页面大小我们得到了每个帧的起始地址。Bootloader对所有可用的内存区域进行页对齐所以我们在这里不需要任何对齐或舍入代码。通过使用[`flat_map`]而不是`map`,我们得到一个`Iterator<Item = u64>`而不是`Iterator<Item = Iterator<Item = u64>`。
@@ -961,7 +961,7 @@ unsafe impl FrameAllocator<Size4KiB> for BootInfoFrameAllocator {
[`Iterator::nth`]: https://doc.rust-lang.org/core/iter/trait.Iterator.html#method.nth
这个实现不是很理想,因为它在每次分配时都会重新创建`usable_frame`分配器。最好的办法是直接将迭代器存储为一个结构域。这样我们就不需要`nth`方法了,可以在每次分配时直接调用[`next`]。这种方法的问题是,目前不可能将 "impl Trait "类型存储在一个结构字段中。当[_name existential types_]完全实现时,它可能会在某一天发挥作用。
这个实现不是很理想,因为它在每次分配时都会重新创建`usable_frame`分配器。最好的办法是直接将迭代器存储为一个结构域。这样我们就不需要`nth`方法了,可以在每次分配时直接调用[`next`]。这种方法的问题是,目前不可能将 "impl Trait "类型存储在一个结构字段中。当 [_named existential types_] 完全实现时,它可能会在某一天发挥作用。
[`next`]: https://doc.rust-lang.org/core/iter/trait.Iterator.html#tymethod.next
[_named existential types_]: https://github.com/rust-lang/rfcs/pull/2071
@@ -992,7 +992,7 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! {
虽然我们的`create_example_mapping`函数只是一些示例代码,但我们现在能够为任意的页面创建新的映射。这对于分配内存或在未来的文章中实现多线程是至关重要的。
此时,我们应该再次删除`create_example_mapping`函数,以避免意外地调用未定义的行为,正如[上面](#create_example_mapping 函数)所解释的那样。
此时,我们应该再次删除`create_example_mapping`函数,以避免意外地调用未定义的行为,正如 [上面](#create-example-mapping-han-shu) 所解释的那样。
## 总结

View File

@@ -9,7 +9,7 @@ chapter = "Memory Management"
# Please update this when updating the translation
translation_based_on_commit = "afeed7477bb19a29d94a96b8b0620fd241b0d55f"
# GitHub usernames of the people that translated this post
translators = ["woodyZootopia", "garasubo"]
translators = ["swnakamura", "garasubo"]
+++
この記事では、私たちのカーネルにヒープ<ruby>割り当て<rp> (</rp><rt>アロケーション</rt><rp>) </rp></ruby>の機能を追加します。まず動的メモリの基礎を説明し、どのようにして借用チェッカがありがちなアロケーションエラーを防いでくれるのかを示します。その後Rustの基本的なアロケーションインターフェースを実装し、ヒープメモリ領域を作成し、アロケータクレートを設定します。この記事を終える頃には、Rustに組み込みの`alloc`クレートのすべてのアロケーション・コレクション型が私たちのカーネルで利用可能になっているでしょう。

File diff suppressed because it is too large Load Diff

View File

@@ -570,11 +570,26 @@ use super::align_up;
use core::mem;
impl LinkedListAllocator {
/// Aligns a given address up to a multiple of
/// `mem::align_of::<ListNode>, which is 8 bytes
/// for x86_64.
fn align_to_list_node(addr: usize) -> usize {
align_up(addr, mem::align_of::<ListNode>())
}
/// Checks to make sure that alignment and size conditions
/// to store a `ListNode` are guaranteed for a given region
/// [addr, addr + size).
fn is_valid_region(addr: usize, size: usize) -> bool {
addr == Self::align_to_list_node(addr) &&
size >= mem::size_of::<ListNode>()
}
/// Adds the given memory region to the front of the list.
unsafe fn add_free_region(&mut self, addr: usize, size: usize) {
// ensure that the freed region is capable of holding ListNode
assert_eq!(align_up(addr, mem::align_of::<ListNode>()), addr);
assert!(size >= mem::size_of::<ListNode>());
// ensure that the region is capable of holding ListNode
assert!(Self::is_valid_region(addr, size));
// create a new list node and append it at the start of the list
let mut node = ListNode::new(size);
@@ -664,18 +679,34 @@ impl LinkedListAllocator {
fn alloc_from_region(region: &ListNode, size: usize, align: usize)
-> Result<usize, ()>
{
let alloc_start = align_up(region.start_addr(), align);
let alloc_end = alloc_start.checked_add(size).ok_or(())?;
let mut alloc_start = align_up(region.start_addr(), align);
if alloc_start != region.start_addr() {
// We have some potential wasted space at the beginning of the region
// that cannot be used due to alignment constraints. We want to be
// able to recycle this space as well in our linked list. Otherwise
// we may never be able to reclaim this space.
// We need to ensure that there is enough space up front for a `ListNode`
// so we need to realign alloc_start after `size_of::<ListNode>` bytes
// from `region.start_addr()`.
// In practice, this can occur in x86_64 only when align is set to 16 bytes.
let pushed_start_addr = region
.start_addr()
.checked_add(mem::size_of::<ListNode>())
.ok_or(())?;
alloc_start = align_up(pushed_start_addr, align);
}
let alloc_end = alloc_start.checked_add(size).ok_or(())?;
if alloc_end > region.end_addr() {
// region too small
return Err(());
}
let excess_size = region.end_addr() - alloc_end;
if excess_size > 0 && excess_size < mem::size_of::<ListNode>() {
// rest of region too small to hold a ListNode (required because the
// allocation splits the region in a used and a free part)
if excess_size > 0 && !Self::is_valid_region(alloc_end, excess_size) {
// Improper alignment or the rest of region too small to hold a ListNode (required
// because the allocation splits the region into a used and up to two free parts).
return Err(());
}
@@ -687,7 +718,16 @@ impl LinkedListAllocator {
First, the function calculates the start and end address of a potential allocation, using the `align_up` function we defined earlier and the [`checked_add`] method. If an overflow occurs or if the end address is behind the end address of the region, the allocation doesn't fit in the region and we return an error.
The function performs a less obvious check after that. This check is necessary because most of the time an allocation does not fit a suitable region perfectly, so that a part of the region remains usable after the allocation. This part of the region must store its own `ListNode` after the allocation, so it must be large enough to do so. The check verifies exactly that: either the allocation fits perfectly (`excess_size == 0`) or the excess size is large enough to store a `ListNode`.
The function performs a couple of less obvious checks on top of that. When we first perform `align_up` we may get an `alloc_start` that is not the same as `region.start_addr()`. In this case, there can still be some free memory we need to keep track of between `region.start_addr()` (inclusive) to this initially aligned `alloc_start` (exclusive). We need to ensure that this region is suitable for storing a `ListNode` by performing the alignment and size checks in `is_valid_region`.
As `region.start_addr()` is guaranteed to satisfy the alignment condition of `ListNode`, we technically only need to guarantee that the size is not too small. We try and realign after accounting for this space to store one `ListNode` instance after `region.start_addr()`. This may end up pushing our end address out of our region, in which case this entire region we are checking will not be sufficient.
It is interesting to note that this situation can occur in one edge case in the 64-bit architecture we are targeting, where `align` is set to 16 bytes and `region.start_addr()` happens to be some number `16*n + 8`. `alloc_start` would then be set to `16*(n+1)`, leaving us `head_excess_size` of just 8 bytes, which would be insufficient to store the 16 bytes required for a `ListNode`.
We could also have some free memory between `alloc_end` (inclusive) to `region.end_addr()` (exclusive). Here `alloc_end` (in general) is not guaranteed to satisfy the alignment condition of `ListNode`, nor is there a guarantee that the remaining space is sufficient to store a `ListNode`. This check is necessary because most of the time an allocation does not fit a suitable region perfectly, so that a part of the region remains usable after the allocation. This part of the region must store its own `ListNode` after the allocation, so it must be large enough to do so, and it must satisfy the alignment condition, which is exactly what our `is_valid_region` method performs.
We shall soon see how we will actually modify the requested layout size and alignment in our implementation of `GlobalAlloc::alloc()` for the `LinkedListAllocator` to ensure that it additionally conforms to the alignment requirements for storing a `ListNode`. This is essential to ensure that `GlobalAllocator::dealloc()` can successfully add the region back into our linked list.
#### Implementing `GlobalAlloc`
@@ -712,10 +752,20 @@ unsafe impl GlobalAlloc for Locked<LinkedListAllocator> {
if let Some((region, alloc_start)) = allocator.find_region(size, align) {
let alloc_end = alloc_start.checked_add(size).expect("overflow");
let excess_size = region.end_addr() - alloc_end;
if excess_size > 0 {
allocator.add_free_region(alloc_end, excess_size);
let start_addr = region.start_addr();
let end_addr = region.end_addr();
let tail_excess_size = end_addr - alloc_end;
if tail_excess_size > 0 {
allocator.add_free_region(alloc_end, tail_excess_size);
}
let head_excess_size = alloc_start - start_addr;
if head_excess_size > 0 {
allocator.add_free_region(start_addr, head_excess_size);
}
alloc_start as *mut u8
} else {
ptr::null_mut()
@@ -735,7 +785,7 @@ Let's start with the `dealloc` method because it is simpler: First, it performs
The `alloc` method is a bit more complex. It starts with the same layout adjustments and also calls the [`Mutex::lock`] function to receive a mutable allocator reference. Then it uses the `find_region` method to find a suitable memory region for the allocation and remove it from the list. If this doesn't succeed and `None` is returned, it returns `null_mut` to signal an error as there is no suitable memory region.
In the success case, the `find_region` method returns a tuple of the suitable region (no longer in the list) and the start address of the allocation. Using `alloc_start`, the allocation size, and the end address of the region, it calculates the end address of the allocation and the excess size again. If the excess size is not null, it calls `add_free_region` to add the excess size of the memory region back to the free list. Finally, it returns the `alloc_start` address casted as a `*mut u8` pointer.
In the success case, the `find_region` method returns a tuple of the suitable region (no longer in the list) and the start address of the allocation. Using `alloc_start`, the allocation size, and the end address of the region, it calculates the end address of the allocation and the excess free fragments that are usable again. If the excess sizes are not zero, it calls `add_free_region` to add the excess sizes of the memory regions back to the free list. Finally, it returns the `alloc_start` address casted as a `*mut u8` pointer.
#### Layout Adjustments
@@ -797,6 +847,51 @@ many_boxes_long_lived... [ok]
This shows that our linked list allocator is able to reuse freed memory for subsequent allocations.
Additionally, to test that we are not leaking any excess segments due to `alloc_start` realignment we can add a simple test case:
```rust
// in tests/heap_allocation.rs
#[test_case]
fn head_excess_reuse() {
#[derive(Debug, Clone, PartialEq, Eq)]
#[repr(C, align(8))]
struct A(u128, u64);
assert_eq!(8, align_of::<A>());
assert_eq!(24, size_of::<A>()); // 24 % 16 = 8
#[derive(Debug, Clone, PartialEq, Eq)]
#[repr(C, align(16))]
struct B(u128, u64);
assert_eq!(16, align_of::<B>());
assert_eq!(32, size_of::<B>());
let a1 = Box::new(A(1, 1));
let b1 = Box::new(B(1, 1));
let a2 = Box::new(A(2, 2));
assert_eq!(*a1, A(1, 1));
assert_eq!(*b1, B(1, 1));
assert_eq!(*a2, A(2, 2));
let a1_raw = Box::into_raw(a1) as usize;
let b1_raw = Box::into_raw(b1) as usize;
let a2_raw = Box::into_raw(a2) as usize;
assert_eq!(HEAP_START, a1);
assert_eq!(HEAP_START + 48, b1);
assert_eq!(HEAP_START + 24, a2);
}
```
In this test case we start off with two identical structs `A` and `B`, with different alignment requirements as specified in their struct `#[repr]` attributes. Instances of `A` will have addresses that are a multiple of 8 and those of `B` will have addresses that are a multiple of `16`.
`a1`, an instance of struct `A` on the heap, takes up space from `HEAP_START` to `HEAP_START + 24`, as `HEAP_START` is a multiple of 8 already. `b1` is an instance of struct `B` on the heap, but it needs an address that is a multiple of 16. Therefore, although `HEAP_START + 24` is available, our `alloc_from_region` will first attempt to set `alloc_start = HEAP_START + 32`. However, this will not leave enough room to store a `ListNode` in the 8 bytes between `HEAP_START + 24` and `HEAP_START + 32`. Next, it will attempt to set `alloc_start = HEAP_START + 48` to satisfy both the alignment constraint and to allow a `ListNode` to account for the excess size at the head end of this region.
Because we are adding the `head_excess_size` fragment after `tail_excess_size` fragment in our `alloc` implementation, and because our linked list implementation follows LIFO (Last In First Out) ordering, our linked list will first search the `head_excess_size` region first on a new heap alloc request. We exploit this fact in this test by trying to allocate `a2`, which is an instance of struct `A`, which should fit neatly in the 24 bytes that were recycled from `HEAP_START + 24` to `HEAP_START + 48` as a part of the `head_excess_size` fragment from the previous allocation for `b1`. We can see that in our final lines of this test we are leaking these Boxed pointers and casting them to `usize` to help perform these assertions to ensure that our linked list allocator accounted for all the excess fragments.
### Discussion
In contrast to the bump allocator, the linked list allocator is much more suitable as a general-purpose allocator, mainly because it is able to directly reuse freed memory. However, it also has some drawbacks. Some of them are only caused by our basic implementation, but there are also fundamental drawbacks of the allocator design itself.

View File

@@ -9,7 +9,7 @@ chapter = "Multitasking"
# Please update this when updating the translation
translation_based_on_commit = "bf4f88107966c7ab1327c3cdc0ebfbd76bad5c5f"
# GitHub usernames of the authors of this translation
translators = ["kahirokunn", "garasubo", "sozysozbot", "woodyZootopia"]
translators = ["kahirokunn", "garasubo", "sozysozbot", "swnakamura"]
# GitHub usernames of the people that contributed to this translation
translation_contributors = ["asami-kawasaki", "Foo-x"]
+++
@@ -469,9 +469,9 @@ ExampleStateMachine::End(_) => {
Futureは `Poll::Ready` を返した後、再びポーリングされるべきではありません。したがって、すでに `End` の状態にあるときに `poll` が呼ばれるとパニックするようにしましょう。
コンパイラが生成するステートマシンとその `Future` traitの実装はこのようになっている**かもしれません**。実際には、コンパイラは異なる方法でコードを生成しています。 (一応、現在は[_generators_]をベースにした実装になっていますが、これはあくまでも実装の詳細です。)
コンパイラが生成するステートマシンとその `Future` traitの実装はこのようになっている**かもしれません**。実際には、コンパイラは異なる方法でコードを生成しています。 (一応、現在は[_coroutines_]をベースにした実装になっていますが、これはあくまでも実装の詳細です。)
[_generators_]: https://doc.rust-lang.org/nightly/unstable-book/language-features/generators.html
[_coroutines_]: https://doc.rust-lang.org/stable/unstable-book/language-features/coroutines.html
パズルの最後のピースは、生成された `example` 関数自体のコードです。関数のヘッダは次のように定義されていたことを思い出してください:

View File

@@ -462,9 +462,9 @@ ExampleStateMachine::End(_) => {
Futures should not be polled again after they returned `Poll::Ready`, so we panic if `poll` is called while we are already in the `End` state.
We now know what the compiler-generated state machine and its implementation of the `Future` trait _could_ look like. In practice, the compiler generates code in a different way. (In case you're interested, the implementation is currently based on [_generators_], but this is only an implementation detail.)
We now know what the compiler-generated state machine and its implementation of the `Future` trait _could_ look like. In practice, the compiler generates code in a different way. (In case you're interested, the implementation is currently based on [_coroutines_], but this is only an implementation detail.)
[_generators_]: https://doc.rust-lang.org/nightly/unstable-book/language-features/generators.html
[_coroutines_]: https://doc.rust-lang.org/stable/unstable-book/language-features/coroutines.html
The last piece of the puzzle is the generated code for the `example` function itself. Remember, the function header was defined like this:

View File

@@ -61,8 +61,7 @@
</div>
<div class="">
<h2>Support Me</h2>
{{ snippets::support() }}
{{ trans(key="support_me", lang=lang) | safe }}
</div>
{% endblock main %}

View File

@@ -91,8 +91,7 @@
</div>
<div class="post-footer-support{% if page.extra.rtl %} right-to-left{% endif %}">
<h2>Support Me</h2>
{{ snippets::support() }}
{{ trans(key="support_me", lang=lang) | safe }}
</div>
<hr>

View File

@@ -1,15 +1,3 @@
{% macro support() %}
<p>
Creating and <a href="{{ get_url(path="@/status-update/_index.md") }}">maintaining</a> this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance.
</p>
<p>
The best way to support me is to <a href="https://github.com/sponsors/phil-opp"><em>sponsor me on GitHub</em></a>, since they don't charge any fees. If you prefer other platforms, I also have <a href="https://www.patreon.com/phil_opp"><em>Patreon</em></a> and <a href="https://donorbox.org/phil-opp"><em>Donorbox</em></a> accounts. The latter is the most flexible as it supports multiple currencies and one-time contributions.
</p>
<p>
Thank you!
</p>
{% endmacro support %}
{% macro giscus(search_term, lang) %}
{% if lang != "en" %}
{% set category = "Post Comments (translated)" %}
@@ -24,12 +12,12 @@
{% if search_term is number %}
{% set discussion_url = "https://github.com/phil-opp/blog_os/discussions/" ~ search_term %}
{% else %}
{% set search_term_encoded = `"` ~ search_term ~ `"` | urlencode %}
{% set search_term_encoded = `"` ~ search_term ~ `"` ~ ` in:title` | urlencode %}
{% set discussion_url = `https://github.com/phil-opp/blog_os/discussions/categories/` ~ category_path ~ `?discussions_q=` ~ search_term_encoded %}
{% endif %}
<p class="comment-note">
Do you have a problem, want to share feedback, or discuss further ideas? Feel free to leave a comment here! Please stick to English and follow Rust's <a href="https://www.rust-lang.org/policies/code-of-conduct">code of conduct</a>. This comment thread directly maps to a <a href="{{ discussion_url | safe }}"><em>discussion on GitHub</em></a>, so you can also comment there if you prefer.
{{ trans(key="comment_note", lang=lang) | replace(from="_discussion_url_", to=discussion_url) | safe }}
</p>
<div class="giscus"></div>