From c1af4e31b14e562826029999b9ab1dce86396b93 Mon Sep 17 00:00:00 2001
From: "Aleksey R. (aka EreTIk)"
Date: Sun, 6 Feb 2022 12:29:29 +0300
Subject: [PATCH 001/107] Fix link to MS Docs (#1077)
---
.../edition-2/posts/01-freestanding-rust-binary/index.ru.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/blog/content/edition-2/posts/01-freestanding-rust-binary/index.ru.md b/blog/content/edition-2/posts/01-freestanding-rust-binary/index.ru.md
index d6192f0b..3c59f65f 100644
--- a/blog/content/edition-2/posts/01-freestanding-rust-binary/index.ru.md
+++ b/blog/content/edition-2/posts/01-freestanding-rust-binary/index.ru.md
@@ -158,7 +158,7 @@ fn panic(_info: &PanicInfo) -> ! {
[language item]: https://github.com/rust-lang/rust/blob/edb368491551a77d77a48446d4ee88b35490c565/src/libpanic_unwind/gcc.rs#L11-L45
[stack unwinding]: https://www.bogotobogo.com/cplusplus/stackunwinding.php
[libunwind]: https://www.nongnu.org/libunwind/
-[structured exception handling]: https://docs.microsoft.com/de-de/windows/win32/debug/structured-exception-handling
+[structured exception handling]: https://docs.microsoft.com/ru-ru/windows/win32/debug/structured-exception-handling
### Отключение раскрутки
From 5b2c60ece3500c2bd2524fcbcce4b65eba871e62 Mon Sep 17 00:00:00 2001
From: Max Desiatov
Date: Wed, 16 Feb 2022 10:37:31 +0000
Subject: [PATCH 002/107] Fix typo in `02-minimal-rust-kernel` (#1080)
`rerun the our build command` -> `rerun our build command`
---
blog/content/edition-2/posts/02-minimal-rust-kernel/index.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/blog/content/edition-2/posts/02-minimal-rust-kernel/index.md b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.md
index 7e5fa8c3..efbad211 100644
--- a/blog/content/edition-2/posts/02-minimal-rust-kernel/index.md
+++ b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.md
@@ -277,7 +277,7 @@ This tells cargo that it should recompile the `core` and `compiler_builtins` lib
-After setting the `unstable.build-std` configuration key and installing the `rust-src` component, we can rerun the our build command:
+After setting the `unstable.build-std` configuration key and installing the `rust-src` component, we can rerun our build command:
```
> cargo build --target x86_64-blog_os.json
From b8272b53cfa285c77e4eedfcbf217e88c78007a7 Mon Sep 17 00:00:00 2001
From: alaincao
Date: Mon, 7 Mar 2022 13:14:39 +0100
Subject: [PATCH 003/107] Fix french in 01-freestanding-rust-binary (#1089)
---
.../edition-2/posts/01-freestanding-rust-binary/index.fr.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/blog/content/edition-2/posts/01-freestanding-rust-binary/index.fr.md b/blog/content/edition-2/posts/01-freestanding-rust-binary/index.fr.md
index 5c70882a..297741ba 100644
--- a/blog/content/edition-2/posts/01-freestanding-rust-binary/index.fr.md
+++ b/blog/content/edition-2/posts/01-freestanding-rust-binary/index.fr.md
@@ -266,7 +266,7 @@ La sortie ci-dessus provient d'un système Linux `x86_64`. Nous pouvons voir que
En compilant pour notre triplé hôte, le compilateur Rust ainsi que le linker supposent qu'il y a un système d'exploitation sous-jacent comme Linux ou Windows qui utilise l'environnement d'exécution C par défaut, ce qui cause les erreurs de linker. Donc pour éviter ces erreurs, nous pouvons compiler pour un environnement différent sans système d'exploitation sous-jacent.
-Un exemple d'un tel envrironnement est le triplé cible `thumbv7em-none-eabihf`, qui décrit un système [ARM] [embarqué]. Les détails ne sont pas importants, tout ce qui compte est que le triplé cible n'a pas de système d'exploitation sous-jacent, ce qui est indiqué par le `none` dans le triplé cible. Pour pouvoir compilé pour cette cible, nous avons besoin de l'ajouter dans rustup :
+Un exemple d'un tel envrironnement est le triplé cible `thumbv7em-none-eabihf`, qui décrit un système [ARM] [embarqué]. Les détails ne sont pas importants, tout ce qui compte est que le triplé cible n'a pas de système d'exploitation sous-jacent, ce qui est indiqué par le `none` dans le triplé cible. Pour pouvoir compiler pour cette cible, nous avons besoin de l'ajouter dans rustup :
[embarqué]: https://fr.wikipedia.org/wiki/Syst%C3%A8me_embarqu%C3%A9
[ARM]: https://fr.wikipedia.org/wiki/Architecture_ARM
@@ -449,7 +449,7 @@ Maintenant notre programme devrait être compilable sur les trois plateformes av
#### Devriez-vous Faire Ça ?
-Bien qu'il soit possible de compiler un exécutable autoporté pour Linux, Windows et macOS, ce n'est probablement pas une bonne idée. La raison est que notre exécutable s'attend toujours à trouver certaines choses, par exemple une pile initialisée lorsque la fonction `_start` est appelée. Sans l'environnement d'exécution C, certains de ces conditions peuvent ne pas être remplies, ce qui pourrait faire planter notre programme, avec par exemple une erreur de segmentation.
+Bien qu'il soit possible de compiler un exécutable autoporté pour Linux, Windows et macOS, ce n'est probablement pas une bonne idée. La raison est que notre exécutable s'attend toujours à trouver certaines choses, par exemple une pile initialisée lorsque la fonction `_start` est appelée. Sans l'environnement d'exécution C, certaines de ces conditions peuvent ne pas être remplies, ce qui pourrait faire planter notre programme, avec par exemple une erreur de segmentation.
Si vous voulez créer un exécutable minimal qui tourne sur un système d'exploitation existant, include `libc` et mettre l'attribut `#[start]` come décrit [ici](https://doc.rust-lang.org/1.16.0/book/no-stdlib.html) semble être une meilleure idée.
From daafb244d9beddf904bea7b932e04cbd95017377 Mon Sep 17 00:00:00 2001
From: Alexandre <50714185+AlexandreMarcq@users.noreply.github.com>
Date: Mon, 7 Mar 2022 13:38:23 +0100
Subject: [PATCH 004/107] Updated list of french translators (#1090)
---
.../edition-2/posts/01-freestanding-rust-binary/index.fr.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/blog/content/edition-2/posts/01-freestanding-rust-binary/index.fr.md b/blog/content/edition-2/posts/01-freestanding-rust-binary/index.fr.md
index 297741ba..b5b658b2 100644
--- a/blog/content/edition-2/posts/01-freestanding-rust-binary/index.fr.md
+++ b/blog/content/edition-2/posts/01-freestanding-rust-binary/index.fr.md
@@ -9,7 +9,7 @@ chapter = "Bare Bones"
# Please update this when updating the translation
translation_based_on_commit = "3e87916b6c2ed792d1bdb8c0947906aef9013ac1"
# GitHub usernames of the people that translated this post
-translators = ["Alekzus"]
+translators = ["AlexandreMarcq", "alaincao"]
+++
La première étape pour créer notre propre noyau de système d'exploitation est de créer un exécutable Rust qui ne relie pas la bibliothèque standard. Cela rend possible l'exécution du code Rust sur la ["bare machine"][machine nue] sans système d'exploitation sous-jacent.
From 0873c3ae1d0abdcabf12a3bbf3272c20c9222992 Mon Sep 17 00:00:00 2001
From: Youngsuk Kim
Date: Wed, 9 Mar 2022 17:05:15 -0500
Subject: [PATCH 005/107] Korean translation for ch-01 (#1079)
---
blog/config.toml | 19 +-
blog/content/_index.ko.md | 14 +
.../01-freestanding-rust-binary/index.ko.md | 547 ++++++++++++++++++
blog/content/edition-2/posts/_index.ko.md | 7 +
4 files changed, 586 insertions(+), 1 deletion(-)
create mode 100644 blog/content/_index.ko.md
create mode 100644 blog/content/edition-2/posts/01-freestanding-rust-binary/index.ko.md
create mode 100644 blog/content/edition-2/posts/_index.ko.md
diff --git a/blog/config.toml b/blog/config.toml
index 5f28be77..29ffe874 100644
--- a/blog/config.toml
+++ b/blog/config.toml
@@ -30,7 +30,7 @@ skip_anchor_prefixes = [
subtitle = "Philipp Oppermann's blog"
author = { name = "Philipp Oppermann" }
default_language = "en"
-languages = ["en", "zh-CN", "zh-TW", "fr", "ja", "fa", "ru"]
+languages = ["en", "zh-CN", "zh-TW", "fr", "ja", "fa", "ru", "ko"]
[languages.en]
title = "Writing an OS in Rust"
@@ -149,3 +149,20 @@ translated_content = "Contenu traduit : "
translated_content_notice = "Ceci est une traduction communautaire de l'article _original.title_. Il peut être incomplet, obsolète ou contenir des erreurs. Veuillez signaler les quelconques problèmes !"
translated_by = "Traduit par : "
word_separator = "et"
+
+# Korean
+[languages.ko]
+title = "Writing an OS in Rust"
+description = "This blog series creates a small operating system in the Rust programming language. Each post is a small tutorial and includes all needed code."
+[languages.ko.translations]
+lang_name = "Korean"
+toc = "목차"
+all_posts = "« 모든 게시글"
+comments = "댓글"
+comments_notice = "댓글은 가능하면 영어로 작성해주세요."
+readmore = "더 읽기 »"
+not_translated = "(아직 번역이 완료되지 않은 게시글입니다)"
+translated_content = "번역된 내용 : "
+translated_content_notice = "이것은 커뮤니티 멤버가 _original.title_ 포스트를 번역한 글입니다. 부족한 설명이나 오류, 혹은 시간이 지나 더 이상 유효하지 않은 정보를 발견하시면 제보해주세요!"
+translated_by = "번역한 사람 : "
+word_separator = "와"
diff --git a/blog/content/_index.ko.md b/blog/content/_index.ko.md
new file mode 100644
index 00000000..6fddbf94
--- /dev/null
+++ b/blog/content/_index.ko.md
@@ -0,0 +1,14 @@
++++
+template = "edition-2/index.html"
++++
+
+Rust로 OS 구현하기
+
+
+
+이 블로그 시리즈는 [Rust 프로그래밍 언어](https://www.rust-lang.org/)로 작은 OS를 구현하는 것을 주제로 합니다.
+각 포스트는 구현에 필요한 소스 코드를 포함한 작은 튜토리얼 형식으로 구성되어 있습니다. 소스 코드는 이 블로그의 [Github 저장소](https://github.com/phil-opp/blog_os)에서도 확인하실 수 있습니다.
+
+최신 포스트:
+
+
diff --git a/blog/content/edition-2/posts/01-freestanding-rust-binary/index.ko.md b/blog/content/edition-2/posts/01-freestanding-rust-binary/index.ko.md
new file mode 100644
index 00000000..6c9d7b68
--- /dev/null
+++ b/blog/content/edition-2/posts/01-freestanding-rust-binary/index.ko.md
@@ -0,0 +1,547 @@
++++
+title = "Rust로 'Freestanding 실행파일' 만들기"
+weight = 1
+path = "ko/freestanding-rust-binary"
+date = 2018-02-10
+
+[extra]
+chapter = "Bare Bones"
+# Please update this when updating the translation
+translation_based_on_commit = "c1af4e31b14e562826029999b9ab1dce86396b93"
+# GitHub usernames of the people that translated this post
+translators = ["JOE1994"]
++++
+
+운영체제 커널을 만드는 첫 단계는 표준 라이브러리(standard library)를 링크하지 않는 Rust 실행파일을 만드는 것입니다.
+이러한 실행파일은 운영체제가 없는 [bare metal] 시스템에서 동작할 수 있습니다.
+
+[bare metal]: https://en.wikipedia.org/wiki/Bare_machine
+
+
+
+이 블로그는 [GitHub 저장소][GitHub]에서 오픈 소스로 개발되고 있으니, 문제나 문의사항이 있다면 저장소의 'Issue' 기능을 이용해 제보해주세요. [페이지 맨 아래][at the bottom]에 댓글을 남기실 수도 있습니다. 이 포스트와 관련된 모든 소스 코드는 저장소의 [`post-01 브랜치`][post branch]에서 확인하실 수 있습니다.
+
+[GitHub]: https://github.com/phil-opp/blog_os
+[at the bottom]: #comments
+
+[post branch]: https://github.com/phil-opp/blog_os/tree/post-01
+
+
+
+## 소개
+운영체제 커널을 만드려면 운영체제에 의존하지 않는 코드가 필요합니다. 자세히 설명하자면, 스레드, 파일, 동적 메모리, 네트워크, 난수 생성기, 표준 출력 및 기타 운영체제의 추상화 또는 특정 하드웨어의 기능을 필요로 하는 것들은 전부 사용할 수 없다는 뜻입니다. 우리는 스스로 운영체제 및 드라이버를 직접 구현하려는 상황이니 어찌 보면 당연한 조건입니다.
+
+운영체제에 의존하지 않으려면 [Rust 표준 라이브러리][Rust standard library]의 많은 부분을 사용할 수 없습니다.
+그래도 우리가 이용할 수 있는 Rust 언어 자체의 기능들은 많이 남아 있습니다. 예를 들어 [반복자][iterators], [클로저][closures], [패턴 매칭][pattern matching], [option] / [result], [문자열 포맷 설정][string formatting], 그리고 [소유권 시스템][ownership system] 등이 있습니다. 이러한 기능들은 우리가 커널을 작성할 때 [undefined behavior]나 [메모리 안전성][memory safety]에 대한 걱정 없이 큰 흐름 단위의 코드를 작성하는 데에 집중할 수 있도록 해줍니다.
+
+[option]: https://doc.rust-lang.org/core/option/
+[result]:https://doc.rust-lang.org/core/result/
+[Rust standard library]: https://doc.rust-lang.org/std/
+[iterators]: https://doc.rust-lang.org/book/ch13-02-iterators.html
+[closures]: https://doc.rust-lang.org/book/ch13-01-closures.html
+[pattern matching]: https://doc.rust-lang.org/book/ch06-00-enums.html
+[string formatting]: https://doc.rust-lang.org/core/macro.write.html
+[ownership system]: https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html
+[undefined behavior]: https://www.nayuki.io/page/undefined-behavior-in-c-and-cplusplus-programs
+[memory safety]: https://tonyarcieri.com/it-s-time-for-a-memory-safety-intervention
+
+Rust로 운영체제 커널을 작성하려면, 운영체제 없이도 실행가능한 실행파일이 필요합니다. 이러한 실행파일은
+보통 "freestanding 실행파일" 혹은 "bare-metal 실행파일" 이라고 불립니다.
+
+이 포스트에서는 "freestanding 실행 파일" 을 만드는 데 필요한 것들을 여러 단계로 나누고, 각 단계가 왜 필요한지에 대해 설명해드립니다. 중간 과정은 생략하고 그저 최소한의 예제 코드만 확인하고 싶으시면 **[요약 섹션으로 넘어가시면 됩니다](#summary)**.
+
+## Rust 표준 라이브러리 링크 해제하기
+모든 Rust 프로그램들은 Rust 표준 라이브러리를 링크하는데, 이 라이브러리는 스레드, 파일, 네트워킹 등의 기능을 제공하기 위해 운영체제에 의존합니다. Rust 표준 라이브러리는 또한 C 표준 라이브러리인 `libc`에도 의존합니다 (`libc`는 운영체제의 여러 기능들을 이용합니다).
+우리가 운영체제를 직접 구현하기 위해서는 운영체제를 이용하는 라이브러리들은 사용할 수 없습니다. 그렇기에 우선 [`no_std` 속성][`no_std` attribute]을 이용해 자동으로 Rust 표준 라이브러리가 링크되는 것을 막아야 합니다.
+
+[standard library]: https://doc.rust-lang.org/std/
+[`no_std` attribute]: https://doc.rust-lang.org/1.30.0/book/first-edition/using-rust-without-the-standard-library.html
+
+제일 먼저 아래의 명령어를 통해 새로운 cargo 애플리케이션 크레이트를 만듭니다.
+
+```
+cargo new blog_os --bin --edition 2018
+```
+
+프로젝트 이름은 `blog_os` 또는 원하시는 이름으로 정해주세요. `--bin` 인자는 우리가 cargo에게 실행 파일 (라이브러리와 대조됨)을 만들겠다고 알려주고, `--edition 2018` 인자는 cargo에게 우리가 [Rust 2018 에디션][2018 edition]을 사용할 것이라고 알려줍니다.
+위 명령어를 실행하고 나면, cargo가 아래와 같은 크레이트 디렉토리를 만들어줍니다.
+
+[2018 edition]: https://doc.rust-lang.org/nightly/edition-guide/rust-2018/index.html
+
+```
+blog_os
+├── Cargo.toml
+└── src
+ └── main.rs
+```
+
+크레이트 설정은 `Cargo.toml`에 전부 기록해야 합니다 (크레이트 이름, 크레이트 원작자, [semantic version] 번호, 의존 라이브러리 목록 등).
+`src/main.rs` 파일에 크레이트 실행 시 맨 처음 호출되는 `main` 함수를 포함한 중추 모듈이 있습니다.
+`cargo build` 명령어를 통해 크레이트를 빌드하면 `target/debug` 디렉토리에 `blog_os` 실행파일이 생성됩니다.
+
+[semantic version]: https://semver.org/
+
+### `no_std` 속성
+
+현재 우리가 만든 크레이트는 암시적으로 Rust 표준 라이브러리를 링크합니다. 아래와 같이 [`no_std` 속성]을 이용해 더 이상 표준 라이브러리가 링크되지 않게 해줍니다.
+
+```rust
+// main.rs
+
+#![no_std]
+
+fn main() {
+ println!("Hello, world!");
+}
+```
+
+이제 `cargo build` 명령어를 다시 실행하면 아래와 같은 오류 메세지가 뜰 것입니다:
+
+```
+error: cannot find macro `println!` in this scope
+ --> src/main.rs:4:5
+ |
+4 | println!("Hello, world!");
+ | ^^^^^^^
+```
+
+이 오류가 뜨는 이유는 [`println` 매크로][`println` macro]를 제공하는 Rust 표준 라이브러리를 우리의 크레이트에 링크하지 않게 되었기 때문입니다.
+`println`은 [표준 입출력][standard output] (운영체제가 제공하는 특별한 파일 서술자)으로 데이터를 쓰기 때문에, 우리는 이제 `println`을 이용해 메세지를 출력할 수 없습니다.
+
+[`println` macro]: https://doc.rust-lang.org/std/macro.println.html
+[standard output]: https://en.wikipedia.org/wiki/Standard_streams#Standard_output_.28stdout.29
+
+`println` 매크로 호출 코드를 지운 후 크레이트를 다시 빌드해봅시다.
+
+```rust
+// main.rs
+
+#![no_std]
+
+fn main() {}
+```
+
+```
+> cargo build
+error: `#[panic_handler]` function required, but not found
+error: language item required, but not found: `eh_personality`
+```
+
+오류 메세지를 통해 컴파일러가 `#[panic_handler]` 함수와 _language item_ 을 필요로 함을 확인할 수 있습니다.
+
+## 패닉 (Panic) 시 호출되는 함수 구현하기
+
+컴파일러는 [패닉][panic]이 일어날 경우 `panic_handler` 속성이 적용된 함수가 호출되도록 합니다. 표준 라이브러리는 패닉 시 호출되는 함수가 제공되지만, `no_std` 환경에서는 우리가 패닉 시 호출될 함수를 직접 설정해야 합니다.
+
+[panic]: https://doc.rust-lang.org/stable/book/ch09-01-unrecoverable-errors-with-panic.html
+
+```rust
+// in main.rs
+
+use core::panic::PanicInfo;
+
+/// 패닉이 일어날 경우, 이 함수가 호출됩니다.
+#[panic_handler]
+fn panic(_info: &PanicInfo) -> ! {
+ loop {}
+}
+```
+
+[`PanicInfo` 인자][PanicInfo]는 패닉이 일어난 파일명, 패닉이 파일 내 몇 번째 줄에서 일어났는지, 그리고 패닉시 전달된 메세지에 대한 정보를 가진 구조체입니다.
+위 `panic` 함수는 절대로 반환하지 않기에 ["never" 타입][“never” type] `!`을 반환하도록 적어 컴파일러에게 이 함수가 [반환 함수][diverging function]임을 알립니다.
+당장 이 함수에서 우리가 하고자 하는 일은 없기에 그저 함수가 반환하지 않도록 무한루프를 넣어줍니다.
+
+[PanicInfo]: https://doc.rust-lang.org/nightly/core/panic/struct.PanicInfo.html
+[diverging function]: https://doc.rust-lang.org/1.30.0/book/first-edition/functions.html#diverging-functions
+[“never” type]: https://doc.rust-lang.org/nightly/std/primitive.never.html
+
+## `eh_personality` Language Item
+
+Language item은 컴파일러가 내부적으로 요구하는 특별한 함수 및 타입들을 가리킵니다. 예를 들어 [`Copy`] 트레잇은 어떤 타입들이 [_copy semantics_][`Copy`] 를 가지는지 컴파일러에게 알려주는 language item 입니다.
+[`Copy` 트레잇이 구현된 코드][copy code]에 있는 `#[lang = "copy"]` 속성을 통해 이 트레잇이 language item으로 선언되어 있음을 확인할 수 있습니다.
+
+[`Copy`]: https://doc.rust-lang.org/nightly/core/marker/trait.Copy.html
+[copy code]: https://github.com/rust-lang/rust/blob/485397e49a02a3b7ff77c17e4a3f16c653925cb3/src/libcore/marker.rs#L296-L299
+
+임의로 구현한 language item을 사용할 수는 있지만, 위험할 수도 있기에 주의해야 합니다.
+그 이유는 language item의 구현 코드는 매우 자주 변경되어 불안정하며, language item에 대해서 컴파일러가 타입 체크 조차 하지 않습니다 (예시: language item 함수의 인자 타입이 정확한지 조차 체크하지 않습니다).
+임의로 구현한 language item을 이용하는 것보다 더 안정적으로 위의 language item 오류를 고칠 방법이 있습니다.
+
+[`eh_personality` language item]은 [스택 되감기 (stack unwinding)][stack unwinding]을 구현하는 함수를 가리킵니다. 기본적으로 Rust는 [패닉][panic]이 일어났을 때 스택 되감기를 통해 스택에 살아있는 각 변수의 소멸자를 호출합니다. 이를 통해 자식 스레드에서 사용 중이던 모든 메모리 리소스가 반환되고, 부모 스레드가 패닉에 대처한 후 계속 실행될 수 있게 합니다. 스택 되감기는 복잡한 과정으로 이루어지며 운영체제마다 특정한 라이브러리를 필요로 하기에 (예: Linux는 [libunwind], Windows는 [structured exception handling]), 우리가 구현할 운영체제에서는 이 기능을 사용하지 않을 것입니다.
+
+[`eh_personality` language item]: https://github.com/rust-lang/rust/blob/edb368491551a77d77a48446d4ee88b35490c565/src/libpanic_unwind/gcc.rs#L11-L45
+[stack unwinding]: https://www.bogotobogo.com/cplusplus/stackunwinding.php
+[libunwind]: https://www.nongnu.org/libunwind/
+[structured exception handling]: https://docs.microsoft.com/en-us/windows/win32/debug/structured-exception-handling
+
+### 스택 되감기를 해제하는 방법
+
+스택 되감기가 불필요한 상황들이 여럿 있기에, Rust 언어는 [패닉 시 실행 종료][abort on panic] 할 수 있는 선택지를 제공합니다. 이는 스택 되감기에 필요한 심볼 정보 생성을 막아주어 실행 파일의 크기 자체도 많이 줄어들게 됩니다. 스택 되감기를 해제하는 방법은 여러가지 있지만, 가장 쉬운 방법은 `Cargo.toml` 파일에 아래의 코드를 추가하는 것입니다.
+
+```toml
+[profile.dev]
+panic = "abort"
+
+[profile.release]
+panic = "abort"
+```
+
+위의 코드를 통해 `dev` 빌드 (`cargo build` 실행)와 `release` 빌드 (`cargo build --release` 실행) 에서 모두 패닉 시 실행이 종료되도록 설정되었습니다.
+이제 더 이상 컴파일러가 `eh_personality` language item을 필요로 하지 않습니다.
+
+[abort on panic]: https://github.com/rust-lang/rust/pull/32900
+
+위에서 본 오류들을 고쳤지만, 크레이트를 빌드하려고 하면 새로운 오류가 뜰 것입니다:
+
+```
+> cargo build
+error: requires `start` lang_item
+```
+
+우리의 프로그램에는 프로그램 실행 시 최초 실행 시작 지점을 지정해주는 `start` language item이 필요합니다.
+
+## `start` 속성
+
+혹자는 프로그램 실행 시 언제나 `main` 함수가 가장 먼저 호출된다고 생각할지도 모릅니다. 대부분의 프로그래밍 언어들은 [런타임 시스템][runtime system]을 가지고 있는데, 이는 가비지 컬렉션 (예시: Java) 혹은 소프트웨어 스레드 (예시: GoLang의 goroutine) 등의 기능을 담당합니다.
+이러한 런타임 시스템은 프로그램 실행 이전에 초기화 되어야 하기에 `main` 함수 호출 이전에 먼저 호출됩니다.
+
+[runtime system]: https://en.wikipedia.org/wiki/Runtime_system
+
+러스트 표준 라이브러리를 링크하는 전형적인 러스트 실행 파일의 경우, 프로그램 실행 시 C 런타임 라이브러리인 `crt0` (“C runtime zero”) 에서 실행이 시작됩니다. `crt0`는 C 프로그램의 환경을 설정하고 초기화하는 런타임 시스템으로, 스택을 만들고 프로그램에 주어진 인자들을 적절한 레지스터에 배치합니다. `crt0`가 작업을 마친 후 `start` language item으로 지정된 [Rust 런타임의 실행 시작 함수][rt::lang_start]를 호출합니다.
+Rust는 최소한의 런타임 시스템을 가지며, 주요 기능은 스택 오버플로우 가드를 초기화하고 패닉 시 역추적 (backtrace) 정보를 출력하는 것입니다. Rust 런타임의 초기화 작업이 끝난 후에야 `main` 함수가 호출됩니다.
+
+[rt::lang_start]: https://github.com/rust-lang/rust/blob/bb4d1491466d8239a7a5fd68bd605e3276e97afb/src/libstd/rt.rs#L32-L73
+
+우리의 "freestanding 실행 파일" 은 Rust 런타임이나 `crt0`에 접근할 수 없기에, 우리가 직접 프로그램 실행 시작 지점을 지정해야 합니다.
+`crt0`가 `start` language item을 호출해주는 방식으로 동작하기에, `start` language item을 구현하고 지정하는 것만으로는 문제를 해결할 수 없습니다.
+대신 우리가 직접 `crt0`의 시작 지점을 대체할 새로운 실행 시작 지점을 제공해야 합니다.
+
+### 실행 시작 지점 덮어쓰기
+`#![no_main]` 속성을 이용해 Rust 컴파일러에게 우리가 일반적인 실행 시작 호출 단계를 이용하지 않겠다고 선언합니다.
+
+```rust
+#![no_std]
+#![no_main]
+
+use core::panic::PanicInfo;
+
+/// 패닉이 일어날 경우, 이 함수가 호출됩니다.
+#[panic_handler]
+fn panic(_info: &PanicInfo) -> ! {
+ loop {}
+}
+```
+
+`main` 함수가 사라진 것을 눈치채셨나요? `main` 함수를 호출해주는 런타임 시스템이 없는 이상 `main` 함수의 존재도 더 이상 의미가 없습니다.
+우리는 운영체제가 호출하는 프로그램 실행 시작 지점 대신 우리의 새로운 `_start` 함수를 실행 시작 지점으로 대체할 것입니다.
+
+```rust
+#[no_mangle]
+pub extern "C" fn _start() -> ! {
+ loop {}
+}
+```
+
+`#[no_mangle]` 속성을 통해 [name mangling]을 해제하여 Rust 컴파일러가 `_start` 라는 이름 그대로 함수를 만들도록 합니다. 이 속성이 없다면, 컴파일러가 각 함수의 이름을 고유하게 만드는 과정에서 이 함수의 실제 이름을 `_ZN3blog_os4_start7hb173fedf945531caE` 라는 이상한 이름으로 바꿔 생성합니다. 우리가 원하는 실제 시작 지점 함수의 이름을 정확히 알고 있어야 링커 (linker)에도 그 이름을 정확히 전달할 수 있기에 (후속 단계에서 진행) `#[no_mangle]` 속성이 필요합니다.
+
+또한 우리는 이 함수에 `extern "C"`라는 표시를 추가하여 이 함수가 Rust 함수 호출 규약 대신에 [C 함수 호출 규약][C calling convention]을 사용하도록 합니다. 함수의 이름을 `_start`로 지정한 이유는 그저 런타임 시스템들의 실행 시작 함수 이름이 대부분 `_start`이기 때문입니다.
+
+[name mangling]: https://en.wikipedia.org/wiki/Name_mangling
+[C calling convention]: https://en.wikipedia.org/wiki/Calling_convention
+
+`!` 반환 타입은 이 함수가 발산 함수라는 것을 의미합니다. 시작 지점 함수는 오직 운영체제나 부트로더에 의해서만 직접 호출됩니다. 따라서 시작 지점 함수는 반환하는 대신 운영체제의 [`exit` 시스템콜][`exit` system call]을 이용해 종료됩니다. 우리의 "freestanding 실행 파일" 은 실행 종료 후 더 이상 실행할 작업이 없기에, 시작 지점 함수가 작업을 마친 후 기기를 종료하는 것이 합리적입니다. 여기서는 일단 `!` 타입의 조건을 만족시키기 위해 무한루프를 넣어 줍니다.
+
+[`exit` system call]: https://en.wikipedia.org/wiki/Exit_(system_call)
+
+다시 `cargo build`를 실행하면, 끔찍한 _링커_ 오류를 마주하게 됩니다.
+
+## 링커 오류
+
+링커는 컴파일러가 생성한 코드들을 묶어 실행파일로 만드는 프로그램입니다. 실행 파일 형식은 Linux, Windows, macOS 마다 전부 다르기에 각 운영체제는 자신만의 링커가 있고 링커마다 다른 오류 메세지를 출력할 것입니다.
+오류가 나는 근본적인 원인은 모두 동일한데, 링커는 주어진 프로그램이 C 런타임 시스템을 이용할 것이라고 가정하는 반면 우리의 크레이트는 그렇지 않기 때문입니다.
+
+이 링커 오류를 해결하려면 링커에게 C 런타임을 링크하지 말라고 알려줘야 합니다. 두 가지 방법이 있는데, 하나는 링커에 특정 인자들을 주는 것이고, 또다른 하나는 크레이트 컴파일 대상 기기를 bare metal 기기로 설정하는 것입니다.
+
+### Bare Metal 시스템을 목표로 빌드하기
+
+기본적으로 Rust는 당신의 현재 시스템 환경에서 실행할 수 있는 실행파일을 생성하고자 합니다. 예를 들어 Windows `x86_64` 사용자의 경우, Rust는 `x86_64` 명령어 셋을 사용하는 `.exe` 확장자 실행파일을 생성합니다. 사용자의 기본 시스템 환경을 "호스트" 시스템이라고 부릅니다.
+
+여러 다른 시스템 환경들을 표현하기 위해 Rust는 [_target triple_]이라는 문자열을 이용합니다. 현재 호스트 시스템의 target triple이 궁금하시다면 `rustc --version --verbose` 명령어를 실행하여 확인 가능합니다.
+
+[_target triple_]: https://clang.llvm.org/docs/CrossCompilation.html#target-triple
+
+```
+rustc 1.35.0-nightly (474e7a648 2019-04-07)
+binary: rustc
+commit-hash: 474e7a6486758ea6fc761893b1a49cd9076fb0ab
+commit-date: 2019-04-07
+host: x86_64-unknown-linux-gnu
+release: 1.35.0-nightly
+LLVM version: 8.0
+```
+
+위의 출력 내용은 `x86_64` Linux 시스템에서 얻은 것입니다. 호스트 target triple이 `x86_64-unknown-linux-gnu`으로 나오는데, 이는 CPU 아키텍쳐 정보 (`x86_64`)와 하드웨어 판매자 (`unknown`), 운영체제 (`linux`) 그리고 [응용 프로그램 이진 인터페이스 (ABI)][ABI] (`gnu`) 정보를 모두 담고 있습니다.
+
+[ABI]: https://en.wikipedia.org/wiki/Application_binary_interface
+
+우리의 호스트 시스템 triple을 위해 컴파일하는 경우, Rust 컴파일러와 링커는 Linux나 Windows와 같은 운영체제가 있다고 가정하고 또한 운영체제가 C 런타임 시스템을 사용할 것이라고 가정하기 때문에 링커 오류 메세지가 출력된 것입니다. 이런 링커 오류를 피하려면 운영체제가 없는 시스템 환경에서 코드가 구동하는 것을 목표로 컴파일해야 합니다.
+
+운영체제가 없는 bare metal 시스템 환경의 한 예시로 `thumbv7em-none-eabihf` target triple이 있습니다 (이는 [임베디드][embedded] [ARM] 시스템을 가리킵니다). Target triple의 `none`은 시스템에 운영체제가 동작하지 않음을 의미하며, 이 target triple의 나머지 부분의 의미는 아직 모르셔도 괜찮습니다. 이 시스템 환경에서 구동 가능하도록 컴파일하려면 rustup에서 해당 시스템 환경을 추가해야 합니다.
+
+[embedded]: https://en.wikipedia.org/wiki/Embedded_system
+[ARM]: https://en.wikipedia.org/wiki/ARM_architecture
+
+```
+rustup target add thumbv7em-none-eabihf
+```
+
+위 명령어를 실행하면 해당 시스템을 위한 Rust 표준 라이브러리 및 코어 라이브러리를 설치합니다. 이제 해당 target triple을 목표로 하는 freestanding 실행파일을 만들 수 있습니다.
+
+```
+cargo build --target thumbv7em-none-eabihf
+```
+
+`--target` 인자를 통해 우리가 해당 bare metal 시스템을 목표로 [크로스 컴파일][cross compile]할 것이라는 것을 cargo에게 알려줍니다. 목표 시스템 환경에 운영체제가 없는 것을 링커도 알기 때문에 C 런타임을 링크하려고 시도하지 않으며 이제는 링커 에러 없이 빌드가 성공할 것입니다.
+
+[cross compile]: https://en.wikipedia.org/wiki/Cross_compiler
+
+우리는 이 방법을 이용하여 우리의 운영체제 커널을 빌드해나갈 것입니다. 위에서 보인 `thumbv7em-none-eabihf` 시스템 환경 대신 bare metal `x86_64` 시스템 환경을 묘사하는 [커스텀 시스템 환경][custom target]을 설정하여 빌드할 것입니다. 더 자세한 내용은 다음 포스트에서 더 설명하겠습니다.
+
+[custom target]: https://doc.rust-lang.org/rustc/targets/custom.html
+
+### 링커 인자
+
+Bare metal 시스템을 목표로 컴파일하는 대신, 링커에게 특정 인자들을 추가로 주어 링커 오류를 해결하는 방법도 있습니다.
+이 방법은 앞으로 우리가 작성해나갈 커널 코드를 빌드할 때는 사용하지 않을 것이지만, 더 알고싶어 하실 분들을 위해서 이 섹션을 준비했습니다.
+아래의 _"링커 인자"_ 텍스트를 눌러 이 섹션의 내용을 확인하세요.
+
+
+
+링커 인자
+
+이 섹션에서는 Linux, Windows 그리고 macOS 각각의 운영체제에서 나타나는 링커 오류에 대해 다루고 각 운영체제마다 링커에 어떤 추가 인자들을 주어 링커 오류를 해결할 수 있는지 설명할 것입니다.
+
+#### Linux
+
+Linux 에서는 아래와 같은 링커 오류 메세지가 출력됩니다 (일부 생략됨):
+
+```
+error: linking with `cc` failed: exit code: 1
+ |
+ = note: "cc" […]
+ = note: /usr/lib/gcc/../x86_64-linux-gnu/Scrt1.o: In function `_start':
+ (.text+0x12): undefined reference to `__libc_csu_fini'
+ /usr/lib/gcc/../x86_64-linux-gnu/Scrt1.o: In function `_start':
+ (.text+0x19): undefined reference to `__libc_csu_init'
+ /usr/lib/gcc/../x86_64-linux-gnu/Scrt1.o: In function `_start':
+ (.text+0x25): undefined reference to `__libc_start_main'
+ collect2: error: ld returned 1 exit status
+```
+
+이 상황을 설명하자면 링커가 기본적으로 C 런타임의 실행 시작 루틴을 링크하는데, 이 루틴 역시 `_start`라는 이름을 가집니다. 이 `_start` 루틴은 C 표준 라이브러리 (`libc`)가 포함하는 여러 symbol들을 필요로 하지만, 우리는 `no_std` 속성을 이용해 크레이트에서 `libc`를 링크하지 않기 때문에 링커가 몇몇 symbol들의 출처를 찾지 못하여 위와 같은 링커 오류 메세지가 출력되는 것입니다. 이 문제를 해결하려면, 링커에게 `--nostartfiles` 인자를 전달하여 더 이상 링커가 C 런타임의 실행 시작 루틴을 링크하지 않도록 해야 합니다.
+
+링커에 인자를 전달하는 한 방법은 `cargo rustc` 명령어를 이용하는 것입니다. 이 명령어는 `cargo build`와 유사하게 동작하나, `rustc`(Rust 컴파일러)에 직접 인자를 전달할 수 있게 해줍니다. `rustc`는 `-C link-arg` 인자를 통해 링커에게 인자를 전달할 수 있게 해줍니다. 우리가 이용할 새로운 빌드 명령어는 아래와 같습니다:
+
+```
+cargo rustc -- -C link-arg=-nostartfiles
+```
+
+이제 우리의 크레이트가 성공적으로 빌드되고 Linux에서 동작하는 freestanding 실행파일이 생성됩니다!
+
+우리는 위의 빌드 명령어에서 실행 시작 함수의 이름을 명시적으로 전달하지 않았는데, 그 이유는 링커가 기본적으로 `_start` 라는 이름의 함수를 찾아 그 함수를 실행 시작 함수로 이용하기 때문입니다.
+
+#### Windows
+
+Windows에서는 다른 링커 오류를 마주하게 됩니다 (일부 생략):
+
+```
+error: linking with `link.exe` failed: exit code: 1561
+ |
+ = note: "C:\\Program Files (x86)\\…\\link.exe" […]
+ = note: LINK : fatal error LNK1561: entry point must be defined
+```
+
+오류 메세지 "entry point must be defined"는 링커가 실행 시작 지점을 찾을 수 없다는 것을 알려줍니다. Windows에서는 기본 실행 시작 지점의 이름이 [사용 중인 서브시스템(subsystem)에 따라 다릅니다][windows-subsystems]. `CONSOLE` 서브시스템의 경우 링커가 `mainCRTStartup`이라는 함수를 실행 시작 지점으로 간주하고, `WINDOWS` 서브시스템의 경우 링커가 `WinMainCRTStartup`이라는 이름의 함수를 실행 시작 지점으로 간주합니다. 이러한 기본값을 변경하여 링커가 `_start`라는 이름의 함수를 실행 시작 지점으로 간주하도록 만드려면 링커에 `/ENTRY` 인자를 넘겨주어야 합니다:
+
+[windows-subsystems]: https://docs.microsoft.com/en-us/cpp/build/reference/entry-entry-point-symbol
+
+```
+cargo rustc -- -C link-arg=/ENTRY:_start
+```
+
+Linux에서와는 다른 인자 형식을 통해 Windows의 링커는 Linux의 링커와 완전히 다른 프로그램이라는 것을 유추할 수 있습니다.
+
+이제 또 다른 링커 오류가 발생합니다:
+
+```
+error: linking with `link.exe` failed: exit code: 1221
+ |
+ = note: "C:\\Program Files (x86)\\…\\link.exe" […]
+ = note: LINK : fatal error LNK1221: a subsystem can't be inferred and must be
+ defined
+```
+
+이 오류가 뜨는 이유는 Windows 실행파일들은 여러 가지 [서브시스템][windows-subsystems]을 사용할 수 있기 때문입니다. 일반적인 프로그램들의 경우, 실행 시작 지점 함수의 이름에 따라 어떤 서브시스템을 사용하는지 추론합니다: 실행 시작 지점의 이름이 `main`인 경우 `CONSOLE` 서브시스템이 사용 중이라는 것을 알 수 있으며, 실행 시작 지점의 이름이 `WinMain`인 경우 `WINDOWS` 서브시스템이 사용 중이라는 것을 알 수 있습니다. 우리는 `_start`라는 새로운 이름의 실행 시작 지점을 이용할 것이기에, 우리가 어떤 서브시스템을 사용할 것인지 인자를 통해 명시적으로 링커에게 알려줘야 합니다:
+
+```
+cargo rustc -- -C link-args="/ENTRY:_start /SUBSYSTEM:console"
+```
+
+위 명령어에서는 `CONSOLE` 서브시스템을 서용했지만, `WINDOWS` 서브시스템을 적용해도 괜찮습니다. `-C link-arg` 인자를 반복해서 쓰는 대신, `-C link-args`인자를 이용해 여러 인자들을 빈칸으로 구분하여 전달할 수 있습니다.
+
+이 명령어를 통해 우리의 실행 파일을 Windows에서도 성공적으로 빌드할 수 있을 것입니다.
+
+#### macOS
+
+macOS에서는 아래와 같은 링커 오류가 출력됩니다 (일부 생략):
+
+```
+error: linking with `cc` failed: exit code: 1
+ |
+ = note: "cc" […]
+ = note: ld: entry point (_main) undefined. for architecture x86_64
+ clang: error: linker command failed with exit code 1 […]
+```
+
+위 오류 메세지는 우리에게 링커가 실행 시작 지점 함수의 기본값 이름 `main`을 찾지 못했다는 것을 알려줍니다 (macOS에서는 무슨 이유에서인지 모든 함수들의 이름 맨 앞에 `_` 문자가 앞에 붙습니다). 실행 시작 지점 함수의 이름을 `_start`로 새롭게 지정해주기 위해 아래와 같이 링커 인자 `-e`를 이용합니다:
+
+```
+cargo rustc -- -C link-args="-e __start"
+```
+
+`-e` 인자를 통해 실행 시작 지점 함수 이름을 설정합니다. macOS에서는 모든 함수의 이름 앞에 추가로 `_` 문자가 붙기에, 실행 시작 지점 함수의 이름을 `_start` 대신 `__start`로 지정해줍니다.
+
+이제 아래와 같은 링커 오류가 나타날 것입니다:
+
+```
+error: linking with `cc` failed: exit code: 1
+ |
+ = note: "cc" […]
+ = note: ld: dynamic main executables must link with libSystem.dylib
+ for architecture x86_64
+ clang: error: linker command failed with exit code 1 […]
+```
+
+macOS는 [공식적으로는 정적으로 링크된 실행파일을 지원하지 않으며][does not officially support statically linked binaries], 기본적으로 모든 프로그램이 `libSystem` 라이브러리를 링크하도록 요구합니다. 이러한 기본 요구사항을 무시하고 정적으로 링크된 실행 파일을 만드려면 링커에게 `-static` 인자를 주어야 합니다:
+
+[does not officially support statically linked binaries]: https://developer.apple.com/library/archive/qa/qa1118/_index.html
+
+```
+cargo rustc -- -C link-args="-e __start -static"
+```
+
+아직도 충분하지 않았는지, 세 번째 링커 오류가 아래와 같이 출력됩니다:
+
+```
+error: linking with `cc` failed: exit code: 1
+ |
+ = note: "cc" […]
+ = note: ld: library not found for -lcrt0.o
+ clang: error: linker command failed with exit code 1 […]
+```
+
+이 오류가 뜨는 이유는 macOS에서 모든 프로그램은 기본적으로 `crt0` (“C runtime zero”)를 링크하기 때문입니다. 이 오류는 우리가 Linux에서 봤던 오류와 유사한 것으로, 똑같이 링커에 `-nostartfiles` 인자를 주어 해결할 수 있습니다:
+
+```
+cargo rustc -- -C link-args="-e __start -static -nostartfiles"
+```
+
+이제는 우리의 프로그램을 macOS에서 성공적으로 빌드할 수 있을 것입니다.
+
+#### 플랫폼 별 빌드 명령어들을 하나로 통합하기
+
+위에서 살펴본 대로 호스트 플랫폼 별로 상이한 빌드 명령어가 필요한데, `.cargo/config.toml` 이라는 파일을 만들고 플랫폼 마다 필요한 상이한 인자들을 명시하여 여러 빌드 명령어들을 하나로 통합할 수 있습니다.
+
+```toml
+# in .cargo/config.toml
+
+[target.'cfg(target_os = "linux")']
+rustflags = ["-C", "link-arg=-nostartfiles"]
+
+[target.'cfg(target_os = "windows")']
+rustflags = ["-C", "link-args=/ENTRY:_start /SUBSYSTEM:console"]
+
+[target.'cfg(target_os = "macos")']
+rustflags = ["-C", "link-args=-e __start -static -nostartfiles"]
+```
+
+`rustflags`에 포함된 인자들은 `rustc`가 실행될 때마다 자동적으로 `rustc`에 인자로 전달됩니다. `.cargo/config.toml`에 대한 더 자세한 정보는 [공식 안내 문서](https://doc.rust-lang.org/cargo/reference/config.html)를 통해 확인해주세요.
+
+이제 `cargo build` 명령어 만으로 세 가지 플랫폼 어디에서도 우리의 프로그램을 성공적으로 빌드할 수 있습니다.
+
+#### 이렇게 하는 것이 괜찮나요?
+
+Linux, Windows 또는 macOS 위에서 동작하는 freestanding 실행파일을 빌드하는 것이 가능하긴 해도 좋은 방법은 아닙니다. 운영체제가 갖춰진 환경을 목표로 빌드를 한다면, 실행 파일 동작 시 다른 많은 조건들이 런타임에 의해 제공될 것이라는 가정 하에 빌드가 이뤄지기 때문입니다 (예: 실행 파일이 `_start` 함수가 호출되는 시점에 이미 스택이 초기화되어있을 것이라고 간주하고 작동합니다). C 런타임 없이는 실행 파일이 필요로 하는 조건들이 갖춰지지 않아 결국 세그멘테이션 오류가 나는 등 프로그램이 제대로 실행되지 못할 수 있습니다.
+
+이미 존재하는 운영체제 위에서 동작하는 최소한의 실행 파일을 만들고 싶다면, `libc`를 링크하고 [이 곳의 설명](https://doc.rust-lang.org/1.16.0/book/no-stdlib.html)에 따라 `#[start]` 속성을 설정하는 것이 더 좋은 방법일 것입니다.
+
+
+
+## 요약 {#summary}
+
+아래와 같은 최소한의 코드로 "freestanding" Rust 실행파일을 만들 수 있습니다:
+
+`src/main.rs`:
+
+```rust
+#![no_std] // Rust 표준 라이브러리를 링크하지 않도록 합니다
+#![no_main] // Rust 언어에서 사용하는 실행 시작 지점 (main 함수)을 사용하지 않습니다
+
+use core::panic::PanicInfo;
+
+#[no_mangle] // 이 함수의 이름을 mangle하지 않습니다
+pub extern "C" fn _start() -> ! {
+ // 링커는 기본적으로 '_start' 라는 이름을 가진 함수를 실행 시작 지점으로 삼기에,
+ // 이 함수는 실행 시작 지점이 됩니다
+ loop {}
+}
+
+/// 패닉이 일어날 경우, 이 함수가 호출됩니다.
+#[panic_handler]
+fn panic(_info: &PanicInfo) -> ! {
+ loop {}
+}
+```
+
+`Cargo.toml`:
+
+```toml
+[package]
+name = "crate_name"
+version = "0.1.0"
+authors = ["Author Name "]
+
+# `cargo build` 실행 시 이용되는 빌드 설정
+[profile.dev]
+panic = "abort" # 패닉 시 스택 되감기를 하지 않고 바로 프로그램 종료
+
+# `cargo build --release` 실행 시 이용되는 빌드 설정
+[profile.release]
+panic = "abort" # 패닉 시 스택 되감기를 하지 않고 바로 프로그램 종료
+```
+
+이 실행 파일을 빌드하려면, `thumbv7em-none-eabihf`와 같은 bare metal 시스템 환경을 목표로 컴파일해야 합니다:
+
+```
+cargo build --target thumbv7em-none-eabihf
+```
+
+또다른 방법으로, 각 호스트 시스템마다 추가적인 링커 인자들을 전달해주어 호스트 시스템 환경을 목표로 컴파일할 수도 있습니다:
+
+```bash
+# Linux
+cargo rustc -- -C link-arg=-nostartfiles
+# Windows
+cargo rustc -- -C link-args="/ENTRY:_start /SUBSYSTEM:console"
+# macOS
+cargo rustc -- -C link-args="-e __start -static -nostartfiles"
+```
+
+주의할 것은 이것이 정말 최소한의 freestanding Rust 실행 파일이라는 것입니다. 실행 파일은 여러 가지 조건들을 가정하는데, 그 예로 실행파일 동작 시 `_start` 함수가 호출될 때 스택이 초기화되어 있을 것을 가정합니다. **이 freestanding 실행 파일을 이용해 실제로 유용한 작업을 처리하려면 아직 더 많은 코드 구현이 필요합니다**.
+
+## 다음 단계는 무엇일까요?
+
+[다음 포스트][next post]에서는 우리의 freestanding 실행 파일을 최소한의 기능을 갖춘 운영체제 커널로 만드는 과정을 단게별로 설명할 것입니다.
+예시로 커스텀 시스템 환경을 설정하는 방법, 우리의 실행 파일을 부트로더와 합치는 방법, 그리고 화면에 메세지를 출력하는 방법 등에 대해 다루겠습니다.
+
+[next post]: @/edition-2/posts/02-minimal-rust-kernel/index.md
diff --git a/blog/content/edition-2/posts/_index.ko.md b/blog/content/edition-2/posts/_index.ko.md
new file mode 100644
index 00000000..c7079c40
--- /dev/null
+++ b/blog/content/edition-2/posts/_index.ko.md
@@ -0,0 +1,7 @@
++++
+title = "Posts"
+sort_by = "weight"
+insert_anchor_links = "left"
+render = false
+page_template = "edition-2/page.html"
++++
From 7954a9f424194379da5ff6496987707943612732 Mon Sep 17 00:00:00 2001
From: Ford Smith <41210901+Programatic@users.noreply.github.com>
Date: Thu, 17 Mar 2022 23:42:00 -0400
Subject: [PATCH 006/107] Set CS register with non-deprecated function
set_cs is deprecated and CS::set_reg is preferred. Update the blog to reflect this change according to the docs.
---
blog/content/edition-2/posts/06-double-faults/index.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/blog/content/edition-2/posts/06-double-faults/index.md b/blog/content/edition-2/posts/06-double-faults/index.md
index 02a13d01..16ea0d05 100644
--- a/blog/content/edition-2/posts/06-double-faults/index.md
+++ b/blog/content/edition-2/posts/06-double-faults/index.md
@@ -368,7 +368,7 @@ pub fn init() {
GDT.0.load();
unsafe {
- set_cs(GDT.1.code_selector);
+ CS::set_reg(GDT.1.code_selector);
load_tss(GDT.1.tss_selector);
}
}
From c393e4442dc6cbe55507a69ace11fed9581fd9c5 Mon Sep 17 00:00:00 2001
From: Ford Smith <41210901+Programatic@users.noreply.github.com>
Date: Sat, 19 Mar 2022 13:02:16 -0400
Subject: [PATCH 007/107] Update
blog/content/edition-2/posts/06-double-faults/index.md
Accidentally forgot to update the use statements.
Co-authored-by: ruhuang
---
blog/content/edition-2/posts/06-double-faults/index.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/blog/content/edition-2/posts/06-double-faults/index.md b/blog/content/edition-2/posts/06-double-faults/index.md
index 16ea0d05..6997a36b 100644
--- a/blog/content/edition-2/posts/06-double-faults/index.md
+++ b/blog/content/edition-2/posts/06-double-faults/index.md
@@ -365,7 +365,8 @@ Now we can use the selectors to reload the `cs` segment register and load our `T
pub fn init() {
use x86_64::instructions::segmentation::set_cs;
use x86_64::instructions::tables::load_tss;
-
+// use x86_64::instructions::segmentation::set_cs;
+use x86_64::instructions::segmentation::{CS, Segment};
GDT.0.load();
unsafe {
CS::set_reg(GDT.1.code_selector);
From f56b4a0a32cdfa230351e1b086950bc64be46035 Mon Sep 17 00:00:00 2001
From: Ford Smith <41210901+Programatic@users.noreply.github.com>
Date: Sat, 19 Mar 2022 13:03:40 -0400
Subject: [PATCH 008/107] Fix formatting and remove commented use statements
---
blog/content/edition-2/posts/06-double-faults/index.md | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/blog/content/edition-2/posts/06-double-faults/index.md b/blog/content/edition-2/posts/06-double-faults/index.md
index 6997a36b..972468b7 100644
--- a/blog/content/edition-2/posts/06-double-faults/index.md
+++ b/blog/content/edition-2/posts/06-double-faults/index.md
@@ -363,10 +363,9 @@ Now we can use the selectors to reload the `cs` segment register and load our `T
// in src/gdt.rs
pub fn init() {
- use x86_64::instructions::segmentation::set_cs;
use x86_64::instructions::tables::load_tss;
-// use x86_64::instructions::segmentation::set_cs;
-use x86_64::instructions::segmentation::{CS, Segment};
+ use x86_64::instructions::segmentation::{CS, Segment};
+
GDT.0.load();
unsafe {
CS::set_reg(GDT.1.code_selector);
From 58eca98ae19412c0923d4de82beec6e2985b862a Mon Sep 17 00:00:00 2001
From: Julien <32807437+julien-me@users.noreply.github.com>
Date: Fri, 8 Apr 2022 01:04:38 +0800
Subject: [PATCH 009/107] Fix typo in '04-testing' (#1095)
---
blog/content/edition-2/posts/04-testing/index.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/blog/content/edition-2/posts/04-testing/index.md b/blog/content/edition-2/posts/04-testing/index.md
index 94b8d4ec..21b82d92 100644
--- a/blog/content/edition-2/posts/04-testing/index.md
+++ b/blog/content/edition-2/posts/04-testing/index.md
@@ -1014,7 +1014,7 @@ Apart from creating `should_panic` tests, disabling the `harness` attribute can
## Summary
-Testing is a very useful technique to ensure that certain components have a desired behavior. Even if they cannot show the absence of bugs, they're still an useful tool for finding them and especially for avoiding regressions.
+Testing is a very useful technique to ensure that certain components have a desired behavior. Even if they cannot show the absence of bugs, they're still a useful tool for finding them and especially for avoiding regressions.
This post explained how to set up a test framework for our Rust kernel. We used the custom test frameworks feature of Rust to implement support for a simple `#[test_case]` attribute in our bare-metal environment. By using the `isa-debug-exit` device of QEMU, our test runner can exit QEMU after running the tests and report the test status out. To print error messages to the console instead of the VGA buffer, we created a basic driver for the serial port.
From ee893a7ba035ae2dcb7fe334227c46c27a71bbe6 Mon Sep 17 00:00:00 2001
From: SilensAngelusNex
Date: Thu, 7 Apr 2022 12:04:43 -0500
Subject: [PATCH 010/107] Fix typo (#1094)
I think you mean "voluntarily" here?
---
blog/content/edition-2/posts/05-cpu-exceptions/index.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/blog/content/edition-2/posts/05-cpu-exceptions/index.md b/blog/content/edition-2/posts/05-cpu-exceptions/index.md
index 4bf60143..2a59311a 100644
--- a/blog/content/edition-2/posts/05-cpu-exceptions/index.md
+++ b/blog/content/edition-2/posts/05-cpu-exceptions/index.md
@@ -135,7 +135,7 @@ It's a [type alias] for an `extern "x86-interrupt" fn` type. The `extern` keywor
## The Interrupt Calling Convention
Exceptions are quite similar to function calls: The CPU jumps to the first instruction of the called function and executes it. Afterwards the CPU jumps to the return address and continues the execution of the parent function.
-However, there is a major difference between exceptions and function calls: A function call is invoked voluntary by a compiler inserted `call` instruction, while an exception might occur at _any_ instruction. In order to understand the consequences of this difference, we need to examine function calls in more detail.
+However, there is a major difference between exceptions and function calls: A function call is invoked voluntarily by a compiler inserted `call` instruction, while an exception might occur at _any_ instruction. In order to understand the consequences of this difference, we need to examine function calls in more detail.
[Calling conventions] specify the details of a function call. For example, they specify where function parameters are placed (e.g. in registers or on the stack) and how results are returned. On x86_64 Linux, the following rules apply for C functions (specified in the [System V ABI]):
From f6777e621dd314eab6679d62cb114105a2ab8536 Mon Sep 17 00:00:00 2001
From: ruhuang
Date: Fri, 8 Apr 2022 01:05:25 +0800
Subject: [PATCH 011/107] Replace deprecated inclusive pattern syntax (#1091)
I am currently using rustc 1.61.0-nightly (10913c000 2022-03-03). The original English blog is correct but this translation is deprecated.
---
blog/content/edition-2/posts/03-vga-text-buffer/index.zh-CN.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/blog/content/edition-2/posts/03-vga-text-buffer/index.zh-CN.md b/blog/content/edition-2/posts/03-vga-text-buffer/index.zh-CN.md
index c5eaf2db..2b4378b8 100644
--- a/blog/content/edition-2/posts/03-vga-text-buffer/index.zh-CN.md
+++ b/blog/content/edition-2/posts/03-vga-text-buffer/index.zh-CN.md
@@ -213,7 +213,7 @@ impl Writer {
for byte in s.bytes() {
match byte {
// 可以是能打印的 ASCII 码字节,也可以是换行符
- 0x20...0x7e | b'\n' => self.write_byte(byte),
+ 0x20..=0x7e | b'\n' => self.write_byte(byte),
// 不包含在上述范围之内的字节
_ => self.write_byte(0xfe),
}
From 5217bc27b7b7a7aa9c19037d5c819593b4e46db3 Mon Sep 17 00:00:00 2001
From: Hofer-Julian <30049909+Hofer-Julian@users.noreply.github.com>
Date: Thu, 7 Apr 2022 19:08:10 +0200
Subject: [PATCH 012/107] Add missing `and` (#1093)
---
blog/content/edition-2/posts/09-paging-implementation/index.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/blog/content/edition-2/posts/09-paging-implementation/index.md b/blog/content/edition-2/posts/09-paging-implementation/index.md
index e9803ff2..1ddc7e50 100644
--- a/blog/content/edition-2/posts/09-paging-implementation/index.md
+++ b/blog/content/edition-2/posts/09-paging-implementation/index.md
@@ -447,7 +447,7 @@ When we run it, we see the following output:
We see that there are various non-empty entries, which all map to different level 3 tables. There are so many regions because kernel code, kernel stack, the physical memory mapping, and the boot information all use separate memory areas.
-To traverse the page tables further and take a look at a level 3 table, we can take the mapped frame of an entry convert it to a virtual address again:
+To traverse the page tables further and take a look at a level 3 table, we can take the mapped frame of an entry and convert it to a virtual address again:
```rust
// in the `for` loop in src/main.rs
From b13f261770dd0ac06f972f5ad82311f10f38ed86 Mon Sep 17 00:00:00 2001
From: Bruno
Date: Mon, 18 Apr 2022 07:06:52 -0300
Subject: [PATCH 013/107] fix: typo (#1103)
---
blog/content/edition-2/posts/12-async-await/index.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/blog/content/edition-2/posts/12-async-await/index.md b/blog/content/edition-2/posts/12-async-await/index.md
index fbe7e69f..8a996b13 100644
--- a/blog/content/edition-2/posts/12-async-await/index.md
+++ b/blog/content/edition-2/posts/12-async-await/index.md
@@ -1499,7 +1499,7 @@ Instead of storing tasks in a [`VecDeque`] like we did for our `SimpleExecutor`,
The `task_queue` field is an [`ArrayQueue`] of task IDs, wrapped into the [`Arc`] type that implements _reference counting_. Reference counting makes it possible to share ownership of the value between multiple owners. It works by allocating the value on the heap and counting the number of active references to it. When the number of active references reaches zero, the value is no longer needed and can be deallocated.
-We use this `Arc` type for the `task_queue` because it will be shared between the executor and wakers. The idea is that the wakers push the ID of the woken task to the queue. The executor sits on the receiving end of the queue, retrieves the woken tasks by their ID from the `tasks` map, and then runs them. The reason for using a fixed-size queue instead of an unbounded queue such as [`SegQueue`] is that interrupt handlers that should not allocate will push to this queue.
+We use this `Arc` type for the `task_queue` because it will be shared between the executor and wakers. The idea is that the wakers push the ID of the woken task to the queue. The executor sits on the receiving end of the queue, retrieves the woken tasks by their ID from the `tasks` map, and then runs them. The reason for using a fixed-size queue instead of an unbounded queue such as [`SegQueue`] is that interrupt handlers should not allocate on push to this queue.
In addition to the `task_queue` and the `tasks` map, the `Executor` type has a `waker_cache` field that is also a map. This map caches the [`Waker`] of a task after its creation. This has two reasons: First, it improves performance by reusing the same waker for multiple wake-ups of the same task instead of creating a new waker each time. Second, it ensures that reference-counted wakers are not deallocated inside interrupt handlers because it could lead to deadlocks (there are more details on this below).
From d8dc4f5395df5dbe4a8e1cadee7580a3898e9e84 Mon Sep 17 00:00:00 2001
From: JOE1994
Date: Sun, 10 Apr 2022 22:40:41 -0400
Subject: [PATCH 014/107] Korean translation for chapter 2
Special thanks to @Quqqu for reviewing the translation!
---
.../disable-red-zone/index.ko.md | 29 +
.../disable-simd/index.ko.md | 45 ++
.../posts/02-minimal-rust-kernel/index.ko.md | 511 ++++++++++++++++++
3 files changed, 585 insertions(+)
create mode 100644 blog/content/edition-2/posts/02-minimal-rust-kernel/disable-red-zone/index.ko.md
create mode 100644 blog/content/edition-2/posts/02-minimal-rust-kernel/disable-simd/index.ko.md
create mode 100644 blog/content/edition-2/posts/02-minimal-rust-kernel/index.ko.md
diff --git a/blog/content/edition-2/posts/02-minimal-rust-kernel/disable-red-zone/index.ko.md b/blog/content/edition-2/posts/02-minimal-rust-kernel/disable-red-zone/index.ko.md
new file mode 100644
index 00000000..b817732e
--- /dev/null
+++ b/blog/content/edition-2/posts/02-minimal-rust-kernel/disable-red-zone/index.ko.md
@@ -0,0 +1,29 @@
++++
+title = "Red Zone 기능 해제하기"
+weight = 1
+path = "ko/red-zone"
+template = "edition-2/extra.html"
++++
+
+[red zone]은 [System V ABI]에서 사용 가능한 최적화 기법으로, 스택 포인터를 변경하지 않은 채로 함수들이 임시적으로 스택 프레임 아래의 128 바이트 공간을 사용할 수 있게 해줍니다:
+
+[red zone]: https://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64#the-red-zone
+[System V ABI]: https://wiki.osdev.org/System_V_ABI
+
+
+
+
+
+위 사진은 `n`개의 지역 변수를 가진 함수의 스택 프레임을 보여줍니다. 함수가 호출되었을 때, 함수의 반환 주소 및 지역 변수들을 스택에 저장할 수 있도록 스택 포인터의 값이 조정됩니다.
+
+red zone은 조정된 스택 포인터 아래의 128바이트의 메모리 구간을 가리킵니다. 함수가 또 다른 함수를 호출하지 않는 구간에서만 사용하는 임시 데이터의 경우, 함수가 이 구간에 해당 데이터를 저장하는 데 이용할 수 있습니다. 따라서 스택 포인터를 조정하기 위해 필요한 명령어 두 개를 생략할 수 있는 상황이 종종 있습니다 (예: 다른 함수를 호출하지 않는 함수).
+
+하지만 이 최적화 기법을 사용하는 도중 소프트웨어 예외(exception) 혹은 하드웨어 인터럽트가 일어날 경우 큰 문제가 생깁니다. 함수가 red zone을 사용하던 도중 예외가 발생한 상황을 가정해보겠습니다:
+
+
+
+CPU와 예외 처리 핸들러가 red zone에 있는 데이터를 덮어씁니다. 하지만 이 데이터는 인터럽트된 함수가 사용 중이었던 것입니다. 따라서 예외 처리 핸들러로부터 반환하여 다시 인터럽트된 함수가 계속 실행되게 되었을 때 변경된 red zone의 데이터로 인해 함수가 오작동할 수 있습니다. 이런 현상으로 인해 [디버깅하는 데에 몇 주씩 걸릴 수 있는 이상한 버그][take weeks to debug]가 발생할지도 모릅니다.
+
+[take weeks to debug]: https://forum.osdev.org/viewtopic.php?t=21720
+
+미래에 예외 처리 로직을 구현할 때 이러한 오류가 일어나는 것을 피하기 위해 우리는 미리 red zone 최적화 기법을 해제한 채로 프로젝트를 진행할 것입니다. 컴파일 대상 환경 설정 파일에 `"disable-redzone": true` 줄을 추가함으로써 해당 기능을 해제할 수 있습니다.
diff --git a/blog/content/edition-2/posts/02-minimal-rust-kernel/disable-simd/index.ko.md b/blog/content/edition-2/posts/02-minimal-rust-kernel/disable-simd/index.ko.md
new file mode 100644
index 00000000..0d3d9476
--- /dev/null
+++ b/blog/content/edition-2/posts/02-minimal-rust-kernel/disable-simd/index.ko.md
@@ -0,0 +1,45 @@
++++
+title = "SIMD 해제하기"
+weight = 2
+path = "ko/disable-simd"
+template = "edition-2/extra.html"
++++
+
+[Single Instruction Multiple Data (SIMD)] 명령어들은 여러 데이터 word에 동시에 덧셈 등의 작업을 실행할 수 있으며, 이를 통해 프로그램의 실행 시간을 상당히 단축할 수 있습니다. `x86_64` 아키텍처는 다양한 SIMD 표준들을 지원합니다:
+
+[Single Instruction Multiple Data (SIMD)]: https://en.wikipedia.org/wiki/SIMD
+
+
+
+- [MMX]: _Multi Media Extension_ 명령어 집합은 1997년에 등장하였으며, `mm0`에서 `mm7`까지 8개의 64비트 레지스터들을 정의합니다. 이 레지스터들은 그저 [x87 부동 소수점 장치][x87 floating point unit]의 레지스터들을 가리키는 별칭입니다.
+- [SSE]: _Streaming SIMD Extensions_ 명령어 집합은 1999년에 등장하였습니다. 부동 소수점 연산용 레지스터를 재사용하는 대신 새로운 레지스터 집합을 도입했습니다. `xmm0`에서 `xmm15`까지 16개의 새로운 128비트 레지스터를 정의합니다.
+- [AVX]: _Advanced Vector Extensions_ 은 SSE에 추가로 멀티미디어 레지스터의 크기를 늘리는 확장 표준입니다. `ymm0`에서 `ymm15`까지 16개의 새로운 256비트 레지스터를 정의합니다. `ymm` 레지스터들은 기존의 `xmm` 레지스터를 확장합니다 (`xmm0`이 `ymm0` 레지스터의 하부 절반을 차지하는 식으로 다른 15개의 짝에도 같은 방식의 확장이 적용됩니다).
+
+[MMX]: https://en.wikipedia.org/wiki/MMX_(instruction_set)
+[x87 floating point unit]: https://en.wikipedia.org/wiki/X87
+[SSE]: https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions
+[AVX]: https://en.wikipedia.org/wiki/Advanced_Vector_Extensions
+
+이러한 SIMD 표준들을 사용하면 프로그램 실행 속도를 많이 향상할 수 있는 경우가 많습니다. 우수한 컴파일러는 [자동 벡터화 (auto-vectorization)][auto-vectorization]이라는 과정을 통해 일반적인 반복문을 SIMD 코드로 변환할 수 있습니다.
+
+[auto-vectorization]: https://en.wikipedia.org/wiki/Automatic_vectorization
+
+하지만 운영체제 커널은 크기가 큰 SIMD 레지스터들을 사용하기에 문제가 있습니다. 그 이유는 하드웨어 인터럽트가 일어날 때마다 커널이 사용 중이던 레지스터들의 상태를 전부 메모리에 백업해야 하기 때문입니다. 이렇게 하지 않으면 인터럽트 되었던 프로그램의 실행이 다시 진행될 때 인터럽트 당시의 프로그램 상태를 보존할 수가 없습니다. 따라서 커널이 SIMD 레지스터들을 사용하는 경우, 커널이 백업해야 하는 데이터 양이 많이 늘어나게 되어 (512-1600 바이트) 커널의 성능이 눈에 띄게 나빠집니다. 이러한 성능 손실을 피하기 위해서 `sse` 및 `mmx` 기능을 해제하는 것이 바람직합니다 (`avx` 기능은 해제된 상태가 기본 상태입니다).
+
+컴파일 대상 환경 설정 파일의 `features` 필드를 이용해 해당 기능들을 해제할 수 있습니다. `mmx` 및 `sse` 기능을 해제하려면 아래와 같이 해당 기능 이름 앞에 빼기 기호를 붙여주면 됩니다:
+
+```json
+"features": "-mmx,-sse"
+```
+
+## 부동소수점 (Floating Point)
+
+우리의 입장에서는 안타깝게도, `x86_64` 아키텍처는 부동 소수점 계산에 SSE 레지스터를 사용합니다. 따라서 SSE 기능이 해제된 상태에서 부동 소수점 계산을 컴파일하면 LLVM이 오류를 일으킵니다. Rust의 core 라이브러리는 이미 부동 소수점 숫자들을 사용하기에 (예: `f32` 및 `f64` 에 대한 각종 trait들을 정의함), 우리의 커널에서 부동 소수점 계산을 피하더라도 부동 소수점 계산을 컴파일하는 것을 피할 수 없습니다.
+
+다행히도 LLVM은 `soft-float` 기능을 지원합니다. 이 기능을 통해 정수 계만으로 모든 부동소수점 연산 결과를 모방하여 산출할 수 있습니다. 일반 부동소수점 계산보다는 느리겠지만, 이 기능을 통해 우리의 커널에서도 SSE 기능 없이 부동소수점을 사용할 수 있습니다.
+
+우리의 커널에서 `soft-float` 기능을 사용하려면 컴파일 대상 환경 설정 파일의 `features` 필드에 덧셈 기호와 함께 해당 기능의 이름을 적어주면 됩니다:
+
+```json
+"features": "-mmx,-sse,+soft-float"
+```
diff --git a/blog/content/edition-2/posts/02-minimal-rust-kernel/index.ko.md b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.ko.md
new file mode 100644
index 00000000..046eab7e
--- /dev/null
+++ b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.ko.md
@@ -0,0 +1,511 @@
++++
+title = "최소 기능을 갖춘 커널"
+weight = 2
+path = "ko/minimal-rust-kernel"
+date = 2018-02-10
+
+[extra]
+chapter = "Bare Bones"
+# Please update this when updating the translation
+translation_based_on_commit = "c1af4e31b14e562826029999b9ab1dce86396b93"
+# GitHub usernames of the people that translated this post
+translators = ["JOE1994"]
++++
+
+이번 포스트에서는 x86 아키텍처에서 최소한의 기능으로 동작하는 64비트 Rust 커널을 함께 만들 것입니다. 지난 포스트 [Rust로 'Freestanding 실행파일' 만들기][freestanding Rust binary] 에서 작업한 것을 토대로 부팅 가능한 디스크 이미지를 만들고 화면에 데이터를 출력해볼 것입니다.
+
+[freestanding Rust binary]: @/edition-2/posts/01-freestanding-rust-binary/index.md
+
+
+
+이 블로그는 [GitHub 저장소][GitHub]에서 오픈 소스로 개발되고 있으니, 문제나 문의사항이 있다면 저장소의 'Issue' 기능을 이용해 제보해주세요. [페이지 맨 아래][at the bottom]에 댓글을 남기실 수도 있습니다. 이 포스트와 관련된 모든 소스 코드는 저장소의 [`post-02 브랜치`][post branch]에서 확인하실 수 있습니다.
+
+[GitHub]: https://github.com/phil-opp/blog_os
+[at the bottom]: #comments
+
+[post branch]: https://github.com/phil-opp/blog_os/tree/post-02
+
+
+
+## 부팅 과정 {#the-boot-process}
+
+전원이 켜졌을 때 컴퓨터가 맨 처음 하는 일은 바로 마더보드의 [롬 (ROM)][ROM]에 저장된 펌웨어 코드를 실행하는 것입니다.
+이 코드는 [시동 자체 시험][power-on self-test]을 진행하고, 사용 가능한 램 (RAM)을 확인하며, CPU 및 하드웨어의 초기화 작업을 진행합니다.
+그 후에는 부팅 가능한 디스크를 감지하고 운영체제 커널을 부팅하기 시작합니다.
+
+[ROM]: https://en.wikipedia.org/wiki/Read-only_memory
+[power-on self-test]: https://en.wikipedia.org/wiki/Power-on_self-test
+
+x86 시스템에는 두 가지 펌웨어 표준이 존재합니다: 하나는 "Basic Input/Output System"(**[BIOS]**)이고 다른 하나는 "Unified Extensible Firmware Interface" (**[UEFI]**) 입니다. BIOS 표준은 구식 표준이지만, 간단하며 1980년대 이후 출시된 어떤 x86 하드웨어에서도 지원이 잘 됩니다. UEFI는 신식 표준으로서 더 많은 기능들을 갖추었지만, 제대로 설정하고 구동시키기까지의 과정이 더 복잡합니다 (적어도 제 주관적 입장에서는 그렇게 생각합니다).
+
+[BIOS]: https://en.wikipedia.org/wiki/BIOS
+[UEFI]: https://en.wikipedia.org/wiki/Unified_Extensible_Firmware_Interface
+
+우리가 만들 운영체제에서는 BIOS 표준만을 지원할 것이지만, UEFI 표준도 지원하고자 하는 계획이 있습니다. UEFI 표준을 지원할 수 있도록 도와주시고 싶다면 [해당 깃헙 이슈][Github issue]를 확인해주세요.
+
+### BIOS 부팅
+
+UEFI 표준으로 동작하는 최신 기기들도 가상 BIOS를 지원하기에, 존재하는 거의 모든 x86 시스템들이 BIOS 부팅을 지원합니다. 덕분에 하나의 BIOS 부팅 로직을 구현하면 여태 만들어진 거의 모든 컴퓨터를 부팅시킬 수 있습니다. 동시에 이 방대한 호환성이 BIOS의 가장 큰 약점이기도 한데,
+그 이유는 1980년대의 구식 부트로더들에 대한 하위 호환성을 유지하기 위해 부팅 전에는 항상 CPU를 16비트 호환 모드 ([real mode]라고도 불림)로 설정해야 하기 때문입니다.
+
+이제 BIOS 부팅 과정의 첫 단계부터 살펴보겠습니다:
+
+여러분이 컴퓨터의 전원을 켜면, 제일 먼저 컴퓨터는 마더보드의 특별한 플래시 메모리로부터 BIOS 이미지를 로드합니다. BIOS 이미지는 자가 점검 및 하드웨어 초기화 작업을 처리한 후에 부팅 가능한 디스크가 있는지 탐색합니다. 부팅 가능한 디스크가 있다면, 제어 흐름은 해당 디스크의 _부트로더 (bootloader)_ 에게 넘겨집니다. 이 부트로더는 디스크의 가장 앞 주소 영역에 저장되는 512 바이트 크기의 실행 파일입니다. 대부분의 부트로더들의 경우 로직을 저장하는 데에 512 바이트보다 더 큰 용량이 필요하기에, 부트로더의 로직을 둘로 쪼개어 첫 단계 로직을 첫 512 바이트 안에 담고, 두 번째 단계 로직은 첫 단계 로직에 의해 로드된 이후 실행됩니다.
+
+부트로더는 커널 이미지가 디스크의 어느 주소에 저장되어있는지 알아낸 후 메모리에 커널 이미지를 로드해야 합니다. 그다음 CPU를 16비트 [real mode]에서 32비트 [protected mode]로 전환하고, 그 후에 다시 CPU를 64비트 [long mode]로 전환한 이후부터 64비트 레지스터 및 메인 메모리의 모든 주소를 사용할 수 있게 됩니다. 부트로더가 세 번째로 할 일은 BIOS로부터 메모리 매핑 정보 등의 필요한 정보를 알아내어 운영체제 커널에 전달하는 것입니다.
+
+[real mode]: https://en.wikipedia.org/wiki/Real_mode
+[protected mode]: https://en.wikipedia.org/wiki/Protected_mode
+[long mode]: https://en.wikipedia.org/wiki/Long_mode
+[memory segmentation]: https://en.wikipedia.org/wiki/X86_memory_segmentation
+
+부트로더를 작성하는 것은 상당히 성가신 작업인데, 그 이유는 어셈블리 코드도 작성해야 하고 "A 레지스터에 B 값을 저장하세요" 와 같이 원리를 단번에 이해하기 힘든 작업이 많이 수반되기 때문입니다. 따라서 이 포스트에서는 부트로더를 만드는 것 자체를 다루지는 않고, 대신 운영체제 커널의 맨 앞에 부트로더를 자동으로 추가해주는 [bootimage]라는 도구를 제공합니다.
+
+[bootimage]: https://github.com/rust-osdev/bootimage
+
+본인의 부트로더를 직접 작성하는 것에 흥미가 있으시다면, 이 주제로 여러 포스트가 나올 계획이니 기대해주세요!
+
+#### Multiboot 표준
+
+운영체제마다 부트로더 구현 방법이 다르다면 한 운영체제에서 동작하는 부트로더가 다른 운영체제에서는 호환이 되지 않을 것입니다. 이런 불편한 점을 막기 위해 [Free Software Foundation]에서 1995년에 [Multiboot]라는 부트로더 표준을 개발했습니다. 이 표준은 부트로더와 운영체제 사이의 상호 작용 방식을 정의하였는데, 이 Multiboot 표준에 따르는 부트로더는 Multiboot 표준을 지원하는 어떤 운영체제에서도 동작합니다. 이 표준을 구현한 대표적인 예로 리눅스 시스템에서 가장 인기 있는 부트로더인 [GNU GRUB]이 있습니다.
+
+[Free Software Foundation]: https://en.wikipedia.org/wiki/Free_Software_Foundation
+[Multiboot]: https://wiki.osdev.org/Multiboot
+[GNU GRUB]: https://en.wikipedia.org/wiki/GNU_GRUB
+
+운영체제 커널이 Multiboot를 지원하게 하려면 커널 파일의 맨 앞에 [Multiboot 헤더][Multiboot header]를 삽입해주면 됩니다. 이렇게 하면 GRUB에서 운영체제를 부팅하는 것이 매우 쉬워집니다. 하지만 GRUB 및 Multiboot 표준도 몇 가지 문제점들을 안고 있습니다:
+
+[Multiboot header]: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#OS-image-format
+
+- 오직 32비트 protected mode만을 지원합니다. 64비트 long mode를 이용하고 싶다면 CPU 설정을 별도로 변경해주어야 합니다.
+- Multiboot 표준 및 GRUB은 부트로더 구현의 단순화를 우선시하여 개발되었기에, 이에 호응하는 커널 측의 구현이 번거로워진다는 단점이 있습니다. 예를 들어, GRUB이 Multiboot 헤더를 제대로 찾을 수 있으려면 커널 측에서 [조정된 기본 페이지 크기 (adjusted default page size)][adjusted default page size]를 링크하는 것이 강제됩니다. 또한, 부트로더가 커널로 전달하는 [부팅 정보][boot information]는 적절한 추상 레벨에서 표준화된 형태로 전달되는 대신 하드웨어 아키텍처마다 상이한 형태로 제공됩니다.
+- GRUB 및 Multiboot 표준에 대한 문서화 작업이 덜 되어 있습니다.
+- GRUB이 호스트 시스템에 설치되어 있어야만 커널 파일로부터 부팅 가능한 디스크 이미지를 만들 수 있습니다. 이 때문에 Windows 및 Mac에서는 부트로더를 개발하는 것이 Linux보다 어렵습니다.
+
+[adjusted default page size]: https://wiki.osdev.org/Multiboot#Multiboot_2
+[boot information]: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format
+
+이러한 단점들 때문에 우리는 GRUB 및 Multiboot 표준을 사용하지 않을 것입니다. 하지만 미래에 우리의 [bootimage] 도구가 Multiboot 표준을 지원하도록 하는 것도 계획 중입니다. Multiboot 표준을 지원하는 운영체제를 커널을 개발하는 것에 관심이 있으시다면, 이 블로그 시리즈의 [첫 번째 에디션][first edition]을 확인해주세요.
+
+[first edition]: @/edition-1/_index.md
+
+### UEFI
+
+(아직 UEFI 표준을 지원하지 않지만, UEFI 표준을 지원할 수 있도록 도와주시려면 해당 [깃헙 이슈](https://github.com/phil-opp/blog_os/issues/349)에 댓글을 남겨주세요!)
+
+## 최소한의 기능을 갖춘 운영체제 커널
+컴퓨터의 부팅 과정에 대해서 대략적으로 알게 되었으니, 이제 우리 스스로 최소한의 기능을 갖춘 운영체제 커널을 작성해볼 차례입니다. 우리의 목표는 부팅 이후 화면에 "Hello World!" 라는 메세지를 출력하는 디스크 이미지를 만드는 것입니다. 지난 포스트에서 만든 [freestanding Rust 실행파일][freestanding Rust binary] 을 토대로 작업을 이어나갑시다.
+
+지난 포스트에서 우리는 `cargo`를 통해 freestanding 실행파일을 만들었었는데, 호스트 시스템의 운영체제에 따라 프로그램 실행 시작 지점의 이름 및 컴파일 인자들을 다르게 설정해야 했습니다. 이것은 `cargo`가 기본적으로 _호스트 시스템_ (여러 분이 실행 중인 컴퓨터 시스템) 을 목표로 빌드하기 때문이었습니다. 우리의 커널은 다른 운영체제 (예를 들어 Windows) 위에서 실행될 것이 아니기에, 호스트 시스템에 설정 값을 맞추는 대신에 우리가 명확히 정의한 _목표 시스템 (target system)_ 을 목표로 컴파일할 것입니다.
+
+### Rust Nightly 설치하기 {#installing-rust-nightly}
+Rust는 _stable_, _beta_ 그리고 _nightly_ 이렇게 세 가지의 채널을 통해 배포됩니다. Rust Book에 [세 채널들 간의 차이에 대해 잘 정리한 챕터]((https://doc.rust-lang.org/book/appendix-07-nightly-rust.html#choo-choo-release-channels-and-riding-the-trains))가 있습니다. 운영체제를 빌드하기 위해서는 _nightly_ 채널에서만 제공하는 실험적인 기능들을 이용해야 하기에 _nightly_ 버전의 Rust를 설치하셔야 합니다.
+
+여러 버전의 Rust 언어 설치 파일들을 관리할 때 [rustup]을 사용하는 것을 강력 추천합니다. rustup을 통해 nightly, beta 그리고 stable 컴파일러들을 모두 설치하고 업데이트할 수 있습니다. `rustup override set nightly` 명령어를 통해 현재 디렉토리에서 항상 nightly 버전의 Rust를 사용하도록 설정할 수 있습니다.
+`rust-toolchain`이라는 파일을 프로젝트 루트 디렉토리에 만들고 이 파일에 `nightly`라는 텍스트를 적어 놓아도 같은 효과를 볼 수 있습니다. `rustc --version` 명령어를 통해 현재 nightly 버전이 설치되어 있는지 확인할 수 있습니다 (출력되는 버전 넘버가 `-nightly`라는 텍스트로 끝나야 합니다).
+
+[rustup]: https://www.rustup.rs/
+
+nightly 컴파일러는 _feature 플래그_ 를 소스코드의 맨 위에 추가함으로써 여러 실험적인 기능들을 선별해 이용할 수 있게 해줍니다. 예를 들어, `#![feature(asm)]` 를 `main.rs`의 맨 위에 추가하면 [`asm!` 매크로][`asm!` macro]를 사용할 수 있습니다. `asm!` 매크로는 인라인 어셈블리 코드를 작성할 때 사용합니다.
+이런 실험적인 기능들은 말 그대로 "실험적인" 기능들이기에 미래의 Rust 버전들에서는 예고 없이 변경되거나 삭제될 수도 있습니다. 그렇기에 우리는 이 실험적인 기능들을 최소한으로만 사용할 것입니다.
+
+[`asm!` macro]: https://doc.rust-lang.org/unstable-book/library-features/asm.html
+
+### 컴파일 대상 정의하기
+Cargo는 `--target` 인자를 통해 여러 컴파일 대상 시스템들을 지원합니다. 컴파일 대상은 소위 _[target triple]_ 을 통해 표현되는데, CPU 아키텍쳐와 CPU 공급 업체, 운영체제, 그리고 [ABI]를 파악할 수 있습니다. 예를 들어 `x86_64-unknown-linux-gnu`는 `x86_64` CPU, 임의의 CPU 공급 업체, Linux 운영체제, 그리고 GNU ABI를 갖춘 시스템을 나타냅니다. Rust는 Android를 위한 `arm-linux-androideabi`와 [WebAssembly를 위한 `wasm32-unknown-unknown`](https://www.hellorust.com/setup/wasm-target/)를 비롯해 [다양한 target triple들][platform-support]을 지원합니다.
+
+[target triple]: https://clang.llvm.org/docs/CrossCompilation.html#target-triple
+[ABI]: https://stackoverflow.com/a/2456882
+[platform-support]: https://forge.rust-lang.org/release/platform-support.html
+[custom-targets]: https://doc.rust-lang.org/nightly/rustc/targets/custom.html
+
+우리가 목표로 하는 컴파일 대상 환경 (운영체제가 따로 없는 환경)을 정의하려면 몇 가지 특별한 설정 인자들을 사용해야 하기에 [Rust 에서 기본적으로 지원하는 target triple][platform-support] 중에서는 우리가 쓸 수 있는 것은 없습니다. 다행히도 Rust에서는 JSON 파일을 이용해 [우리가 목표로 하는 컴파일 대상 환경][custom-targets]을 직접 정의할 수 있습니다. 예를 들어, `x86_64-unknown-linux-gnu` 환경을 직접 정의하는 JSON 파일의 내용은 아래와 같습니다:
+
+```json
+{
+ "llvm-target": "x86_64-unknown-linux-gnu",
+ "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
+ "arch": "x86_64",
+ "target-endian": "little",
+ "target-pointer-width": "64",
+ "target-c-int-width": "32",
+ "os": "linux",
+ "executables": true,
+ "linker-flavor": "gcc",
+ "pre-link-args": ["-m64"],
+ "morestack": false
+}
+```
+
+대부분의 필드 값들은 LLVM이 해당 환경을 목표로 코드를 생성하는 과정에서 필요합니다. 예시로, [`data-layout`] 필드는 다양한 정수, 부동소수점 표기 소수, 포인터 등의 메모리 상 실제 크기를 지정합니다. 또한 `target-pointer-width`와 같이 Rust가 조건부 컴파일을 하는 과정에서 이용하는 필드들도 있습니다.
+마지막 남은 종류의 필드들은 crate가 어떻게 빌드되어야 하는지 결정합니다. 예를 들어 `pre-link-args` 필드는 [링커][linker]에 전달될 인자들을 설정합니다.
+
+[`data-layout`]: https://llvm.org/docs/LangRef.html#data-layout
+[linker]: https://en.wikipedia.org/wiki/Linker_(computing)
+
+우리도 `x86_64` 시스템에서 구동할 운영체제 커널을 작성할 것이기에, 우리가 사용할 컴파일 대상 환경 환경 설정 파일 (JSON 파일) 또한 위의 내용과 많이 유사할 것입니다. 일단 `x86_64-blog_os.json`이라는 파일을 만들고 아래와 같이 파일 내용을 작성해주세요:
+
+```json
+{
+ "llvm-target": "x86_64-unknown-none",
+ "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
+ "arch": "x86_64",
+ "target-endian": "little",
+ "target-pointer-width": "64",
+ "target-c-int-width": "32",
+ "os": "none",
+ "executables": true
+}
+```
+
+우리의 운영체제는 bare metal 환경에서 동작할 것이기에, `llvm-target` 필드의 운영체제 값과 `os` 필드의 값은 `none`입니다.
+
+아래의 빌드 관련 설정들을 추가해줍니다:
+
+
+```json
+"linker-flavor": "ld.lld",
+"linker": "rust-lld",
+```
+
+현재 사용 중인 플랫폼의 기본 링커 대신 Rust와 함께 배포되는 크로스 플랫폼 [LLD] 링커를 사용해 커널을 링크합니다 (기본 링커는 리눅스 환경을 지원하지 않을 수 있습니다).
+
+[LLD]: https://lld.llvm.org/
+
+```json
+"panic-strategy": "abort",
+```
+
+해당 환경이 패닉 시 [스택 되감기][stack unwinding]을 지원하지 않기에, 위 설정을 통해 패닉 시 프로그램이 즉시 실행 종료되도록 합니다. 위 설정은 Cargo.toml 파일에 `panic = "abort"` 설정을 추가하는 것과 비슷한 효과이기에, Cargo.toml에서는 해당 설정을 지우셔도 괜찮습니다 (다만, Cargo.toml에서의 설정과는 달리 이 설정은 이후 단계에서 우리가 `core` 라이브러리를 재컴파일할 때에도 유효하게 적용된다는 점이 중요합니다. 위 설정은 꼭 추가해주세요!).
+
+[stack unwinding]: https://www.bogotobogo.com/cplusplus/stackunwinding.php
+
+```json
+"disable-redzone": true,
+```
+
+커널을 작성하려면, 커널이 인터럽트에 대해 어떻게 대응하는지에 대한 로직도 작성하게 될 것입니다. 안전하게 이런 로직을 작성하기 위해서는 _“red zone”_ 이라고 불리는 스택 포인터 최적화 기능을 해제해야 합니다 (그렇지 않으면 해당 기능으로 인해 스택 메모리가 우리가 원치 않는 값으로 덮어쓰일 수 있습니다). 이 내용에 대해 더 자세히 알고 싶으시면 [red zone 기능 해제][disabling the red zone] 포스트를 확인해주세요.
+
+[disabling the red zone]: @/edition-2/posts/02-minimal-rust-kernel/disable-red-zone/index.ko.md
+
+```json
+"features": "-mmx,-sse,+soft-float",
+```
+
+`features` 필드는 컴파일 대상 환경의 기능들을 활성화/비활성화 하는 데 이용합니다. 우리는 `-` 기호를 통해 `mmx`와 `sse` 기능들을 비활성화시키고 `+` 기호를 통해 `soft-float` 기능을 활성화시킬 것입니다. `features` 필드의 문자열 내부 플래그들 사이에 빈칸이 없도록 해야 합니다. 그렇지 않으면 LLVM이 `features` 필드의 문자열 값을 제대로 해석하지 못하기 때문입니다.
+
+`mmx`와 `sse`는 [Single Instruction Multiple Data (SIMD)] 명령어들의 사용 여부를 결정하는데, 해당 명령어들은 프로그램의 실행 속도를 훨씬 빠르게 만드는 데에 도움을 줄 수 있습니다. 하지만 운영체제에서 큰 SIMD 레지스터를 사용할 경우 커널의 성능에 문제가 생길 수 있습니다. 그 이유는 커널이 인터럽트 되었던 프로그램을 다시 실행하기 전에 모든 레지스터 값들을 인터럽트 직전 시점의 상태로 복원시켜야 하기 때문입니다. 커널이 SIMD 레지스터를 사용하려면 각 시스템 콜 및 하드웨어 인터럽트가 일어날 때마다 모든 SIMD 레지스터에 저장된 값들을 메인 메모리에 저장해야 할 것입니다. SIMD 레지스터들이 총 차지하는 용량은 매우 크고 (512-1600 바이트) 인터럽트 또한 자주 일어날 수 있기에,
+SIMD 레지스터 값들을 메모리에 백업하고 또 다시 복구하는 과정은 커널의 성능을 심각하게 해칠 수 있습니다. 이를 피하기 위해 커널이 SIMD 명령어를 사용하지 않도록 설정합니다 (물론 우리의 커널 위에서 구동할 프로그램들은 SIMD 명령어들을 사용할 수 있습니다!).
+
+[Single Instruction Multiple Data (SIMD)]: https://en.wikipedia.org/wiki/SIMD
+
+`x86_64` 환경에서 SIMD 기능을 비활성화하는 것에는 걸림돌이 하나 있는데, 그것은 바로 `x86_64` 환경에서 부동소수점 계산 시 기본적으로 SIMD 레지스터가 사용된다는 것입니다. 이 문제를 해결하기 위해 `soft-float` 기능 (일반 정수 계산만을 이용해 부동소수점 계산을 소프트웨어 단에서 모방)을 활성화시킵니다.
+
+더 자세히 알고 싶으시다면, 저희가 작성한 [SIMD 기능 해제](@/edition-2/posts/02-minimal-rust-kernel/disable-simd/index.ko.md)에 관한 포스트를 확인해주세요.
+
+#### 요약
+컴파일 대상 환경 설정 파일을 아래와 같이 작성합니다:
+
+```json
+{
+ "llvm-target": "x86_64-unknown-none",
+ "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
+ "arch": "x86_64",
+ "target-endian": "little",
+ "target-pointer-width": "64",
+ "target-c-int-width": "32",
+ "os": "none",
+ "executables": true,
+ "linker-flavor": "ld.lld",
+ "linker": "rust-lld",
+ "panic-strategy": "abort",
+ "disable-redzone": true,
+ "features": "-mmx,-sse,+soft-float"
+}
+```
+
+### 커널 빌드하기
+우리가 정의한 새로운 컴파일 대상 환경을 목표로 컴파일할 때에 리눅스 시스템의 관례를 따를 것입니다 (LLVM이 기본적으로 리눅스 시스템 관례를 따르기에 그렇습니다). 즉, [지난 포스트][previous post]에서 설명한 것처럼 우리는 실행 시작 지점의 이름을 `_start`로 지정할 것입니다:
+
+[previous post]: @/edition-2/posts/01-freestanding-rust-binary/index.md
+
+```rust
+// src/main.rs
+
+#![no_std] // Rust 표준 라이브러리를 링크하지 않도록 합니다
+#![no_main] // Rust 언어에서 사용하는 실행 시작 지점 (main 함수)을 사용하지 않습니다
+
+use core::panic::PanicInfo;
+
+/// 패닉이 일어날 경우, 이 함수가 호출됩니다.
+#[panic_handler]
+fn panic(_info: &PanicInfo) -> ! {
+ loop {}
+}
+
+#[no_mangle] // 이 함수의 이름을 mangle하지 않습니다
+pub extern "C" fn _start() -> ! {
+ // 링커는 기본적으로 '_start' 라는 이름을 가진 함수를 실행 시작 지점으로 삼기에,
+ // 이 함수는 실행 시작 지점이 됩니다
+ loop {}
+}
+```
+
+호스트 운영체제에 관계 없이 실행 시작 지점 함수의 이름은 `_start`로 지정해야 함을 기억해주세요.
+
+이제 `--target` 인자를 통해 위에서 다룬 JSON 파일의 이름을 전달하여 우리가 정의한 새로운 컴파일 대상 환경을 목표로 커널을 빌드할 수 있습니다:
+
+```
+> cargo build --target x86_64-blog_os.json
+
+error[E0463]: can't find crate for `core`
+```
+
+실패하였군요! 이 오류는 Rust 컴파일러가 더 이상 [`core` 라이브러리][`core` library]를 찾지 못한다는 것을 알려줍니다. 이 라이브러리는 `Result`와 `Option` 그리고 반복자 등 Rust의 기본적인 타입들을 포함하며, 모든 `no_std` 크레이트에 암시적으로 링크됩니다.
+
+[`core` library]: https://doc.rust-lang.org/nightly/core/index.html
+
+문제는 core 라이브러리가 _미리 컴파일된 상태_ 의 라이브러리로 Rust 컴파일러와 함께 배포된다는 것입니다. `x86_64-unknown-linux-gnu` 등 배포된 라이브러리가 지원하는 컴파일 목표 환경을 위해 빌드하는 경우 문제가 없지만, 우리가 정의한 커스텀 환경을 위해 빌드하는 경우에는 라이브러리를 이용할 수 없습니다. 기본적으로 지원되지 않는 새로운 시스템 환경을 위해 코드를 빌드하기 위해서는 새로운 시스템 환경에서 구동 가능하도록 `core` 라이브러리를 새롭게 빌드해야 합니다.
+
+#### `build-std` 기능
+
+이제 cargo의 [`build-std 기능`][`build-std` feature]이 필요한 시점이 왔습니다. Rust 언어 설치파일에 함께 배포된 `core` 및 다른 표준 라이브러리 크레이트 버전을 사용하는 대신, 이 기능을 이용하여 해당 크레이트들을 직접 재컴파일하여 사용할 수 있습니다. 이 기능은 아직 비교적 새로운 기능이며 아직 완성된 기능이 아니기에, "unstable" 한 기능으로 표기되며 [nightly 버전의 Rust 컴파일러][nightly Rust compilers]에서만 이용가능합니다.
+
+[`build-std` feature]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std
+[nightly Rust compilers]: #installing-rust-nightly
+
+해당 기능을 사용하려면, [cargo 설정][cargo configuration] 파일 `.cargo/config.toml`을 아래와 같이 만들어야 합니다:
+
+```toml
+# .cargo/config.toml 에 들어갈 내용
+
+[unstable]
+build-std = ["core", "compiler_builtins"]
+```
+
+위 설정은 cargo에게 `core`와 `compiler_builtins` 라이브러리를 새로 컴파일하도록 지시합니다. `compiler_builtins`는 `core`가 사용하는 라이브러리입니다. 해당 라이브러리들의 소스 코드가 있어야 새로 컴파일할 수 있기에, `rustup component add rust-src` 명령어를 통해 소스 코드를 설치합니다.
+
+
+
+**주의:** `unstable.build-std` 설정 키를 이용하려면 2020-07-15 혹은 그 이후에 출시된 Rust nightly 버전을 사용하셔야 합니다.
+
+
+
+cargo 설정 키 `unstable.build-std`를 설정하고 `rust-src` 컴포넌트를 설치한 후에 다시 빌드 명령어를 실행합니다:
+
+```
+> cargo build --target x86_64-blog_os.json
+ Compiling core v0.0.0 (/…/rust/src/libcore)
+ Compiling rustc-std-workspace-core v1.99.0 (/…/rust/src/tools/rustc-std-workspace-core)
+ Compiling compiler_builtins v0.1.32
+ Compiling blog_os v0.1.0 (/…/blog_os)
+ Finished dev [unoptimized + debuginfo] target(s) in 0.29 secs
+```
+
+이제 `cargo build` 명령어가 `core`, `rustc-std-workspace-cord` (`compiler_builtins`가 필요로 하는 라이브러리) 그리고 `compiler_builtins` 라이브러리를 우리의 커스텀 컴파일 대상을 위해 다시 컴파일하는 것을 확인할 수 있습니다.
+
+#### 메모리 관련 내장 함수
+
+Rust 컴파일러는 특정 군의 내장 함수들이 (built-in function) 모든 시스템에서 주어진다고 가정합니다. 대부분의 내장 함수들은 우리가 방금 컴파일한 `compiler_builtins` 크레이트가 이미 갖추고 있습니다. 하지만 그중 몇몇 메모리 관련 함수들은 기본적으로 사용 해제 상태가 되어 있는데, 그 이유는 해당 함수들을 호스트 시스템의 C 라이브러리가 제공하는 것이 관례이기 때문입니다. `memset`(메모리 블럭 전체에 특정 값 저장하기), `memcpy` (한 메모리 블럭의 데이터를 다른 메모리 블럭에 옮겨쓰기), `memcmp` (메모리 블럭 두 개의 데이터를 비교하기) 등이 이 분류에 해당합니다. 여태까지는 우리가 이 함수들 중 어느 하나도 사용하지 않았지만, 운영체제 구현을 더 추가하다 보면 필수적으로 사용될 함수들입니다 (예를 들어, 구조체를 복사하여 다른 곳에 저장할 때).
+
+우리는 운영체제의 C 라이브러리를 링크할 수 없기에, 다른 방식으로 이러한 내장 함수들을 컴파일러에 전달해야 합니다. 한 방법은 우리가 직접 `memset` 등의 내장함수들을 구현하고 컴파일 과정에서 함수명이 바뀌지 않도록 `#[no_mangle]` 속성을 적용하는 것입니다. 하지만 이 방법의 경우 우리가 직접 구현한 함수 로직에 아주 작은 실수만 있어도 undefined behavior를 일으킬 수 있기에 위험합니다. 예를 들어 `memcpy`를 구현하는 데에 `for`문을 사용한다면 무한 재귀 루프가 발생할 수 있는데, 그 이유는 `for`문의 구현이 내부적으로 trait 함수인 [`IntoIterator::into_iter`]를 호출하고 이 함수가 다시 `memcpy` 를 호출할 수 있기 때문입니다. 그렇기에 충분히 검증된 기존의 구현 중 하나를 사용하는 것이 바람직합니다.
+
+[`IntoIterator::into_iter`]: https://doc.rust-lang.org/stable/core/iter/trait.IntoIterator.html#tymethod.into_iter
+
+다행히도 `compiler_builtins` 크레이트가 이미 필요한 내장함수 구현을 전부 갖추고 있으며, C 라이브러리에서 오는 내장함수 구현과 충돌하지 않도록 사용 해제되어 있었던 것 뿐입니다. cargo의 [`build-std-features`] 플래그를 `["compiler-builtins-mem"]`으로 설정함으로써 `compiler_builtins`에 포함된 내장함수 구현을 사용할 수 있습니다. `build-std` 플래그와 유사하게 이 플래그 역시 커맨드 라인에서 `-Z` 플래그를 이용해 인자로 전달하거나 `.cargo/config.toml`의 `[unstable]` 테이블에서 설정할 수 있습니다. 우리는 매번 이 플래그를 사용하여 빌드할 예정이기에 `.cargo/config.toml`을 통해 설정을 하는 것이 장기적으로 더 편리할 것입니다:
+
+[`build-std-features`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std-features
+
+```toml
+# .cargo/config.toml 에 들어갈 내용
+
+[unstable]
+build-std-features = ["compiler-builtins-mem"]
+build-std = ["core", "compiler_builtins"]
+```
+
+(`compiler-builtins-mem` 기능에 대한 지원이 [굉장히 최근에 추가되었기에](https://github.com/rust-lang/rust/pull/77284), Rust nightly `2020-09-30` 이상의 버전을 사용하셔야 합니다.)
+
+이 기능은 `compiler_builtins` 크레이트의 [`mem` 기능 (feature)][`mem` feature]를 활성화 시킵니다. 이는 `#[no_mangle]` 속성이 [`memcpy` 등의 함수 구현][`memcpy` etc. implementations]에 적용되게 하여 링크가 해당 함수들을 식별하고 사용할 수 있게 합니다.
+
+[`mem` feature]: https://github.com/rust-lang/compiler-builtins/blob/eff506cd49b637f1ab5931625a33cef7e91fbbf6/Cargo.toml#L54-L55
+[`memcpy` etc. implementations]: https://github.com/rust-lang/compiler-builtins/blob/eff506cd49b637f1ab5931625a33cef7e91fbbf6/src/mem.rs#L12-L69
+
+이제 우리의 커널은 컴파일러가 요구하는 함수들에 대한 유효한 구현을 모두 갖추게 되었기에, 커널 코드가 더 복잡해지더라도 상관 없이 컴파일하는 데에 문제가 없을 것입니다.
+
+#### 기본 컴파일 대상 환경 설정하기
+
+기본 컴파일 대상 환경을 지정하여 설정해놓으면 `cargo build` 명령어를 실행할 때마다 `--target` 인자를 넘기지 않아도 됩니다. [cargo 설정][cargo configuration] 파일인 `.cargo/config.toml`에 아래의 내용을 추가해주세요:
+
+[cargo configuration]: https://doc.rust-lang.org/cargo/reference/config.html
+
+```toml
+# .cargo/config.toml 에 들어갈 내용
+
+[build]
+target = "x86_64-blog_os.json"
+```
+
+이로써 `cargo`는 명시적으로 `--target` 인자가 주어지지 않으면 `x86_64-blog_os.json`에 명시된 컴파일 대상 환경을 기본 값으로 이용합니다. `cargo build` 만으로 간단히 커널을 빌드할 수 있게 되었습니다. cargo 설정 옵션들에 대해 더 자세한 정보를 원하시면 [공식 문서][cargo configuration]을 확인해주세요.
+
+`cargo build`만으로 이제 bare metal 환경을 목표로 커널을 빌드할 수 있지만, 아직 실행 시작 지점 함수 `_start`는 텅 비어 있습니다.
+이제 이 함수에 코드를 추가하여 화면에 메세지를 출력해볼 것입니다.
+
+### 화면에 출력하기
+현재 단계에서 가장 쉽게 화면에 문자를 출력할 수 있는 방법은 바로 [VGA 텍스트 버퍼][VGA text buffer]를 이용하는 것입니다. 이것은 VGA 하드웨어에 매핑되는 특수한 메모리 영역이며 화면에 출력될 내용이 저장됩니다. 주로 이 버퍼는 주로 25행 80열 (행마다 80개의 문자 저장)로 구성됩니다. 각 문자는 ASCII 문자로서 전경색 혹은 배경색과 함께 화면에 출력됩니다. 화면 출력 결과의 모습은 아래와 같습니다:
+
+[VGA text buffer]: https://en.wikipedia.org/wiki/VGA-compatible_text_mode
+
+
+
+VGA 버퍼가 정확히 어떤 구조를 하고 있는지는 다음 포스트에서 VGA 버퍼 드라이버를 작성하면서 다룰 것입니다. "Hello World!" 메시지를 출력하는 데에는 그저 버퍼의 시작 주소가 `0xb8000`이라는 것, 그리고 각 문자는 ASCII 문자를 위한 1바이트와 색상 표기를 위한 1바이트가 필요하다는 것만 알면 충분합니다.
+
+코드 구현은 아래와 같습니다:
+
+```rust
+static HELLO: &[u8] = b"Hello World!";
+
+#[no_mangle]
+pub extern "C" fn _start() -> ! {
+ let vga_buffer = 0xb8000 as *mut u8;
+
+ for (i, &byte) in HELLO.iter().enumerate() {
+ unsafe {
+ *vga_buffer.offset(i as isize * 2) = byte;
+ *vga_buffer.offset(i as isize * 2 + 1) = 0xb;
+ }
+ }
+
+ loop {}
+}
+```
+
+우선 정수 `0xb8000`을 [raw 포인터][raw pointer]로 형변환 합니다. 그 다음 [static (정적 변수)][static] [바이트 문자열][byte string] `HELLO`의 반복자를 통해 각 바이트를 읽고, [`enumerate`] 함수를 통해 각 바이트의 문자열 내에서의 인덱스 값 `i`를 얻습니다. for문의 내부에서는 [`offset`] 함수를 통해 VGA 버퍼에 문자열의 각 바이트 및 색상 코드를 저장합니다 (`0xb`: light cyan 색상 코드).
+
+[iterate]: https://doc.rust-lang.org/stable/book/ch13-02-iterators.html
+[static]: https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#the-static-lifetime
+[`enumerate`]: https://doc.rust-lang.org/core/iter/trait.Iterator.html#method.enumerate
+[byte string]: https://doc.rust-lang.org/reference/tokens.html#byte-string-literals
+[raw pointer]: https://doc.rust-lang.org/stable/book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer
+[`offset`]: https://doc.rust-lang.org/std/primitive.pointer.html#method.offset
+
+메모리 쓰기 작업을 위한 코드 주변에 [`unsafe`] 블록이 있는 것에 주목해주세요. 여기서 `unsafe` 블록이 필요한 이유는 Rust 컴파일러가 우리가 만든 raw 포인터가 유효한 포인터인지 검증할 능력이 없기 때문입니다. `unsafe` 블록 안에 포인터에 대한 쓰기 작업 코드를 적음으로써, 우리는 컴파일러에게 해당 메모리 쓰기 작업이 확실히 안전하다고 선언한 것입니다. `unsafe` 블록이 Rust의 모든 안전성 체크를 해제하는 것은 아니며, `unsafe` 블록 안에서만 [다섯 가지 작업들을 추가적으로][five additional things] 할 수 있습니다.
+
+[`unsafe`]: https://doc.rust-lang.org/stable/book/ch19-01-unsafe-rust.html
+[five additional things]: https://doc.rust-lang.org/stable/book/ch19-01-unsafe-rust.html#unsafe-superpowers
+
+**이런 식의 Rust 코드를 작성하는 것은 절대 바람직하지 않다는 것을 강조드립니다!** unsafe 블록 안에서 raw pointer를 쓰다보면 메모리 버퍼 크기를 넘어선 메모리 주소에 데이터를 저장하는 등의 실수를 범하기 매우 쉽습니다.
+
+그렇기에 `unsafe` 블록의 사용을 최소화하는 것이 바람직하며, 그렇게 하기 위해 Rust에서 우리는 안전한 추상 계층을 만들어 이용할 수 있습니다. 예를 들어, 모든 위험한 요소들을 전부 캡슐화한 VGA 버퍼 타입을 만들어 외부 사용자가 해당 타입을 사용 중에 메모리 안전성을 해칠 가능성을 _원천 차단_ 할 수 있습니다. 이런 설계를 통해 최소한의 `unsafe` 블록만을 사용하면서 동시에 우리가 [메모리 안전성][memory safety]을 해치는 일이 없을 것이라 자신할 수 있습니다. 이러한 안전한 추상 레벨을 더한 VGA 버퍼 타입은 다음 포스트에서 만들게 될 것입니다.
+
+[memory safety]: https://en.wikipedia.org/wiki/Memory_safety
+
+## 커널 실행시키기
+
+이제 우리가 얻은 실행 파일을 실행시켜볼 차례입니다. 우선 컴파일 완료된 커널을 부트로더와 링크하여 부팅 가능한 디스크 이미지를 만들어야 합니다. 그 다음에 해당 디스크 이미지를 QEMU 가상머신에서 실행시키거나 USB 드라이브를 이용해 실제 컴퓨터에서 부팅할 수 있습니다.
+
+### 부팅 가능한 디스크 이미지 만들기
+
+부팅 가능한 디스크 이미지를 만들기 위해서는 컴파일된 커널을 부트로더와 링크해야합니다. [부팅에 대한 섹션][section about booting]에서 알아봤듯이, 부트로더는 CPU를 초기화하고 커널을 불러오는 역할을 합니다.
+
+[section about booting]: #the-boot-process
+
+우리는 부트로더를 직접 작성하는 대신에 [`bootloader`] 크레이트를 사용할 것입니다. 이 크레이트는 Rust와 인라인 어셈블리만으로 간단한 BIOS 부트로더를 구현합니다. 운영체제 커널을 부팅하는 데에 이 크레이트를 쓰기 위해 의존 크레이트 목록에 추가해줍니다:
+
+[`bootloader`]: https://crates.io/crates/bootloader
+
+```toml
+# Cargo.toml 에 들어갈 내용
+
+[dependencies]
+bootloader = "0.9.8"
+```
+
+부트로더를 의존 크레이트로 추가하는 것만으로는 부팅 가능한 디스크 이미지를 만들 수 없습니다. 커널 컴파일이 끝난 후 커널을 부트로더와 함께 링크할 수 있어야 하는데, cargo는 현재 [빌드 직후 스크립트 실행][post-build scripts] 기능을 지원하지 않습니다.
+
+[post-build scripts]: https://github.com/rust-lang/cargo/issues/545
+
+이 문제를 해결하기 위해 저희가 `bootimage` 라는 도구를 만들었습니다. 이 도구는 커널과 부트로더를 각각 컴파일 한 이후에 둘을 링크하여 부팅 가능한 디스크 이미지를 생성해줍니다. 이 도구를 설치하려면 터미널에서 아래의 명령어를 실행해주세요.
+
+```
+cargo install bootimage
+```
+
+`bootimage` 도구를 실행시키고 부트로더를 빌드하려면 `llvm-tools-preview` 라는 rustup 컴포넌트가 필요합니다. 명령어 `rustup component add llvm-tools-preview`를 통해 해당 컴포넌트를 설치합니다.
+
+`bootimage` 도구를 설치하고 `llvm-tools-preview` 컴포넌트를 추가하셨다면, 이제 아래의 명령어를 통해 부팅 가능한 디스크 이미지를 만들 수 있습니다:
+
+```
+> cargo bootimage
+```
+
+이 도구가 `cargo build`를 통해 커널을 다시 컴파일한다는 것을 확인하셨을 것입니다. 덕분에 커널 코드가 변경되어도 `cargo bootimage` 명령어 만으로도 해당 변경 사항이 바로 빌드에 반영됩니다. 그 다음 단계로 이 도구가 부트로더를 컴파일 할 것인데, 시간이 제법 걸릴 수 있습니다. 일반적인 의존 크레이트들과 마찬가지로 한 번 빌드한 후에 빌드 결과가 캐시(cache)되기 때문에, 두 번째 빌드부터는 소요 시간이 훨씬 적습니다. 마지막 단계로 `bootimage` 도구가 부트로더와 커널을 하나로 합쳐 부팅 가능한 디스크 이미지를 생성합니다.
+
+명령어 실행이 끝난 후, `target/x86_64-blog_os/debug` 디렉토리에 `bootimage-blog_os.bin`이라는 부팅 가능한 디스크 이미지가 생성되어 있을 것입니다. 이것을 가상머신에서 부팅하거나 USB 드라이브에 복사한 뒤 실제 컴퓨터에서 부팅할 수 있습니다 (우리가 만든 디스크 이미지는 CD 이미지와는 파일 형식이 다르기 때문에 CD에 복사해서 부팅하실 수는 없습니다).
+
+#### 어떻게 동작하는 걸까요?
+
+`bootimage` 도구는 아래의 작업들을 순서대로 진행합니다:
+
+- 커널을 컴파일하여 [ELF] 파일 생성
+- 부트로더 크레이트를 독립된 실행파일로서 컴파일
+- 커널의 ELF 파일을 부트로더에 링크
+
+[ELF]: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
+[rust-osdev/bootloader]: https://github.com/rust-osdev/bootloader
+
+부팅이 시작되면, 부트로더는 커널의 ELF 파일을 읽고 파싱합니다. 그 다음 프로그램의 세그먼트들을 페이지 테이블의 가상 주소에 매핑하고, `bss` 섹션의 모든 메모리 값을 0으로 초기화하며, 스택을 초기화합니다. 마지막으로, 프로그램 실행 시작 지점의 주소 (`_start` 함수의 주소)에서 제어 흐름이 계속되도록 점프합니다.
+
+### QEMU에서 커널 부팅하기
+
+이제 우리의 커널 디스크 이미지를 가상 머신에서 부팅할 수 있습니다. [QEMU]에서 부팅하려면 아래의 명령어를 실행하세요:
+
+[QEMU]: https://www.qemu.org/
+
+```
+> qemu-system-x86_64 -drive format=raw,file=target/x86_64-blog_os/debug/bootimage-blog_os.bin
+warning: TCG doesn't support requested feature: CPUID.01H:ECX.vmx [bit 5]
+```
+
+위 명령어를 실행하면 아래와 같은 새로운 창이 열릴 것입니다:
+
+
+
+화면에 "Hello World!" 메세지가 출력된 것을 확인하실 수 있습니다.
+
+### 실제 컴퓨터에서 부팅하기
+
+USB 드라이브에 우리의 커널을 저장한 후 실제 컴퓨터에서 부팅하는 것도 가능합니다:
+
+```
+> dd if=target/x86_64-blog_os/debug/bootimage-blog_os.bin of=/dev/sdX && sync
+```
+
+`sdX` 대신 여러분이 소지한 USB 드라이브의 기기명을 입력하시면 됩니다. 해당 기기에 쓰인 데이터는 전부 덮어씌워지기 때문에 정확한 기기명을 입력하도록 주의해주세요.
+
+이미지를 USB 드라이브에 다 덮어썼다면, 이제 실제 하드웨어에서 해당 이미지를 통해 부트하여 실행할 수 있습니다. 아마 특별한 부팅 메뉴를 사용하거나 BIOS 설정에서 부팅 순서를 변경하여 USB로부터 부팅하도록 설정해야 할 것입니다. `bootloader` 크레이트가 아직 UEFI를 지원하지 않기에, UEFI 표준을 사용하는 기기에서는 부팅할 수 없습니다.
+
+### `cargo run` 명령어 사용하기
+
+QEMU에서 커널을 쉽게 실행할 수 있게 아래처럼 `runner`라는 새로운 cargo 설정 키 값을 추가합니다.
+
+```toml
+# .cargo/config.toml 에 들어갈 내용
+
+[target.'cfg(target_os = "none")']
+runner = "bootimage runner"
+```
+
+`target.'cfg(target_os = "none")'`가 붙은 키 값은 `"os"` 필드 설정이 `"none"`으로 되어 있는 컴파일 대상 환경에만 적용됩니다. 따라서 우리의 `x86_64-blog_os.json` 또한 적용 대상에 포함됩니다. `runner` 키 값은 `cargo run` 명령어 실행 시 어떤 명령어를 실행할지 지정합니다. 빌드가 성공적으로 끝난 후에 `runner` 키 값의 명령어가 실행됩니다. [cargo 공식 문서][cargo configuration]를 통해 더 자세한 내용을 확인하실 수 있습니다.
+
+명령어 `bootimage runner`는 프로젝트의 부트로더 라이브러리를 링크한 후에 QEMU를 실행시킵니다.
+그렇기에 일반적인 `runner` 실행파일을 실행하듯이 `bootimage runner` 명령어를 사용하실 수 있습니다. [`bootimage` 도구의 Readme 문서][Readme of `bootimage`]를 통해 더 자세한 내용 및 다른 가능한 설정 옵션들을 확인하세요.
+
+[Readme of `bootimage`]: https://github.com/rust-osdev/bootimage
+
+이제 `cargo run` 명령어를 통해 우리의 커널을 컴파일하고 QEMU에서 부팅할 수 있습니다.
+
+## 다음 단계는 무엇일까요?
+
+다음 글에서는 VGA 텍스트 버퍼 (text buffer)에 대해 더 알아보고 VGA text buffer와 안전하게 상호작용할 수 있는 방법을 구현할 것입니다.
+또한 `println` 매크로를 사용할 수 있도록 기능을 추가할 것입니다.
From 71870a05b0d2511a765caaf345c8b0e3632da95d Mon Sep 17 00:00:00 2001
From: MysticalUser <61061695+MysticalUser@users.noreply.github.com>
Date: Sun, 15 May 2022 15:46:34 -0400
Subject: [PATCH 015/107] Fix minor grammar mistakes
---
blog/content/edition-2/posts/05-cpu-exceptions/index.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/blog/content/edition-2/posts/05-cpu-exceptions/index.md b/blog/content/edition-2/posts/05-cpu-exceptions/index.md
index 2a59311a..f90b33f4 100644
--- a/blog/content/edition-2/posts/05-cpu-exceptions/index.md
+++ b/blog/content/edition-2/posts/05-cpu-exceptions/index.md
@@ -432,7 +432,7 @@ pub extern "C" fn _start() -> ! {
}
```
-Remember, this `_start` function is used when running `cargo test --lib`, since Rust's tests the `lib.rs` completely independent of the `main.rs`. We need to call `init` here to set up an IDT before running the tests.
+Remember, this `_start` function is used when running `cargo test --lib`, since Rust tests the `lib.rs` completely independently of the `main.rs`. We need to call `init` here to set up an IDT before running the tests.
Now we can create a `test_breakpoint_exception` test:
From 611698ec5fc9778012c56b58dc22489c9e370f41 Mon Sep 17 00:00:00 2001
From: asami
Date: Tue, 17 May 2022 14:57:54 +0900
Subject: [PATCH 016/107] Delete unnecessary brackets
---
blog/content/edition-2/posts/12-async-await/index.ja.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/blog/content/edition-2/posts/12-async-await/index.ja.md b/blog/content/edition-2/posts/12-async-await/index.ja.md
index 121bcfc1..f717fada 100644
--- a/blog/content/edition-2/posts/12-async-await/index.ja.md
+++ b/blog/content/edition-2/posts/12-async-await/index.ja.md
@@ -425,7 +425,7 @@ ExampleStateMachine::WaitingOnFooTxt(state) => {
};
*self = ExampleStateMachine::WaitingOnBarTxt(state);
} else {
- *self = ExampleStateMachine::End(EndState));
+ *self = ExampleStateMachine::End(EndState);
return Poll::Ready(content);
}
}
@@ -446,7 +446,7 @@ ExampleStateMachine::WaitingOnBarTxt(state) => {
match state.bar_txt_future.poll(cx) {
Poll::Pending => return Poll::Pending,
Poll::Ready(bar_txt) => {
- *self = ExampleStateMachine::End(EndState));
+ *self = ExampleStateMachine::End(EndState);
// from body of `example`
return Poll::Ready(state.content + &bar_txt);
}
From 1a750f5374db541e8ca0342e1db090001f13fd9e Mon Sep 17 00:00:00 2001
From: asami
Date: Tue, 17 May 2022 21:05:54 +0900
Subject: [PATCH 017/107] Japanese Correction
---
blog/content/edition-2/posts/12-async-await/index.ja.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/blog/content/edition-2/posts/12-async-await/index.ja.md b/blog/content/edition-2/posts/12-async-await/index.ja.md
index 121bcfc1..6164d68f 100644
--- a/blog/content/edition-2/posts/12-async-await/index.ja.md
+++ b/blog/content/edition-2/posts/12-async-await/index.ja.md
@@ -701,7 +701,7 @@ unsafe {
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll
```
-このメソッドが通常の`&mut self`ではなく`self: Pin<&mut Self>`を取る理由は、[上][self-ref-async-await]で見たように、async/awaitから生成されるfutureのインスタンスはしばしば自己参照しているためです。`Self` を `Pin` にラップして、async/await から生成された自己参照のfutureに対して、コンパイラに `Unpin` を選択させることで、`poll` 呼び出しの間にfutureがメモリ内で移動しないことが保証されます。これにより、すべての内部参照が有効であることが保証されます。
+このメソッドが通常の`&mut self`ではなく`self: Pin<&mut Self>`を取る理由は、[上][self-ref-async-await]で見たように、async/awaitから生成されるfutureのインスタンスはしばしば自己参照しているためです。`Self` を `Pin` にラップして、async/await から生成された自己参照のfutureに対して、コンパイラに `Unpin` をオプトアウトさせることで、`poll` 呼び出しの間にfutureがメモリ内で移動しないことが保証されます。これにより、すべての内部参照が有効であることが保証されます。
[self-ref-async-await]: @/edition-2/posts/12-async-await/index.md#self-referential-structs
From d78dd1582b69f5a02f32a584376a58e8801a8b02 Mon Sep 17 00:00:00 2001
From: asami
Date: Tue, 17 May 2022 21:09:50 +0900
Subject: [PATCH 018/107] add translators
---
blog/content/edition-2/posts/12-async-await/index.ja.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/blog/content/edition-2/posts/12-async-await/index.ja.md b/blog/content/edition-2/posts/12-async-await/index.ja.md
index 6164d68f..34fda6ca 100644
--- a/blog/content/edition-2/posts/12-async-await/index.ja.md
+++ b/blog/content/edition-2/posts/12-async-await/index.ja.md
@@ -9,7 +9,7 @@ chapter = "Multitasking"
# Please update this when updating the translation
translation_based_on_commit = "bf4f88107966c7ab1327c3cdc0ebfbd76bad5c5f"
# GitHub usernames of the people that translated this post
-translators = ["kahirokunn", "garasubo", "sozysozbot", "woodyZootopia"]
+translators = ["kahirokunn", "garasubo", "sozysozbot", "woodyZootopia", "asami-kawasaki"]
+++
この記事では、Rustの**協調的マルチタスク**と**async/await**機能について説明します。Rustのasync/await機能については、`Future` trait の設計、ステートマシンの変換、 **pinning** などを含めて詳しく説明します。そして、非同期キーボードタスクと基本的なexecutorを作成することで、カーネルにasync/awaitの基本的なサポートを追加します。
From 884247cb1d3f2a86bb24a8c4ed92cb58048ca14b Mon Sep 17 00:00:00 2001
From: woodyZootopia
Date: Fri, 20 May 2022 21:23:15 +0900
Subject: [PATCH 019/107] Made the Japanese file, translated about 1/8
---
.../posts/10-heap-allocation/index.ja.md | 800 ++++++++++++++++++
1 file changed, 800 insertions(+)
create mode 100644 blog/content/edition-2/posts/10-heap-allocation/index.ja.md
diff --git a/blog/content/edition-2/posts/10-heap-allocation/index.ja.md b/blog/content/edition-2/posts/10-heap-allocation/index.ja.md
new file mode 100644
index 00000000..be9a8344
--- /dev/null
+++ b/blog/content/edition-2/posts/10-heap-allocation/index.ja.md
@@ -0,0 +1,800 @@
++++
+title = "ヒープ割り当て"
+weight = 10
+path = "ja/heap-allocation"
+date = 2019-06-26
+
+[extra]
+chapter = "Memory Management"
+translation_based_on_commit = ""
+translators = ["woodyZootopia"]
++++
+
+この記事では、私たちのカーネルにヒープ割り当ての機能を追加します。まず動的メモリの基礎を説明し、いかにして借用チェッカがありがちなアロケーションエラーを防ぐのかを示します。その後Rustの基礎的なアロケーションインターフェースを実装し、ヒープメモリ領域を作成し、アロケータクレートを作成します。この記事を終える頃には、Rustに組み込みの`alloc`クレートのすべてのアロケーション・コレクション型が私たちのカーネルで利用可能になっているでしょう。
+
+
+
+このブログの内容は [GitHub] 上で公開・開発されています。何か問題や質問などがあれば issue をたててください (訳注: リンクは原文(英語)のものになります)。また[こちら][at the bottom]にコメントを残すこともできます。この記事の完全なソースコードは[`post-10` ブランチ][post branch]にあります。
+
+[GitHub]: https://github.com/phil-opp/blog_os
+[at the bottom]: #comments
+
+[post branch]: https://github.com/phil-opp/blog_os/tree/post-10
+
+
+
+# TODO:translation_based_on_commitを埋める
+# TODO:リンクを日本語記事の物に変更する
+
+## ローカル変数とスタティック変数
+
+私たちのカーネルでは現在二種類の変数が使用されています:ローカル変数と`static`変数です。ローカル変数は[コールスタック][call stack]に格納されており、変数の定義された関数がリターンするまでの間のみ有効です。スタティック変数はメモリ上の固定された場所に格納されており、プログラムのライフタイム全体で常に生存します。
+
+### ローカル変数
+
+ローカル変数は[コールスタック][call stack]に格納されています。これは`push`と`pop`という命令をサポートする[スタックというデータ構造][stack data structure]です。関数に入るたびに、パラメータ、リターンアドレス、呼び出された関数のローカル変数がコンパイラによってpushされます:
+
+[call stack]: https://en.wikipedia.org/wiki/Call_stack
+[stack data structure]: https://en.wikipedia.org/wiki/Stack_(abstract_data_type)
+
+
+
+上の例は、`outer`関数が`inner`関数を呼び出した後のコールスタックを示しています。コールスタックは`outer`のローカル変数を先に持っていることが分かります。`inner`を呼び出すと、パラメータ`1`とこの関数のリターンアドレスがpushされます。そこで制御は`inner`へと移り、`inner`は自身のローカル変数をpushします。
+
+`inner`関数がリターンした後で、コールスタックのその関数の部分がpopされ、`outer`のローカル変数のみが残ります:
+
+
+
+`inner`関数のローカル変数はリターンまでしか生存していないことが分かります。Rustコンパイラはこの生存期間を強制し、私たちが値を長く使いすぎてしまうとエラーを投げます。例えば、ローカル変数への参照を返そうとすると:
+
+```rust
+fn inner(i: usize) -> &'static u32 {
+ let z = [1, 2, 3];
+ &z[i]
+}
+```
+
+([run the example on the playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=6186a0f3a54f468e1de8894996d12819))
+
+この例で参照を返そうとすることには意味がありませんが、変数に関数よりも長く生存して欲しいというケースは存在します。すでに私たちのカーネルでそのようなケースに遭遇しています。[割り込み記述子表をロード][load an interrupt descriptor table]しようとしたときで、ライフタイムを延ばすために`static`変数を使う必要があったのでした。
+
+[load an interrupt descriptor table]: @/edition-2/posts/05-cpu-exceptions/index.md#loading-the-idt
+
+### スタティック変数
+
+スタティック変数は、スタックとは別の固定されたメモリ位置に格納されます。このメモリ位置はコンパイル時にリンカによって指定され、実行可能ファイルにエンコードされています。スタティック変数はプログラムの実行中ずっと生存するため、`'static`ライフタイムを持っており、ローカル変数によっていつでも参照されることができます。
+
+![The same outer/inner example with the difference that inner has a `static Z: [u32; 3] = [1,2,3];` and returns a `&Z[i]` reference](call-stack-static.svg)
+
+上の例で`inner`関数がリターンするとき、それに対応するコールスタックは破棄されます。スタティック変数は絶対に破棄されない別のメモリ領域にあるため、参照`&Z[1]`はリターン後も有効です。
+
+`'static`ライフタイムの他にもスタティック変数には利点があります:位置がコンパイル時に分かるため、アクセスするために参照が必要ないのです。この特性を私たちの`println`マクロに利用しました:[スタティックな`Writer`][static `Writer`]を内部で使うことで、マクロを呼び出す際に`&mut Writer`が必要でなくなるのですが、これは他の変数にアクセスできない[例外処理][exception handlers]においてとても便利なのです。
+
+[static `Writer`]: @/edition-2/posts/03-vga-text-buffer/index.md#a-global-interface
+[exception handlers]: @/edition-2/posts/05-cpu-exceptions/index.md#implementation
+
+しかし、スタティック変数のこの特性には重大な欠点がついてきます:デフォルトでは読み込み専用なのです。Rustがこのルールを強制するのは、例えば二つのスレッドがあるスタティック変数を同時に変更した場合[データ競合][data race]が発生するためです。スタティック変数を変更する唯一の方法は、それを[`Mutex`]型にカプセル化し、あらゆる時刻において`&mut`参照が一つしか存在しないことを保証することです。`Mutex`はすでに[スタティックなVGAバッファへの`Writer`][vga mutex]を作ったときに使いました。
+
+[data race]: https://doc.rust-lang.org/nomicon/races.html
+[`Mutex`]: https://docs.rs/spin/0.5.2/spin/struct.Mutex.html
+[vga mutex]: @/edition-2/posts/03-vga-text-buffer/index.md#spinlocks
+
+## 動的メモリ
+
+ローカル変数とスタティック変数を組み合わせれば、それら自体とても強力であり、殆どのユースケースを満足します。しかし、両方に制限が存在することも見てきました:
+
+- ローカル変数はそれを定義する関数やブロックが終わるまでしか生存しません。なぜなら、これらはコールスタックに存在し、関数がリターンした段階で破棄されるからです。
+- スタティック変数はプログラムの実行中常に生存するため、必要なくなったときでもメモリを取り戻したり再利用したりする方法がありません。また、所有権のセマンティクスが不明瞭であり、すべての関数からアクセスできてしまうため、変更しようと思ったときには[`Mutex`]で保護してやらないといけません。
+
+ローカル・スタティック変数の制約としてもう一つ、固定サイズであることが挙げられます。従ってこれらは要素が追加されたときに動的に大きくなるコレクションを格納することができません(Rustにおいて動的サイズのローカル変数を可能にする[unsized rvalues]の提案が行われていますが、これはいくつかの特定のケースでしかうまく動きません)。
+
+[unsized rvalues]: https://github.com/rust-lang/rust/issues/48055
+
+これらの欠点を回避するために、プログラミング言語はしばしば、変数を格納するための第三の領域である**ヒープ**をサポートします。ヒープは、`allocate`と`deallocate`という二つの関数を通じて、実行時の**動的メモリ割り当て**をサポートします。以下のように:`allocate`関数は、変数を格納するのに使える、指定されたサイズの解放されたメモリの塊を返します。この変数は、`deallocate`関数をその変数への参照を引数に呼び出すことによって解放されるまで生存します。
+
+例を使って見てみましょう:
+
+![The inner function calls `allocate(size_of([u32; 3]))`, writes `z.write([1,2,3]);`, and returns `(z as *mut u32).offset(i)`. The outer function does `deallocate(y, size_of(u32))` on the returned value `y`.](call-stack-heap.svg)
+
+ここで`inner`関数は`z`を格納するためにスタティック変数ではなくヒープメモリを使っています。まず要求されたサイズのメモリブロックを割り当て、`*mut u32`の[生ポインタ][raw pointer]を返されます。その後で[`ptr::write`]メソッドを使って配列`[1,2,3]`をこれに書き込みます。最後のステップとして、[`offset`]関数を使って`i`番目の要素へのポインタを計算しそれを返します(簡単のため、必要なキャストやunsafeブロックをいくつか省略しました)。
+
+[raw pointer]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer
+[`ptr::write`]: https://doc.rust-lang.org/core/ptr/fn.write.html
+[`offset`]: https://doc.rust-lang.org/std/primitive.pointer.html#method.offset
+
+割り当てられたメモリは`deallocate`の呼び出しによって明示的に解放されるまで生存します。したがって、返されたポインタは`inner`がリターンし、コールスタックの対応する部分が破棄された後も有効です。スタティックメモリと比較したときのヒープメモリの長所は、解放後に再利用できると言うことです(`outer`内の`deallocate`呼び出しでまさにこれを行っています)。この呼び出しの後、状況は以下のようになります。
+
+![The call stack contains the local variables of outer, the heap contains z[0] and z[2], but no longer z[1].](call-stack-heap-freed.svg)
+
+`z[1]`スロットが再び解放され、次の`allocate`呼び出しで再利用できることが分かります。しかし、`z[0]`と`z[2]`は永久にdeallocateされず、したがって永久に解放されないことも分かります。このようなバグは**メモリリーク**と呼ばれており、しばしばプログラムによる過剰なメモリ消費を引き起こします(`inner`をループ内で何度も呼び出したらどんなことになるか、想像してみてください)。これ自体良くないことに思われるかもしれませんが、実は動的割り当てでは遙かに危険性の高いバグも発生しうるのです。
+
+### Common Errors
+
+Apart from memory leaks, which are unfortunate but don't make the program vulnerable to attackers, there are two common types of bugs with more severe consequences:
+
+- When we accidentally continue to use a variable after calling `deallocate` on it, we have a so-called **use-after-free** vulnerability. Such a bug causes undefined behavior and can often be exploited by attackers to execute arbitrary code.
+- When we accidentally free a variable twice, we have a **double-free** vulnerability. This is problematic because it might free a different allocation that was allocated in the same spot after the first `deallocate` call. Thus, it can lead to an use-after-free vulnerability again.
+
+These types of vulnerabilities are commonly known, so one might expect that people learned how to avoid them by now. But no, such vulnerabilities are still regularly found, for example this recent [use-after-free vulnerability in Linux][linux vulnerability] that allowed arbitrary code execution. This shows that even the best programmers are not always able to correctly handle dynamic memory in complex projects.
+
+[linux vulnerability]: https://securityboulevard.com/2019/02/linux-use-after-free-vulnerability-found-in-linux-2-6-through-4-20-11/
+
+To avoid these issues, many languages such as Java or Python manage dynamic memory automatically using a technique called [_garbage collection_]. The idea is that the programmer never invokes `deallocate` manually. Instead, the program is regularly paused and scanned for unused heap variables, which are then automatically deallocated. Thus, the above vulnerabilities can never occur. The drawbacks are the performance overhead of the regular scan and the probably long pause times.
+
+[_garbage collection_]: https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)
+
+Rust takes a different approach to the problem: It uses a concept called [_ownership_] that is able to check the correctness of dynamic memory operations at compile time. Thus no garbage collection is needed to avoid the mentioned vulnerabilities, which means that there is no performance overhead. Another advantage of this approach is that the programmer still has fine-grained control over the use of dynamic memory, just like with C or C++.
+
+[_ownership_]: https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html
+
+### Allocations in Rust
+
+Instead of letting the programmer manually call `allocate` and `deallocate`, the Rust standard library provides abstraction types that call these functions implicitly. The most important type is [**`Box`**], which is an abstraction for a heap-allocated value. It provides a [`Box::new`] constructor function that takes a value, calls `allocate` with the size of the value, and then moves the value to the newly allocated slot on the heap. To free the heap memory again, the `Box` type implements the [`Drop` trait] to call `deallocate` when it goes out of scope:
+
+[**`Box`**]: https://doc.rust-lang.org/std/boxed/index.html
+[`Box::new`]: https://doc.rust-lang.org/alloc/boxed/struct.Box.html#method.new
+[`Drop` trait]: https://doc.rust-lang.org/book/ch15-03-drop.html
+
+```rust
+{
+ let z = Box::new([1,2,3]);
+ […]
+} // z goes out of scope and `deallocate` is called
+```
+
+This pattern has the strange name [_resource acquisition is initialization_] (or _RAII_ for short). It originated in C++, where it is used to implement a similar abstraction type called [`std::unique_ptr`].
+
+[_resource acquisition is initialization_]: https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization
+[`std::unique_ptr`]: https://en.cppreference.com/w/cpp/memory/unique_ptr
+
+Such a type alone does not suffice to prevent all use-after-free bugs since programmers can still hold on to references after the `Box` goes out of scope and the corresponding heap memory slot is deallocated:
+
+```rust
+let x = {
+ let z = Box::new([1,2,3]);
+ &z[1]
+}; // z goes out of scope and `deallocate` is called
+println!("{}", x);
+```
+
+This is where Rust's ownership comes in. It assigns an abstract [lifetime] to each reference, which is the scope in which the reference is valid. In the above example, the `x` reference is taken from the `z` array, so it becomes invalid after `z` goes out of scope. When you [run the above example on the playground][playground-2] you see that the Rust compiler indeed throws an error:
+
+[lifetime]: https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html
+[playground-2]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=28180d8de7b62c6b4a681a7b1f745a48
+
+```
+error[E0597]: `z[_]` does not live long enough
+ --> src/main.rs:4:9
+ |
+2 | let x = {
+ | - borrow later stored here
+3 | let z = Box::new([1,2,3]);
+4 | &z[1]
+ | ^^^^^ borrowed value does not live long enough
+5 | }; // z goes out of scope and `deallocate` is called
+ | - `z[_]` dropped here while still borrowed
+```
+
+The terminology can be a bit confusing at first. Taking a reference to a value is called _borrowing_ the value since it's similar to a borrow in real life: You have temporary access to an object but need to return it sometime and you must not destroy it. By checking that all borrows end before an object is destroyed, the Rust compiler can guarantee that no use-after-free situation can occur.
+
+Rust's ownership system goes even further and does not only prevent use-after-free bugs, but provides complete [_memory safety_] like garbage collected languages like Java or Python do. Additionally, it guarantees [_thread safety_] and is thus even safer than those languages in multi-threaded code. And most importantly, all these checks happen at compile time, so there is no runtime overhead compared to hand written memory management in C.
+
+[_memory safety_]: https://en.wikipedia.org/wiki/Memory_safety
+[_thread safety_]: https://en.wikipedia.org/wiki/Thread_safety
+
+### Use Cases
+
+We now know the basics of dynamic memory allocation in Rust, but when should we use it? We've come really far with our kernel without dynamic memory allocation, so why do we need it now?
+
+First, dynamic memory allocation always comes with a bit of performance overhead, since we need to find a free slot on the heap for every allocation. For this reason local variables are generally preferable, especially in performance sensitive kernel code. However, there are cases where dynamic memory allocation is the best choice.
+
+As a basic rule, dynamic memory is required for variables that have a dynamic lifetime or a variable size. The most important type with a dynamic lifetime is [**`Rc`**], which counts the references to its wrapped value and deallocates it after all references went out of scope. Examples for types with a variable size are [**`Vec`**], [**`String`**], and other [collection types] that dynamically grow when more elements are added. These types work by allocating a larger amount of memory when they become full, copying all elements over, and then deallocating the old allocation.
+
+[**`Rc`**]: https://doc.rust-lang.org/alloc/rc/index.html
+[**`Vec`**]: https://doc.rust-lang.org/alloc/vec/index.html
+[**`String`**]: https://doc.rust-lang.org/alloc/string/index.html
+[collection types]: https://doc.rust-lang.org/alloc/collections/index.html
+
+For our kernel we will mostly need the collection types, for example for storing a list of active tasks when implementing multitasking in future posts.
+
+## The Allocator Interface
+
+The first step in implementing a heap allocator is to add a dependency on the built-in [`alloc`] crate. Like the [`core`] crate, it is a subset of the standard library that additionally contains the allocation and collection types. To add the dependency on `alloc`, we add the following to our `lib.rs`:
+
+[`alloc`]: https://doc.rust-lang.org/alloc/
+[`core`]: https://doc.rust-lang.org/core/
+
+```rust
+// in src/lib.rs
+
+extern crate alloc;
+```
+
+Contrary to normal dependencies, we don't need to modify the `Cargo.toml`. The reason is that the `alloc` crate ships with the Rust compiler as part of the standard library, so the compiler already knows about the crate. By adding this `extern crate` statement, we specify that the compiler should try to include it. (Historically, all dependencies needed an `extern crate` statement, which is now optional).
+
+Since we are compiling for a custom target, we can't use the precompiled version of `alloc` that is shipped with the Rust installation. Instead, we have to tell cargo to recompile the crate from source. We can do that, by adding it to the `unstable.build-std` array in our `.cargo/config.toml` file:
+
+```toml
+# in .cargo/config.toml
+
+[unstable]
+build-std = ["core", "compiler_builtins", "alloc"]
+````
+
+Now the compiler will recompile and include the `alloc` crate in our kernel.
+
+The reason that the `alloc` crate is disabled by default in `#[no_std]` crates is that it has additional requirements. We can see these requirements as errors when we try to compile our project now:
+
+```
+error: no global memory allocator found but one is required; link to std or add
+ #[global_allocator] to a static item that implements the GlobalAlloc trait.
+
+error: `#[alloc_error_handler]` function required, but not found
+```
+
+The first error occurs because the `alloc` crate requires an heap allocator, which is an object that provides the `allocate` and `deallocate` functions. In Rust, heap allocators are described by the [`GlobalAlloc`] trait, which is mentioned in the error message. To set the heap allocator for the crate, the `#[global_allocator]` attribute must be applied to a `static` variable that implements the `GlobalAlloc` trait.
+
+The second error occurs because calls to `allocate` can fail, most commonly when there is no more memory available. Our program must be able to react to this case, which is what the `#[alloc_error_handler]` function is for.
+
+[`GlobalAlloc`]: https://doc.rust-lang.org/alloc/alloc/trait.GlobalAlloc.html
+
+We will describe these traits and attributes in detail in the following sections.
+
+### The `GlobalAlloc` Trait
+
+The [`GlobalAlloc`] trait defines the functions that a heap allocator must provide. The trait is special because it is almost never used directly by the programmer. Instead, the compiler will automatically insert the appropriate calls to the trait methods when using the allocation and collection types of `alloc`.
+
+Since we will need to implement the trait for all our allocator types, it is worth taking a closer look at its declaration:
+
+```rust
+pub unsafe trait GlobalAlloc {
+ unsafe fn alloc(&self, layout: Layout) -> *mut u8;
+ unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout);
+
+ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { ... }
+ unsafe fn realloc(
+ &self,
+ ptr: *mut u8,
+ layout: Layout,
+ new_size: usize
+ ) -> *mut u8 { ... }
+}
+```
+
+It defines the two required methods [`alloc`] and [`dealloc`], which correspond to the `allocate` and `deallocate` functions we used in our examples:
+- The [`alloc`] method takes a [`Layout`] instance as argument, which describes the desired size and alignment that the allocated memory should have. It returns a [raw pointer] to the first byte of the allocated memory block. Instead of an explicit error value, the `alloc` method returns a null pointer to signal an allocation error. This is a bit non-idiomatic, but it has the advantage that wrapping existing system allocators is easy, since they use the same convention.
+- The [`dealloc`] method is the counterpart and responsible for freeing a memory block again. It receives two arguments, the pointer returned by `alloc` and the `Layout` that was used for the allocation.
+
+[`alloc`]: https://doc.rust-lang.org/alloc/alloc/trait.GlobalAlloc.html#tymethod.alloc
+[`dealloc`]: https://doc.rust-lang.org/alloc/alloc/trait.GlobalAlloc.html#tymethod.dealloc
+[`Layout`]: https://doc.rust-lang.org/alloc/alloc/struct.Layout.html
+
+The trait additionally defines the two methods [`alloc_zeroed`] and [`realloc`] with default implementations:
+
+- The [`alloc_zeroed`] method is equivalent to calling `alloc` and then setting the allocated memory block to zero, which is exactly what the provided default implementation does. An allocator implementation can override the default implementations with a more efficient custom implementation if possible.
+- The [`realloc`] method allows to grow or shrink an allocation. The default implementation allocates a new memory block with the desired size and copies over all the content from the previous allocation. Again, an allocator implementation can probably provide a more efficient implementation of this method, for example by growing/shrinking the allocation in-place if possible.
+
+[`alloc_zeroed`]: https://doc.rust-lang.org/alloc/alloc/trait.GlobalAlloc.html#method.alloc_zeroed
+[`realloc`]: https://doc.rust-lang.org/alloc/alloc/trait.GlobalAlloc.html#method.realloc
+
+#### Unsafety
+
+One thing to notice is that both the trait itself and all trait methods are declared as `unsafe`:
+
+- The reason for declaring the trait as `unsafe` is that the programmer must guarantee that the trait implementation for an allocator type is correct. For example, the `alloc` method must never return a memory block that is already used somewhere else because this would cause undefined behavior.
+- Similarly, the reason that the methods are `unsafe` is that the caller must ensure various invariants when calling the methods, for example that the `Layout` passed to `alloc` specifies a non-zero size. This is not really relevant in practice since the methods are normally called directly by the compiler, which ensures that the requirements are met.
+
+### A `DummyAllocator`
+
+Now that we know what an allocator type should provide, we can create a simple dummy allocator. For that we create a new `allocator` module:
+
+```rust
+// in src/lib.rs
+
+pub mod allocator;
+```
+
+Our dummy allocator does the absolute minimum to implement the trait and always return an error when `alloc` is called. It looks like this:
+
+```rust
+// in src/allocator.rs
+
+use alloc::alloc::{GlobalAlloc, Layout};
+use core::ptr::null_mut;
+
+pub struct Dummy;
+
+unsafe impl GlobalAlloc for Dummy {
+ unsafe fn alloc(&self, _layout: Layout) -> *mut u8 {
+ null_mut()
+ }
+
+ unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
+ panic!("dealloc should be never called")
+ }
+}
+```
+
+The struct does not need any fields, so we create it as a [zero sized type]. As mentioned above, we always return the null pointer from `alloc`, which corresponds to an allocation error. Since the allocator never returns any memory, a call to `dealloc` should never occur. For this reason we simply panic in the `dealloc` method. The `alloc_zeroed` and `realloc` methods have default implementations, so we don't need to provide implementations for them.
+
+[zero sized type]: https://doc.rust-lang.org/nomicon/exotic-sizes.html#zero-sized-types-zsts
+
+We now have a simple allocator, but we still have to tell the Rust compiler that it should use this allocator. This is where the `#[global_allocator]` attribute comes in.
+
+### The `#[global_allocator]` Attribute
+
+The `#[global_allocator]` attribute tells the Rust compiler which allocator instance it should use as the global heap allocator. The attribute is only applicable to a `static` that implements the `GlobalAlloc` trait. Let's register an instance of our `Dummy` allocator as the global allocator:
+
+```rust
+// in src/allocator.rs
+
+#[global_allocator]
+static ALLOCATOR: Dummy = Dummy;
+```
+
+Since the `Dummy` allocator is a [zero sized type], we don't need to specify any fields in the initialization expression.
+
+When we now try to compile it, the first error should be gone. Let's fix the remaining second error:
+
+```
+error: `#[alloc_error_handler]` function required, but not found
+```
+
+### The `#[alloc_error_handler]` Attribute
+
+As we learned when discussing the `GlobalAlloc` trait, the `alloc` function can signal an allocation error by returning a null pointer. The question is: how should the Rust runtime react to such an allocation failure? This is where the `#[alloc_error_handler]` attribute comes in. It specifies a function that is called when an allocation error occurs, similar to how our panic handler is called when a panic occurs.
+
+Let's add such a function to fix the compilation error:
+
+```rust
+// in src/lib.rs
+
+#![feature(alloc_error_handler)] // at the top of the file
+
+#[alloc_error_handler]
+fn alloc_error_handler(layout: alloc::alloc::Layout) -> ! {
+ panic!("allocation error: {:?}", layout)
+}
+```
+
+The `alloc_error_handler` function is still unstable, so we need a feature gate to enable it. The function receives a single argument: the `Layout` instance that was passed to `alloc` when the allocation failure occurred. There's nothing we can do to resolve the failure, so we just panic with a message that contains the `Layout` instance.
+
+With this function, the compilation errors should be fixed. Now we can use the allocation and collection types of `alloc`, for example we can use a [`Box`] to allocate a value on the heap:
+
+[`Box`]: https://doc.rust-lang.org/alloc/boxed/struct.Box.html
+
+```rust
+// in src/main.rs
+
+extern crate alloc;
+
+use alloc::boxed::Box;
+
+fn kernel_main(boot_info: &'static BootInfo) -> ! {
+ // […] print "Hello World!", call `init`, create `mapper` and `frame_allocator`
+
+ let x = Box::new(41);
+
+ // […] call `test_main` in test mode
+
+ println!("It did not crash!");
+ blog_os::hlt_loop();
+}
+
+```
+
+Note that we need to specify the `extern crate alloc` statement in our `main.rs` too. This is required because the `lib.rs` and `main.rs` part are treated as separate crates. However, we don't need to create another `#[global_allocator]` static because the global allocator applies to all crates in the project. In fact, specifying an additional allocator in another crate would be an error.
+
+When we run the above code, we see that our `alloc_error_handler` function is called:
+
+
+
+The error handler is called because the `Box::new` function implicitly calls the `alloc` function of the global allocator. Our dummy allocator always returns a null pointer, so every allocation fails. To fix this we need to create an allocator that actually returns usable memory.
+
+## Creating a Kernel Heap
+
+Before we can create a proper allocator, we first need to create a heap memory region from which the allocator can allocate memory. To do this, we need to define a virtual memory range for the heap region and then map this region to physical frames. See the [_"Introduction To Paging"_] post for an overview of virtual memory and page tables.
+
+[_"Introduction To Paging"_]: @/edition-2/posts/08-paging-introduction/index.md
+
+The first step is to define a virtual memory region for the heap. We can choose any virtual address range that we like, as long as it is not already used for a different memory region. Let's define it as the memory starting at address `0x_4444_4444_0000` so that we can easily recognize a heap pointer later:
+
+```rust
+// in src/allocator.rs
+
+pub const HEAP_START: usize = 0x_4444_4444_0000;
+pub const HEAP_SIZE: usize = 100 * 1024; // 100 KiB
+```
+
+We set the heap size to 100 KiB for now. If we need more space in the future, we can simply increase it.
+
+If we tried to use this heap region now, a page fault would occur since the virtual memory region is not mapped to physical memory yet. To resolve this, we create an `init_heap` function that maps the heap pages using the [`Mapper` API] that we introduced in the [_"Paging Implementation"_] post:
+
+[`Mapper` API]: @/edition-2/posts/09-paging-implementation/index.md#using-offsetpagetable
+[_"Paging Implementation"_]: @/edition-2/posts/09-paging-implementation/index.md
+
+```rust
+// in src/allocator.rs
+
+use x86_64::{
+ structures::paging::{
+ mapper::MapToError, FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB,
+ },
+ VirtAddr,
+};
+
+pub fn init_heap(
+ mapper: &mut impl Mapper,
+ frame_allocator: &mut impl FrameAllocator,
+) -> Result<(), MapToError> {
+ let page_range = {
+ let heap_start = VirtAddr::new(HEAP_START as u64);
+ let heap_end = heap_start + HEAP_SIZE - 1u64;
+ let heap_start_page = Page::containing_address(heap_start);
+ let heap_end_page = Page::containing_address(heap_end);
+ Page::range_inclusive(heap_start_page, heap_end_page)
+ };
+
+ for page in page_range {
+ let frame = frame_allocator
+ .allocate_frame()
+ .ok_or(MapToError::FrameAllocationFailed)?;
+ let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE;
+ unsafe {
+ mapper.map_to(page, frame, flags, frame_allocator)?.flush()
+ };
+ }
+
+ Ok(())
+}
+```
+
+The function takes mutable references to a [`Mapper`] and a [`FrameAllocator`] instance, both limited to 4KiB pages by using [`Size4KiB`] as generic parameter. The return value of the function is a [`Result`] with the unit type `()` as success variant and a [`MapToError`] as error variant, which is the error type returned by the [`Mapper::map_to`] method. Reusing the error type makes sense here because the `map_to` method is the main source of errors in this function.
+
+[`Mapper`]:https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/mapper/trait.Mapper.html
+[`FrameAllocator`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/trait.FrameAllocator.html
+[`Size4KiB`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/page/enum.Size4KiB.html
+[`Result`]: https://doc.rust-lang.org/core/result/enum.Result.html
+[`MapToError`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/mapper/enum.MapToError.html
+[`Mapper::map_to`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/mapper/trait.Mapper.html#method.map_to
+
+The implementation can be broken down into two parts:
+
+- **Creating the page range:**: To create a range of the pages that we want to map, we convert the `HEAP_START` pointer to a [`VirtAddr`] type. Then we calculate the heap end address from it by adding the `HEAP_SIZE`. We want an inclusive bound (the address of the last byte of the heap), so we subtract 1. Next, we convert the addresses into [`Page`] types using the [`containing_address`] function. Finally, we create a page range from the start and end pages using the [`Page::range_inclusive`] function.
+
+- **Mapping the pages:** The second step is to map all pages of the page range we just created. For that we iterate over the pages in that range using a `for` loop. For each page, we do the following:
+
+ - We allocate a physical frame that the page should be mapped to using the [`FrameAllocator::allocate_frame`] method. This method returns [`None`] when there are no more frames left. We deal with that case by mapping it to a [`MapToError::FrameAllocationFailed`] error through the [`Option::ok_or`] method and then apply the [question mark operator] to return early in the case of an error.
+
+ - We set the required `PRESENT` flag and the `WRITABLE` flag for the page. With these flags both read and write accesses are allowed, which makes sense for heap memory.
+
+ - We use the [`Mapper::map_to`] method for creating the mapping in the active page table. The method can fail, therefore we use the [question mark operator] again to forward the error to the caller. On success, the method returns a [`MapperFlush`] instance that we can use to update the [_translation lookaside buffer_] using the [`flush`] method.
+
+[`VirtAddr`]: https://docs.rs/x86_64/0.14.2/x86_64/addr/struct.VirtAddr.html
+[`Page`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/page/struct.Page.html
+[`containing_address`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/page/struct.Page.html#method.containing_address
+[`Page::range_inclusive`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/page/struct.Page.html#method.range_inclusive
+[`FrameAllocator::allocate_frame`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/trait.FrameAllocator.html#tymethod.allocate_frame
+[`None`]: https://doc.rust-lang.org/core/option/enum.Option.html#variant.None
+[`MapToError::FrameAllocationFailed`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/mapper/enum.MapToError.html#variant.FrameAllocationFailed
+[`Option::ok_or`]: https://doc.rust-lang.org/core/option/enum.Option.html#method.ok_or
+[question mark operator]: https://doc.rust-lang.org/edition-guide/rust-2018/error-handling-and-panics/the-question-mark-operator-for-easier-error-handling.html
+[`MapperFlush`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/mapper/struct.MapperFlush.html
+[_translation lookaside buffer_]: @/edition-2/posts/08-paging-introduction/index.md#the-translation-lookaside-buffer
+[`flush`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/mapper/struct.MapperFlush.html#method.flush
+
+The final step is to call this function from our `kernel_main`:
+
+```rust
+// in src/main.rs
+
+fn kernel_main(boot_info: &'static BootInfo) -> ! {
+ use blog_os::allocator; // new import
+ use blog_os::memory::{self, BootInfoFrameAllocator};
+
+ println!("Hello World{}", "!");
+ blog_os::init();
+
+ let phys_mem_offset = VirtAddr::new(boot_info.physical_memory_offset);
+ let mut mapper = unsafe { memory::init(phys_mem_offset) };
+ let mut frame_allocator = unsafe {
+ BootInfoFrameAllocator::init(&boot_info.memory_map)
+ };
+
+ // new
+ allocator::init_heap(&mut mapper, &mut frame_allocator)
+ .expect("heap initialization failed");
+
+ let x = Box::new(41);
+
+ // […] call `test_main` in test mode
+
+ println!("It did not crash!");
+ blog_os::hlt_loop();
+}
+```
+
+We show the full function for context here. The only new lines are the `blog_os::allocator` import and the call to `allocator::init_heap` function. In case the `init_heap` function returns an error, we panic using the [`Result::expect`] method since there is currently no sensible way for us to handle this error.
+
+[`Result::expect`]: https://doc.rust-lang.org/core/result/enum.Result.html#method.expect
+
+We now have a mapped heap memory region that is ready to be used. The `Box::new` call still uses our old `Dummy` allocator, so you will still see the "out of memory" error when you run it. Let's fix this by using a proper allocator.
+
+## Using an Allocator Crate
+
+Since implementing an allocator is somewhat complex, we start by using an external allocator crate. We will learn how to implement our own allocator in the next post.
+
+A simple allocator crate for `no_std` applications is the [`linked_list_allocator`] crate. It's name comes from the fact that it uses a linked list data structure to keep track of deallocated memory regions. See the next post for a more detailed explanation of this approach.
+
+To use the crate, we first need to add a dependency on it in our `Cargo.toml`:
+
+[`linked_list_allocator`]: https://github.com/phil-opp/linked-list-allocator/
+
+```toml
+# in Cargo.toml
+
+[dependencies]
+linked_list_allocator = "0.9.0"
+```
+
+Then we can replace our dummy allocator with the allocator provided by the crate:
+
+```rust
+// in src/allocator.rs
+
+use linked_list_allocator::LockedHeap;
+
+#[global_allocator]
+static ALLOCATOR: LockedHeap = LockedHeap::empty();
+```
+
+The struct is named `LockedHeap` because it uses the [`spinning_top::Spinlock`] type for synchronization. This is required because multiple threads could access the `ALLOCATOR` static at the same time. As always when using a spinlock or a mutex, we need to be careful to not accidentally cause a deadlock. This means that we shouldn't perform any allocations in interrupt handlers, since they can run at an arbitrary time and might interrupt an in-progress allocation.
+
+[`spinning_top::Spinlock`]: https://docs.rs/spinning_top/0.1.0/spinning_top/type.Spinlock.html
+
+Setting the `LockedHeap` as global allocator is not enough. The reason is that we use the [`empty`] constructor function, which creates an allocator without any backing memory. Like our dummy allocator, it always returns an error on `alloc`. To fix this, we need to initialize the allocator after creating the heap:
+
+[`empty`]: https://docs.rs/linked_list_allocator/0.9.0/linked_list_allocator/struct.LockedHeap.html#method.empty
+
+```rust
+// in src/allocator.rs
+
+pub fn init_heap(
+ mapper: &mut impl Mapper,
+ frame_allocator: &mut impl FrameAllocator,
+) -> Result<(), MapToError> {
+ // […] map all heap pages to physical frames
+
+ // new
+ unsafe {
+ ALLOCATOR.lock().init(HEAP_START, HEAP_SIZE);
+ }
+
+ Ok(())
+}
+```
+
+We use the [`lock`] method on the inner spinlock of the `LockedHeap` type to get an exclusive reference to the wrapped [`Heap`] instance, on which we then call the [`init`] method with the heap bounds as arguments. It is important that we initialize the heap _after_ mapping the heap pages, since the [`init`] function already tries to write to the heap memory.
+
+[`lock`]: https://docs.rs/lock_api/0.3.3/lock_api/struct.Mutex.html#method.lock
+[`Heap`]: https://docs.rs/linked_list_allocator/0.9.0/linked_list_allocator/struct.Heap.html
+[`init`]: https://docs.rs/linked_list_allocator/0.9.0/linked_list_allocator/struct.Heap.html#method.init
+
+After initializing the heap, we can now use all allocation and collection types of the built-in [`alloc`] crate without error:
+
+```rust
+// in src/main.rs
+
+use alloc::{boxed::Box, vec, vec::Vec, rc::Rc};
+
+fn kernel_main(boot_info: &'static BootInfo) -> ! {
+ // […] initialize interrupts, mapper, frame_allocator, heap
+
+ // allocate a number on the heap
+ let heap_value = Box::new(41);
+ println!("heap_value at {:p}", heap_value);
+
+ // create a dynamically sized vector
+ let mut vec = Vec::new();
+ for i in 0..500 {
+ vec.push(i);
+ }
+ println!("vec at {:p}", vec.as_slice());
+
+ // create a reference counted vector -> will be freed when count reaches 0
+ let reference_counted = Rc::new(vec![1, 2, 3]);
+ let cloned_reference = reference_counted.clone();
+ println!("current reference count is {}", Rc::strong_count(&cloned_reference));
+ core::mem::drop(reference_counted);
+ println!("reference count is {} now", Rc::strong_count(&cloned_reference));
+
+ // […] call `test_main` in test context
+ println!("It did not crash!");
+ blog_os::hlt_loop();
+}
+```
+
+This code example shows some uses of the [`Box`], [`Vec`], and [`Rc`] types. For the `Box` and `Vec` types we print the underlying heap pointers using the [`{:p}` formatting specifier]. For showcasing `Rc`, we create a reference counted heap value and use the [`Rc::strong_count`] function to print the current reference count, before and after dropping an instance (using [`core::mem::drop`]).
+
+[`Vec`]: https://doc.rust-lang.org/alloc/vec/
+[`Rc`]: https://doc.rust-lang.org/alloc/rc/
+[`{:p}` formatting specifier]: https://doc.rust-lang.org/core/fmt/trait.Pointer.html
+[`Rc::strong_count`]: https://doc.rust-lang.org/alloc/rc/struct.Rc.html#method.strong_count
+[`core::mem::drop`]: https://doc.rust-lang.org/core/mem/fn.drop.html
+
+When we run it, we see the following:
+
+
+
+As expected, we see that the `Box` and `Vec` values live on the heap, as indicated by the pointer starting with the `0x_4444_4444_*` prefix. The reference counted value also behaves as expected, with the reference count being 2 after the `clone` call, and 1 again after one of the instances was dropped.
+
+The reason that the vector starts at offset `0x800` is not that the boxed value is `0x800` bytes large, but the [reallocations] that occur when the vector needs to increase its capacity. For example, when the vector's capacity is 32 and we try to add the next element, the vector allocates a new backing array with capacity 64 behind the scenes and copies all elements over. Then it frees the old allocation.
+
+[reallocations]: https://doc.rust-lang.org/alloc/vec/struct.Vec.html#capacity-and-reallocation
+
+Of course there are many more allocation and collection types in the `alloc` crate that we can now all use in our kernel, including:
+
+- the thread-safe reference counted pointer [`Arc`]
+- the owned string type [`String`] and the [`format!`] macro
+- [`LinkedList`]
+- the growable ring buffer [`VecDeque`]
+- the [`BinaryHeap`] priority queue
+- [`BTreeMap`] and [`BTreeSet`]
+
+[`Arc`]: https://doc.rust-lang.org/alloc/sync/struct.Arc.html
+[`String`]: https://doc.rust-lang.org/alloc/string/struct.String.html
+[`format!`]: https://doc.rust-lang.org/alloc/macro.format.html
+[`LinkedList`]: https://doc.rust-lang.org/alloc/collections/linked_list/struct.LinkedList.html
+[`VecDeque`]: https://doc.rust-lang.org/alloc/collections/vec_deque/struct.VecDeque.html
+[`BinaryHeap`]: https://doc.rust-lang.org/alloc/collections/binary_heap/struct.BinaryHeap.html
+[`BTreeMap`]: https://doc.rust-lang.org/alloc/collections/btree_map/struct.BTreeMap.html
+[`BTreeSet`]: https://doc.rust-lang.org/alloc/collections/btree_set/struct.BTreeSet.html
+
+These types will become very useful when we want to implement thread lists, scheduling queues, or support for async/await.
+
+## Adding a Test
+
+To ensure that we don't accidentally break our new allocation code, we should add an integration test for it. We start by creating a new `tests/heap_allocation.rs` file with the following content:
+
+```rust
+// in tests/heap_allocation.rs
+
+#![no_std]
+#![no_main]
+#![feature(custom_test_frameworks)]
+#![test_runner(blog_os::test_runner)]
+#![reexport_test_harness_main = "test_main"]
+
+extern crate alloc;
+
+use bootloader::{entry_point, BootInfo};
+use core::panic::PanicInfo;
+
+entry_point!(main);
+
+fn main(boot_info: &'static BootInfo) -> ! {
+ unimplemented!();
+}
+
+#[panic_handler]
+fn panic(info: &PanicInfo) -> ! {
+ blog_os::test_panic_handler(info)
+}
+```
+
+We reuse the `test_runner` and `test_panic_handler` functions from our `lib.rs`. Since we want to test allocations, we enable the `alloc` crate through the `extern crate alloc` statement. For more information about the test boilerplate check out the [_Testing_] post.
+
+[_Testing_]: @/edition-2/posts/04-testing/index.md
+
+The implementation of the `main` function looks like this:
+
+```rust
+// in tests/heap_allocation.rs
+
+fn main(boot_info: &'static BootInfo) -> ! {
+ use blog_os::allocator;
+ use blog_os::memory::{self, BootInfoFrameAllocator};
+ use x86_64::VirtAddr;
+
+ blog_os::init();
+ let phys_mem_offset = VirtAddr::new(boot_info.physical_memory_offset);
+ let mut mapper = unsafe { memory::init(phys_mem_offset) };
+ let mut frame_allocator = unsafe {
+ BootInfoFrameAllocator::init(&boot_info.memory_map)
+ };
+ allocator::init_heap(&mut mapper, &mut frame_allocator)
+ .expect("heap initialization failed");
+
+ test_main();
+ loop {}
+}
+```
+
+It is very similar to the `kernel_main` function in our `main.rs`, with the differences that we don't invoke `println`, don't include any example allocations, and call `test_main` unconditionally.
+
+Now we're ready to add a few test cases. First, we add a test that performs some simple allocations using [`Box`] and checks the allocated values, to ensure that basic allocations work:
+
+```rust
+// in tests/heap_allocation.rs
+use alloc::boxed::Box;
+
+#[test_case]
+fn simple_allocation() {
+ let heap_value_1 = Box::new(41);
+ let heap_value_2 = Box::new(13);
+ assert_eq!(*heap_value_1, 41);
+ assert_eq!(*heap_value_2, 13);
+}
+```
+
+Most importantly, this test verifies that no allocation error occurs.
+
+Next, we iteratively build a large vector, to test both large allocations and multiple allocations (due to reallocations):
+
+```rust
+// in tests/heap_allocation.rs
+
+use alloc::vec::Vec;
+
+#[test_case]
+fn large_vec() {
+ let n = 1000;
+ let mut vec = Vec::new();
+ for i in 0..n {
+ vec.push(i);
+ }
+ assert_eq!(vec.iter().sum::(), (n - 1) * n / 2);
+}
+```
+
+We verify the sum by comparing it with the formula for the [n-th partial sum]. This gives us some confidence that the allocated values are all correct.
+
+[n-th partial sum]: https://en.wikipedia.org/wiki/1_%2B_2_%2B_3_%2B_4_%2B_%E2%8B%AF#Partial_sums
+
+As a third test, we create ten thousand allocations after each other:
+
+```rust
+// in tests/heap_allocation.rs
+
+use blog_os::allocator::HEAP_SIZE;
+
+#[test_case]
+fn many_boxes() {
+ for i in 0..HEAP_SIZE {
+ let x = Box::new(i);
+ assert_eq!(*x, i);
+ }
+}
+```
+
+This test ensures that the allocator reuses freed memory for subsequent allocations since it would run out of memory otherwise. This might seem like an obvious requirement for an allocator, but there are allocator designs that don't do this. An example is the bump allocator design that will be explained in the next post.
+
+Let's run our new integration test:
+
+```
+> cargo test --test heap_allocation
+[…]
+Running 3 tests
+simple_allocation... [ok]
+large_vec... [ok]
+many_boxes... [ok]
+```
+
+All three tests succeeded! You can also invoke `cargo test` (without `--test` argument) to run all unit and integration tests.
+
+## Summary
+
+This post gave an introduction to dynamic memory and explained why and where it is needed. We saw how Rust's borrow checker prevents common vulnerabilities and learned how Rust's allocation API works.
+
+After creating a minimal implementation of Rust's allocator interface using a dummy allocator, we created a proper heap memory region for our kernel. For that we defined a virtual address range for the heap and then mapped all pages of that range to physical frames using the `Mapper` and `FrameAllocator` from the previous post.
+
+Finally, we added a dependency on the `linked_list_allocator` crate to add a proper allocator to our kernel. With this allocator, we were able to use `Box`, `Vec`, and other allocation and collection types from the `alloc` crate.
+
+## What's next?
+
+While we already added heap allocation support in this post, we left most of the work to the `linked_list_allocator` crate. The next post will show in detail how an allocator can be implemented from scratch. It will present multiple possible allocator designs, shows how to implement simple versions of them, and explain their advantages and drawbacks.
From 21c5b62c3810da381248c8b090fd217462798d19 Mon Sep 17 00:00:00 2001
From: Philipp Oppermann
Date: Sun, 22 May 2022 12:23:15 +0200
Subject: [PATCH 020/107] Add a new `translation_contributors` front matter
field
This field should list people that contributed to the translation, e.g. by fixing errors. The idea behind this field is to properly acknowledge all contributors, while still keeping the list of the original translation authors separate.
---
blog/config.toml | 8 ++++++++
blog/sass/css/edition-2/main.scss | 7 +++++++
blog/templates/edition-2/page.html | 12 ++++++++++++
3 files changed, 27 insertions(+)
diff --git a/blog/config.toml b/blog/config.toml
index 29ffe874..51332e0e 100644
--- a/blog/config.toml
+++ b/blog/config.toml
@@ -46,6 +46,7 @@ not_translated = "(This post is not translated yet.)"
translated_content = "Translated Content:"
translated_content_notice = "This is a community translation of the _original.title_ post. It might be incomplete, outdated or contain errors. Please report any issues!"
translated_by = "Translation by"
+translation_contributors = "With contributions from"
word_separator = "and"
# Chinese (simplified)
@@ -63,6 +64,7 @@ not_translated = "(该文章还没有被翻译。)"
translated_content = "翻译内容:"
translated_content_notice = "这是对原文章 _original.title_ 的社区中文翻译。它可能不完整,过时或者包含错误。可以在 这个 Issue 上评论和提问!"
translated_by = "翻译者:"
+translation_contributors = "With contributions from"
word_separator = "和"
# Chinese (traditional)
@@ -80,6 +82,7 @@ not_translated = "(該文章還沒有被翻譯。)"
translated_content = "翻譯內容:"
translated_content_notice = "這是對原文章 _original.title_ 的社區中文翻譯。它可能不完整,過時或者包含錯誤。可以在 這個 Issue 上評論和提問!"
translated_by = "翻譯者:"
+translation_contributors = "With contributions from"
word_separator = "和"
# Japanese
@@ -97,6 +100,7 @@ not_translated = "(この記事はまだ翻訳されていません。)"
translated_content = "この記事は翻訳されたものです:"
translated_content_notice = "この記事は_original.title_をコミュニティの手により翻訳したものです。そのため、翻訳が完全・最新でなかったり、原文にない誤りを含んでいる可能性があります。問題があればこのissue上で報告してください!"
translated_by = "翻訳者:"
+translation_contributors = "With contributions from"
word_separator = "及び"
# Persian
@@ -114,6 +118,7 @@ not_translated = "(.این پست هنوز ترجمه نشده است)"
translated_content = "محتوای ترجمه شده:"
translated_content_notice = "این یک ترجمه از جامعه کاربران برای پست _original.title_ است. ممکن است ناقص، منسوخ شده یا دارای خطا باشد. لطفا هر گونه مشکل را در این ایشو گزارش دهید!"
translated_by = "ترجمه توسط"
+translation_contributors = "With contributions from"
word_separator = "و"
# Russian
@@ -131,6 +136,7 @@ not_translated = "(Этот пост еще не переведен.)"
translated_content = "Переведенное содержание:"
translated_content_notice = "Это перевод сообщества поста _original.title_. Он может быть неполным, устаревшим или содержать ошибки. Пожалуйста, сообщайте о любых проблемах!"
translated_by = "Перевод сделан"
+translation_contributors = "With contributions from"
word_separator = "и"
# French
@@ -148,6 +154,7 @@ not_translated = "(Cet article n'est pas encore traduit.)"
translated_content = "Contenu traduit : "
translated_content_notice = "Ceci est une traduction communautaire de l'article _original.title_. Il peut être incomplet, obsolète ou contenir des erreurs. Veuillez signaler les quelconques problèmes !"
translated_by = "Traduit par : "
+translation_contributors = "With contributions from"
word_separator = "et"
# Korean
@@ -165,4 +172,5 @@ not_translated = "(아직 번역이 완료되지 않은 게시글입니다)"
translated_content = "번역된 내용 : "
translated_content_notice = "이것은 커뮤니티 멤버가 _original.title_ 포스트를 번역한 글입니다. 부족한 설명이나 오류, 혹은 시간이 지나 더 이상 유효하지 않은 정보를 발견하시면 제보해주세요!"
translated_by = "번역한 사람 : "
+translation_contributors = "With contributions from"
word_separator = "와"
diff --git a/blog/sass/css/edition-2/main.scss b/blog/sass/css/edition-2/main.scss
index f2ca3806..53f7932c 100644
--- a/blog/sass/css/edition-2/main.scss
+++ b/blog/sass/css/edition-2/main.scss
@@ -587,6 +587,13 @@ main img {
color: #999999;
}
+.translation_contributors {
+ display: block;
+ margin-top: 0.5rem;
+ font-size: 0.9rem;
+ opacity: 0.7;
+}
+
.post-category {
margin-right: 0.5rem;
text-transform: uppercase;
diff --git a/blog/templates/edition-2/page.html b/blog/templates/edition-2/page.html
index f7000823..f4b1aadf 100644
--- a/blog/templates/edition-2/page.html
+++ b/blog/templates/edition-2/page.html
@@ -69,6 +69,18 @@
{%- endif -%}
@{{user}}
{%- endfor %}.
+
+ {%- if page.extra.translation_contributors %}
+
+ {{ trans(key="translation_contributors", lang=lang) }} {% for user in page.extra.translation_contributors -%}
+ {%- if not loop.first -%}
+ {%- if loop.last %} {{ trans(key="word_separator", lang=lang) }} {% else %}, {% endif -%}
+ {%- endif -%}
+ @{{user}}
+ {%- endfor %}.
+
+ {% endif -%}
+
{% endif -%}
From 5a1b8417110089049b07228575ca66d84387a878 Mon Sep 17 00:00:00 2001
From: Philipp Oppermann
Date: Sun, 22 May 2022 12:29:05 +0200
Subject: [PATCH 021/107] Use new `translation_contributors` field
---
blog/content/edition-2/posts/12-async-await/index.ja.md | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/blog/content/edition-2/posts/12-async-await/index.ja.md b/blog/content/edition-2/posts/12-async-await/index.ja.md
index 34fda6ca..33757819 100644
--- a/blog/content/edition-2/posts/12-async-await/index.ja.md
+++ b/blog/content/edition-2/posts/12-async-await/index.ja.md
@@ -8,8 +8,10 @@ date = 2020-03-27
chapter = "Multitasking"
# Please update this when updating the translation
translation_based_on_commit = "bf4f88107966c7ab1327c3cdc0ebfbd76bad5c5f"
-# GitHub usernames of the people that translated this post
-translators = ["kahirokunn", "garasubo", "sozysozbot", "woodyZootopia", "asami-kawasaki"]
+# GitHub usernames of the authors of this translation
+translators = ["kahirokunn", "garasubo", "sozysozbot", "woodyZootopia"]
+# GitHub usernames of the people that contributed to this translation
+translation_contributors = ["asami-kawasaki", "Foo-x"]
+++
この記事では、Rustの**協調的マルチタスク**と**async/await**機能について説明します。Rustのasync/await機能については、`Future` trait の設計、ステートマシンの変換、 **pinning** などを含めて詳しく説明します。そして、非同期キーボードタスクと基本的なexecutorを作成することで、カーネルにasync/awaitの基本的なサポートを追加します。
From ad8e0b4783713e0beeae9331c4a5d484d0111cdb Mon Sep 17 00:00:00 2001
From: Philipp Oppermann
Date: Sun, 22 May 2022 12:44:37 +0200
Subject: [PATCH 022/107] Use gray font instead of reduced opacity for
translation contributors list
This way the contributors are listed in link color with full opacity.
---
blog/sass/css/edition-2/main.scss | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/blog/sass/css/edition-2/main.scss b/blog/sass/css/edition-2/main.scss
index 53f7932c..0065e686 100644
--- a/blog/sass/css/edition-2/main.scss
+++ b/blog/sass/css/edition-2/main.scss
@@ -591,7 +591,7 @@ main img {
display: block;
margin-top: 0.5rem;
font-size: 0.9rem;
- opacity: 0.7;
+ color: gray;
}
.post-category {
From eab9b70c68567b8be5adb579c35be45e07d5a097 Mon Sep 17 00:00:00 2001
From: Philipp Oppermann
Date: Sun, 22 May 2022 12:39:55 +0200
Subject: [PATCH 023/107] Fix Rust documentation links
---
blog/content/edition-2/posts/02-minimal-rust-kernel/index.fa.md | 2 +-
blog/content/edition-2/posts/02-minimal-rust-kernel/index.ja.md | 2 +-
blog/content/edition-2/posts/02-minimal-rust-kernel/index.ko.md | 2 +-
blog/content/edition-2/posts/02-minimal-rust-kernel/index.md | 2 +-
blog/content/edition-2/posts/02-minimal-rust-kernel/index.ru.md | 2 +-
.../edition-2/posts/02-minimal-rust-kernel/index.zh-CN.md | 2 +-
blog/content/edition-2/posts/03-vga-text-buffer/index.fa.md | 2 +-
blog/content/edition-2/posts/03-vga-text-buffer/index.ja.md | 2 +-
blog/content/edition-2/posts/03-vga-text-buffer/index.md | 2 +-
blog/content/edition-2/posts/03-vga-text-buffer/index.zh-CN.md | 2 +-
blog/content/news/2018-03-09-pure-rust.md | 2 +-
11 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/blog/content/edition-2/posts/02-minimal-rust-kernel/index.fa.md b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.fa.md
index b5396c8c..6893643f 100644
--- a/blog/content/edition-2/posts/02-minimal-rust-kernel/index.fa.md
+++ b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.fa.md
@@ -106,7 +106,7 @@ rtl = true
کامپایلر شبانه به ما امکان میدهد با استفاده از به اصطلاح _feature flags_ در بالای فایل، از ویژگیهای مختلف آزمایشی استفاده کنیم. به عنوان مثال، میتوانیم [`asm!` macro] آزمایشی را برای اجرای دستورات اسمبلیِ اینلاین (تلفظ: inline) با اضافه کردن `[feature(asm)]!#` به بالای فایل `main.rs` فعال کنیم. توجه داشته باشید که این ویژگیهای آزمایشی، کاملاً ناپایدار هستند، به این معنی که نسخههای آتی Rust ممکن است بدون هشدار قبلی آنها را تغییر داده یا حذف کند. به همین دلیل ما فقط در صورت لزوم از آنها استفاده خواهیم کرد.
-[`asm!` macro]: https://doc.rust-lang.org/unstable-book/library-features/asm.html
+[`asm!` macro]: https://doc.rust-lang.org/stable/reference/inline-assembly.html
### مشخصات هدف
diff --git a/blog/content/edition-2/posts/02-minimal-rust-kernel/index.ja.md b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.ja.md
index 2858a722..bf3b19f1 100644
--- a/blog/content/edition-2/posts/02-minimal-rust-kernel/index.ja.md
+++ b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.ja.md
@@ -101,7 +101,7 @@ Rustの実行環境を管理するのには、[rustup]を強くおすすめし
nightlyコンパイラでは、いわゆる**feature flag**をファイルの先頭につけることで、いろいろな実験的機能を使うことを選択できます。例えば、`#![feature(asm)]`を`main.rs`の先頭につけることで、インラインアセンブリのための実験的な[`asm!`マクロ][`asm!` macro]を有効化することができます。ただし、これらの実験的機能は全くもって不安定であり、将来のRustバージョンにおいては事前の警告なく変更されたり取り除かれたりする可能性があることに注意してください。このため、絶対に必要なときにのみこれらを使うことにします。
-[`asm!` macro]: https://doc.rust-lang.org/unstable-book/library-features/asm.html
+[`asm!` macro]: https://doc.rust-lang.org/stable/reference/inline-assembly.html
### ターゲットの仕様
Cargoは`--target`パラメータを使ってさまざまなターゲットをサポートします。ターゲットはいわゆる[target triple][target triple]によって表されます。これはCPUアーキテクチャ、製造元、オペレーティングシステム、そして[ABI]を表します。例えば、`x86_64-unknown-linux-gnu`というtarget tripleは、`x86_64`のCPU、製造元不明、GNU ABIのLinuxオペレーティングシステム向けのシステムを表します。Rustは[多くのtarget triple][platform-support]をサポートしており、その中にはAndroidのための`arm-linux-androideabi`や[WebAssemblyのための`wasm32-unknown-unknown`](https://www.hellorust.com/setup/wasm-target/)などがあります。
diff --git a/blog/content/edition-2/posts/02-minimal-rust-kernel/index.ko.md b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.ko.md
index 046eab7e..931a4881 100644
--- a/blog/content/edition-2/posts/02-minimal-rust-kernel/index.ko.md
+++ b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.ko.md
@@ -109,7 +109,7 @@ Rust는 _stable_, _beta_ 그리고 _nightly_ 이렇게 세 가지의 채널을
nightly 컴파일러는 _feature 플래그_ 를 소스코드의 맨 위에 추가함으로써 여러 실험적인 기능들을 선별해 이용할 수 있게 해줍니다. 예를 들어, `#![feature(asm)]` 를 `main.rs`의 맨 위에 추가하면 [`asm!` 매크로][`asm!` macro]를 사용할 수 있습니다. `asm!` 매크로는 인라인 어셈블리 코드를 작성할 때 사용합니다.
이런 실험적인 기능들은 말 그대로 "실험적인" 기능들이기에 미래의 Rust 버전들에서는 예고 없이 변경되거나 삭제될 수도 있습니다. 그렇기에 우리는 이 실험적인 기능들을 최소한으로만 사용할 것입니다.
-[`asm!` macro]: https://doc.rust-lang.org/unstable-book/library-features/asm.html
+[`asm!` macro]: https://doc.rust-lang.org/stable/reference/inline-assembly.html
### 컴파일 대상 정의하기
Cargo는 `--target` 인자를 통해 여러 컴파일 대상 시스템들을 지원합니다. 컴파일 대상은 소위 _[target triple]_ 을 통해 표현되는데, CPU 아키텍쳐와 CPU 공급 업체, 운영체제, 그리고 [ABI]를 파악할 수 있습니다. 예를 들어 `x86_64-unknown-linux-gnu`는 `x86_64` CPU, 임의의 CPU 공급 업체, Linux 운영체제, 그리고 GNU ABI를 갖춘 시스템을 나타냅니다. Rust는 Android를 위한 `arm-linux-androideabi`와 [WebAssembly를 위한 `wasm32-unknown-unknown`](https://www.hellorust.com/setup/wasm-target/)를 비롯해 [다양한 target triple들][platform-support]을 지원합니다.
diff --git a/blog/content/edition-2/posts/02-minimal-rust-kernel/index.md b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.md
index efbad211..29394d43 100644
--- a/blog/content/edition-2/posts/02-minimal-rust-kernel/index.md
+++ b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.md
@@ -97,7 +97,7 @@ To manage Rust installations I highly recommend [rustup]. It allows you to insta
The nightly compiler allows us to opt-in to various experimental features by using so-called _feature flags_ at the top of our file. For example, we could enable the experimental [`asm!` macro] for inline assembly by adding `#![feature(asm)]` to the top of our `main.rs`. Note that such experimental features are completely unstable, which means that future Rust versions might change or remove them without prior warning. For this reason we will only use them if absolutely necessary.
-[`asm!` macro]: https://doc.rust-lang.org/unstable-book/library-features/asm.html
+[`asm!` macro]: https://doc.rust-lang.org/stable/reference/inline-assembly.html
### Target Specification
Cargo supports different target systems through the `--target` parameter. The target is described by a so-called _[target triple]_, which describes the CPU architecture, the vendor, the operating system, and the [ABI]. For example, the `x86_64-unknown-linux-gnu` target triple describes a system with a `x86_64` CPU, no clear vendor and a Linux operating system with the GNU ABI. Rust supports [many different target triples][platform-support], including `arm-linux-androideabi` for Android or [`wasm32-unknown-unknown` for WebAssembly](https://www.hellorust.com/setup/wasm-target/).
diff --git a/blog/content/edition-2/posts/02-minimal-rust-kernel/index.ru.md b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.ru.md
index 6f3690b8..64ae4511 100644
--- a/blog/content/edition-2/posts/02-minimal-rust-kernel/index.ru.md
+++ b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.ru.md
@@ -103,7 +103,7 @@ Rust имеет три релизных канала: _stable_, _beta_ и _night
Nightly версия компилятора позволяет нам подключать различные экспериментальные возможности с помощью так называемых _флагов_ в верхней части нашего файла. Например, мы можем включить экспериментальный [макрос `asm!``asm!` macro] для встроенного ассемблера, добавив `#![feature(asm)]` в начало нашего `main.rs`. Обратите внимание, что такие экспериментальные возможности совершенно нестабильны, что означает, что будущие версии Rust могут изменить или удалить их без предварительного предупреждения. По этой причине мы будем использовать их только в случае крайней необходимости.
-[`asm!` macro]: https://doc.rust-lang.org/unstable-book/library-features/asm.html
+[`asm!` macro]: https://doc.rust-lang.org/stable/reference/inline-assembly.html
### Спецификация целевой платформы
diff --git a/blog/content/edition-2/posts/02-minimal-rust-kernel/index.zh-CN.md b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.zh-CN.md
index 8a7076d6..8baf4672 100644
--- a/blog/content/edition-2/posts/02-minimal-rust-kernel/index.zh-CN.md
+++ b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.zh-CN.md
@@ -73,7 +73,7 @@ Rust 语言有三个**发行频道**(release channel),分别是 stable、b
Nightly 版本的编译器允许我们在源码的开头插入**特性标签**(feature flag),来自由选择并使用大量实验性的功能。举个例子,要使用实验性的[内联汇编(asm!宏)][asm feature],我们可以在 `main.rs` 的顶部添加 `#![feature(asm)]`。要注意的是,这样的实验性功能**不稳定**(unstable),意味着未来的 Rust 版本可能会修改或移除这些功能,而不会有预先的警告过渡。因此我们只有在绝对必要的时候,才应该使用这些特性。
-[asm feature]: https://doc.rust-lang.org/unstable-book/library-features/asm.html
+[asm feature]: https://doc.rust-lang.org/stable/reference/inline-assembly.html
### 目标配置清单
diff --git a/blog/content/edition-2/posts/03-vga-text-buffer/index.fa.md b/blog/content/edition-2/posts/03-vga-text-buffer/index.fa.md
index cdf40906..ea2cc4c7 100644
--- a/blog/content/edition-2/posts/03-vga-text-buffer/index.fa.md
+++ b/blog/content/edition-2/posts/03-vga-text-buffer/index.fa.md
@@ -651,7 +651,7 @@ pub fn _print(args: fmt::Arguments) {
از آنجا که ماکروها باید بتوانند از خارج از ماژول، `_print` را فراخوانی کنند، تابع باید عمومی (public) باشد. با این حال ، از آنجا که این جزئیات پیاده سازی را خصوصی (private) در نظر می گیریم، [ویژگی `doc(hidden)`] را اضافه می کنیم تا از مستندات تولید شده پنهان شود.
-[ویژگی `doc(hidden)`]: https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#dochidden
+[ویژگی `doc(hidden)`]: https://doc.rust-lang.org/nightly/rustdoc/write-documentation/the-doc-attribute.html#hidden
### Hello World توسط `println`
اکنون می توانیم از `println` در تابع `_start` استفاده کنیم:
diff --git a/blog/content/edition-2/posts/03-vga-text-buffer/index.ja.md b/blog/content/edition-2/posts/03-vga-text-buffer/index.ja.md
index dd9fac5a..d5ade97c 100644
--- a/blog/content/edition-2/posts/03-vga-text-buffer/index.ja.md
+++ b/blog/content/edition-2/posts/03-vga-text-buffer/index.ja.md
@@ -664,7 +664,7 @@ pub fn _print(args: fmt::Arguments) {
マクロは`_print`をモジュールの外側から呼び出せる必要があるので、この関数は公開されていなければなりません。しかし、これは非公開の実装の詳細であると考え、[`doc(hidden)`属性][`doc(hidden)` attribute]をつけることで、生成されたドキュメントから隠すようにします。
-[`doc(hidden)` attribute]: https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#dochidden
+[`doc(hidden)` attribute]: https://doc.rust-lang.org/nightly/rustdoc/write-documentation/the-doc-attribute.html#hidden
### `println`を使ってHello World
こうすることで、`_start`関数で`println`を使えるようになります:
diff --git a/blog/content/edition-2/posts/03-vga-text-buffer/index.md b/blog/content/edition-2/posts/03-vga-text-buffer/index.md
index 9176ba4d..be82edd7 100644
--- a/blog/content/edition-2/posts/03-vga-text-buffer/index.md
+++ b/blog/content/edition-2/posts/03-vga-text-buffer/index.md
@@ -647,7 +647,7 @@ The `_print` function locks our static `WRITER` and calls the `write_fmt` method
Since the macros need to be able to call `_print` from outside of the module, the function needs to be public. However, since we consider this a private implementation detail, we add the [`doc(hidden)` attribute] to hide it from the generated documentation.
-[`doc(hidden)` attribute]: https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#dochidden
+[`doc(hidden)` attribute]: https://doc.rust-lang.org/nightly/rustdoc/write-documentation/the-doc-attribute.html#hidden
### Hello World using `println`
Now we can use `println` in our `_start` function:
diff --git a/blog/content/edition-2/posts/03-vga-text-buffer/index.zh-CN.md b/blog/content/edition-2/posts/03-vga-text-buffer/index.zh-CN.md
index 2b4378b8..af33ecc8 100644
--- a/blog/content/edition-2/posts/03-vga-text-buffer/index.zh-CN.md
+++ b/blog/content/edition-2/posts/03-vga-text-buffer/index.zh-CN.md
@@ -592,7 +592,7 @@ pub fn _print(args: fmt::Arguments) {
另外,`_print` 函数将占有静态变量 `WRITER` 的锁,并调用它的 `write_fmt` 方法。这个方法是从名为 `Write` 的 trait 中获得的,所以我们需要导入这个 trait。额外的 `unwrap()` 函数将在打印不成功的时候 panic;但既然我们的 `write_str` 总是返回 `Ok`,这种情况不应该发生。
-如果这个宏将能在模块外访问,它们也应当能访问 `_print` 函数,因此这个函数必须是公有的(public)。然而,考虑到这是一个私有的实现细节,我们添加一个 [`doc(hidden)` 属性](https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#dochidden),防止它在生成的文档中出现。
+如果这个宏将能在模块外访问,它们也应当能访问 `_print` 函数,因此这个函数必须是公有的(public)。然而,考虑到这是一个私有的实现细节,我们添加一个 [`doc(hidden)` 属性](https://doc.rust-lang.org/nightly/rustdoc/write-documentation/the-doc-attribute.html#hidden),防止它在生成的文档中出现。
### 使用 `println!` 的 Hello World
diff --git a/blog/content/news/2018-03-09-pure-rust.md b/blog/content/news/2018-03-09-pure-rust.md
index ffc70ca1..ccf7a911 100644
--- a/blog/content/news/2018-03-09-pure-rust.md
+++ b/blog/content/news/2018-03-09-pure-rust.md
@@ -38,7 +38,7 @@ There are lots of alternatives to `make`, including some Rust tools such as [jus
[just]: https://github.com/casey/just
[cargo-make]: https://sagiegurari.github.io/cargo-make/
-[`global_asm`]: https://doc.rust-lang.org/unstable-book/library-features/global-asm.html
+[`global_asm`]: https://doc.rust-lang.org/stable/core/arch/macro.global_asm.html
## A custom Bootloader
To avoid the dependency on GRUB and to make things more ergonomic, we decided to write [our own bootloader] using Rust's [`global_asm`] feature. This way, the kernel can be significantly simplified, since the switch to long mode and the initial page table layout can already be done in the bootloader. Thus, we can avoid the initial assembly level blog posts in the second edition and directly start with high level Rust code.
From 385d0e13062611970f222fdc861ae2e82ed29f4c Mon Sep 17 00:00:00 2001
From: woodyZootopia
Date: Sat, 4 Jun 2022 13:14:17 +0900
Subject: [PATCH 024/107] Translated to the end
---
.../posts/10-heap-allocation/index.ja.md | 252 +++++++++---------
1 file changed, 129 insertions(+), 123 deletions(-)
diff --git a/blog/content/edition-2/posts/10-heap-allocation/index.ja.md b/blog/content/edition-2/posts/10-heap-allocation/index.ja.md
index be9a8344..cd72a2f2 100644
--- a/blog/content/edition-2/posts/10-heap-allocation/index.ja.md
+++ b/blog/content/edition-2/posts/10-heap-allocation/index.ja.md
@@ -102,7 +102,7 @@ fn inner(i: usize) -> &'static u32 {
[`ptr::write`]: https://doc.rust-lang.org/core/ptr/fn.write.html
[`offset`]: https://doc.rust-lang.org/std/primitive.pointer.html#method.offset
-割り当てられたメモリは`deallocate`の呼び出しによって明示的に解放されるまで生存します。したがって、返されたポインタは`inner`がリターンし、コールスタックの対応する部分が破棄された後も有効です。スタティックメモリと比較したときのヒープメモリの長所は、解放後に再利用できると言うことです(`outer`内の`deallocate`呼び出しでまさにこれを行っています)。この呼び出しの後、状況は以下のようになります。
+割り当てられたメモリは`deallocate`の呼び出しによって明示的に解放されるまで生存します。したがって、返されたポインタは、`inner`がリターンしコールスタックの対応する部分が破棄された後も有効です。スタティックメモリと比較したときのヒープメモリの長所は、解放後に再利用できると言うことです(`outer`内の`deallocate`呼び出しでまさにこれを行っています)。この呼び出しの後、状況は以下のようになります。
![The call stack contains the local variables of outer, the heap contains z[0] and z[2], but no longer z[1].](call-stack-heap-freed.svg)
@@ -110,26 +110,26 @@ fn inner(i: usize) -> &'static u32 {
### Common Errors
-Apart from memory leaks, which are unfortunate but don't make the program vulnerable to attackers, there are two common types of bugs with more severe consequences:
+メモリリークは困りものですが、プログラムを攻撃者に対して脆弱にはしません。しかしこのほかに、より深刻な結果を招く二種類のバグが存在します:
-- When we accidentally continue to use a variable after calling `deallocate` on it, we have a so-called **use-after-free** vulnerability. Such a bug causes undefined behavior and can often be exploited by attackers to execute arbitrary code.
-- When we accidentally free a variable twice, we have a **double-free** vulnerability. This is problematic because it might free a different allocation that was allocated in the same spot after the first `deallocate` call. Thus, it can lead to an use-after-free vulnerability again.
+- もし変数に対して`deallocate`を呼んだ後にも間違ってそれを使い続けたら、いわゆるuse-after-free脆弱性が発生します。このようなバグは未定義動作を引き起こし、しばしば攻撃者が任意コードを実行するのに利用されます。
+- 間違ってある変数を二度解放したら、double-free脆弱性が発生します。これが問題になるのは、最初の`deallocate`呼び出しの後に同じ場所にallocateされた別の割り当てを解放してしまうかもしれないからです。従って、これもまたuse-after-free脆弱性につながりかねません。
-These types of vulnerabilities are commonly known, so one might expect that people learned how to avoid them by now. But no, such vulnerabilities are still regularly found, for example this recent [use-after-free vulnerability in Linux][linux vulnerability] that allowed arbitrary code execution. This shows that even the best programmers are not always able to correctly handle dynamic memory in complex projects.
+これらの種類の脆弱性は広く知られているため、回避する方法も分かっているはずだとお思いになるかもしれません。しかし答えはいいえで、このような脆弱性は未だ散見され、例えば最近でも任意コード実行を許す[Linuxのuse-after-free脆弱性][linux vulnerability]が存在しました。このことは、最高のプログラマーであっても、複雑なプロジェクトにおいて常に正しく動的メモリを扱えはしないと言うことを示しています。
[linux vulnerability]: https://securityboulevard.com/2019/02/linux-use-after-free-vulnerability-found-in-linux-2-6-through-4-20-11/
-To avoid these issues, many languages such as Java or Python manage dynamic memory automatically using a technique called [_garbage collection_]. The idea is that the programmer never invokes `deallocate` manually. Instead, the program is regularly paused and scanned for unused heap variables, which are then automatically deallocated. Thus, the above vulnerabilities can never occur. The drawbacks are the performance overhead of the regular scan and the probably long pause times.
+これらの問題を回避するため、JavaやPythonといった多くの言語では[**ガベージコレクション**][_garbage collection_]という技法を使って自動的に動的メモリを管理しています。発想としては、プログラマが絶対に自分の手で`deallocate`を呼び出すことがないようにするというものです。代わりに、プログラムが定期的に一時停止されてスキャンされ、未使用のヒープ変数が見つかったら自動的にdeallocateされるのです。従って、上のような脆弱性は絶対に発生し得ません。欠点としては,定期的にスキャンすることによる性能のオーバーヘッドが発生することと、一時停止の時間が長くなりうるということがあります。
[_garbage collection_]: https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)
-Rust takes a different approach to the problem: It uses a concept called [_ownership_] that is able to check the correctness of dynamic memory operations at compile time. Thus no garbage collection is needed to avoid the mentioned vulnerabilities, which means that there is no performance overhead. Another advantage of this approach is that the programmer still has fine-grained control over the use of dynamic memory, just like with C or C++.
+Rustはこの問題に対して別のアプローチを取ります:[**所有権**][_ownership_]と呼ばれる概念を使って、動的メモリの操作の正確性をコンパイル時にチェックするのです。従って前述の脆弱性を回避するためのガベージコレクションの必要がなく、性能のオーバーヘッドが存在しないと言うことです。このアプローチのもう一つの利点として、CやC++と同様、プログラマが動的メモリの使用に関して精緻な制御を行うことができるということが挙げられます。
[_ownership_]: https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html
-### Allocations in Rust
+### Rustにおける割り当て
-Instead of letting the programmer manually call `allocate` and `deallocate`, the Rust standard library provides abstraction types that call these functions implicitly. The most important type is [**`Box`**], which is an abstraction for a heap-allocated value. It provides a [`Box::new`] constructor function that takes a value, calls `allocate` with the size of the value, and then moves the value to the newly allocated slot on the heap. To free the heap memory again, the `Box` type implements the [`Drop` trait] to call `deallocate` when it goes out of scope:
+プログラマーに自分の手で`allocate`と`deallocate`を呼ばせる代わりに、Rustの標準ライブラリはこれらの関数を暗黙の内に呼ぶ抽象型を提供しています。最も重要な型は[**`Box`**]で、これはヒープに割り当てられた値の抽象化です。これは[`Box::new`]コンストラクタ関数を提供しており、これは値を引数として、その値のサイズを引数に`allocate`を呼び出し、ヒープ上に新しく割り当てられたスロットにその値を移動します。ヒープメモリを再び解放するために、`Box`型は自身がスコープから出た際に`deallocate`を呼べるよう[`Drop`トレイト][`Drop` trait]を実装しています。
[**`Box`**]: https://doc.rust-lang.org/std/boxed/index.html
[`Box::new`]: https://doc.rust-lang.org/alloc/boxed/struct.Box.html#method.new
@@ -139,25 +139,25 @@ Instead of letting the programmer manually call `allocate` and `deallocate`, the
{
let z = Box::new([1,2,3]);
[…]
-} // z goes out of scope and `deallocate` is called
+} // zがスコープから出て`deallocate`が呼ばれる
```
-This pattern has the strange name [_resource acquisition is initialization_] (or _RAII_ for short). It originated in C++, where it is used to implement a similar abstraction type called [`std::unique_ptr`].
+このようなパターンは[リソース確保が初期化である][_resource acquisition is initialization_](resource acquisition is initialization、略してRAII)という奇妙な名前を持っています。このパターンは、C++で[`std::unique_ptr`]という似た抽象型を実装するのに使われたのが起源です。
[_resource acquisition is initialization_]: https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization
[`std::unique_ptr`]: https://en.cppreference.com/w/cpp/memory/unique_ptr
-Such a type alone does not suffice to prevent all use-after-free bugs since programmers can still hold on to references after the `Box` goes out of scope and the corresponding heap memory slot is deallocated:
+このような型自体はすべてのuse-after-freeバグを防ぐのに十分ではありません。なぜなら、プログラマは、`Box`がスコープ外に出て対応するヒープメモリスロットがdeallocateされた後でも参照を利用し続けることができるからです:
```rust
let x = {
let z = Box::new([1,2,3]);
&z[1]
-}; // z goes out of scope and `deallocate` is called
+}; // zがスコープから出て`deallocate`が呼ばれる
println!("{}", x);
```
-This is where Rust's ownership comes in. It assigns an abstract [lifetime] to each reference, which is the scope in which the reference is valid. In the above example, the `x` reference is taken from the `z` array, so it becomes invalid after `z` goes out of scope. When you [run the above example on the playground][playground-2] you see that the Rust compiler indeed throws an error:
+ここでRustの所有権の出番です。所有権システムは、参照が有効なスコープを表す抽象[ライフタイム][lifetime]をそれぞれの参照に指定します。上の例では、参照`x`は配列`z`から取られているので、`z`ガスコープ外に出ると無効になります。[上の例をplaygroundで実行する][playground-2]と、確かにRustコンパイラがエラーを投げるのが分かります:
[lifetime]: https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html
[playground-2]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=28180d8de7b62c6b4a681a7b1f745a48
@@ -175,31 +175,31 @@ error[E0597]: `z[_]` does not live long enough
| - `z[_]` dropped here while still borrowed
```
-The terminology can be a bit confusing at first. Taking a reference to a value is called _borrowing_ the value since it's similar to a borrow in real life: You have temporary access to an object but need to return it sometime and you must not destroy it. By checking that all borrows end before an object is destroyed, the Rust compiler can guarantee that no use-after-free situation can occur.
+ここで使われている用語は初見では少しわかりにくいかもしれません。値の参照を取ることは値を借用する (borrow) と呼ばれています。これは現実での借用と似ているためです:オブジェクトへの一時的なアクセスができるようになりますが、それをいつか返さなければならず、また破壊することも許されません。オブジェクトが破壊される前にすべての借用が終了することを確かめることにより、Rustコンパイラはuse-after-freeが起こりえないことを保証できるのです。
-Rust's ownership system goes even further and does not only prevent use-after-free bugs, but provides complete [_memory safety_] like garbage collected languages like Java or Python do. Additionally, it guarantees [_thread safety_] and is thus even safer than those languages in multi-threaded code. And most importantly, all these checks happen at compile time, so there is no runtime overhead compared to hand written memory management in C.
+Rustの所有権システムはさらに突き詰められており、use-after-freeバグを防ぐだけでなく、JavaやPythonのようなガベージコレクション型言語と同じ完全な[メモリ安全性][_memory safety_]を提供しています。さらに[スレッド安全性][_thread safety_]も保証されており、マルチスレッドのプログラムにおいてはこれらの言語よりもさらに安全です。さらに最も重要なことに、これらのチェックは全てコンパイル時に行われるため、C言語で手書きされたメモリ管理と比べても、実行時のオーバーヘッドはありません。
[_memory safety_]: https://en.wikipedia.org/wiki/Memory_safety
[_thread safety_]: https://en.wikipedia.org/wiki/Thread_safety
-### Use Cases
+### 使い方
-We now know the basics of dynamic memory allocation in Rust, but when should we use it? We've come really far with our kernel without dynamic memory allocation, so why do we need it now?
+Rustにおける動的メモリ割り当ての基礎を学んだわけですが、これをいつ使えば良いのでしょうか?私たちのカーネルは動的メモリ割り当てなしにこれだけやってこられたのに、どうして今になってこれが必要なのでしょうか?
-First, dynamic memory allocation always comes with a bit of performance overhead, since we need to find a free slot on the heap for every allocation. For this reason local variables are generally preferable, especially in performance sensitive kernel code. However, there are cases where dynamic memory allocation is the best choice.
+まず覚えておいて欲しいのは、動的メモリ割り当てには、割り当てを行うたびにヒープから空いているスロットを探してこないといけないので、少しだけ性能オーバーヘッドがあるということです。このため、特に性能が重要となるカーネルのプログラムにおいては、一般にローカル変数の方が好ましいです。しかし、動的メモリ割り当てが最良の選択肢であるようなケースも存在するのです。
-As a basic rule, dynamic memory is required for variables that have a dynamic lifetime or a variable size. The most important type with a dynamic lifetime is [**`Rc`**], which counts the references to its wrapped value and deallocates it after all references went out of scope. Examples for types with a variable size are [**`Vec`**], [**`String`**], and other [collection types] that dynamically grow when more elements are added. These types work by allocating a larger amount of memory when they become full, copying all elements over, and then deallocating the old allocation.
+基本的なルールとして、動的メモリは動的なライフタイムや可変サイズを持つような変数に必要とされます。動的なライフタイムを持つ最も重要な型は[**`Rc`**]で、これはラップされた値に対する参照を数えておき、すべての参照がスコープから外れたらそれをdeallocateするというものです。可変サイズを持つ型の例には、[**`Vec`**]、[**`String`**]、その他の[コレクション型][collection types]といった、要素が追加されたときに動的に大きくなるような型が挙げられます。これらの型は、容量が一杯になると、より大きい量のメモリを割り当て、すべての要素をコピーし、古い割り当てをdeallocateすることにより対処します。
[**`Rc`**]: https://doc.rust-lang.org/alloc/rc/index.html
[**`Vec`**]: https://doc.rust-lang.org/alloc/vec/index.html
[**`String`**]: https://doc.rust-lang.org/alloc/string/index.html
[collection types]: https://doc.rust-lang.org/alloc/collections/index.html
-For our kernel we will mostly need the collection types, for example for storing a list of active tasks when implementing multitasking in future posts.
+私たちのカーネルでは主にコレクション型を必要とし、例えば、将来の記事でマルチタスキングを実行するときにアクティブなタスクのリストを格納するために使います。
-## The Allocator Interface
+## アロケータのインターフェース
-The first step in implementing a heap allocator is to add a dependency on the built-in [`alloc`] crate. Like the [`core`] crate, it is a subset of the standard library that additionally contains the allocation and collection types. To add the dependency on `alloc`, we add the following to our `lib.rs`:
+ヒープアロケータを実装するための最初のステップは、組み込みの[`alloc`]クレートへの依存関係を津一過することです。[`core`]クレートと同様、これは標準ライブラリのサブセットであり、アロケーション型やコレクション型を含んでいます。`alloc`への依存関係を追加するために、以下を`lib.rs`に追加します:
[`alloc`]: https://doc.rust-lang.org/alloc/
[`core`]: https://doc.rust-lang.org/core/
@@ -210,9 +210,15 @@ The first step in implementing a heap allocator is to add a dependency on the bu
extern crate alloc;
```
-Contrary to normal dependencies, we don't need to modify the `Cargo.toml`. The reason is that the `alloc` crate ships with the Rust compiler as part of the standard library, so the compiler already knows about the crate. By adding this `extern crate` statement, we specify that the compiler should try to include it. (Historically, all dependencies needed an `extern crate` statement, which is now optional).
+通常の依存関係と異なり`Cargo.toml`を修正する必要はありません。その理由は、`alloc`クレートは標準ライブラリの一部としてRustコンパイラに同梱されているため、コンパイラはすでにこのクレートのことを知っているためです。この`extern crate`宣言を追加することで、コンパイラにこれをインクルードしようと試みるよう指定しています(昔はすべての依存関係が`extern crate`宣言を必要としていたのですが、いまは任意です)。
-Since we are compiling for a custom target, we can't use the precompiled version of `alloc` that is shipped with the Rust installation. Instead, we have to tell cargo to recompile the crate from source. We can do that, by adding it to the `unstable.build-std` array in our `.cargo/config.toml` file:
+
+
+**訳注:** もう少し詳しく解説します。Rust 2018から通常のクレートに関しては`extern crate`が必要なくなりましたが、Rust自体に同梱されるsysrootと呼ばれるクレートに関しては、特殊な状況でしか必要ないものがあるため明示的な`extern crate`がない限りリンクされません。詳しくは、[edition guideの対応するページ](https://doc.rust-jp.rs/edition-guide/rust-2018/path-changes.html#%E3%81%95%E3%82%88%E3%81%86%E3%81%AA%E3%82%89extern-crate)をご覧ください。
+
+
+
+カスタムターゲット向けにコンパイルしようとしているので、Rustインストール時に同梱されていたコンパイル済みの`alloc`を使用することはできません。代わりにcargoにこのクレートをソースから再コンパイルするよう命令する必要があります。これは、配列`unstable.build-std`を`.cargo/config.toml`ファイルに追加することで行えます。
```toml
# in .cargo/config.toml
@@ -221,9 +227,9 @@ Since we are compiling for a custom target, we can't use the precompiled version
build-std = ["core", "compiler_builtins", "alloc"]
````
-Now the compiler will recompile and include the `alloc` crate in our kernel.
+これでコンパイラは`alloc`クレートを再コンパイルして私たちのカーネルにインクルードしてくれます。
-The reason that the `alloc` crate is disabled by default in `#[no_std]` crates is that it has additional requirements. We can see these requirements as errors when we try to compile our project now:
+`alloc`クレートが`#[no_std]`なクレートで標準では無効化されている理由は、これが追加の要件を持っているからです。今私たちのプロジェクトをコンパイルしようとすると、その要件が何なのか分かります:
```
error: no global memory allocator found but one is required; link to std or add
@@ -232,19 +238,19 @@ error: no global memory allocator found but one is required; link to std or add
error: `#[alloc_error_handler]` function required, but not found
```
-The first error occurs because the `alloc` crate requires an heap allocator, which is an object that provides the `allocate` and `deallocate` functions. In Rust, heap allocators are described by the [`GlobalAlloc`] trait, which is mentioned in the error message. To set the heap allocator for the crate, the `#[global_allocator]` attribute must be applied to a `static` variable that implements the `GlobalAlloc` trait.
+最初のエラーは、`alloc`クレートが、ヒープアロケータという`allocate`と`deallocate`関数を提供するオブジェクトを必要とするために発生します。Rustにおいては、ヒープアロケータの性質は[`GlobalAlloc`]トレイトによって定義されており、エラーメッセージでもそのことについて触れられています。クレートにヒープアロケータを設定するためには、`#[global_allocator]`属性を`GlobalAlloc`トレイトを実装する何らかの`static`変数に適用する必要があります。
-The second error occurs because calls to `allocate` can fail, most commonly when there is no more memory available. Our program must be able to react to this case, which is what the `#[alloc_error_handler]` function is for.
+二つ目のエラーは、(主にメモリが不足している場合)`allocate`への呼び出しが失敗しうるために発生します。私たちのプログラムがこのケースに対処できるようになっている必要があり、そのために使われる関数が`#[alloc_error_handler]`なのです。
[`GlobalAlloc`]: https://doc.rust-lang.org/alloc/alloc/trait.GlobalAlloc.html
-We will describe these traits and attributes in detail in the following sections.
+次のセクションでこのトレイトと属性について説明します。
-### The `GlobalAlloc` Trait
+### `GlobalAlloc`トレイト
-The [`GlobalAlloc`] trait defines the functions that a heap allocator must provide. The trait is special because it is almost never used directly by the programmer. Instead, the compiler will automatically insert the appropriate calls to the trait methods when using the allocation and collection types of `alloc`.
+[`GlobalAlloc`]トレイトはヒープアロケータの提供しなければならない関数を定義します。このトレイトは、プログラマが絶対に直接使わないという点において特別です。代わりに、`alloc`のアロケーション・コレクション型を使うときに、コンパイラがトレイトメソッドへの適切な呼び出しを自動的に挿入します。
-Since we will need to implement the trait for all our allocator types, it is worth taking a closer look at its declaration:
+このトレイトを私たちのアロケータ型全てに実装しなければならないので、その宣言は詳しく見ておく価値があるでしょう:
```rust
pub unsafe trait GlobalAlloc {
@@ -261,32 +267,32 @@ pub unsafe trait GlobalAlloc {
}
```
-It defines the two required methods [`alloc`] and [`dealloc`], which correspond to the `allocate` and `deallocate` functions we used in our examples:
-- The [`alloc`] method takes a [`Layout`] instance as argument, which describes the desired size and alignment that the allocated memory should have. It returns a [raw pointer] to the first byte of the allocated memory block. Instead of an explicit error value, the `alloc` method returns a null pointer to signal an allocation error. This is a bit non-idiomatic, but it has the advantage that wrapping existing system allocators is easy, since they use the same convention.
-- The [`dealloc`] method is the counterpart and responsible for freeing a memory block again. It receives two arguments, the pointer returned by `alloc` and the `Layout` that was used for the allocation.
+このトレイトは[`alloc`]と[`dealloc`]という必須メソッドを定義しており、これは上の例で使った`allocate`と`deallocate`関数に相当します:
+- [`alloc`]メソッドは[`Layout`]インスタンス(割り当てられたメモリの持つべきサイズとアラインメントを記述する)を引数として取ります。メソッドは割り当てられたメモリブロックの最初のバイトへの[生ポインタ][raw pointer]を返します。割り当てエラーが起きたことを示す際は、明示的なエラー値を返す代わりにヌルポインタを返します。このやり方は(Rustの)慣習とはやや外れていますが、同じ慣習に従っている既存のシステムのアロケータをラップするのが簡単になるという利点があります。
+- [`dealloc`]はその対で、メモリブロックを開放する役割を持ちます。このメソッドは、`alloc`によって返されたポインタと割り当ての際に使われた`Layout`という二つの引数を取ります。
[`alloc`]: https://doc.rust-lang.org/alloc/alloc/trait.GlobalAlloc.html#tymethod.alloc
[`dealloc`]: https://doc.rust-lang.org/alloc/alloc/trait.GlobalAlloc.html#tymethod.dealloc
[`Layout`]: https://doc.rust-lang.org/alloc/alloc/struct.Layout.html
-The trait additionally defines the two methods [`alloc_zeroed`] and [`realloc`] with default implementations:
+このトレイトは[`alloc_zeroed`]と[`realloc`]という二つのデフォルト実装付きメソッドも定義しています。
-- The [`alloc_zeroed`] method is equivalent to calling `alloc` and then setting the allocated memory block to zero, which is exactly what the provided default implementation does. An allocator implementation can override the default implementations with a more efficient custom implementation if possible.
-- The [`realloc`] method allows to grow or shrink an allocation. The default implementation allocates a new memory block with the desired size and copies over all the content from the previous allocation. Again, an allocator implementation can probably provide a more efficient implementation of this method, for example by growing/shrinking the allocation in-place if possible.
+- [`alloc_zeroed`]メソッドは`alloc`を呼んでから割り当てられたメモリブロックの値を0にするのに等しく、デフォルト実装もまさに同じことをしています。より効率的なカスタム実装がもしあるならば、デフォルト実装を上書きすることもできます。
+- [`realloc`]メソッドは割り当てたメモリを拡大したり縮小したりすることができます。デフォルト実装では、要求されたサイズの新しいメモリブロックを割り当て、以前のアロケーションから中身を全てコピーします。同じく、アロケータの実装でこのメソッドをより効率的にすることができるかもしれません。例えば、可能な場合はその場でアロケーションを拡大・縮小するなど。
[`alloc_zeroed`]: https://doc.rust-lang.org/alloc/alloc/trait.GlobalAlloc.html#method.alloc_zeroed
[`realloc`]: https://doc.rust-lang.org/alloc/alloc/trait.GlobalAlloc.html#method.realloc
-#### Unsafety
+#### Unsafe
-One thing to notice is that both the trait itself and all trait methods are declared as `unsafe`:
+トレイト自体とすべてのトレイトメソッドが`unsafe`として宣言されていることに気をつけましょう:
-- The reason for declaring the trait as `unsafe` is that the programmer must guarantee that the trait implementation for an allocator type is correct. For example, the `alloc` method must never return a memory block that is already used somewhere else because this would cause undefined behavior.
-- Similarly, the reason that the methods are `unsafe` is that the caller must ensure various invariants when calling the methods, for example that the `Layout` passed to `alloc` specifies a non-zero size. This is not really relevant in practice since the methods are normally called directly by the compiler, which ensures that the requirements are met.
+- トレイトを`unsafe`として宣言する理由は、プログラマがアロケータ型のトレイト実装が正しいことを保証しなければならないからです。例えば、`alloc`メソッドは他のどこかですでに使用されているメモリブロックを決して返してはならず、もしそうなると未定義動作が発生してしまいます。
+- 同様に、メソッドが`unsafe`である理由は、メソッドを呼び出す際に呼び出し元がいくつかの不変条件を保証しなければならないからです。例えば、`alloc`に渡される`Layout`が非ゼロサイズを指定していることなどです。実際にはこれは大して重要ではなく、というのもこれらのメソッドはコンパイラによって直接呼び出されるため、この要件が満たされていることは保証されているからです。
-### A `DummyAllocator`
+### `DummyAllocator`
-Now that we know what an allocator type should provide, we can create a simple dummy allocator. For that we create a new `allocator` module:
+アロケータ型が何を提供しないといけないかを理解したので、シンプルなダミーのアロケータを作ることができます。そのためまず新しく`allocator`モジュールを作りましょう:
```rust
// in src/lib.rs
@@ -294,7 +300,7 @@ Now that we know what an allocator type should provide, we can create a simple d
pub mod allocator;
```
-Our dummy allocator does the absolute minimum to implement the trait and always return an error when `alloc` is called. It looks like this:
+私たちのダミーアロケータではトレイトを実装するための最小限のことしかせず、`alloc`が呼び出されたら常にエラーを返すようにします。以下のようになります:
```rust
// in src/allocator.rs
@@ -315,15 +321,15 @@ unsafe impl GlobalAlloc for Dummy {
}
```
-The struct does not need any fields, so we create it as a [zero sized type]. As mentioned above, we always return the null pointer from `alloc`, which corresponds to an allocation error. Since the allocator never returns any memory, a call to `dealloc` should never occur. For this reason we simply panic in the `dealloc` method. The `alloc_zeroed` and `realloc` methods have default implementations, so we don't need to provide implementations for them.
+この構造体はフィールドを必要としないので、[サイズがゼロの型][zero sized type]として作成します。上で述べたように、この`alloc`では常に割り当てエラーに相当するヌルポインタを返すようにします。アロケータがメモリを返すことは絶対に起きないのだから、`dealloc`の呼び出しも絶対に起きないはずです。このため`dealloc`メソッドでは単にpanicすることにします。`alloc_zeroed`と`realloc`メソッドにはデフォルト実装があるので、これらを実装する必要はありません。
[zero sized type]: https://doc.rust-lang.org/nomicon/exotic-sizes.html#zero-sized-types-zsts
-We now have a simple allocator, but we still have to tell the Rust compiler that it should use this allocator. This is where the `#[global_allocator]` attribute comes in.
+単純なアロケータを手に入れたわけですが、さらにRustコンパイラにこのアロケータを使うよう指示しないといけません。ここで`#[global_allocator]`属性の出番です。
-### The `#[global_allocator]` Attribute
+### `#[global_allocator]`属性
-The `#[global_allocator]` attribute tells the Rust compiler which allocator instance it should use as the global heap allocator. The attribute is only applicable to a `static` that implements the `GlobalAlloc` trait. Let's register an instance of our `Dummy` allocator as the global allocator:
+`#[global_allocator]`属性は、どのアロケータインスタンスをグローバルヒープアロケータとして使うべきかをRustコンパイラに指示します。この属性は`GlobalAlloc`トレイトを実装する`static`にのみ適用できます。私たちの`Dummy`アロケータのインスタンスをグローバルアロケータとして登録してみましょう:
```rust
// in src/allocator.rs
@@ -332,24 +338,24 @@ The `#[global_allocator]` attribute tells the Rust compiler which allocator inst
static ALLOCATOR: Dummy = Dummy;
```
-Since the `Dummy` allocator is a [zero sized type], we don't need to specify any fields in the initialization expression.
+`Dummy`アロケータは[サイズがゼロの型][zero sized type]なので、初期化式でフィールドを指定する必要はありません。
-When we now try to compile it, the first error should be gone. Let's fix the remaining second error:
+これをコンパイルしようとすると、最初のエラーは消えているはずです。残っている二つ目のエラーを修正しましょう:
```
error: `#[alloc_error_handler]` function required, but not found
```
-### The `#[alloc_error_handler]` Attribute
+### `#[alloc_error_handler]`属性
-As we learned when discussing the `GlobalAlloc` trait, the `alloc` function can signal an allocation error by returning a null pointer. The question is: how should the Rust runtime react to such an allocation failure? This is where the `#[alloc_error_handler]` attribute comes in. It specifies a function that is called when an allocation error occurs, similar to how our panic handler is called when a panic occurs.
+`GlobalAlloc`トレイトについて議論したときに学んだように、`alloc`関数はヌルポインタを返すことによって割り当てエラーを示します。ここで生じる疑問は、そのように割り当てが失敗したときRustランタイムはどう対処するべきなのかということです。ここで`#[alloc_error_handler]`属性の出番です。この属性は、パニックが起こったときに私たちのパニックハンドラが呼ばれるのと同様に、割り当てエラーが起こったときに呼ばれる関数を指定するのです。
-Let's add such a function to fix the compilation error:
+コンパイルエラーを修正するためにそんな関数を追加してみましょう:
```rust
// in src/lib.rs
-#![feature(alloc_error_handler)] // at the top of the file
+#![feature(alloc_error_handler)] // ファイルの先頭に書く
#[alloc_error_handler]
fn alloc_error_handler(layout: alloc::alloc::Layout) -> ! {
@@ -357,9 +363,9 @@ fn alloc_error_handler(layout: alloc::alloc::Layout) -> ! {
}
```
-The `alloc_error_handler` function is still unstable, so we need a feature gate to enable it. The function receives a single argument: the `Layout` instance that was passed to `alloc` when the allocation failure occurred. There's nothing we can do to resolve the failure, so we just panic with a message that contains the `Layout` instance.
+`alloc_error_handler`関数はまだunstableなので、この機能を有効化する必要があります。この関数は引数を一つ取ります:割り当てエラーが起こったとき`alloc`関数に渡されていた`Layout`のインスタンスです。この失敗を解決するためにできることはないので、`Layout`インスタンスを含めたメッセージを表示してただpanicすることにしましょう。
-With this function, the compilation errors should be fixed. Now we can use the allocation and collection types of `alloc`, for example we can use a [`Box`] to allocate a value on the heap:
+この関数を追加したことで、コンパイルエラーは修正されたはずです。これで`alloc`のアロケーション・コレクション型を使えるようになりました。例えば、[`Box`]を使ってヒープに値を割り当てることができます:
[`Box`]: https://doc.rust-lang.org/alloc/boxed/struct.Box.html
@@ -371,11 +377,11 @@ extern crate alloc;
use alloc::boxed::Box;
fn kernel_main(boot_info: &'static BootInfo) -> ! {
- // […] print "Hello World!", call `init`, create `mapper` and `frame_allocator`
+ // […] "Hello World!"を表示, `init`の呼び出し, `mapper`と`frame_allocator`を作成
let x = Box::new(41);
- // […] call `test_main` in test mode
+ // […] テストモードでは`test_main`を呼ぶ
println!("It did not crash!");
blog_os::hlt_loop();
@@ -383,21 +389,21 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! {
```
-Note that we need to specify the `extern crate alloc` statement in our `main.rs` too. This is required because the `lib.rs` and `main.rs` part are treated as separate crates. However, we don't need to create another `#[global_allocator]` static because the global allocator applies to all crates in the project. In fact, specifying an additional allocator in another crate would be an error.
+`main.rs`においても`extern crate alloc`文を指定する必要があることに注意してください。`lib.rs`と`main.rs`は別のクレートとして取り扱われているためです。しかしながら、グローバルアロケータはプロジェクト内のすべてのクレートに適用されるため、`#[global_allocator]`静的変数をもう一つ作る必要はありません。実際、別のクレートで新しいアロケータを指定するとエラーになります。
-When we run the above code, we see that our `alloc_error_handler` function is called:
+上のコードを実行すると、`alloc_error_handler`関数が呼ばれるのが分かります:

-The error handler is called because the `Box::new` function implicitly calls the `alloc` function of the global allocator. Our dummy allocator always returns a null pointer, so every allocation fails. To fix this we need to create an allocator that actually returns usable memory.
+`Box::new`関数は暗黙のうちにグローバルアロケータの`alloc`関数を呼び出すため、エラーハンドラが呼ばれました。私たちのダミーアロケータは常にヌルポインタを返すので、あらゆる割り当ては失敗します。これを修正するためには、使用可能なメモリを実際に返すアロケータを作る必要があります。
## Creating a Kernel Heap
-Before we can create a proper allocator, we first need to create a heap memory region from which the allocator can allocate memory. To do this, we need to define a virtual memory range for the heap region and then map this region to physical frames. See the [_"Introduction To Paging"_] post for an overview of virtual memory and page tables.
+適当なアロケータを作る前にまず、そのアロケータがメモリを割り当てるためのヒープメモリ領域を作らないといけません。このために、ヒープ領域のための仮想メモリ範囲を定義し、その領域を物理フレームに対応付ける必要があります。仮想メモリとページテーブルの概要については、[ページング入門][_"Introduction To Paging"_]の記事を読んでください。
[_"Introduction To Paging"_]: @/edition-2/posts/08-paging-introduction/index.md
-The first step is to define a virtual memory region for the heap. We can choose any virtual address range that we like, as long as it is not already used for a different memory region. Let's define it as the memory starting at address `0x_4444_4444_0000` so that we can easily recognize a heap pointer later:
+最初のステップはヒープのための仮想メモリ領域を定義することです。他のメモリ領域に使われていない限り、どんな仮想アドレス範囲でも構いません。ここでは、あとからそこがヒープポインタだと簡単に分かるよう、`0x_4444_4444_0000`から始まるメモリとしましょう。
```rust
// in src/allocator.rs
@@ -406,9 +412,9 @@ pub const HEAP_START: usize = 0x_4444_4444_0000;
pub const HEAP_SIZE: usize = 100 * 1024; // 100 KiB
```
-We set the heap size to 100 KiB for now. If we need more space in the future, we can simply increase it.
+今のところヒープの大きさは100 KiBとします。将来より多くの領域が必要になったら大きくすれば良いです。
-If we tried to use this heap region now, a page fault would occur since the virtual memory region is not mapped to physical memory yet. To resolve this, we create an `init_heap` function that maps the heap pages using the [`Mapper` API] that we introduced in the [_"Paging Implementation"_] post:
+今このヒープ領域を使おうとすると、仮想メモリ領域が物理メモリにまだ対応付けられていないためページフォルトが発生します。これを解決するために、[ページング入門][_"Paging Implementation"_]の記事で導入した[`Mapper` API]を使ってヒープページを対応付ける関数`init_heap`を作ります:
[`Mapper` API]: @/edition-2/posts/09-paging-implementation/index.md#using-offsetpagetable
[_"Paging Implementation"_]: @/edition-2/posts/09-paging-implementation/index.md
@@ -449,7 +455,7 @@ pub fn init_heap(
}
```
-The function takes mutable references to a [`Mapper`] and a [`FrameAllocator`] instance, both limited to 4KiB pages by using [`Size4KiB`] as generic parameter. The return value of the function is a [`Result`] with the unit type `()` as success variant and a [`MapToError`] as error variant, which is the error type returned by the [`Mapper::map_to`] method. Reusing the error type makes sense here because the `map_to` method is the main source of errors in this function.
+この関数は[`Mapper`]と[`FrameAllocator`]への可変参照を取ります。引数はどちらも[`Size4KiB`]をジェネリックパラメータとすることで4KiBページのみに制限しています。この関数の戻り値は[`Result`]で、成功ヴァリアントが`()`、失敗ヴァリアントが([`Mapper::map_to`]メソッドによって失敗時に返されるエラー型である)[`MapToError`]です。このエラー型を流用するのは理にかなっており、というのもこの関数における主なエラーの原因は`map_to`メソッドだからです。
[`Mapper`]:https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/mapper/trait.Mapper.html
[`FrameAllocator`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/trait.FrameAllocator.html
@@ -458,17 +464,17 @@ The function takes mutable references to a [`Mapper`] and a [`FrameAllocator`] i
[`MapToError`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/mapper/enum.MapToError.html
[`Mapper::map_to`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/mapper/trait.Mapper.html#method.map_to
-The implementation can be broken down into two parts:
+実装内容は以下の二つに分けられます:
-- **Creating the page range:**: To create a range of the pages that we want to map, we convert the `HEAP_START` pointer to a [`VirtAddr`] type. Then we calculate the heap end address from it by adding the `HEAP_SIZE`. We want an inclusive bound (the address of the last byte of the heap), so we subtract 1. Next, we convert the addresses into [`Page`] types using the [`containing_address`] function. Finally, we create a page range from the start and end pages using the [`Page::range_inclusive`] function.
+- **ページ範囲の作成:** 対応付けたいページ領域を作成するために、ポインタ`HEAP_START`を[`VirtAddr`]型に変換します。つぎに`HEAP_SIZE`を足すことによってヒープの終端アドレスを計算します。端が含まれる境界にしたい(ヒープの最後のバイトのアドレスが欲しい)ので1を引きます。次に、これらのアドレスを[`containing_address`]関数を使って[`Page`]型に変換します。最後に、[`Page::range_inclusive`]関数を使って最初と最後のページからページ範囲を作成します。
-- **Mapping the pages:** The second step is to map all pages of the page range we just created. For that we iterate over the pages in that range using a `for` loop. For each page, we do the following:
+- **ページの対応付け:** 二つ目のステップは、今作ったページ範囲のすべてのページに対して対応付けを行うことです。これを行うため、`for`ループを使ってこのページ範囲に対して繰り返し処理を行います。それぞれのページに対して以下を行います:
- - We allocate a physical frame that the page should be mapped to using the [`FrameAllocator::allocate_frame`] method. This method returns [`None`] when there are no more frames left. We deal with that case by mapping it to a [`MapToError::FrameAllocationFailed`] error through the [`Option::ok_or`] method and then apply the [question mark operator] to return early in the case of an error.
+ - [`FrameAllocator::allocate_frame`]メソッドを使って、ページのマップされるべき物理フレームを割り当てます。このメソッドはもうフレームが残っていないとき[`None`]を返します。このケースに対処するため、[`Option::ok_or`]メソッドを使ってこれを[`MapToError::FrameAllocationFailed`]に変換し、エラーの場合は[`?`演算子][question mark operator]を使って早期リターンしています。
- - We set the required `PRESENT` flag and the `WRITABLE` flag for the page. With these flags both read and write accesses are allowed, which makes sense for heap memory.
+ - このページに対し、必要となる`PRESENT`フラグと`WRITABLE`フラグをセットします。これらのフラグにより読み書きのアクセスが許可され、ヒープメモリとして理にかなっています。
- - We use the [`Mapper::map_to`] method for creating the mapping in the active page table. The method can fail, therefore we use the [question mark operator] again to forward the error to the caller. On success, the method returns a [`MapperFlush`] instance that we can use to update the [_translation lookaside buffer_] using the [`flush`] method.
+ - [`Mapper::map_to`]メソッドを使ってアクティブなページテーブルに対応付けを作成します。このメソッドは失敗しうるので、同様に[`?`演算子][question mark operator]を使ってエラーを呼び出し元に受け渡します。成功時には、このメソッドは[`MapperFlush`]インスタンスを返しますが、これを使って[`flush`]メソッドを呼ぶことで[**トランスレーション・ルックアサイド・バッファ**][_translation lookaside buffer_]を更新することができます。
[`VirtAddr`]: https://docs.rs/x86_64/0.14.2/x86_64/addr/struct.VirtAddr.html
[`Page`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/page/struct.Page.html
@@ -483,13 +489,13 @@ The implementation can be broken down into two parts:
[_translation lookaside buffer_]: @/edition-2/posts/08-paging-introduction/index.md#the-translation-lookaside-buffer
[`flush`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/mapper/struct.MapperFlush.html#method.flush
-The final step is to call this function from our `kernel_main`:
+最後のステップは、この関数を`kernel_main`から呼び出すことです:
```rust
// in src/main.rs
fn kernel_main(boot_info: &'static BootInfo) -> ! {
- use blog_os::allocator; // new import
+ use blog_os::allocator; // 新しいインポート
use blog_os::memory::{self, BootInfoFrameAllocator};
println!("Hello World{}", "!");
@@ -501,32 +507,32 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! {
BootInfoFrameAllocator::init(&boot_info.memory_map)
};
- // new
+ // ここを追加
allocator::init_heap(&mut mapper, &mut frame_allocator)
.expect("heap initialization failed");
let x = Box::new(41);
- // […] call `test_main` in test mode
+ // […] テストモードでは`test_main`を呼ぶ
println!("It did not crash!");
blog_os::hlt_loop();
}
```
-We show the full function for context here. The only new lines are the `blog_os::allocator` import and the call to `allocator::init_heap` function. In case the `init_heap` function returns an error, we panic using the [`Result::expect`] method since there is currently no sensible way for us to handle this error.
+ここで、文脈が分かるよう関数の全体を示しています。新しい行は`blog_os::allocator`のインポートと`allocator::init_heap`の呼び出しだけです。`init_heap`関数がエラーを返した場合、これを処理する賢い方法は今のところないため、[`Result::expect`]メソッドを使ってパニックします。
[`Result::expect`]: https://doc.rust-lang.org/core/result/enum.Result.html#method.expect
-We now have a mapped heap memory region that is ready to be used. The `Box::new` call still uses our old `Dummy` allocator, so you will still see the "out of memory" error when you run it. Let's fix this by using a proper allocator.
+これで、使用する準備のできた、対応付けられたヒープメモリ領域を手に入れました。`Box::new`の呼び出しはまだ私たちの古い`Dummy`を使っているので、実行しても依然として「メモリ切れ」のエラーを見ることになるでしょう。適切なアロケータを使うようにして、このエラーを修正してみましょう。
-## Using an Allocator Crate
+## アロケータクレートを使う
-Since implementing an allocator is somewhat complex, we start by using an external allocator crate. We will learn how to implement our own allocator in the next post.
+アロケータを実装するのは少々複雑なので、まずは既製のアロケータを使うことにしましょう。自前のアロケータを実装する方法については次の記事で学びます。
-A simple allocator crate for `no_std` applications is the [`linked_list_allocator`] crate. It's name comes from the fact that it uses a linked list data structure to keep track of deallocated memory regions. See the next post for a more detailed explanation of this approach.
+`no_std`のアプリケーションのためのシンプルなアロケータのひとつに[`linked_list_allocator`]クレートがあります。この名前は、割り当てられていないメモリ領域を連結リストを使って管理しているところから来ています。この手法のより詳しい説明については次の記事を読んでください。
-To use the crate, we first need to add a dependency on it in our `Cargo.toml`:
+このクレートを使うためには、まず依存関係を`Cargo.toml`に追加する必要があります:
[`linked_list_allocator`]: https://github.com/phil-opp/linked-list-allocator/
@@ -537,7 +543,7 @@ To use the crate, we first need to add a dependency on it in our `Cargo.toml`:
linked_list_allocator = "0.9.0"
```
-Then we can replace our dummy allocator with the allocator provided by the crate:
+次に私たちのダミーアロケータをこのクレートによって提供されるアロケータで置き換えます:
```rust
// in src/allocator.rs
@@ -548,11 +554,11 @@ use linked_list_allocator::LockedHeap;
static ALLOCATOR: LockedHeap = LockedHeap::empty();
```
-The struct is named `LockedHeap` because it uses the [`spinning_top::Spinlock`] type for synchronization. This is required because multiple threads could access the `ALLOCATOR` static at the same time. As always when using a spinlock or a mutex, we need to be careful to not accidentally cause a deadlock. This means that we shouldn't perform any allocations in interrupt handlers, since they can run at an arbitrary time and might interrupt an in-progress allocation.
+この構造体は同期のために`spinning_top::Spinlock`型を使うため`LockedHeap`という名前が付いています。同期が必要なのは、静的変数`ALLOCATOR`に複数のスレッドが同時にアクセスすることがありえるからです。スピンロックやmutexを使うときはいつもそうであるように、誤ってデッドロックを起こさないように注意する必要があります。これが意味するのは、我々は割り込みハンドラ内で一切アロケーションを行ってはいけないと言うことです。なぜなら、割り込みハンドラはどんなタイミングでも走る可能性があるため、進行中のアロケーションに割り込んでいることがあるからです。
[`spinning_top::Spinlock`]: https://docs.rs/spinning_top/0.1.0/spinning_top/type.Spinlock.html
-Setting the `LockedHeap` as global allocator is not enough. The reason is that we use the [`empty`] constructor function, which creates an allocator without any backing memory. Like our dummy allocator, it always returns an error on `alloc`. To fix this, we need to initialize the allocator after creating the heap:
+`LockedHeap`をグローバルアロケータとして設定するだけでは十分ではありません。いま[`empty`]コンストラクタ関数を使っていますが、この関数はメモリを与えることなくアロケータを作るからです。私たちのダミーアロケータと同じく、これ(今の状態の`LockedHeap`)は`alloc`を行うと常にエラーを返します。この問題を修正するため、ヒープを作った後でアロケータを初期化する必要があります:
[`empty`]: https://docs.rs/linked_list_allocator/0.9.0/linked_list_allocator/struct.LockedHeap.html#method.empty
@@ -574,13 +580,13 @@ pub fn init_heap(
}
```
-We use the [`lock`] method on the inner spinlock of the `LockedHeap` type to get an exclusive reference to the wrapped [`Heap`] instance, on which we then call the [`init`] method with the heap bounds as arguments. It is important that we initialize the heap _after_ mapping the heap pages, since the [`init`] function already tries to write to the heap memory.
+`LockedHeap`型の内部のスピンロックの[`lock`]メソッドを呼ぶことで、ラップされた[`Heap`]インスタンスへの排他的参照を得て、これの[`init`]メソッドをヒープの境界を引数として呼んでいます。`init`関数自体がヒープメモリに書き込もうとするので、ヒープページを対応付けた **後に** ヒープを初期化することが重要です。
[`lock`]: https://docs.rs/lock_api/0.3.3/lock_api/struct.Mutex.html#method.lock
[`Heap`]: https://docs.rs/linked_list_allocator/0.9.0/linked_list_allocator/struct.Heap.html
[`init`]: https://docs.rs/linked_list_allocator/0.9.0/linked_list_allocator/struct.Heap.html#method.init
-After initializing the heap, we can now use all allocation and collection types of the built-in [`alloc`] crate without error:
+ヒープを初期化できたら、組み込みの[`alloc`]クレートのあらゆるアロケーション・コレクション型がエラーなく使用できます:
```rust
// in src/main.rs
@@ -590,31 +596,31 @@ use alloc::{boxed::Box, vec, vec::Vec, rc::Rc};
fn kernel_main(boot_info: &'static BootInfo) -> ! {
// […] initialize interrupts, mapper, frame_allocator, heap
- // allocate a number on the heap
+ // ヒープに数字をアロケートする
let heap_value = Box::new(41);
println!("heap_value at {:p}", heap_value);
- // create a dynamically sized vector
+ // 動的サイズのベクタを作成する
let mut vec = Vec::new();
for i in 0..500 {
vec.push(i);
}
println!("vec at {:p}", vec.as_slice());
- // create a reference counted vector -> will be freed when count reaches 0
+ // 参照カウントされたベクタを作成する -> カウントが0になると解放される
let reference_counted = Rc::new(vec![1, 2, 3]);
let cloned_reference = reference_counted.clone();
println!("current reference count is {}", Rc::strong_count(&cloned_reference));
core::mem::drop(reference_counted);
println!("reference count is {} now", Rc::strong_count(&cloned_reference));
- // […] call `test_main` in test context
+ // […] テストでは `test_main` を呼ぶ
println!("It did not crash!");
blog_os::hlt_loop();
}
```
-This code example shows some uses of the [`Box`], [`Vec`], and [`Rc`] types. For the `Box` and `Vec` types we print the underlying heap pointers using the [`{:p}` formatting specifier]. For showcasing `Rc`, we create a reference counted heap value and use the [`Rc::strong_count`] function to print the current reference count, before and after dropping an instance (using [`core::mem::drop`]).
+このコード例では[`Box`], [`Vec`], [`Rc`]型を使ってみせています。`Box`型と`Vec`型については対応するヒープポインタを[`{:p}`フォーマット指定子][`{:p}` formatting specifier]を使って出力しています。`Rc`について例示するために、参照カウントされたヒープ値を作成し、インスタンスを([`core::mem::drop`]を使って)ドロップする前と後に[`Rc::strong_count`]関数を使って現在の参照カウントを出力しています。
[`Vec`]: https://doc.rust-lang.org/alloc/vec/
[`Rc`]: https://doc.rust-lang.org/alloc/rc/
@@ -622,7 +628,7 @@ This code example shows some uses of the [`Box`], [`Vec`], and [`Rc`] types. For
[`Rc::strong_count`]: https://doc.rust-lang.org/alloc/rc/struct.Rc.html#method.strong_count
[`core::mem::drop`]: https://doc.rust-lang.org/core/mem/fn.drop.html
-When we run it, we see the following:
+実行すると、以下のような結果を得ます:

-As expected, we see that the `Box` and `Vec` values live on the heap, as indicated by the pointer starting with the `0x_4444_4444_*` prefix. The reference counted value also behaves as expected, with the reference count being 2 after the `clone` call, and 1 again after one of the instances was dropped.
+期待したとおり、`Box`と`Vec`の値はヒープ上にあり、そのことはポインタが`0x_4444_4444_*`で始まることから分かります。参照カウントされた値も期待したとおり振る舞っており、`clone`呼び出しの後では参照カウントは2に、インスタンスの一方がドロップされた後では再び1になっています。
-The reason that the vector starts at offset `0x800` is not that the boxed value is `0x800` bytes large, but the [reallocations] that occur when the vector needs to increase its capacity. For example, when the vector's capacity is 32 and we try to add the next element, the vector allocates a new backing array with capacity 64 behind the scenes and copies all elements over. Then it frees the old allocation.
+ベクタがヒープメモリの先頭から`0x800`だけずれた場所から始まるのは、Box内の値が`0x800`バイトの大きさがあるためではなく、ベクタが容量を増やさなければならないときに発生する[再割り当て][reallocations]のためです。例えば、ベクタの容量が32の際に次の要素を追加しようとすると、ベクタは内部で容量64の配列を新たに割り当て、すべての要素をコピーしているのです。その後古い割り当てを解放します。
[reallocations]: https://doc.rust-lang.org/alloc/vec/struct.Vec.html#capacity-and-reallocation
-Of course there are many more allocation and collection types in the `alloc` crate that we can now all use in our kernel, including:
+もちろん`alloc`クレートにはもっと多くのアロケーション・コレクション型があり、今やそれらのすべてを私たちのカーネルで使うことができます。それには以下が含まれます:
-- the thread-safe reference counted pointer [`Arc`]
-- the owned string type [`String`] and the [`format!`] macro
+- スレッドセーフな参照カウントポインタ[`Arc`]
+- 所有された文字列型[`String`]と[`format!`]マクロ
- [`LinkedList`]
-- the growable ring buffer [`VecDeque`]
-- the [`BinaryHeap`] priority queue
-- [`BTreeMap`] and [`BTreeSet`]
+- 必要に応じてサイズを大きくできるリングバッファ[`VecDeque`]
+- プライオリティキューである[`BinaryHeap`]
+- [`BTreeMap`]と[`BTreeSet`]
[`Arc`]: https://doc.rust-lang.org/alloc/sync/struct.Arc.html
[`String`]: https://doc.rust-lang.org/alloc/string/struct.String.html
@@ -655,11 +661,11 @@ Of course there are many more allocation and collection types in the `alloc` cra
[`BTreeMap`]: https://doc.rust-lang.org/alloc/collections/btree_map/struct.BTreeMap.html
[`BTreeSet`]: https://doc.rust-lang.org/alloc/collections/btree_set/struct.BTreeSet.html
-These types will become very useful when we want to implement thread lists, scheduling queues, or support for async/await.
+これらの型は、スレッドリスト、スケジュールキュー、async/awaitのサポートを実装しようとするときにとても有用になります。
-## Adding a Test
+## テストを追加する
-To ensure that we don't accidentally break our new allocation code, we should add an integration test for it. We start by creating a new `tests/heap_allocation.rs` file with the following content:
+私たちの新しい割り当てコードを間違って壊してしまうことがないことを保証するために、結合テストを追加しなければなりません。まず、次のような内容のファイル`tests/heap_allocation.rs`を作成します。
```rust
// in tests/heap_allocation.rs
@@ -687,11 +693,11 @@ fn panic(info: &PanicInfo) -> ! {
}
```
-We reuse the `test_runner` and `test_panic_handler` functions from our `lib.rs`. Since we want to test allocations, we enable the `alloc` crate through the `extern crate alloc` statement. For more information about the test boilerplate check out the [_Testing_] post.
+`lib.rs`の`test_runner`関数と`test_panic_handler`関数を再利用します。私たちはアロケーションをテストしたいので、`extern crate alloc`宣言を使って`alloc`クレートを有効化します。テストに共通する定型部については[テスト][_Testing_]の記事を読んでください。
[_Testing_]: @/edition-2/posts/04-testing/index.md
-The implementation of the `main` function looks like this:
+`main`関数の実装は以下のようになります:
```rust
// in tests/heap_allocation.rs
@@ -715,9 +721,9 @@ fn main(boot_info: &'static BootInfo) -> ! {
}
```
-It is very similar to the `kernel_main` function in our `main.rs`, with the differences that we don't invoke `println`, don't include any example allocations, and call `test_main` unconditionally.
+私たちの`main.rs`内の`kernel_main`関数によく似ていますが、`println`を呼び出さず、例として行ったアロケーションも行わず、また`test_main`を無条件で呼び出しているという違いがあります。
-Now we're ready to add a few test cases. First, we add a test that performs some simple allocations using [`Box`] and checks the allocated values, to ensure that basic allocations work:
+これでいくつかテストケースを追加する準備ができました。まず、[`Box`]を使って単純な割り当てを行い、割り当てられた値を確かめることで基本的なアロケーションがうまくいっていることを確かめましょう:
```rust
// in tests/heap_allocation.rs
@@ -732,9 +738,9 @@ fn simple_allocation() {
}
```
-Most importantly, this test verifies that no allocation error occurs.
+最も重要なのは、このテストはアロケーションエラーが起きないことを検証してくれるということです。
-Next, we iteratively build a large vector, to test both large allocations and multiple allocations (due to reallocations):
+次に、反復によって大きなベクタを作り、大きな割り当てと(再割り当てによる)複数回の割り当ての両方をテストしましょう:
```rust
// in tests/heap_allocation.rs
@@ -752,11 +758,11 @@ fn large_vec() {
}
```
-We verify the sum by comparing it with the formula for the [n-th partial sum]. This gives us some confidence that the allocated values are all correct.
+このベクタの和を[n次部分和][n-th partial sum]の公式と比較することで検証しています。これにより、割り当てられた値はすべて正しいことがある程度保証されます。
[n-th partial sum]: https://en.wikipedia.org/wiki/1_%2B_2_%2B_3_%2B_4_%2B_%E2%8B%AF#Partial_sums
-As a third test, we create ten thousand allocations after each other:
+3つ目のテストとして、一万回次々にアロケーションを行います:
```rust
// in tests/heap_allocation.rs
@@ -772,9 +778,9 @@ fn many_boxes() {
}
```
-This test ensures that the allocator reuses freed memory for subsequent allocations since it would run out of memory otherwise. This might seem like an obvious requirement for an allocator, but there are allocator designs that don't do this. An example is the bump allocator design that will be explained in the next post.
+このテストではアロケータが解放されたメモリを次の割り当てで再利用していることを保証してくれます。もしそうなっていなければ、メモリ不足が起きるでしょう。これはアロケータにとって当たり前の要件だと思われるかもしれませんが、これを行わないようなアロケータの設計も存在するのです。その例として、次の記事で説明するbump allocatorがあります。
-Let's run our new integration test:
+では、私たちの新しい結合テストを実行してみましょう:
```
> cargo test --test heap_allocation
@@ -785,16 +791,16 @@ large_vec... [ok]
many_boxes... [ok]
```
-All three tests succeeded! You can also invoke `cargo test` (without `--test` argument) to run all unit and integration tests.
+すべてのテストが成功しました!`cargo test`コマンドを(`--test`引数なしに)呼ぶことで、すべての結合テストを実行することもできます。
-## Summary
+## まとめ
-This post gave an introduction to dynamic memory and explained why and where it is needed. We saw how Rust's borrow checker prevents common vulnerabilities and learned how Rust's allocation API works.
+この記事では動的メモリについて入門し、なぜ、そしていつそれが必要になるのかを説明しました。Rustの借用チェッカがどのようにしてよくある脆弱性を防ぐのか、そしてRustのアロケーションAPIの仕組みを理解しました。
-After creating a minimal implementation of Rust's allocator interface using a dummy allocator, we created a proper heap memory region for our kernel. For that we defined a virtual address range for the heap and then mapped all pages of that range to physical frames using the `Mapper` and `FrameAllocator` from the previous post.
+ダミーアロケータでRustのアロケータインターフェースの最小限の実装を作成した後、私たちのカーネル用の適切なヒープメモリ領域を作成しました。これを行うために、ヒープ用の仮想アドレス範囲を定義し、前の記事で説明した`Mapper`と`FrameAllocator`を使ってその範囲のすべてのページを物理フレームに対応付けました。
-Finally, we added a dependency on the `linked_list_allocator` crate to add a proper allocator to our kernel. With this allocator, we were able to use `Box`, `Vec`, and other allocation and collection types from the `alloc` crate.
+最後に、`linked_list_allocator`クレートへの依存関係を追加し、適切なアロケータを私たちのカーネルに追加しました。このアロケータのおかげで、`alloc`クレートに含まれる`Box`、`Vec`、その他のアロケーション・コレクション型を使えるようになりました。
-## What's next?
+## 次は?
-While we already added heap allocation support in this post, we left most of the work to the `linked_list_allocator` crate. The next post will show in detail how an allocator can be implemented from scratch. It will present multiple possible allocator designs, shows how to implement simple versions of them, and explain their advantages and drawbacks.
+この記事でヒープ割り当て機能のサポートを追加したとは言え、ほとんどの仕事は`linked_list_allocator`クレートに任せてしまっています。次の記事では、アロケータをゼロから実装する方法を詳細にお伝えします。可能なアロケータの設計を複数示し、それらを単純化した者を実装する方法を示し、その利点と欠点を示します。
From b05c07d21c23a6ff7bbf1b6973c0ba8bd6944ade Mon Sep 17 00:00:00 2001
From: Philipp Oppermann
Date: Tue, 7 Jun 2022 10:06:09 +0200
Subject: [PATCH 025/107] Move comment threads for translated posts to separate
category
Avoids that a search for the post title returns a thread for a translated post.
---
blog/templates/edition-2/extra.html | 2 +-
blog/templates/edition-2/page.html | 7 ++++++-
blog/templates/news-page.html | 2 +-
blog/templates/snippets.html | 14 +++++++++++---
blog/templates/status-update-page.html | 2 +-
5 files changed, 20 insertions(+), 7 deletions(-)
diff --git a/blog/templates/edition-2/extra.html b/blog/templates/edition-2/extra.html
index e6bb8d86..f4be5d5a 100644
--- a/blog/templates/edition-2/extra.html
+++ b/blog/templates/edition-2/extra.html
@@ -17,6 +17,6 @@
- {{ snippets::giscus(search_term=page.title ~ " (Extra Post)") }}
+ {{ snippets::giscus(search_term=page.title ~ " (Extra Post)", translated=false) }}
{% endblock after_main %}
diff --git a/blog/templates/edition-2/page.html b/blog/templates/edition-2/page.html
index f4b1aadf..5364bd82 100644
--- a/blog/templates/edition-2/page.html
+++ b/blog/templates/edition-2/page.html
@@ -118,7 +118,12 @@
{% else %}
{% set search_term=page.title %}
{% endif %}
- {{ snippets::giscus(search_term=search_term) }}
+ {% if page.lang != "en" %}
+ {% set translated = true %}
+ {% else %}
+ {% set translated = false %}
+ {% endif %}
+ {{ snippets::giscus(search_term=search_term, translated=translated) }}
{%- if page.lang != "en" %}
- {{ snippets::giscus(search_term=page.title ~ " (News Post)") }}
+ {{ snippets::giscus(search_term=page.title ~ " (News Post)", translated=false) }}
{% endblock after_main %}
diff --git a/blog/templates/snippets.html b/blog/templates/snippets.html
index 9065977e..1e1498c2 100644
--- a/blog/templates/snippets.html
+++ b/blog/templates/snippets.html
@@ -10,12 +10,20 @@
{% endmacro support %}
-{% macro giscus(search_term) %}
+{% macro giscus(search_term, translated) %}
+ {% if translated %}
+ {% set category = "Post Comments" %}
+ {% set category_path = "post-comments" %}
+ {% else %}
+ {% set category = "Post Comments (translated)" %}
+ {% set category_path = "post-comments-translated" %}
+ {% endif %}
+
{% 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 discussion_url = `https://github.com/phil-opp/blog_os/discussions/categories/post-comments?discussions_q=` ~ search_term_encoded %}
+ {% set discussion_url = `https://github.com/phil-opp/blog_os/discussions/categories/` ~ category_path ~ `?discussions_q=` ~ search_term_encoded %}
{% endif %}