diff --git a/blog/content/edition-1/posts/02-entering-longmode/index.md b/blog/content/edition-1/posts/02-entering-longmode/index.md index 1d047bb9..7adae373 100644 --- a/blog/content/edition-1/posts/02-entering-longmode/index.md +++ b/blog/content/edition-1/posts/02-entering-longmode/index.md @@ -151,7 +151,7 @@ Don't worry, you don't need to understand the details. ### Long Mode check Now we can use CPUID to detect whether long mode can be used. I use code from [OSDev][long mode detection] again: -[long mode detection]: https://wiki.osdev.org/Setting_Up_Long_Mode#x86_or_x86-64 +[long mode detection]: wiki.osdev.org/Setting_Up_Long_Mode#Checking_for_long_mode_support ```nasm check_long_mode: @@ -173,7 +173,7 @@ check_long_mode: ``` Like many low-level things, CPUID is a bit strange. Instead of taking a parameter, the `cpuid` instruction implicitly uses the `eax` register as argument. To test if long mode is available, we need to call `cpuid` with `0x80000001` in `eax`. This loads some information to the `ecx` and `edx` registers. Long mode is supported if the 29th bit in `edx` is set. [Wikipedia][cpuid long mode] has detailed information. -[cpuid long mode]: https://en.wikipedia.org/wiki/CPUID#EAX.3D80000001h:_Extended_Processor_Info_and_Feature_Bits +[cpuid long mode]: https://en.wikipedia.org/wiki/CPUID#EAX=8000'0001h:_Extended_Processor_Info_and_Feature_Bits If you look at the assembly above, you'll probably notice that we call `cpuid` twice. The reason is that the CPUID command started with only a few functions and was extended over time. So old processors may not know the `0x80000001` argument at all. To test if they do, we need to invoke `cpuid` with `0x80000000` in `eax` first. It returns the highest supported parameter value in `eax`. If it's at least `0x80000001`, we can test for long mode as described above. Else the CPU is old and doesn't know what long mode is either. In that case, we directly jump to `.no_long_mode` through the `jb` instruction (“jump if below”). @@ -229,20 +229,20 @@ But what happens to bits 48-63 of the 64-bit virtual address? Well, they can't b An entry in the P4, P3, P2, and P1 tables consists of the page aligned 52-bit _physical_ address of the frame or the next page table and the following bits that can be OR-ed in: -Bit(s) | Name | Meaning ---------------------- | ------ | ---------------------------------- -0 | present | the page is currently in memory -1 | writable | it's allowed to write to this page -2 | user accessible | if not set, only kernel mode code can access this page -3 | write through caching | writes go directly to memory -4 | disable cache | no cache is used for this page -5 | accessed | the CPU sets this bit when this page is used -6 | dirty | the CPU sets this bit when a write to this page occurs -7 | huge page/null | must be 0 in P1 and P4, creates a 1GiB page in P3, creates a 2MiB page in P2 -8 | global | page isn't flushed from caches on address space switch (PGE bit of CR4 register must be set) -9-11 | available | can be used freely by the OS -52-62 | available | can be used freely by the OS -63 | no execute | forbid executing code on this page (the NXE bit in the EFER register must be set) +| Bit(s) | Name | Meaning | +| ------ | --------------------- | -------------------------------------------------------------------------------------------- | +| 0 | present | the page is currently in memory | +| 1 | writable | it's allowed to write to this page | +| 2 | user accessible | if not set, only kernel mode code can access this page | +| 3 | write through caching | writes go directly to memory | +| 4 | disable cache | no cache is used for this page | +| 5 | accessed | the CPU sets this bit when this page is used | +| 6 | dirty | the CPU sets this bit when a write to this page occurs | +| 7 | huge page/null | must be 0 in P1 and P4, creates a 1GiB page in P3, creates a 2MiB page in P2 | +| 8 | global | page isn't flushed from caches on address space switch (PGE bit of CR4 register must be set) | +| 9-11 | available | can be used freely by the OS | +| 52-62 | available | can be used freely by the OS | +| 63 | no execute | forbid executing code on this page (the NXE bit in the EFER register must be set) | ### Set Up Identity Paging When we switch to long mode, paging will be activated automatically. The CPU will then try to read the instruction at the following address, but this address is now a virtual address. So we need to do _identity mapping_, i.e. map a physical address to the same virtual address. @@ -387,18 +387,18 @@ Today almost everyone uses Paging instead of Segmentation (and so do we). But on A GDT always starts with a 0-entry and contains an arbitrary number of segment entries afterwards. A 64-bit entry has the following format: -Bit(s) | Name | Meaning ---------------------- | ------ | ---------------------------------- -0-41 | ignored | ignored in 64-bit mode -42 | conforming | the current privilege level can be higher than the specified level for code segments (else it must match exactly) -43 | executable | if set, it's a code segment, else it's a data segment -44 | descriptor type | should be 1 for code and data segments -45-46 | privilege | the [ring level]: 0 for kernel, 3 for user -47 | present | must be 1 for valid selectors -48-52 | ignored | ignored in 64-bit mode -53 | 64-bit | should be set for 64-bit code segments -54 | 32-bit | must be 0 for 64-bit segments -55-63 | ignored | ignored in 64-bit mode +| Bit(s) | Name | Meaning | +| ------ | --------------- | ----------------------------------------------------------------------------------------------------------------- | +| 0-41 | ignored | ignored in 64-bit mode | +| 42 | conforming | the current privilege level can be higher than the specified level for code segments (else it must match exactly) | +| 43 | executable | if set, it's a code segment, else it's a data segment | +| 44 | descriptor type | should be 1 for code and data segments | +| 45-46 | privilege | the [ring level]: 0 for kernel, 3 for user | +| 47 | present | must be 1 for valid selectors | +| 48-52 | ignored | ignored in 64-bit mode | +| 53 | 64-bit | should be set for 64-bit code segments | +| 54 | 32-bit | must be 0 for 64-bit segments | +| 55-63 | ignored | ignored in 64-bit mode | [ring level]: https://wiki.osdev.org/Security#Rings diff --git a/blog/content/edition-1/posts/04-printing-to-screen/index.md b/blog/content/edition-1/posts/04-printing-to-screen/index.md index 395f9f75..daeaf8bc 100644 --- a/blog/content/edition-1/posts/04-printing-to-screen/index.md +++ b/blog/content/edition-1/posts/04-printing-to-screen/index.md @@ -26,25 +26,25 @@ This post uses recent unstable features, so you need an up-to-date nighly compil ## The VGA Text Buffer The text buffer starts at physical address `0xb8000` and contains the characters displayed on screen. It has 25 rows and 80 columns. Each screen character has the following format: -Bit(s) | Value ------- | ---------------- -0-7 | ASCII code point -8-11 | Foreground color -12-14 | Background color -15 | Blink +| Bit(s) | Value | +| ------ | ---------------- | +| 0-7 | ASCII code point | +| 8-11 | Foreground color | +| 12-14 | Background color | +| 15 | Blink | The following colors are available: -Number | Color | Number + Bright Bit | Bright Color ------- | ---------- | ------------------- | ------------- -0x0 | Black | 0x8 | Dark Gray -0x1 | Blue | 0x9 | Light Blue -0x2 | Green | 0xa | Light Green -0x3 | Cyan | 0xb | Light Cyan -0x4 | Red | 0xc | Light Red -0x5 | Magenta | 0xd | Pink -0x6 | Brown | 0xe | Yellow -0x7 | Light Gray | 0xf | White +| Number | Color | Number + Bright Bit | Bright Color | +| ------ | ---------- | ------------------- | ------------ | +| 0x0 | Black | 0x8 | Dark Gray | +| 0x1 | Blue | 0x9 | Light Blue | +| 0x2 | Green | 0xa | Light Green | +| 0x3 | Cyan | 0xb | Light Cyan | +| 0x4 | Red | 0xc | Light Red | +| 0x5 | Magenta | 0xd | Pink | +| 0x6 | Brown | 0xe | Yellow | +| 0x7 | Light Gray | 0xf | White | Bit 4 is the _bright bit_, which turns for example blue into light blue. It is unavailable in background color as the bit is used to control if the text should blink. If you want to use a light background color (e.g. white) you have to disable blinking through a [BIOS function][disable blinking]. @@ -441,7 +441,7 @@ But we can't use it to print anything! You can try it yourself in the `print_som To resolve it, we could use a [mutable static]. But then every read and write to it would be unsafe since it could easily introduce data races and other bad things. Using `static mut` is highly discouraged, there are even proposals to [remove it][remove static mut]. -[mutable static]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable +[mutable static]: https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable [remove static mut]: https://internals.rust-lang.org/t/pre-rfc-remove-static-mut/1437 But what are the alternatives? We could try to use a cell type like [RefCell] or even [UnsafeCell] to provide [interior mutability]. But these types aren't [Sync] \(with good reason), so we can't use them in statics. diff --git a/blog/content/edition-1/posts/09-handling-exceptions/index.md b/blog/content/edition-1/posts/09-handling-exceptions/index.md index e3b896ed..ef86d44a 100644 --- a/blog/content/edition-1/posts/09-handling-exceptions/index.md +++ b/blog/content/edition-1/posts/09-handling-exceptions/index.md @@ -35,26 +35,26 @@ For the full list of exceptions check out the [OSDev wiki][exceptions]. ### The Interrupt Descriptor Table In order to catch and handle exceptions, we have to set up a so-called _Interrupt Descriptor Table_ (IDT). In this table we can specify a handler function for each CPU exception. The hardware uses this table directly, so we need to follow a predefined format. Each entry must have the following 16-byte structure: -Type| Name | Description -----|--------------------------|----------------------------------- -u16 | Function Pointer [0:15] | The lower bits of the pointer to the handler function. -u16 | GDT selector | Selector of a code segment in the GDT. -u16 | Options | (see below) -u16 | Function Pointer [16:31] | The middle bits of the pointer to the handler function. -u32 | Function Pointer [32:63] | The remaining bits of the pointer to the handler function. -u32 | Reserved | +| Type | Name | Description | +| ---- | ------------------------ | ---------------------------------------------------------- | +| u16 | Function Pointer [0:15] | The lower bits of the pointer to the handler function. | +| u16 | GDT selector | Selector of a code segment in the GDT. | +| u16 | Options | (see below) | +| u16 | Function Pointer [16:31] | The middle bits of the pointer to the handler function. | +| u32 | Function Pointer [32:63] | The remaining bits of the pointer to the handler function. | +| u32 | Reserved | The options field has the following format: -Bits | Name | Description -------|-----------------------------------|----------------------------------- -0-2 | Interrupt Stack Table Index | 0: Don't switch stacks, 1-7: Switch to the n-th stack in the Interrupt Stack Table when this handler is called. -3-7 | Reserved | -8 | 0: Interrupt Gate, 1: Trap Gate | If this bit is 0, interrupts are disabled when this handler is called. -9-11 | must be one | -12 | must be zero | -13‑14 | Descriptor Privilege Level (DPL) | The minimal privilege level required for calling this handler. -15 | Present | +| Bits | Name | Description | +| ----- | -------------------------------- | --------------------------------------------------------------------------------------------------------------- | +| 0-2 | Interrupt Stack Table Index | 0: Don't switch stacks, 1-7: Switch to the n-th stack in the Interrupt Stack Table when this handler is called. | +| 3-7 | Reserved | +| 8 | 0: Interrupt Gate, 1: Trap Gate | If this bit is 0, interrupts are disabled when this handler is called. | +| 9-11 | must be one | +| 12 | must be zero | +| 13‑14 | Descriptor Privilege Level (DPL) | The minimal privilege level required for calling this handler. | +| 15 | Present | Each exception has a predefined IDT index. For example the invalid opcode exception has table index 6 and the page fault exception has table index 14. Thus, the hardware can automatically load the corresponding IDT entry for each exception. The [Exception Table][exceptions] in the OSDev wiki shows the IDT indexes of all exceptions in the “Vector nr.” column. @@ -147,10 +147,10 @@ In contrast, a called function is allowed to overwrite _scratch_ registers witho On x86_64, the C calling convention specifies the following preserved and scratch registers: -preserved registers | scratch registers ----|--- -`rbp`, `rbx`, `rsp`, `r12`, `r13`, `r14`, `r15` | `rax`, `rcx`, `rdx`, `rsi`, `rdi`, `r8`, `r9`, `r10`, `r11` -_callee-saved_ | _caller-saved_ +| preserved registers | scratch registers | +| ----------------------------------------------- | ----------------------------------------------------------- | +| `rbp`, `rbx`, `rsp`, `r12`, `r13`, `r14`, `r15` | `rax`, `rcx`, `rdx`, `rsi`, `rdi`, `r8`, `r9`, `r10`, `r11` | +| _callee-saved_ | _caller-saved_ | The compiler knows these rules, so it generates the code accordingly. For example, most functions begin with a `push rbp`, which backups `rbp` on the stack (because it's a callee-saved register). @@ -317,7 +317,7 @@ pub fn init() { There are two problems with this. First, statics are immutable, so we can't modify the breakpoint entry from our `init` function. Second, the `Idt::new` function is not a [`const` function], so it can't be used to initialize a `static`. We could solve this problem by using a [`static mut`] of type `Option`: [`const` function]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md -[`static mut`]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable +[`static mut`]: https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable ```rust static mut IDT: Option = None; diff --git a/blog/content/edition-2/posts/02-minimal-rust-kernel/index.es.md b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.es.md index 5727bb8c..31a5079b 100644 --- a/blog/content/edition-2/posts/02-minimal-rust-kernel/index.es.md +++ b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.es.md @@ -374,7 +374,7 @@ pub extern "C" fn _start() -> ! { Primero, convertimos el entero `0xb8000` en un [raw pointer]. Luego, [iteramos] sobre los bytes de la [cadena de bytes estática] `HELLO`. Usamos el método [`enumerate`] para obtener adicionalmente una variable de conteo `i`. En el cuerpo del bucle `for`, utilizamos el método [`offset`] para escribir el byte de la cadena y el byte de color correspondiente (`0xb` representa un cian claro). [iteramos]: https://doc.rust-lang.org/stable/book/ch13-02-iterators.html -[raw pointer]: https://doc.rust-lang.org/stable/book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer +[raw pointer]: https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#dereferencing-a-raw-pointer [estática]: 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 [cadena de bytes]: https://doc.rust-lang.org/reference/tokens.html#byte-string-literals @@ -383,7 +383,7 @@ Primero, convertimos el entero `0xb8000` en un [raw pointer]. Luego, [iteramos] Ten en cuenta que hay un bloque [`unsafe`] alrededor de todas las escrituras de memoria. Esto se debe a que el compilador de Rust no puede probar que los punteros crudos que creamos son válidos. Podrían apuntar a cualquier lugar y causar corrupción de datos. Al poner estas operaciones en un bloque `unsafe`, básicamente le decimos al compilador que estamos absolutamente seguros de que las operaciones son válidas. Sin embargo, un bloque `unsafe` no desactiva las verificaciones de seguridad de Rust; simplemente permite realizar [cinco operaciones adicionales]. [`unsafe`]: https://doc.rust-lang.org/stable/book/ch19-01-unsafe-rust.html -[cinco operaciones adicionales]: https://doc.rust-lang.org/stable/book/ch19-01-unsafe-rust.html#unsafe-superpowers +[cinco operaciones adicionales]: https://doc.rust-lang.org/stable/book/ch20-01-unsafe-rust.html#unsafe-superpowers Quiero enfatizar que **esta no es la forma en que queremos hacer las cosas en Rust**. Es muy fácil cometer errores al trabajar con punteros crudos dentro de bloques `unsafe`. Por ejemplo, podríamos escribir más allá del final del búfer si no somos cuidadosos. 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 8562f565..26c72d65 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 @@ -388,13 +388,13 @@ pub extern "C" fn _start() -> ! { [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 -[اشاره‌گر خام]: https://doc.rust-lang.org/stable/book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer +[اشاره‌گر خام]: https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#dereferencing-a-raw-pointer [`offset`]: https://doc.rust-lang.org/std/primitive.pointer.html#method.offset توجه داشته باشید که یک بلوک [`unsafe`] همیشه هنگام نوشتن در حافظه مورد استفاده قرار می‌گیرد. دلیل این امر این است که کامپایلر Rust نمی‌تواند معتبر بودن اشاره‌گرهای خام که ایجاد میکنیم را ثابت کند. آن‌ها می‌توانند به هر کجا اشاره کنند و منجر به خراب شدن داده‌ها شوند. با قرار دادن آن‌ها در یک بلوک `unsafe`، ما در اصل به کامپایلر می‌گوییم که کاملاً از معتبر بودن عملیات اطمینان داریم. توجه داشته باشید که یک بلوک `unsafe`، بررسی‌های ایمنی Rust را خاموش نمی‌کند. فقط به شما این امکان را می‌دهد که [پنج کار اضافی] انجام دهید. [`unsafe`]: https://doc.rust-lang.org/stable/book/ch19-01-unsafe-rust.html -[پنج کار اضافی]: https://doc.rust-lang.org/stable/book/ch19-01-unsafe-rust.html#unsafe-superpowers +[پنج کار اضافی]: https://doc.rust-lang.org/stable/book/ch20-01-unsafe-rust.html#unsafe-superpowers می خواهم تأکید کنم که **این روشی نیست که ما بخواهیم در Rust کارها را از طریق آن پبش ببریم!** به هم ریختگی هنگام کار با اشاره‌گرهای خام در داخل بلوک‌های ناامن بسیار محتمل و ساده است، به عنوان مثال، اگر مواظب نباشیم به راحتی می‌توانیم فراتر از انتهای بافر بنویسیم. diff --git a/blog/content/edition-2/posts/02-minimal-rust-kernel/index.fr.md b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.fr.md index 4e99e8a9..8cdac4a4 100644 --- a/blog/content/edition-2/posts/02-minimal-rust-kernel/index.fr.md +++ b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.fr.md @@ -383,13 +383,13 @@ D'abord, nous transformons l'entier `0xb8000` en un [pointeur brut][raw pointer] [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 +[raw pointer]: https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#dereferencing-a-raw-pointer [`offset`]: https://doc.rust-lang.org/std/primitive.pointer.html#method.offset Notez qu'il y a un bloc [`unsafe`] qui enveloppe les écritures mémoire. La raison en est que le compilateur Rust ne peut pas prouver que les pointeurs bruts que nous créons sont valides. Ils pourraient pointer n'importe où et mener à une corruption de données. En les mettant dans un bloc `unsafe`, nous disons fondamentalement au compilateur que nous sommes absolument certains que les opérations sont valides. Notez qu'un bloc `unsafe` ne désactive pas les contrôles de sécurité de Rust. Il permet seulement de faire [cinq choses supplémentaires][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 +[five additional things]: https://doc.rust-lang.org/stable/book/ch20-01-unsafe-rust.html#unsafe-superpowers Je veux souligner que **ce n'est pas comme cela que les choses se font en Rust!** Il est très facile de faire des erreurs en travaillant avec des pointeurs bruts à l'intérieur de blocs `unsafe`. Par exemple, nous pourrions facilement écrire au-delà de la fin du tampon si nous ne sommes pas prudents. 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 a6e03e28..540fbec3 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 @@ -392,13 +392,13 @@ pub extern "C" fn _start() -> ! { [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 +[raw pointer]: https://doc.rust-lang.org/book/ch20-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 +[five additional things]: https://doc.rust-lang.org/stable/book/ch20-01-unsafe-rust.html#unsafe-superpowers **이런 식의 Rust 코드를 작성하는 것은 절대 바람직하지 않다는 것을 강조드립니다!** unsafe 블록 안에서 raw pointer를 쓰다보면 메모리 버퍼 크기를 넘어선 메모리 주소에 데이터를 저장하는 등의 실수를 범하기 매우 쉽습니다. 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 73deb37f..f1385a76 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 @@ -378,13 +378,13 @@ First, we cast the integer `0xb8000` into a [raw pointer]. Then we [iterate] ove [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 +[raw pointer]: https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#dereferencing-a-raw-pointer [`offset`]: https://doc.rust-lang.org/std/primitive.pointer.html#method.offset Note that there's an [`unsafe`] block around all memory writes. The reason is that the Rust compiler can't prove that the raw pointers we create are valid. They could point anywhere and lead to data corruption. By putting them into an `unsafe` block, we're basically telling the compiler that we are absolutely sure that the operations are valid. Note that an `unsafe` block does not turn off Rust's safety checks. It only allows you to do [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 +[five additional things]: https://doc.rust-lang.org/stable/book/ch20-01-unsafe-rust.html#unsafe-superpowers I want to emphasize that **this is not the way we want to do things in Rust!** It's very easy to mess up when working with raw pointers inside unsafe blocks. For example, we could easily write beyond the buffer's end if we're not careful. diff --git a/blog/content/edition-2/posts/02-minimal-rust-kernel/index.pt-BR.md b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.pt-BR.md index 456a0afd..6f8c68a4 100644 --- a/blog/content/edition-2/posts/02-minimal-rust-kernel/index.pt-BR.md +++ b/blog/content/edition-2/posts/02-minimal-rust-kernel/index.pt-BR.md @@ -381,13 +381,13 @@ Primeiro, convertemos o inteiro `0xb8000` em um [ponteiro bruto]. Então [iteram [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 -[ponteiro bruto]: https://doc.rust-lang.org/stable/book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer +[ponteiro bruto]: https://doc.rust-lang.org/stable/book/ch20-01-unsafe-rust.html#dereferencing-a-raw-pointer [`offset`]: https://doc.rust-lang.org/std/primitive.pointer.html#method.offset Note que há um bloco [`unsafe`] em torno de todas as escritas de memória. A razão é que o compilador Rust não pode provar que os ponteiros brutos que criamos são válidos. Eles poderiam apontar para qualquer lugar e levar à corrupção de dados. Ao colocá-los em um bloco `unsafe`, estamos basicamente dizendo ao compilador que temos absoluta certeza de que as operações são válidas. Note que um bloco `unsafe` não desativa as verificações de segurança do Rust. Ele apenas permite que você faça [cinco coisas adicionais]. [`unsafe`]: https://doc.rust-lang.org/stable/book/ch19-01-unsafe-rust.html -[cinco coisas adicionais]: https://doc.rust-lang.org/stable/book/ch19-01-unsafe-rust.html#unsafe-superpowers +[cinco coisas adicionais]: https://doc.rust-lang.org/stable/book/ch20-01-unsafe-rust.html#unsafe-superpowers Quero enfatizar que **esta não é a maneira como queremos fazer as coisas em Rust!** É muito fácil bagunçar ao trabalhar com ponteiros brutos dentro de blocos unsafe. Por exemplo, poderíamos facilmente escrever além do fim do buffer se não tivermos cuidado. @@ -501,4 +501,4 @@ Agora podemos usar `cargo run` para compilar nosso kernel e inicializá-lo no QE ## O que vem a seguir? -No próximo post, exploraremos o buffer de texto VGA em mais detalhes e escreveremos uma interface segura para ele. Também adicionaremos suporte para a macro `println`. \ No newline at end of file +No próximo post, exploraremos o buffer de texto VGA em mais detalhes e escreveremos uma interface segura para ele. Também adicionaremos suporte para a macro `println`. 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 f1cfef1b..c78c471f 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 @@ -385,13 +385,13 @@ pub extern "C" fn _start() -> ! { [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 +[raw pointer]: https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#dereferencing-a-raw-pointer [`offset`]: https://doc.rust-lang.org/std/primitive.pointer.html#method.offset Обратите внимание, что вокруг всех записей в память стоит блок [`unsafe`]. Причина в том, что компилятор Rust не может доказать, что сырые указатели, которые мы создаем, действительны. Они могут указывать куда угодно и привести к повреждению данных. Помещая их в блок `unsafe`, мы, по сути, говорим компилятору, что абсолютно уверены в правильности операций. Обратите внимание, что блок `unsafe` не отключает проверки безопасности Rust. Он только позволяет вам делать [пять дополнительных вещей][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 +[five additional things]: https://doc.rust-lang.org/stable/book/ch20-01-unsafe-rust.html#unsafe-superpowers Я хочу подчеркнуть, что **это не тот способ, которым стоит что-либо делать в Rust!** Очень легко ошибиться при работе с сырыми указателями внутри блоков `unsafe`: например, мы можем легко записать за конец буфера, если не будем осторожны. diff --git a/blog/content/edition-2/posts/03-vga-text-buffer/index.es.md b/blog/content/edition-2/posts/03-vga-text-buffer/index.es.md index ec6f7b12..d705ec3f 100644 --- a/blog/content/edition-2/posts/03-vga-text-buffer/index.es.md +++ b/blog/content/edition-2/posts/03-vga-text-buffer/index.es.md @@ -251,7 +251,7 @@ pub fn print_something() { ``` Primero crea un nuevo Writer que apunta al buffer VGA en `0xb8000`. La sintaxis para esto podría parecer un poco extraña: Primero, convertimos el entero `0xb8000` como un [puntero sin procesar] mutable. Luego lo convertimos en una referencia mutable al desreferenciarlo (a través de `*`) y tomarlo prestado inmediatamente (a través de `&mut`). Esta conversión requiere un [bloque `unsafe`], ya que el compilador no puede garantizar que el puntero sin procesar sea válido. -[puntero sin procesar]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer +[puntero sin procesar]: https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#dereferencing-a-raw-pointer [bloque `unsafe`]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html Luego escribe el byte `b'H'` en él. El prefijo `b` crea un [literal de byte], que representa un carácter ASCII. Al escribir las cadenas `"ello "` y `"Wörld!"`, probamos nuestro método `write_string` y el manejo de caracteres no imprimibles. Para ver la salida, necesitamos llamar a la función `print_something` desde nuestra función `_start`: 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 ad2766a9..c2a11977 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 @@ -31,12 +31,12 @@ rtl = true ## بافر متن VGA برای چاپ یک کاراکتر روی صفحه در حالت متن VGA ، باید آن را در بافر متن سخت افزار VGA بنویسید. بافر متن VGA یک آرایه دو بعدی است که به طور معمول 25 ردیف و 80 ستون دارد که مستقیماً به صفحه نمایش داده(رندر) می شود. هر خانه آرایه یک کاراکتر صفحه نمایش را از طریق قالب زیر توصیف می کند: -Bit(s) | Value ------- | ---------------- -0-7 | ASCII code point -8-11 | Foreground color -12-14 | Background color -15 | Blink +| Bit(s) | Value | +| ------ | ---------------- | +| 0-7 | ASCII code point | +| 8-11 | Foreground color | +| 12-14 | Background color | +| 15 | Blink | اولین بایت کاراکتری در [کدگذاری ASCII] را نشان می دهد که باید چاپ شود. اگر بخواهیم دقیق باشیم ، دقیقاً ASCII نیست ، بلکه مجموعه ای از کاراکترها به نام [_کد صفحه 437_] با برخی کاراکتر های اضافی و تغییرات جزئی است. برای سادگی ، ما در این پست آنرا یک کاراکتر ASCII می نامیم. @@ -45,16 +45,16 @@ Bit(s) | Value بایت دوم نحوه نمایش کاراکتر را مشخص می کند. چهار بیت اول رنگ پیش زمینه را مشخص می کند ، سه بیت بعدی رنگ پس زمینه و بیت آخر اینکه کاراکتر باید چشمک بزند یا نه. رنگ های زیر موجود است: -Number | Color | Number + Bright Bit | Bright Color ------- | ---------- | ------------------- | ------------- -0x0 | Black | 0x8 | Dark Gray -0x1 | Blue | 0x9 | Light Blue -0x2 | Green | 0xa | Light Green -0x3 | Cyan | 0xb | Light Cyan -0x4 | Red | 0xc | Light Red -0x5 | Magenta | 0xd | Pink -0x6 | Brown | 0xe | Yellow -0x7 | Light Gray | 0xf | White +| Number | Color | Number + Bright Bit | Bright Color | +| ------ | ---------- | ------------------- | ------------ | +| 0x0 | Black | 0x8 | Dark Gray | +| 0x1 | Blue | 0x9 | Light Blue | +| 0x2 | Green | 0xa | Light Green | +| 0x3 | Cyan | 0xb | Light Cyan | +| 0x4 | Red | 0xc | Light Red | +| 0x5 | Magenta | 0xd | Pink | +| 0x6 | Brown | 0xe | Yellow | +| 0x7 | Light Gray | 0xf | White | بیت 4، بیت روشنایی است ، که به عنوان مثال آبی به آبی روشن تبدیل می‌کند. برای رنگ پس زمینه ، این بیت به عنوان بیت چشمک مورد استفاده قرار می گیرد. @@ -264,7 +264,7 @@ pub fn print_something() { ``` ابتدا یک Writer جدید ایجاد می کند که به بافر VGA در `0xb8000` اشاره دارد. سینتکس این ممکن است کمی عجیب به نظر برسد: اول ، ما عدد صحیح `0xb8000` را به عنوان [اشاره گر خام] قابل تغییر در نظر می گیریم. سپس با dereferencing کردن آن (از طریق "*") و بلافاصله ارجاع مجدد (از طریق `&mut`) آن را به یک مرجع قابل تغییر تبدیل می کنیم. این تبدیل به یک [بلوک `غیرایمن`] احتیاج دارد ، زیرا کامپایلر نمی تواند صحت اشاره‌گر خام را تضمین کند. -[اشاره گر خام]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer +[اشاره گر خام]: https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#dereferencing-a-raw-pointer [بلوک `غیرایمن`]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html سپس بایت `b'H'` را روی آن می نویسد. پیشوند `b` یک [بایت لیترال] ایجاد می کند ، که بیانگر یک کاراکتر ASCII است. با نوشتن رشته های `"ello "` و `"Wörld!"` ، ما متد `write_string` و واکنش به کاراکترهای غیر قابل چاپ را آزمایش می کنیم. برای دیدن خروجی ، باید تابع `print_something` را از تابع `_start` فراخوانی کنیم: @@ -525,7 +525,7 @@ lazy_static! { با این حال ، این `WRITER` بسیار بی فایده است زیرا غیر قابل تغییر است. این بدان معنی است که ما نمی توانیم چیزی در آن بنویسیم (از آنجا که همه متد های نوشتن `&mut self` را در ورودی میگیرند). یک راه حل ممکن استفاده از [استاتیک قابل تغییر] است. اما پس از آن هر خواندن و نوشتن آن ناامن (unsafe) است زیرا می تواند به راحتی باعث data race و سایر موارد بد باشد. استفاده از `static mut` بسیار نهی شده است ، حتی پیشنهادهایی برای [حذف آن][remove static mut] وجود داشت. اما گزینه های دیگر چیست؟ ما می توانیم سعی کنیم از یک استاتیک تغییرناپذیر با نوع سلول مانند [RefCell] یا حتی [UnsafeCell] استفاده کنیم که [تغییر پذیری داخلی] را فراهم می کند. اما این انواع [Sync] نیستند (با دلیل کافی) ، بنابراین نمی توانیم از آنها در استاتیک استفاده کنیم. -[استاتیک قابل تغییر]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable +[استاتیک قابل تغییر]: https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable [remove static mut]: https://internals.rust-lang.org/t/pre-rfc-remove-static-mut/1437 [RefCell]: https://doc.rust-lang.org/book/ch15-05-interior-mutability.html#keeping-track-of-borrows-at-runtime-with-refcellt [UnsafeCell]: https://doc.rust-lang.org/nightly/core/cell/struct.UnsafeCell.html @@ -584,7 +584,7 @@ pub extern "C" fn _start() -> ! { ### یک ماکروی println اکنون که یک نویسنده گلوبال داریم ، می توانیم یک ماکرو `println` اضافه کنیم که می تواند از هر کجا در کد استفاده شود. [سینتکس ماکروی] راست کمی عجیب است ، بنابراین ما سعی نمی کنیم ماکرو را از ابتدا بنویسیم. در عوض به سورس [ماکروی `println!`] در کتابخانه استاندارد نگاه می کنیم: -[سینتکس ماکروی]: https://doc.rust-lang.org/nightly/book/ch19-06-macros.html#declarative-macros-with-macro_rules-for-general-metaprogramming +[سینتکس ماکروی]: https://doc.rust-lang.org/nightly/book/ch20-05-macros.html#declarative-macros-for-general-metaprogramming [ماکروی `println!`]: https://doc.rust-lang.org/nightly/std/macro.println!.html ```rust 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 3a018d48..f8618495 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 @@ -30,12 +30,12 @@ translators = ["swnakamura", "JohnTitor"] ## VGAテキストバッファ VGAテキストモードにおいて、文字を画面に出力するには、VGAハードウェアのテキストバッファにそれを書き込まないといけません。VGAテキストバッファは、普通25行と80列からなる2次元配列で、画面に直接書き出されます。それぞれの配列の要素は画面上の一つの文字を以下の形式で表現しています: -ビット | 値 ------- | ---------------- -0-7 | ASCII コードポイント -8-11 | フォアグラウンド(前景)色 -12-14 | バックグラウンド(背景)色 -15 | 点滅 +| ビット | 値 | +| ------ | -------------------------- | +| 0-7 | ASCII コードポイント | +| 8-11 | フォアグラウンド(前景)色 | +| 12-14 | バックグラウンド(背景)色 | +| 15 | 点滅 | 最初の1バイトは、出力されるべき文字を[ASCIIエンコーディング][ASCII encoding]で表します。正確に言うと、完全にASCIIではなく、[コードページ437][_code page 437_]という、いくつか文字が追加され、軽微な修正のなされたものです。簡単のため、この記事ではASCII文字と呼ぶことにします。 @@ -44,16 +44,16 @@ VGAテキストモードにおいて、文字を画面に出力するには、VG 2つ目のバイトはその文字がどのように出力されるのかを定義します。最初の4ビットが前景色(訳注:文字自体の色)を、次の3ビットが背景色を、最後のビットがその文字が点滅するのかを決めます。以下の色を使うことができます: -数字 | 色 | 数字 + Bright Bit | Bright (明るい) 色 ------- | ---------- | ------------------- | ------------- -0x0 | 黒 | 0x8 | 暗いグレー -0x1 | 青 | 0x9 | 明るい青 -0x2 | 緑 | 0xa | 明るい緑 -0x3 | シアン | 0xb | 明るいシアン -0x4 | 赤 | 0xc | 明るい赤 -0x5 | マゼンタ | 0xd | ピンク -0x6 | 茶色 | 0xe | 黄色 -0x7 | 明るいグレー| 0xf | 白 +| 数字 | 色 | 数字 + Bright Bit | Bright (明るい) 色 | +| ---- | ------------ | ----------------- | ----------------------------------------------------------- | +| 0x0 | 黒 | 0x8 | 暗いグレー | +| 0x1 | 青 | 0x9 | 明るい青 | +| 0x2 | 緑 | 0xa | 明るい緑 | +| 0x3 | シアン | 0xb | 明るいシアン | +| 0x4 | 赤 | 0xc | 明るい赤 | +| 0x5 | マゼンタ | 0xd | ピンク | +| 0x6 | 茶色 | 0xe | 黄色 | +| 0x7 | 明るいグレー | 0xf | 白 | 4ビット目は **bright bit** で、これは(1になっているとき)たとえば青を明るい青に変えます。背景色については、このビットは点滅ビットとして再利用されています。 @@ -597,7 +597,7 @@ pub extern "C" fn _start() -> ! { ### printlnマクロ 大域的なwriterを手に入れたので、プログラムのどこでも使える`println`マクロを追加できます。Rustの[マクロの構文][macro syntax]はすこしややこしいので、一からマクロを書くことはしません。代わりに、標準ライブラリで[`println!`マクロ][`println!` macro]のソースを見てみます: -[macro syntax]: https://doc.rust-lang.org/nightly/book/ch19-06-macros.html#declarative-macros-with-macro_rules-for-general-metaprogramming +[macro syntax]: https://doc.rust-lang.org/nightly/book/ch20-05-macros.html#declarative-macros-for-general-metaprogramming [`println!` macro]: https://doc.rust-lang.org/nightly/std/macro.println!.html ```rust diff --git a/blog/content/edition-2/posts/03-vga-text-buffer/index.ko.md b/blog/content/edition-2/posts/03-vga-text-buffer/index.ko.md index c9feeffd..ba6178e1 100644 --- a/blog/content/edition-2/posts/03-vga-text-buffer/index.ko.md +++ b/blog/content/edition-2/posts/03-vga-text-buffer/index.ko.md @@ -30,12 +30,12 @@ translators = ["JOE1994", "Quqqu"] ## VGA 텍스트 버퍼 VGA 텍스트 모드에서 화면에 문자를 출력하려면 VGA 하드웨어의 텍스트 버퍼에 해당 문자를 저장해야 합니다. VGA 텍스트 버퍼는 보통 25행 80열 크기의 2차원 배열이며, 해당 버퍼에 저장된 값들은 즉시 화면에 렌더링 됩니다. 배열의 각 원소는 화면에 출력될 문자를 아래의 형식으로 표현합니다: -비트 | 값 ------- | ---------------- -0-7 | ASCII 코드 -8-11 | 전경색 -12-14 | 배경색 -15 | 깜빡임 여부 +| 비트 | 값 | +| ----- | ----------- | +| 0-7 | ASCII 코드 | +| 8-11 | 전경색 | +| 12-14 | 배경색 | +| 15 | 깜빡임 여부 | 첫 바이트는 [ASCII 인코딩][ASCII encoding]으로 출력될 문자를 나타냅니다. 엄밀히 따지자면 ASCII 인코딩이 아닌, 해당 인코딩에 문자들을 추가하고 살짝 변형한 [_code page 437_] 이라는 인코딩을 이용합니다. 설명을 간소화하기 위해 이하 본문에서는 그냥 ASCII 문자로 지칭하겠습니다. @@ -44,16 +44,16 @@ VGA 텍스트 모드에서 화면에 문자를 출력하려면 VGA 하드웨어 두 번째 바이트는 표현하는 문자가 어떻게 표시될 것인지를 정의합니다. 두 번째 바이트의 첫 4비트는 전경색을 나타내고, 그 다음 3비트는 배경색을 나타내며, 마지막 비트는 해당 문자가 화면에서 깜빡이도록 할지 결정합니다. 아래의 색상들을 이용할 수 있습니다: -숫자 값 | 색상 | 색상 + 밝기 조정 비트 | 밝기 조정 후 최종 색상 ------- | ---------- | ------------------- | ------------- -0x0 | Black | 0x8 | Dark Gray -0x1 | Blue | 0x9 | Light Blue -0x2 | Green | 0xa | Light Green -0x3 | Cyan | 0xb | Light Cyan -0x4 | Red | 0xc | Light Red -0x5 | Magenta | 0xd | Pink -0x6 | Brown | 0xe | Yellow -0x7 | Light Gray | 0xf | White +| 숫자 값 | 색상 | 색상 + 밝기 조정 비트 | 밝기 조정 후 최종 색상 | +| ------- | ---------- | --------------------- | ---------------------- | +| 0x0 | Black | 0x8 | Dark Gray | +| 0x1 | Blue | 0x9 | Light Blue | +| 0x2 | Green | 0xa | Light Green | +| 0x3 | Cyan | 0xb | Light Cyan | +| 0x4 | Red | 0xc | Light Red | +| 0x5 | Magenta | 0xd | Pink | +| 0x6 | Brown | 0xe | Yellow | +| 0x7 | Light Gray | 0xf | White | 두 번째 바이트의 네 번째 비트 (_밝기 조정 비트_)를 통해 파란색을 하늘색으로 조정하는 등 색의 밝기를 변경할 수 있습니다. 배경색을 지정하는 3비트 이후의 마지막 비트는 깜빡임 여부를 지정합니다. @@ -263,7 +263,7 @@ pub fn print_something() { ``` 우선 메모리 주소 `0xb8000`을 가리키는 새로운 Writer 인스턴스를 생성합니다. 이를 구현한 코드가 다소 난해하게 느껴질 수 있으니 단계별로 나누어 설명드리겠습니다: 먼저 정수 `0xb8000`을 읽기/쓰기 모두 가능한 (mutable) [포인터][raw pointer]로 타입 변환합니다. 그 후 `*` 연산자를 통해 이 포인터를 역참조 (dereference) 하고 `&mut`를 통해 즉시 borrow 함으로써 해당 주소에 저장된 값을 변경할 수 있는 레퍼런스 (mutable reference)를 만듭니다. 여기서 Rust 컴파일러는 포인터의 유효성 및 안전성을 보증할 수 없기에, [`unsafe` 블록][`unsafe` block]을 사용해야만 포인터를 레퍼런스로 변환할 수 있습니다. -[raw pointer]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer +[raw pointer]: https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#dereferencing-a-raw-pointer [`unsafe` block]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html 그 다음 Writer 인스턴스에 바이트 `b'H'`를 적습니다. 접두사 `b`는 ASCII 문자를 나타내는 [바이트 상수 (literal)][byte literal] 를 생성합니다. 문자열 `"ello "`와 `"Wörld!"`를 적음으로써 `write_string` 함수 및 출력 불가능한 문자에 대한 특수 처리가 잘 구현되었는지 테스트 해봅니다. 화면에 메시지가 출력되는지 확인하기 위해 `print_something` 함수를 `_start` 함수에서 호출합니다: @@ -524,7 +524,7 @@ lazy_static! { 현재 `WRITER`는 immutable (읽기 가능, 쓰기 불가능) 하여 실질적인 쓸모가 없습니다. 모든 쓰기 함수들은 첫 인자로 `&mut self`를 받기 때문에 `WRITER`로 어떤 쓰기 작업도 할 수가 없습니다. 이에 대한 해결책으로 [mutable static]은 어떨까요? 이 선택지를 고른다면 모든 읽기 및 쓰기 작업이 데이터 레이스 (data race) 및 기타 위험에 노출되기에 안전을 보장할 수 없게 됩니다. Rust에서 `static mut`는 웬만하면 사용하지 않도록 권장되며, 심지어 [Rust 언어에서 완전히 `static mut`를 제거하자는 제안][remove static mut]이 나오기도 했습니다. 이것 이외에도 대안이 있을까요? [내부 가변성 (interior mutability)][interior mutability]을 제공하는 [RefCell] 혹은 [UnsafeCell] 을 통해 immutable한 정적 변수를 만드는 것은 어떨까요? 이 타입들은 중요한 이유로 [Sync] 트레이트를 구현하지 않기에 정적 변수를 선언할 때에는 사용할 수 없습니다. -[mutable static]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable +[mutable static]: https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable [remove static mut]: https://internals.rust-lang.org/t/pre-rfc-remove-static-mut/1437 [RefCell]: https://doc.rust-lang.org/book/ch15-05-interior-mutability.html#keeping-track-of-borrows-at-runtime-with-refcellt [UnsafeCell]: https://doc.rust-lang.org/nightly/core/cell/struct.UnsafeCell.html @@ -583,7 +583,7 @@ pub extern "C" fn _start() -> ! { ### println 매크로 전역 변수 `Writer`도 갖추었으니 이제 프로젝트 내 어디서든 사용할 수 있는 `println` 매크로를 추가할 수 있습니다. Rust의 [매크로 문법][macro syntax]은 다소 난해하기에, 우리에게 필요한 매크로를 밑바닥부터 작성하지는 않을 것입니다. 그 대신 표준 라이브러리의 [`println!` 매크로][`println!` macro] 구현 코드를 참조할 것입니다: -[macro syntax]: https://doc.rust-lang.org/nightly/book/ch19-06-macros.html#declarative-macros-with-macro_rules-for-general-metaprogramming +[macro syntax]: https://doc.rust-lang.org/nightly/book/ch20-05-macros.html#declarative-macros-for-general-metaprogramming [`println!` macro]: https://doc.rust-lang.org/nightly/std/macro.println!.html ```rust 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 b74e1612..28e50422 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 @@ -27,12 +27,12 @@ This blog is openly developed on [GitHub]. If you have any problems or questions ## The VGA Text Buffer To print a character to the screen in VGA text mode, one has to write it to the text buffer of the VGA hardware. The VGA text buffer is a two-dimensional array with typically 25 rows and 80 columns, which is directly rendered to the screen. Each array entry describes a single screen character through the following format: -Bit(s) | Value ------- | ---------------- -0-7 | ASCII code point -8-11 | Foreground color -12-14 | Background color -15 | Blink +| Bit(s) | Value | +| ------ | ---------------- | +| 0-7 | ASCII code point | +| 8-11 | Foreground color | +| 12-14 | Background color | +| 15 | Blink | The first byte represents the character that should be printed in the [ASCII encoding]. To be more specific, it isn't exactly ASCII, but a character set named [_code page 437_] with some additional characters and slight modifications. For simplicity, we will proceed to call it an ASCII character in this post. @@ -41,16 +41,16 @@ The first byte represents the character that should be printed in the [ASCII enc The second byte defines how the character is displayed. The first four bits define the foreground color, the next three bits the background color, and the last bit whether the character should blink. The following colors are available: -Number | Color | Number + Bright Bit | Bright Color ------- | ---------- | ------------------- | ------------- -0x0 | Black | 0x8 | Dark Gray -0x1 | Blue | 0x9 | Light Blue -0x2 | Green | 0xa | Light Green -0x3 | Cyan | 0xb | Light Cyan -0x4 | Red | 0xc | Light Red -0x5 | Magenta | 0xd | Pink -0x6 | Brown | 0xe | Yellow -0x7 | Light Gray | 0xf | White +| Number | Color | Number + Bright Bit | Bright Color | +| ------ | ---------- | ------------------- | ------------ | +| 0x0 | Black | 0x8 | Dark Gray | +| 0x1 | Blue | 0x9 | Light Blue | +| 0x2 | Green | 0xa | Light Green | +| 0x3 | Cyan | 0xb | Light Cyan | +| 0x4 | Red | 0xc | Light Red | +| 0x5 | Magenta | 0xd | Pink | +| 0x6 | Brown | 0xe | Yellow | +| 0x7 | Light Gray | 0xf | White | Bit 4 is the _bright bit_, which turns, for example, blue into light blue. For the background color, this bit is repurposed as the blink bit. @@ -260,7 +260,7 @@ pub fn print_something() { ``` It first creates a new Writer that points to the VGA buffer at `0xb8000`. The syntax for this might seem a bit strange: First, we cast the integer `0xb8000` as a mutable [raw pointer]. Then we convert it to a mutable reference by dereferencing it (through `*`) and immediately borrowing it again (through `&mut`). This conversion requires an [`unsafe` block], since the compiler can't guarantee that the raw pointer is valid. -[raw pointer]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer +[raw pointer]: https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#dereferencing-a-raw-pointer [`unsafe` block]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html Then it writes the byte `b'H'` to it. The `b` prefix creates a [byte literal], which represents an ASCII character. By writing the strings `"ello "` and `"Wörld!"`, we test our `write_string` method and the handling of unprintable characters. To see the output, we need to call the `print_something` function from our `_start` function: @@ -522,7 +522,7 @@ lazy_static! { However, this `WRITER` is pretty useless since it is immutable. This means that we can't write anything to it (since all the write methods take `&mut self`). One possible solution would be to use a [mutable static]. But then every read and write to it would be unsafe since it could easily introduce data races and other bad things. Using `static mut` is highly discouraged. There were even proposals to [remove it][remove static mut]. But what are the alternatives? We could try to use an immutable static with a cell type like [RefCell] or even [UnsafeCell] that provides [interior mutability]. But these types aren't [Sync] \(with good reason), so we can't use them in statics. -[mutable static]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable +[mutable static]: https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable [remove static mut]: https://internals.rust-lang.org/t/pre-rfc-remove-static-mut/1437 [RefCell]: https://doc.rust-lang.org/book/ch15-05-interior-mutability.html#keeping-track-of-borrows-at-runtime-with-refcellt [UnsafeCell]: https://doc.rust-lang.org/nightly/core/cell/struct.UnsafeCell.html @@ -581,7 +581,7 @@ Note that we only have a single unsafe block in our code, which is needed to cre ### A println Macro Now that we have a global writer, we can add a `println` macro that can be used from anywhere in the codebase. Rust's [macro syntax] is a bit strange, so we won't try to write a macro from scratch. Instead, we look at the source of the [`println!` macro] in the standard library: -[macro syntax]: https://doc.rust-lang.org/nightly/book/ch19-06-macros.html#declarative-macros-with-macro_rules-for-general-metaprogramming +[macro syntax]: https://doc.rust-lang.org/nightly/book/ch20-05-macros.html#declarative-macros-for-general-metaprogramming [`println!` macro]: https://doc.rust-lang.org/nightly/std/macro.println!.html ```rust diff --git a/blog/content/edition-2/posts/03-vga-text-buffer/index.pt-BR.md b/blog/content/edition-2/posts/03-vga-text-buffer/index.pt-BR.md index 225a71bf..1a605677 100644 --- a/blog/content/edition-2/posts/03-vga-text-buffer/index.pt-BR.md +++ b/blog/content/edition-2/posts/03-vga-text-buffer/index.pt-BR.md @@ -31,12 +31,12 @@ Este blog é desenvolvido abertamente no [GitHub]. Se você tiver algum problema ## O Buffer de Texto VGA Para imprimir um caractere na tela em modo de texto VGA, é preciso escrevê-lo no buffer de texto do hardware VGA. O buffer de texto VGA é um array bidimensional com tipicamente 25 linhas e 80 colunas, que é renderizado diretamente na tela. Cada entrada do array descreve um único caractere da tela através do seguinte formato: -Bit(s) | Valor ------- | ---------------- -0-7 | Ponto de código ASCII -8-11 | Cor do primeiro plano -12-14 | Cor do fundo -15 | Piscar +| Bit(s) | Valor | +| ------ | --------------------- | +| 0-7 | Ponto de código ASCII | +| 8-11 | Cor do primeiro plano | +| 12-14 | Cor do fundo | +| 15 | Piscar | O primeiro byte representa o caractere que deve ser impresso na [codificação ASCII]. Para ser mais específico, não é exatamente ASCII, mas um conjunto de caracteres chamado [_página de código 437_] com alguns caracteres adicionais e pequenas modificações. Para simplificar, continuaremos chamando-o de caractere ASCII neste post. @@ -45,16 +45,16 @@ O primeiro byte representa o caractere que deve ser impresso na [codificação A O segundo byte define como o caractere é exibido. Os primeiros quatro bits definem a cor do primeiro plano, os próximos três bits a cor do fundo, e o último bit se o caractere deve piscar. As seguintes cores estão disponíveis: -Número | Cor | Número + Bit Brilhante | Cor Brilhante ------- | ------------- | ---------------------- | -------------- -0x0 | Preto | 0x8 | Cinza Escuro -0x1 | Azul | 0x9 | Azul Claro -0x2 | Verde | 0xa | Verde Claro -0x3 | Ciano | 0xb | Ciano Claro -0x4 | Vermelho | 0xc | Vermelho Claro -0x5 | Magenta | 0xd | Rosa -0x6 | Marrom | 0xe | Amarelo -0x7 | Cinza Claro | 0xf | Branco +| Número | Cor | Número + Bit Brilhante | Cor Brilhante | +| ------ | ----------- | ---------------------- | -------------- | +| 0x0 | Preto | 0x8 | Cinza Escuro | +| 0x1 | Azul | 0x9 | Azul Claro | +| 0x2 | Verde | 0xa | Verde Claro | +| 0x3 | Ciano | 0xb | Ciano Claro | +| 0x4 | Vermelho | 0xc | Vermelho Claro | +| 0x5 | Magenta | 0xd | Rosa | +| 0x6 | Marrom | 0xe | Amarelo | +| 0x7 | Cinza Claro | 0xf | Branco | O bit 4 é o _bit brilhante_, que transforma, por exemplo, azul em azul claro. Para a cor de fundo, este bit é reaproveitado como o bit de piscar. @@ -264,7 +264,7 @@ pub fn print_something() { ``` Primeiro ele cria um novo Writer que aponta para o buffer VGA em `0xb8000`. A sintaxe para isso pode parecer um pouco estranha: Primeiro, convertemos o inteiro `0xb8000` como um [ponteiro bruto] mutável. Então o convertemos em uma referência mutável ao desreferenciá-lo (através de `*`) e imediatamente emprestar novamente (através de `&mut`). Esta conversão requer um [bloco `unsafe`], pois o compilador não pode garantir que o ponteiro bruto é válido. -[ponteiro bruto]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer +[ponteiro bruto]: https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#dereferencing-a-raw-pointer [bloco `unsafe`]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html Então ele escreve o byte `b'H'` nele. O prefixo `b` cria um [byte literal], que representa um caractere ASCII. Ao escrever as strings `"ello "` e `"Wörld!"`, testamos nosso método `write_string` e o tratamento de caracteres não imprimíveis. Para ver a saída, precisamos chamar a função `print_something` da nossa função `_start`: @@ -526,7 +526,7 @@ lazy_static! { No entanto, este `WRITER` é praticamente inútil, pois é imutável. Isso significa que não podemos escrever nada nele (já que todos os métodos de escrita recebem `&mut self`). Uma solução possível seria usar um [static mutável]. Mas então cada leitura e escrita nele seria unsafe, pois poderia facilmente introduzir data races e outras coisas ruins. Usar `static mut` é altamente desencorajado. Até houve propostas para [removê-lo][remove static mut]. Mas quais são as alternativas? Poderíamos tentar usar um static imutável com um tipo de célula como [RefCell] ou até [UnsafeCell] que fornece [mutabilidade interior]. Mas esses tipos não são [Sync] \(com boa razão), então não podemos usá-los em statics. -[static mutável]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable +[static mutável]: https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable [remove static mut]: https://internals.rust-lang.org/t/pre-rfc-remove-static-mut/1437 [RefCell]: https://doc.rust-lang.org/book/ch15-05-interior-mutability.html#keeping-track-of-borrows-at-runtime-with-refcellt [UnsafeCell]: https://doc.rust-lang.org/nightly/core/cell/struct.UnsafeCell.html @@ -585,7 +585,7 @@ Note que temos apenas um bloco unsafe no nosso código, que é necessário para ### Uma Macro println Agora que temos um writer global, podemos adicionar uma macro `println` que pode ser usada de qualquer lugar na base de código. A [sintaxe de macro] do Rust é um pouco estranha, então não tentaremos escrever uma macro do zero. Em vez disso, olhamos para o código-fonte da [macro `println!`] na biblioteca padrão: -[sintaxe de macro]: https://doc.rust-lang.org/nightly/book/ch19-06-macros.html#declarative-macros-with-macro_rules-for-general-metaprogramming +[sintaxe de macro]: https://doc.rust-lang.org/nightly/book/ch20-05-macros.html#declarative-macros-with-macro_rules-for-general-metaprogramming [macro `println!`]: https://doc.rust-lang.org/nightly/std/macro.println!.html ```rust @@ -700,4 +700,4 @@ Neste post, aprendemos sobre a estrutura do buffer de texto VGA e como ele pode Graças ao cargo, também vimos como é fácil adicionar dependências em bibliotecas de terceiros. As duas dependências que adicionamos, `lazy_static` e `spin`, são muito úteis no desenvolvimento de SO e as usaremos em mais lugares em posts futuros. ## O que vem a seguir? -O próximo post explica como configurar o framework de testes unitários embutido do Rust. Criaremos então alguns testes unitários básicos para o módulo de buffer VGA deste post. \ No newline at end of file +O próximo post explica como configurar o framework de testes unitários embutido do Rust. Criaremos então alguns testes unitários básicos para o módulo de buffer VGA deste post. 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 5fcd5f41..53fa1fee 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 @@ -248,7 +248,7 @@ pub fn print_something() { } ``` -这个函数首先创建一个指向 `0xb8000` 地址VGA缓冲区的 `Writer`。实现这一点,我们需要编写的代码可能看起来有点奇怪:首先,我们把整数 `0xb8000` 强制转换为一个可变的**裸指针**([raw pointer](https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer));之后,通过运算符`*`,我们将这个裸指针解引用;最后,我们再通过 `&mut`,再次获得它的可变借用。这些转换需要 **`unsafe` 语句块**([unsafe block](https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html)),因为编译器并不能保证这个裸指针是有效的。 +这个函数首先创建一个指向 `0xb8000` 地址VGA缓冲区的 `Writer`。实现这一点,我们需要编写的代码可能看起来有点奇怪:首先,我们把整数 `0xb8000` 强制转换为一个可变的**裸指针**([raw pointer](https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#dereferencing-a-raw-pointer));之后,通过运算符`*`,我们将这个裸指针解引用;最后,我们再通过 `&mut`,再次获得它的可变借用。这些转换需要 **`unsafe` 语句块**([unsafe block](https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html)),因为编译器并不能保证这个裸指针是有效的。 然后它将字节 `b'H'` 写入缓冲区内. 前缀 `b` 创建了一个字节常量([byte literal](https://doc.rust-lang.org/reference/tokens.html#byte-literals)),表示单个 ASCII 码字符;通过尝试写入 `"ello "` 和 `"Wörld!"`,我们可以测试 `write_string` 方法和其后对无法打印字符的处理逻辑。为了观察输出,我们需要在 `_start` 函数中调用 `print_something` 方法: @@ -489,7 +489,7 @@ lazy_static! { } ``` -然而,这个 `WRITER` 可能没有什么用途,因为它目前还是**不可变变量**(immutable variable):这意味着我们无法向它写入数据,因为所有与写入数据相关的方法都需要实例的可变引用 `&mut self`。一种解决方案是使用**可变静态**([mutable static](https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable))的变量,但所有对它的读写操作都被规定为不安全的(unsafe)操作,因为这很容易导致数据竞争或发生其它不好的事情——使用 `static mut` 极其不被赞成,甚至有一些提案认为[应该将它删除](https://internals.rust-lang.org/t/pre-rfc-remove-static-mut/1437)。也有其它的替代方案,比如可以尝试使用比如 [RefCell](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html#keeping-track-of-borrows-at-runtime-with-refcellt) 或甚至 [UnsafeCell](https://doc.rust-lang.org/nightly/core/cell/struct.UnsafeCell.html) 等类型提供的**内部可变性**([interior mutability](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html));但这些类型都被设计为非同步类型,即不满足 [Sync](https://doc.rust-lang.org/nightly/core/marker/trait.Sync.html) 约束,所以我们不能在静态变量中使用它们。 +然而,这个 `WRITER` 可能没有什么用途,因为它目前还是**不可变变量**(immutable variable):这意味着我们无法向它写入数据,因为所有与写入数据相关的方法都需要实例的可变引用 `&mut self`。一种解决方案是使用**可变静态**([mutable static](https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable))的变量,但所有对它的读写操作都被规定为不安全的(unsafe)操作,因为这很容易导致数据竞争或发生其它不好的事情——使用 `static mut` 极其不被赞成,甚至有一些提案认为[应该将它删除](https://internals.rust-lang.org/t/pre-rfc-remove-static-mut/1437)。也有其它的替代方案,比如可以尝试使用比如 [RefCell](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html#keeping-track-of-borrows-at-runtime-with-refcellt) 或甚至 [UnsafeCell](https://doc.rust-lang.org/nightly/core/cell/struct.UnsafeCell.html) 等类型提供的**内部可变性**([interior mutability](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html));但这些类型都被设计为非同步类型,即不满足 [Sync](https://doc.rust-lang.org/nightly/core/marker/trait.Sync.html) 约束,所以我们不能在静态变量中使用它们。 ### spinlock @@ -541,7 +541,7 @@ pub extern "C" fn _start() -> ! { ### `println!` 宏 -现在我们有了一个全局的 `Writer` 实例,我们就可以基于它实现 `println!` 宏,这样它就能被任意地方的代码使用了。Rust 提供的[宏定义语法](https://doc.rust-lang.org/nightly/book/ch19-06-macros.html#declarative-macros-with-macro_rules-for-general-metaprogramming)需要时间理解,所以我们将不从零开始编写这个宏。我们先看看标准库中 [`println!` 宏的实现源码](https://doc.rust-lang.org/nightly/std/macro.println!.html): +现在我们有了一个全局的 `Writer` 实例,我们就可以基于它实现 `println!` 宏,这样它就能被任意地方的代码使用了。Rust 提供的[宏定义语法](https://doc.rust-lang.org/nightly/book/ch20-05-macros.html#declarative-macros-for-general-metaprogramming)需要时间理解,所以我们将不从零开始编写这个宏。我们先看看标准库中 [`println!` 宏的实现源码](https://doc.rust-lang.org/nightly/std/macro.println!.html): ```rust #[macro_export] diff --git a/blog/content/edition-2/posts/04-testing/index.es.md b/blog/content/edition-2/posts/04-testing/index.es.md index bc282618..3f00f961 100644 --- a/blog/content/edition-2/posts/04-testing/index.es.md +++ b/blog/content/edition-2/posts/04-testing/index.es.md @@ -277,7 +277,7 @@ Una forma simple de enviar los datos es usar el [puerto serial], un estándar de Los chips que implementan una interfaz serial se llaman [UARTs]. Hay [muchos modelos de UART] en x86, pero afortunadamente las únicas diferencias entre ellos son algunas características avanzadas que no necesitamos. Los UART comunes hoy en día son todos compatibles con el [UART 16550], así que utilizaremos ese modelo para nuestro framework de pruebas. [UARTs]: https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter -[muchos modelos de UART]: https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter#UART_models +[muchos modelos de UART]: https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter#Models [UART 16550]: https://en.wikipedia.org/wiki/16550_UART Usaremos la crate [`uart_16550`] para inicializar el UART y enviar datos a través del puerto serial. Para añadirlo como dependencia, actualizamos nuestro `Cargo.toml` y `main.rs`: @@ -548,7 +548,7 @@ where Implementamos la función `run` imprimiendo primero el nombre de la función utilizando la función [`any::type_name`] . Esta función se implementa directamente en el compilador y devuelve una descripción de cadena de cada tipo. Para las funciones, el tipo es su nombre, así que esto es exactamente lo que queremos en este caso. El carácter `\t` es el [carácter de tabulación], que añade algo de alineación a los mensajes `[ok]`. [`any::type_name`]: https://doc.rust-lang.org/stable/core/any/fn.type_name.html -[carácter de tabulación]: https://en.wikipedia.org/wiki/Tab_key#Tab_characters +[carácter de tabulación]: https://en.wikipedia.org/wiki/Tab_character Después de imprimir el nombre de la función, invocamos la función de prueba a través de `self()`. Esto solo funciona porque requerimos que `self` implemente el trait `Fn()`. Después de que la función de prueba retorna, imprimimos `[ok]` para indicar que la función no provocó un pánico. diff --git a/blog/content/edition-2/posts/04-testing/index.fa.md b/blog/content/edition-2/posts/04-testing/index.fa.md index cfba58c5..2925c75b 100644 --- a/blog/content/edition-2/posts/04-testing/index.fa.md +++ b/blog/content/edition-2/posts/04-testing/index.fa.md @@ -273,7 +273,7 @@ test-success-exit-code = 33 # (0x10 << 1) | 1 تراشه‌های پیاده سازی یک رابط سریال [UART] نامیده می‌شوند. در x86 [مدلهای UART زیادی] وجود دارد، اما خوشبختانه تنها تفاوت آنها ویژگی‌های پیشرفته‌ای است که نیازی به آن‌ها نداریم. UART هایِ رایج امروزه همه با [16550 UART] سازگار هستند، بنابراین ما از آن مدل برای فریم‌ورک تست خود استفاده خواهیم کرد. [UARTs]: https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter -[مدلهای UART زیادی]: https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter#UART_models +[مدلهای UART زیادی]: https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter#Models [16550 UART]: https://en.wikipedia.org/wiki/16550_UART ما از کریت [`uart_16550`] برای شروع اولیه UART و ارسال داده‌ها از طریق پورت سریال استفاده خواهیم کرد. برای افزودن آن به عنوان یک وابستگی، ما `Cargo.toml` و `main.rs` خود را به روز می‌کنیم: @@ -540,7 +540,7 @@ where ما با اولین چاپِ نام تابع از طریق تابعِ [`any::type_name`]، تابع `run` را پیاده سازی می کنیم. این تابع مستقیماً در کامپایلر پیاده سازی شده و یک رشته توضیح از هر نوع را برمی‌گرداند. برای توابع، نوع آنها نامشان است، بنابراین این دقیقاً همان چیزی است که ما در این مورد می‌خواهیم. کاراکتر `\t` [کاراکتر tab] است، که مقداری ترازبندی‌ به پیام‌های `[ok]` اضافه می‌کند. [`any::type_name`]: https://doc.rust-lang.org/stable/core/any/fn.type_name.html -[کاراکتر tab]: https://en.wikipedia.org/wiki/Tab_key#Tab_characters +[کاراکتر tab]: https://en.wikipedia.org/wiki/Tab_character پس از چاپ نام تابع، ما از طریق `self ()` تابع تست را فراخوانی می‌کنیم. این فقط به این دلیل کار می‌کند که ما نیاز داریم که `self` تریت `Fn()` را پیاده سازی کند. بعد از بازگشت تابع تست، ما `[ok]` را چاپ می‌کنیم تا نشان دهد که تابع پنیک نکرده است. diff --git a/blog/content/edition-2/posts/04-testing/index.ja.md b/blog/content/edition-2/posts/04-testing/index.ja.md index 52878a9d..73bcb46a 100644 --- a/blog/content/edition-2/posts/04-testing/index.ja.md +++ b/blog/content/edition-2/posts/04-testing/index.ja.md @@ -278,7 +278,7 @@ test-success-exit-code = 33 # (0x10 << 1) | 1 シリアルインターフェースを実装しているチップは[UART][UARTs]と呼ばれています。x86には[多くのUARTのモデルがありますが][lots of UART models]、幸運なことに、それらの違いは私達の必要としないような高度な機能だけです。今日よく見られるUARTはすべて[16550 UART]に互換性があるので、このモデルを私達のテストフレームワークに使いましょう。 [UARTs]: https://ja.wikipedia.org/wiki/UART -[lots of UART models]: https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter#UART_models +[lots of UART models]: https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter#Models [16550 UART]: https://ja.wikipedia.org/wiki/16550_UART [`uart_16550`]クレートを使ってUARTを初期化しデータをシリアルポートを使って送信しましょう。これを依存先として追加するため、`Cargo.toml`と`main.rs`を書き換えます: diff --git a/blog/content/edition-2/posts/04-testing/index.ko.md b/blog/content/edition-2/posts/04-testing/index.ko.md index 9de2584e..bf771258 100644 --- a/blog/content/edition-2/posts/04-testing/index.ko.md +++ b/blog/content/edition-2/posts/04-testing/index.ko.md @@ -282,7 +282,7 @@ test_runner는 이제 테스트 결과를 출력한 후 QEMU를 자동으로 종 직렬 통신을 구현하는 칩을 [UART][UARTs]라고 부릅니다. x86에서 사용할 수 있는 [다양한 종류의 UART 구현 모델들][lots of UART models]이 존재하며, 다양한 구현 모델들 간 차이는 우리가 쓰지 않을 고급 기능 사용 시에만 유의미합니다. 우리의 테스트 프레임워크에서는 대부분의 UART 구현 모델들과 호환되는 [16550 UART] 모델을 이용할 것입니다. [UARTs]: https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter -[lots of UART models]: https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter#UART_models +[lots of UART models]: https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter#Models [16550 UART]: https://en.wikipedia.org/wiki/16550_UART [`uart_16550`] 크레이트를 이용해 UART 초기 설정을 마친 후 직렬 포트를 통해 데이터를 전송할 것입니다. `Cargo.toml`과 `main.rs`에 아래의 내용을 추가하여 의존 크레이트를 추가합니다. @@ -556,7 +556,7 @@ where `run` 함수에서 먼저 [`any::type_name`] 함수를 이용해 테스트 함수의 이름을 출력합니다. 이 함수는 컴파일러 단에서 구현된 함수로, 주어진 타입의 이름을 문자열로 반환합니다. 함수 타입의 경우, 함수 이름이 곧 타입의 이름입니다. `\t` 문자는 [탭 문자][tab character]인데 `[ok]` 메시지를 출력 이전에 여백을 삽입합니다. [`any::type_name`]: https://doc.rust-lang.org/stable/core/any/fn.type_name.html -[tab character]: https://en.wikipedia.org/wiki/Tab_key#Tab_characters +[tab character]: https://en.wikipedia.org/wiki/Tab_character 함수명을 출력한 후 `self()`를 통해 테스트 함수를 호출합니다. `self`가 `Fn()` 트레이트를 구현한다는 조건을 걸어놨기 때문에 이것이 가능합니다. 테스트 함수가 반환된 후, `[ok]` 메시지를 출력하여 테스트 함수가 패닉하지 않았다는 것을 알립니다. diff --git a/blog/content/edition-2/posts/04-testing/index.md b/blog/content/edition-2/posts/04-testing/index.md index 7cdabea2..0c134ba5 100644 --- a/blog/content/edition-2/posts/04-testing/index.md +++ b/blog/content/edition-2/posts/04-testing/index.md @@ -560,7 +560,7 @@ where We implement the `run` function by first printing the function name using the [`any::type_name`] function. This function is implemented directly in the compiler and returns a string description of every type. For functions, the type is their name, so this is exactly what we want in this case. The `\t` character is the [tab character], which adds some alignment to the `[ok]` messages. [`any::type_name`]: https://doc.rust-lang.org/stable/core/any/fn.type_name.html -[tab character]: https://en.wikipedia.org/wiki/Tab_key#Tab_characters +[tab character]: https://en.wikipedia.org/wiki/Tab_character After printing the function name, we invoke the test function through `self()`. This only works because we require that `self` implements the `Fn()` trait. After the test function returns, we print `[ok]` to indicate that the function did not panic. diff --git a/blog/content/edition-2/posts/04-testing/index.pt-BR.md b/blog/content/edition-2/posts/04-testing/index.pt-BR.md index 7b193a28..29d14bbd 100644 --- a/blog/content/edition-2/posts/04-testing/index.pt-BR.md +++ b/blog/content/edition-2/posts/04-testing/index.pt-BR.md @@ -562,7 +562,7 @@ where Implementamos a função `run` primeiro imprimindo o nome da função usando a função [`any::type_name`]. Esta função é implementada diretamente no compilador e retorna uma descrição em string de cada tipo. Para funções, o tipo é seu nome, então isso é exatamente o que queremos neste caso. O caractere `\t` é o [caractere tab], que adiciona algum alinhamento às mensagens `[ok]`. [`any::type_name`]: https://doc.rust-lang.org/stable/core/any/fn.type_name.html -[caractere tab]: https://en.wikipedia.org/wiki/Tab_key#Tab_characters +[caractere tab]: https://en.wikipedia.org/wiki/Tab_character Após imprimir o nome da função, invocamos a função de teste através de `self()`. Isso só funciona porque exigimos que `self` implemente a trait `Fn()`. Após a função de teste retornar, imprimimos `[ok]` para indicar que a função não entrou em panic. @@ -1041,4 +1041,4 @@ Agora temos um framework de testes que executa em um ambiente realista dentro do ## O que vem a seguir? -No próximo post, exploraremos _exceções de CPU_. Essas exceções são lançadas pela CPU quando algo ilegal acontece, como uma divisão por zero ou um acesso a uma página de memória não mapeada (um chamado "page fault"). Ser capaz de capturar e examinar essas exceções é muito importante para depuração de erros futuros. O tratamento de exceções também é muito similar ao tratamento de interrupções de hardware, que é necessário para suporte a teclado. \ No newline at end of file +No próximo post, exploraremos _exceções de CPU_. Essas exceções são lançadas pela CPU quando algo ilegal acontece, como uma divisão por zero ou um acesso a uma página de memória não mapeada (um chamado "page fault"). Ser capaz de capturar e examinar essas exceções é muito importante para depuração de erros futuros. O tratamento de exceções também é muito similar ao tratamento de interrupções de hardware, que é necessário para suporte a teclado. diff --git a/blog/content/edition-2/posts/04-testing/index.zh-CN.md b/blog/content/edition-2/posts/04-testing/index.zh-CN.md index 236d51d3..6b00b181 100644 --- a/blog/content/edition-2/posts/04-testing/index.zh-CN.md +++ b/blog/content/edition-2/posts/04-testing/index.zh-CN.md @@ -278,7 +278,7 @@ test-success-exit-code = 33 # (0x10 << 1) | 1 用来实现串行接口的芯片被称为 [UARTs]。在x86上,有[很多UART模型][lots of UART models],但是幸运的是,它们之间仅有的那些不同之处都是我们用不到的高级功能。目前通用的UARTs都会兼容[16550 UART],所以我们在我们测试框架里采用该模型。 [UARTs]: https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter -[lots of UART models]: https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter#UART_models +[lots of UART models]: https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter#Models [16550 UART]: https://en.wikipedia.org/wiki/16550_UART 我们使用 [`uart_16550`] crate来初始化UART,并通过串口来发送数据。为了将该crate添加为依赖,我们需要将 `Cargo.toml` 和 `main.rs` 修改为如下: @@ -549,7 +549,7 @@ where 我们实现的 `run` 函数中,首先使用 [`any::type_name`] 输出了函数名,这个函数事实上是被编译器实现的,可以返回任意类型的字符串形式。对于函数而言,其类型的字符串形式就是它的函数名,而函数名也正是我们想要的测试用例名称。至于 `\t` 则代表 [制表符][tab character],其作用是为后面的 `[ok]` 输出增加一点左边距。 [`any::type_name`]: https://doc.rust-lang.org/stable/core/any/fn.type_name.html -[tab character]: https://en.wikipedia.org/wiki/Tab_key#Tab_characters +[tab character]: https://en.wikipedia.org/wiki/Tab_character 输出函数名之后,我们通过 `self()` 调用了测试函数本身,该调用方式属于 `Fn()` trait 独有,如果测试函数顺利执行完毕,则 `[ok]` 也会被输出出来。 diff --git a/blog/content/edition-2/posts/05-cpu-exceptions/index.es.md b/blog/content/edition-2/posts/05-cpu-exceptions/index.es.md index 960c24d4..87e78525 100644 --- a/blog/content/edition-2/posts/05-cpu-exceptions/index.es.md +++ b/blog/content/edition-2/posts/05-cpu-exceptions/index.es.md @@ -132,7 +132,7 @@ type HandlerFunc = extern "x86-interrupt" fn(_: InterruptStackFrame); Es un [alias de tipo] para un tipo de `extern "x86-interrupt" fn`. La palabra clave `extern` define una función con una [convención de llamada foránea] y se utiliza a menudo para comunicarse con código C (`extern "C" fn`). Pero, ¿cuál es la convención de llamada `x86-interrupt`? -[alias de tipo]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#creating-type-synonyms-with-type-aliases +[alias de tipo]: https://doc.rust-lang.org/book/ch20-03-advanced-types.html#creating-type-synonyms-with-type-aliases [convención de llamada foránea]: https://doc.rust-lang.org/nomicon/ffi.html#foreign-calling-conventions ## La convención de llamada de interrupción @@ -329,7 +329,7 @@ pub fn init_idt() { Sin embargo, hay un problema: las estáticas son inmutables, por lo que no podemos modificar la entrada de punto de interrupción desde nuestra función `init`. Podríamos resolver este problema utilizando un [`static mut`]: -[`static mut`]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable +[`static mut`]: https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable ```rust static mut IDT: InterruptDescriptorTable = InterruptDescriptorTable::new(); diff --git a/blog/content/edition-2/posts/05-cpu-exceptions/index.fa.md b/blog/content/edition-2/posts/05-cpu-exceptions/index.fa.md index 39f43ed4..c25e4f49 100644 --- a/blog/content/edition-2/posts/05-cpu-exceptions/index.fa.md +++ b/blog/content/edition-2/posts/05-cpu-exceptions/index.fa.md @@ -47,28 +47,28 @@ rtl = true ### جدول توصیف کننده وقفه برای گرفتن و رسیدگی به استثنا‌ها ، باید اصطلاحاً _جدول توصیفگر وقفه_ (IDT) را تنظیم کنیم. در این جدول می توانیم برای هر استثنا پردازنده یک عملکرد تابع کننده مشخص کنیم. سخت افزار به طور مستقیم از این جدول استفاده می کند ، بنابراین باید از یک قالب از پیش تعریف شده پیروی کنیم. هر ورودی جدول باید ساختار 16 بایتی زیر را داشته باشد: -Type| Name | Description -----|--------------------------|----------------------------------- -u16 | Function Pointer [0:15] | The lower bits of the pointer to the handler function. -u16 | GDT selector | Selector of a code segment in the [global descriptor table]. -u16 | Options | (see below) -u16 | Function Pointer [16:31] | The middle bits of the pointer to the handler function. -u32 | Function Pointer [32:63] | The remaining bits of the pointer to the handler function. -u32 | Reserved | +| Type | Name | Description | +| ---- | ------------------------ | ------------------------------------------------------------ | +| u16 | Function Pointer [0:15] | The lower bits of the pointer to the handler function. | +| u16 | GDT selector | Selector of a code segment in the [global descriptor table]. | +| u16 | Options | (see below) | +| u16 | Function Pointer [16:31] | The middle bits of the pointer to the handler function. | +| u32 | Function Pointer [32:63] | The remaining bits of the pointer to the handler function. | +| u32 | Reserved | [global descriptor table]: https://en.wikipedia.org/wiki/Global_Descriptor_Table قسمت گزینه ها (Options) دارای قالب زیر است: -Bits | Name | Description -------|-----------------------------------|----------------------------------- -0-2 | Interrupt Stack Table Index | 0: Don't switch stacks, 1-7: Switch to the n-th stack in the Interrupt Stack Table when this handler is called. -3-7 | Reserved | -8 | 0: Interrupt Gate, 1: Trap Gate | If this bit is 0, interrupts are disabled when this handler is called. -9-11 | must be one | -12 | must be zero | -13‑14 | Descriptor Privilege Level (DPL) | The minimal privilege level required for calling this handler. -15 | Present | +| Bits | Name | Description | +| ----- | -------------------------------- | --------------------------------------------------------------------------------------------------------------- | +| 0-2 | Interrupt Stack Table Index | 0: Don't switch stacks, 1-7: Switch to the n-th stack in the Interrupt Stack Table when this handler is called. | +| 3-7 | Reserved | +| 8 | 0: Interrupt Gate, 1: Trap Gate | If this bit is 0, interrupts are disabled when this handler is called. | +| 9-11 | must be one | +| 12 | must be zero | +| 13‑14 | Descriptor Privilege Level (DPL) | The minimal privilege level required for calling this handler. | +| 15 | Present | هر استثنا دارای یک اندیس از پیش تعریف شده در IDT است. به عنوان مثال استثنا کد نامعتبر دارای اندیس 6 و استثنا خطای صفحه دارای اندیس 14 است. بنابراین ، سخت افزار می تواند به طور خودکار عنصر مربوطه را برای هر استثنا بارگذاری کند. [جدول استثناها][exceptions] در ویکی OSDev ، اندیس های IDT کلیه استثناها را در ستون “Vector nr.” نشان داده است. @@ -134,7 +134,7 @@ type HandlerFunc = extern "x86-interrupt" fn(_: InterruptStackFrame); این یک [نوع مستعار(type alias)] برای نوع "`extern "x86-interrupt" fn` است. کلمه کلیدی `extern` تابعی را با یک [قرارداد فراخوانی خارجی] تعریف می کند و اغلب برای برقراری ارتباط با کد C استفاده می شود(`extern "C" fn`) . اما قرارداد فراخوانی `x86-interrupt` چیست؟ -[نوع مستعار(type alias)]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#creating-type-synonyms-with-type-aliases +[نوع مستعار(type alias)]: https://doc.rust-lang.org/book/ch20-03-advanced-types.html#creating-type-synonyms-with-type-aliases [قرارداد فراخوانی خارجی]: https://doc.rust-lang.org/nomicon/ffi.html#foreign-calling-conventions ## قرارداد فراخوانی وقفه @@ -164,10 +164,10 @@ type HandlerFunc = extern "x86-interrupt" fn(_: InterruptStackFrame); در x86_64 ، قرارداد فراخوانی C ثبات‌های محفوظ شده و تغییرشونده زیر را مشخص می کند: -preserved registers | scratch registers ----|--- -`rbp`, `rbx`, `rsp`, `r12`, `r13`, `r14`, `r15` | `rax`, `rcx`, `rdx`, `rsi`, `rdi`, `r8`, `r9`, `r10`, `r11` -_callee-saved_ | _caller-saved_ +| preserved registers | scratch registers | +| ----------------------------------------------- | ----------------------------------------------------------- | +| `rbp`, `rbx`, `rsp`, `r12`, `r13`, `r14`, `r15` | `rax`, `rcx`, `rdx`, `rsi`, `rdi`, `r8`, `r9`, `r10`, `r11` | +| _callee-saved_ | _caller-saved_ | کامپایلر این قوانین را می داند ، بنابراین کد را متناسب با آن تولید می کند. به عنوان مثال ، بیشتر توابع با `push rbp` شروع می شوند که پشتیبان گیری از`rbp` روی پشته است (زیرا این یک ثبات _caller-saved_). @@ -330,7 +330,7 @@ pub fn init_idt() { با این وجود، یک مشکل وجود دارد: استاتیک‌ها تغییرناپذیر هستند، پس نمی توانیم ورودی بریک‌پوینت را از تابع `init` تغییر دهیم. می توانیم این مشکل را با استفاده از [`static mut`] حل کنیم: -[`static mut`]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable +[`static mut`]: https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable ```rust static mut IDT: InterruptDescriptorTable = InterruptDescriptorTable::new(); diff --git a/blog/content/edition-2/posts/05-cpu-exceptions/index.ja.md b/blog/content/edition-2/posts/05-cpu-exceptions/index.ja.md index 15246b46..6725ca1f 100644 --- a/blog/content/edition-2/posts/05-cpu-exceptions/index.ja.md +++ b/blog/content/edition-2/posts/05-cpu-exceptions/index.ja.md @@ -46,28 +46,28 @@ x86には20種類のCPU例外があります。中でも重要なものは: ### 割り込み記述子表 例外を捕捉し処理するためには、いわゆる割り込み記述子表 (Interrupt Descriptor Table, IDT) を設定しないといけません。この表にそれぞれのCPU例外に対するハンドラ関数を指定することができます。ハードウェアはこの表を直接使うので、決められたフォーマットに従わないといけません。それぞれのエントリは以下の16バイトの構造を持たなければなりません: -型 | 名前 | 説明 -----|--------------------------|----------------------------------- -u16 | 関数ポインタ [0:15] | ハンドラ関数へのポインタの下位ビット。 -u16 | GDTセレクタ | [大域記述子表 (Global Descriptor Table)][global descriptor table] におけるコードセグメントのセレクタ。 -u16 | オプション | (下を参照) -u16 | 関数ポインタ [16:31] | ハンドラ関数へのポインタの中位ビット。 -u32 | 関数ポインタ [32:63] | ハンドラ関数へのポインタの上位ビット。 -u32 | 予約済 | +| 型 | 名前 | 説明 | +| --- | ------------------------ | ------------------------------------------------------------------------------------------------------ | +| u16 | 関数ポインタ [0:15] | ハンドラ関数へのポインタの下位ビット。 | +| u16 | GDTセレクタ | [大域記述子表 (Global Descriptor Table)][global descriptor table] におけるコードセグメントのセレクタ。 | +| u16 | オプション | (下を参照) | +| u16 | 関数ポインタ [16:31] | ハンドラ関数へのポインタの中位ビット。 | +| u32 | 関数ポインタ [32:63] | ハンドラ関数へのポインタの上位ビット。 | +| u32 | 予約済 | [global descriptor table]: https://en.wikipedia.org/wiki/Global_Descriptor_Table オプション部は以下のフォーマットになっています: -ビット | 名前 | 説明 ---------|------------------------------------------------------------------------------------------------|------------------------------------- -0-2 | 割り込みスタックテーブルインデックス | 0ならスタックを変えない。1から7なら、ハンドラが呼ばれたとき、割り込みスタック表のその数字のスタックに変える。 -3-7 | 予約済 | -8 | 0: 割り込みゲート、1: トラップゲート | 0なら、このハンドラが呼ばれたとき割り込みは無効化される。 -9-11 | 1にしておかないといけない | -12 | 0にしておかないといけない | -13‑14 | 記述子の特権レベル (Descriptor Privilege Level) (DPL) | このハンドラを呼ぶ際に必要になる最低限の特権レベル。 -15 | Present | +| ビット | 名前 | 説明 | +| ------ | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | +| 0-2 | 割り込みスタックテーブルインデックス | 0ならスタックを変えない。1から7なら、ハンドラが呼ばれたとき、割り込みスタック表のその数字のスタックに変える。 | +| 3-7 | 予約済 | +| 8 | 0: 割り込みゲート、1: トラップゲート | 0なら、このハンドラが呼ばれたとき割り込みは無効化される。 | +| 9-11 | 1にしておかないといけない | +| 12 | 0にしておかないといけない | +| 13‑14 | 記述子の特権レベル (Descriptor Privilege Level) (DPL) | このハンドラを呼ぶ際に必要になる最低限の特権レベル。 | +| 15 | Present | それぞれの例外がIDTの何番目に対応するかは事前に定義されています。例えば、「無効な命令コード」の例外は6番目で、「ページフォルト」例外は14番目です。これにより、ハードウェアがそれぞれの例外に対応するIDTの設定を(特に設定の必要なく)自動的に読み出せるというわけです。OSDev wikiの[「例外表」][exceptions]の "Vector nr." 列に、すべての例外についてIDTの何番目かが記されています。 @@ -132,7 +132,7 @@ type HandlerFunc = extern "x86-interrupt" fn(_: InterruptStackFrame); これは、`extern "x86-interrupt" fn`型への[型エイリアス][type alias]です。`extern`は[外部呼び出し規約][foreign calling convention]に従う関数を定義するのに使われ、おもにC言語のコードと連携したいときに使われます (`extern "C" fn`) 。しかし、`x86-interrupt`呼び出し規約とは何なのでしょう? -[type alias]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#creating-type-synonyms-with-type-aliases +[type alias]: https://doc.rust-lang.org/book/ch20-03-advanced-types.html#creating-type-synonyms-with-type-aliases [foreign calling convention]: https://doc.rust-lang.org/nomicon/ffi.html#foreign-calling-conventions ## 例外の呼び出し規約 @@ -162,10 +162,10 @@ preservedレジスタの値は関数呼び出しの前後で変化してはい x86_64においては、C言語の呼び出し規約は以下のpreservedレジスタとscratchレジスタを指定します: -preservedレジスタ | scratchレジスタ ---- | --- -`rbp`, `rbx`, `rsp`, `r12`, `r13`, `r14`, `r15` | `rax`, `rcx`, `rdx`, `rsi`, `rdi`, `r8`, `r9`, `r10`, `r11` -_callee-saved_ | _caller-saved_ +| preservedレジスタ | scratchレジスタ | +| ----------------------------------------------- | ----------------------------------------------------------- | +| `rbp`, `rbx`, `rsp`, `r12`, `r13`, `r14`, `r15` | `rax`, `rcx`, `rdx`, `rsi`, `rdi`, `r8`, `r9`, `r10`, `r11` | +| _callee-saved_ | _caller-saved_ | コンパイラはこれらのルールを知っているので、それにしたがってコードを生成します。例えば、ほとんどの関数は`push rbp`から始まるのですが、これは`rbp`をスタックにバックアップしているのです(`rbp`はcallee-savedなレジスタであるため)。 @@ -329,7 +329,7 @@ pub fn init_idt() { しかし、問題が発生します:staticは不変 (イミュータブル) なので、`init`関数でエントリを変更することができません。これは[`static mut`]を使って解決できそうです: -[`static mut`]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable +[`static mut`]: https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable ```rust static mut IDT: InterruptDescriptorTable = InterruptDescriptorTable::new(); diff --git a/blog/content/edition-2/posts/05-cpu-exceptions/index.ko.md b/blog/content/edition-2/posts/05-cpu-exceptions/index.ko.md index 99808eb3..b8275263 100644 --- a/blog/content/edition-2/posts/05-cpu-exceptions/index.ko.md +++ b/blog/content/edition-2/posts/05-cpu-exceptions/index.ko.md @@ -49,14 +49,14 @@ x86 아키텍처에는 20가지 정도의 CPU 예외가 존재합니다. 그 중 예외 발생을 포착하고 대응할 수 있으려면 _인터럽트 서술자 테이블 (Interrupt Descriptor Table; IDT)_ 이 필요합니다. 이 테이블을 통해 우리는 각각의 CPU 예외를 어떤 예외 처리 함수가 처리할지 지정합니다. 하드웨어에서 이 테이블을 직접 사용하므로 테이블의 형식은 정해진 표준에 따라야 합니다. 테이블의 각 엔트리는 아래와 같은 16 바이트 구조를 따릅니다: -| 타입 | 이름 | 설명 | -| ---- | ------------------------ | ------------------------------------------------------------------------------------------------------------- | -| u16 | Function Pointer [0:15] | 예외 처리 함수에 대한 64비트 포인터의 하위 16비트 | +| 타입 | 이름 | 설명 | +| ---- | ------------------------ | ------------------------------------------------------------------------------------------------------- | +| u16 | Function Pointer [0:15] | 예외 처리 함수에 대한 64비트 포인터의 하위 16비트 | | u16 | GDT selector | [전역 서술자 테이블 (global descriptor table)][global descriptor table]에서 코드 세그먼트를 선택하는 값 | -| u16 | Options | (표 아래의 설명 참조) | -| u16 | Function Pointer [16:31] | 예외 처리 함수에 대한 64비트 포인터의 2번째 하위 16비트 | -| u32 | Function Pointer [32:63] | 예외 처리 함수에 대한 64비트 포인터의 상위 32비트 | -| u32 | Reserved | 사용 보류 중인 영역 | +| u16 | Options | (표 아래의 설명 참조) | +| u16 | Function Pointer [16:31] | 예외 처리 함수에 대한 64비트 포인터의 2번째 하위 16비트 | +| u32 | Function Pointer [32:63] | 예외 처리 함수에 대한 64비트 포인터의 상위 32비트 | +| u32 | Reserved | 사용 보류 중인 영역 | [global descriptor table]: https://en.wikipedia.org/wiki/Global_Descriptor_Table @@ -135,7 +135,7 @@ type HandlerFunc = extern "x86-interrupt" fn(_: InterruptStackFrame); `HandlerFunc`는 함수 타입 `extern "x86-interrupt" fn`의 [타입 별칭][type alias]입니다. `extern` 키워드는 [외부 함수 호출 규약 (foreign calling convention)][foreign calling convention]을 사용하는 함수를 정의할 때 쓰이는데, 주로 C 함수와 상호작용하는 경우에 쓰입니다 (`extern "C" fn`). `x86-interrupt` 함수 호출 규약은 무엇일까요? -[type alias]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#creating-type-synonyms-with-type-aliases +[type alias]: https://doc.rust-lang.org/book/ch20-03-advanced-types.html#creating-type-synonyms-with-type-aliases [foreign calling convention]: https://doc.rust-lang.org/nomicon/ffi.html#foreign-calling-conventions ## 인터럽트 호출 규약 @@ -334,7 +334,7 @@ pub fn init_idt() { 문제는 static 변수의 값은 변경할 수가 없어서, `init` 함수 실행 시 breakpoint 예외에 대응하는 IDT 엔트리를 수정할 수 없습니다. 대신 `IDT`를 [`static mut`] 변수에 저장해보겠습니다: -[`static mut`]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable +[`static mut`]: https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable ```rust static mut IDT: InterruptDescriptorTable = InterruptDescriptorTable::new(); 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 5c51b5b0..5933f5f0 100644 --- a/blog/content/edition-2/posts/05-cpu-exceptions/index.md +++ b/blog/content/edition-2/posts/05-cpu-exceptions/index.md @@ -43,28 +43,28 @@ For the full list of exceptions, check out the [OSDev wiki][exceptions]. ### The Interrupt Descriptor Table In order to catch and handle exceptions, we have to set up a so-called _Interrupt Descriptor Table_ (IDT). In this table, we can specify a handler function for each CPU exception. The hardware uses this table directly, so we need to follow a predefined format. Each entry must have the following 16-byte structure: -Type| Name | Description -----|--------------------------|----------------------------------- -u16 | Function Pointer [0:15] | The lower bits of the pointer to the handler function. -u16 | GDT selector | Selector of a code segment in the [global descriptor table]. -u16 | Options | (see below) -u16 | Function Pointer [16:31] | The middle bits of the pointer to the handler function. -u32 | Function Pointer [32:63] | The remaining bits of the pointer to the handler function. -u32 | Reserved | +| Type | Name | Description | +| ---- | ------------------------ | ------------------------------------------------------------ | +| u16 | Function Pointer [0:15] | The lower bits of the pointer to the handler function. | +| u16 | GDT selector | Selector of a code segment in the [global descriptor table]. | +| u16 | Options | (see below) | +| u16 | Function Pointer [16:31] | The middle bits of the pointer to the handler function. | +| u32 | Function Pointer [32:63] | The remaining bits of the pointer to the handler function. | +| u32 | Reserved | [global descriptor table]: https://en.wikipedia.org/wiki/Global_Descriptor_Table The options field has the following format: -Bits | Name | Description -------|-----------------------------------|----------------------------------- -0-2 | Interrupt Stack Table Index | 0: Don't switch stacks, 1-7: Switch to the n-th stack in the Interrupt Stack Table when this handler is called. -3-7 | Reserved | -8 | 0: Interrupt Gate, 1: Trap Gate | If this bit is 0, interrupts are disabled when this handler is called. -9-11 | must be one | -12 | must be zero | -13‑14 | Descriptor Privilege Level (DPL) | The minimal privilege level required for calling this handler. -15 | Present | +| Bits | Name | Description | +| ----- | -------------------------------- | --------------------------------------------------------------------------------------------------------------- | +| 0-2 | Interrupt Stack Table Index | 0: Don't switch stacks, 1-7: Switch to the n-th stack in the Interrupt Stack Table when this handler is called. | +| 3-7 | Reserved | +| 8 | 0: Interrupt Gate, 1: Trap Gate | If this bit is 0, interrupts are disabled when this handler is called. | +| 9-11 | must be one | +| 12 | must be zero | +| 13‑14 | Descriptor Privilege Level (DPL) | The minimal privilege level required for calling this handler. | +| 15 | Present | Each exception has a predefined IDT index. For example, the invalid opcode exception has table index 6 and the page fault exception has table index 14. Thus, the hardware can automatically load the corresponding IDT entry for each exception. The [Exception Table][exceptions] in the OSDev wiki shows the IDT indexes of all exceptions in the “Vector nr.” column. @@ -129,7 +129,7 @@ type HandlerFunc = extern "x86-interrupt" fn(_: InterruptStackFrame); It's a [type alias] for an `extern "x86-interrupt" fn` type. The `extern` keyword defines a function with a [foreign calling convention] and is often used to communicate with C code (`extern "C" fn`). But what is the `x86-interrupt` calling convention? -[type alias]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#creating-type-synonyms-with-type-aliases +[type alias]: https://doc.rust-lang.org/book/ch20-03-advanced-types.html#creating-type-synonyms-with-type-aliases [foreign calling convention]: https://doc.rust-lang.org/nomicon/ffi.html#foreign-calling-conventions ## The Interrupt Calling Convention @@ -159,10 +159,10 @@ In contrast, a called function is allowed to overwrite _scratch_ registers witho On x86_64, the C calling convention specifies the following preserved and scratch registers: -preserved registers | scratch registers ----|--- -`rbp`, `rbx`, `rsp`, `r12`, `r13`, `r14`, `r15` | `rax`, `rcx`, `rdx`, `rsi`, `rdi`, `r8`, `r9`, `r10`, `r11` -_callee-saved_ | _caller-saved_ +| preserved registers | scratch registers | +| ----------------------------------------------- | ----------------------------------------------------------- | +| `rbp`, `rbx`, `rsp`, `r12`, `r13`, `r14`, `r15` | `rax`, `rcx`, `rdx`, `rsi`, `rdi`, `r8`, `r9`, `r10`, `r11` | +| _callee-saved_ | _caller-saved_ | The compiler knows these rules, so it generates the code accordingly. For example, most functions begin with a `push rbp`, which backups `rbp` on the stack (because it's a callee-saved register). @@ -327,7 +327,7 @@ pub fn init_idt() { However, there is a problem: Statics are immutable, so we can't modify the breakpoint entry from our `init` function. We could solve this problem by using a [`static mut`]: -[`static mut`]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable +[`static mut`]: https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable ```rust static mut IDT: InterruptDescriptorTable = InterruptDescriptorTable::new(); diff --git a/blog/content/edition-2/posts/05-cpu-exceptions/index.pt-BR.md b/blog/content/edition-2/posts/05-cpu-exceptions/index.pt-BR.md index a9035393..3f662233 100644 --- a/blog/content/edition-2/posts/05-cpu-exceptions/index.pt-BR.md +++ b/blog/content/edition-2/posts/05-cpu-exceptions/index.pt-BR.md @@ -48,28 +48,28 @@ Para a lista completa de exceções, consulte a [wiki do OSDev][exceptions]. ### A Tabela de Descritores de Interrupção Para capturar e manipular exceções, precisamos configurar uma chamada _Tabela de Descritores de Interrupção_ (IDT - Interrupt Descriptor Table). Nesta tabela, podemos especificar uma função manipuladora para cada exceção de CPU. O hardware usa esta tabela diretamente, então precisamos seguir um formato predefinido. Cada entrada deve ter a seguinte estrutura de 16 bytes: -Tipo| Nome | Descrição -----|--------------------------|----------------------------------- -u16 | Function Pointer [0:15] | Os bits inferiores do ponteiro para a função manipuladora. -u16 | GDT selector | Seletor de um segmento de código na [tabela de descritores globais]. -u16 | Options | (veja abaixo) -u16 | Function Pointer [16:31] | Os bits do meio do ponteiro para a função manipuladora. -u32 | Function Pointer [32:63] | Os bits restantes do ponteiro para a função manipuladora. -u32 | Reserved | +| Tipo | Nome | Descrição | +| ---- | ------------------------ | -------------------------------------------------------------------- | +| u16 | Function Pointer [0:15] | Os bits inferiores do ponteiro para a função manipuladora. | +| u16 | GDT selector | Seletor de um segmento de código na [tabela de descritores globais]. | +| u16 | Options | (veja abaixo) | +| u16 | Function Pointer [16:31] | Os bits do meio do ponteiro para a função manipuladora. | +| u32 | Function Pointer [32:63] | Os bits restantes do ponteiro para a função manipuladora. | +| u32 | Reserved | [tabela de descritores globais]: https://en.wikipedia.org/wiki/Global_Descriptor_Table O campo options tem o seguinte formato: -Bits | Nome | Descrição -------|-----------------------------------|----------------------------------- -0-2 | Interrupt Stack Table Index | 0: Não troca stacks, 1-7: Troca para a n-ésima stack na Interrupt Stack Table quando este manipulador é chamado. -3-7 | Reserved | -8 | 0: Interrupt Gate, 1: Trap Gate | Se este bit é 0, as interrupções são desativadas quando este manipulador é chamado. -9-11 | must be one | -12 | must be zero | -13‑14 | Descriptor Privilege Level (DPL) | O nível mínimo de privilégio necessário para chamar este manipulador. -15 | Present | +| Bits | Nome | Descrição | +| ----- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------- | +| 0-2 | Interrupt Stack Table Index | 0: Não troca stacks, 1-7: Troca para a n-ésima stack na Interrupt Stack Table quando este manipulador é chamado. | +| 3-7 | Reserved | +| 8 | 0: Interrupt Gate, 1: Trap Gate | Se este bit é 0, as interrupções são desativadas quando este manipulador é chamado. | +| 9-11 | must be one | +| 12 | must be zero | +| 13‑14 | Descriptor Privilege Level (DPL) | O nível mínimo de privilégio necessário para chamar este manipulador. | +| 15 | Present | Cada exceção tem um índice predefinido na IDT. Por exemplo, a exceção invalid opcode tem índice de tabela 6 e a exceção page fault tem índice de tabela 14. Assim, o hardware pode automaticamente carregar a entrada IDT correspondente para cada exceção. A [Tabela de Exceções][exceptions] na wiki do OSDev mostra os índices IDT de todas as exceções na coluna "Vector nr.". @@ -134,7 +134,7 @@ type HandlerFunc = extern "x86-interrupt" fn(_: InterruptStackFrame); É um [type alias] para um tipo `extern "x86-interrupt" fn`. A palavra-chave `extern` define uma função com uma [convenção de chamada estrangeira] e é frequentemente usada para se comunicar com código C (`extern "C" fn`). Mas o que é a convenção de chamada `x86-interrupt`? -[type alias]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#creating-type-synonyms-with-type-aliases +[type alias]: https://doc.rust-lang.org/book/ch20-03-advanced-types.html#creating-type-synonyms-with-type-aliases [convenção de chamada estrangeira]: https://doc.rust-lang.org/nomicon/ffi.html#foreign-calling-conventions ## A Convenção de Chamada de Interrupção @@ -164,10 +164,10 @@ Em contraste, uma função chamada tem permissão para sobrescrever registradore No x86_64, a convenção de chamada C especifica os seguintes registradores preservados e scratch: -registradores preservados | registradores scratch ----|--- -`rbp`, `rbx`, `rsp`, `r12`, `r13`, `r14`, `r15` | `rax`, `rcx`, `rdx`, `rsi`, `rdi`, `r8`, `r9`, `r10`, `r11` -_callee-saved_ | _caller-saved_ +| registradores preservados | registradores scratch | +| ----------------------------------------------- | ----------------------------------------------------------- | +| `rbp`, `rbx`, `rsp`, `r12`, `r13`, `r14`, `r15` | `rax`, `rcx`, `rdx`, `rsi`, `rdi`, `r8`, `r9`, `r10`, `r11` | +| _callee-saved_ | _caller-saved_ | O compilador conhece essas regras, então gera o código de acordo. Por exemplo, a maioria das funções começa com um `push rbp`, que faz backup de `rbp` na pilha (porque é um registrador callee-saved). @@ -471,4 +471,4 @@ A convenção de chamada `x86-interrupt` e o tipo [`InterruptDescriptorTable`] t Capturamos com sucesso nossa primeira exceção e retornamos dela! O próximo passo é garantir que capturemos todas as exceções porque uma exceção não capturada causa um [triple fault] fatal, que leva a uma redefinição do sistema. A próxima postagem explica como podemos evitar isso capturando corretamente [double faults]. [triple fault]: https://wiki.osdev.org/Triple_Fault -[double faults]: https://wiki.osdev.org/Double_Fault#Double_Fault \ No newline at end of file +[double faults]: https://wiki.osdev.org/Double_Fault#Double_Fault diff --git a/blog/content/edition-2/posts/05-cpu-exceptions/index.zh-CN.md b/blog/content/edition-2/posts/05-cpu-exceptions/index.zh-CN.md index 127f7f5a..b793cac0 100644 --- a/blog/content/edition-2/posts/05-cpu-exceptions/index.zh-CN.md +++ b/blog/content/edition-2/posts/05-cpu-exceptions/index.zh-CN.md @@ -134,7 +134,7 @@ type HandlerFunc = extern "x86-interrupt" fn(_: InterruptStackFrame); 这是一个针对 `extern "x86-interrupt" fn` 类型的 [类型别名][type alias]。`extern` 关键字使用 [外部调用约定][foreign calling convention] 定义了一个函数,这种定义方式多用于和C语言代码通信(`extern "C" fn`),那么这里的外部调用约定又究竟调用了哪些东西? -[type alias]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#creating-type-synonyms-with-type-aliases +[type alias]: https://doc.rust-lang.org/book/ch20-03-advanced-types.html#creating-type-synonyms-with-type-aliases [foreign calling convention]: https://doc.rust-lang.org/nomicon/ffi.html#foreign-calling-conventions ## 中断调用约定 @@ -331,7 +331,7 @@ pub fn init_idt() { 然而这样就会引入一个新问题:静态变量是不可修改的,这样我们就无法在 `init` 函数中修改里面的数据了,所以需要把变量类型修改为 [`static mut`]: -[`static mut`]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable +[`static mut`]: https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable ```rust static mut IDT: InterruptDescriptorTable = InterruptDescriptorTable::new(); diff --git a/blog/content/edition-2/posts/07-hardware-interrupts/index.es.md b/blog/content/edition-2/posts/07-hardware-interrupts/index.es.md index e809f207..c66d4faa 100644 --- a/blog/content/edition-2/posts/07-hardware-interrupts/index.es.md +++ b/blog/content/edition-2/posts/07-hardware-interrupts/index.es.md @@ -643,7 +643,7 @@ extern "x86-interrupt" fn keyboard_interrupt_handler( El código anterior traduce las pulsaciones de las teclas numéricas 0-9 y ignora todas las otras teclas. Utiliza una declaración [match] para asignar un carácter o `None` a cada scancode. Luego, utiliza [`if let`] para desestructurar la opción `key`. Al usar el mismo nombre de variable `key` en el patrón, [somos sombras de] la declaración anterior, lo cual es un patrón común para desestructurar tipos `Option` en Rust. [match]: https://doc.rust-lang.org/book/ch06-02-match.html -[`if let`]: https://doc.rust-lang.org/book/ch18-01-all-the-places-for-patterns.html#conditional-if-let-expressions +[`if let`]: https://doc.rust-lang.org/book/ch19-01-all-the-places-for-patterns.html#conditional-if-let-expressions [sombra]: https://doc.rust-lang.org/book/ch03-01-variables-and-mutabilidad.html#shadowing Ahora podemos escribir números: diff --git a/blog/content/edition-2/posts/07-hardware-interrupts/index.fa.md b/blog/content/edition-2/posts/07-hardware-interrupts/index.fa.md index 4ac9a16e..2602150d 100644 --- a/blog/content/edition-2/posts/07-hardware-interrupts/index.fa.md +++ b/blog/content/edition-2/posts/07-hardware-interrupts/index.fa.md @@ -278,16 +278,16 @@ pub fn _print(args: fmt::Arguments) { `WRITER` را قفل می کند، `write_fmt` را روی آن فراخوانی می کند و در انتهای تابع به طور ضمنی قفل آن را باز می کند. حال تصور کنید که در حالی که `WRITER` قفل شده است وقفه رخ دهد و کنترل کننده وقفه نیز سعی کند چیزی را چاپ کند: -Timestep | _start | interrupt_handler ----------|------|------------------ -0 | calls `println!` |   -1 | `print` locks `WRITER` |   -2 | | **interrupt occurs**, handler begins to run -3 | | calls `println!` | -4 | | `print` tries to lock `WRITER` (already locked) -5 | | `print` tries to lock `WRITER` (already locked) -… | | … -_never_ | _unlock `WRITER`_ | +| Timestep | _start | interrupt_handler | +| -------- | ---------------------- | ----------------------------------------------- | +| 0 | calls `println!` |   | +| 1 | `print` locks `WRITER` |   | +| 2 | | **interrupt occurs**, handler begins to run | +| 3 | | calls `println!` | +| 4 | | `print` tries to lock `WRITER` (already locked) | +| 5 | | `print` tries to lock `WRITER` (already locked) | +| … | | … | +| _never_ | _unlock `WRITER`_ | `WRITER` قفل شده است ، بنابراین کنترل کننده وقفه منتظر می ماند تا آزاد شود. اما این هرگز اتفاق نمی افتد ، زیرا تابع `start_` فقط پس از بازگشت کنترل کننده وقفه ادامه می یابد. بنابراین کل سیستم هنگ است. @@ -644,7 +644,7 @@ extern "x86-interrupt" fn keyboard_interrupt_handler( کد بالا فشردن کلیدهای عددی 9-0 را ترجمه کرده و کلیه کلیدهای دیگر را نادیده می گیرد. از عبارت [match] برای اختصاص یک کاراکتر یا `None` به هر اسکن کد استفاده می کند. سپس با استفاده از [`if let`] اپشن `key` را از بین می برد. با استفاده از همان نام متغیر `key` در الگو که یک روش معمول برای از بین بردن انواع`Option` در راست است تعریف قبلی را [سایه می زنیم]. [match]: https://doc.rust-lang.org/book/ch06-02-match.html -[`if let`]: https://doc.rust-lang.org/book/ch18-01-all-the-places-for-patterns.html#conditional-if-let-expressions +[`if let`]: https://doc.rust-lang.org/book/ch19-01-all-the-places-for-patterns.html#conditional-if-let-expressions [سایه می زنیم]: https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing اکنون می توانیم اعداد را بنویسیم: diff --git a/blog/content/edition-2/posts/07-hardware-interrupts/index.ja.md b/blog/content/edition-2/posts/07-hardware-interrupts/index.ja.md index 158bb83e..8a9d01f4 100644 --- a/blog/content/edition-2/posts/07-hardware-interrupts/index.ja.md +++ b/blog/content/edition-2/posts/07-hardware-interrupts/index.ja.md @@ -277,16 +277,16 @@ pub fn _print(args: fmt::Arguments) { _print 関数は `WRITER` をロックし、`write_fmt` を呼び出し、そして関数の最後で暗黙にロックを解放します。では、`WRITER` がロックされている間に割り込みが発生し、割り込みハンドラもなにかを表示しようとしていると想像してみてください: -時刻 | _start | 割り込みハンドラ ----------|------|------------------ -0 | `println!` を呼び出す |   -1 | `print` が `WRITER` をロック |   -2 | | **割り込みが発生**、割り込みハンドラが動き出す -3 | | `println!` を呼び出す | -4 | | `print` が `WRITER` をロックしようとする (既にロック済み) -5 | | `print` が `WRITER` をロックしようとする (既にロック済み) -… | | … -_(決して起こらない)_ | _`WRITER` のロックを解放する_ | +| 時刻 | _start | 割り込みハンドラ | +| -------------------- | ----------------------------- | --------------------------------------------------------- | +| 0 | `println!` を呼び出す |   | +| 1 | `print` が `WRITER` をロック |   | +| 2 | | **割り込みが発生**、割り込みハンドラが動き出す | +| 3 | | `println!` を呼び出す | +| 4 | | `print` が `WRITER` をロックしようとする (既にロック済み) | +| 5 | | `print` が `WRITER` をロックしようとする (既にロック済み) | +| … | | … | +| _(決して起こらない)_ | _`WRITER` のロックを解放する_ | `WRITER` はロックされているので、割り込みハンドラはそれが解放されるのを待ちます。しかし `_start` 関数は割り込みハンドラから処理が戻らないと実行されることはないので、ロックが解放されることはありません。このためシステム全体がハングしてしまいます。 @@ -645,7 +645,7 @@ extern "x86-interrupt" fn keyboard_interrupt_handler( 上記のコードは、0から9の数字キーが押された場合に変換を行い、それ以外のキーは無視します。全てのスキャンコードに対し、[match] 文を使って対応する文字か `None` を割り当てます。そのあと [`if let`] 構文を使ってオプション型の `key` から値を取り出します。パターン部分に `key` という同じ変数名を使うことでそれ以前の宣言を[シャドーイング][shadow]します。これは Rust において `Option` 型から値を取り出すときによく使うパターンです。 [match]: https://doc.rust-lang.org/book/ch06-02-match.html -[`if let`]: https://doc.rust-lang.org/book/ch18-01-all-the-places-for-patterns.html#conditional-if-let-expressions +[`if let`]: https://doc.rust-lang.org/book/ch19-01-all-the-places-for-patterns.html#conditional-if-let-expressions [shadow]: https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing これで数字が表示できるようになりました: diff --git a/blog/content/edition-2/posts/07-hardware-interrupts/index.ko.md b/blog/content/edition-2/posts/07-hardware-interrupts/index.ko.md index 3b62d287..04354645 100644 --- a/blog/content/edition-2/posts/07-hardware-interrupts/index.ko.md +++ b/blog/content/edition-2/posts/07-hardware-interrupts/index.ko.md @@ -645,7 +645,7 @@ extern "x86-interrupt" fn keyboard_interrupt_handler( 위 코드는 0-9의 숫자 키 누름을 인식하고 출력하며, 다른 키는 무시합니다. [match]문을 사용해 각 스캔코드에 문자 또는 `None`을 배정합니다. 그 후 [`if let`]을 사용해 스캔 코드에 배정된 문자 `key`를 추출합니다. 이미 존재하던 변수 `key`와 같은 이름을 패턴에서 사용해 기존 변수의 정의를 [shadow]하는데, 이는 Rust에서 `Option` 타입 안의 값을 추출할 때 자주 사용되는 방식입니다. [match]: https://doc.rust-lang.org/book/ch06-02-match.html -[`if let`]: https://doc.rust-lang.org/book/ch18-01-all-the-places-for-patterns.html#conditional-if-let-expressions +[`if let`]: https://doc.rust-lang.org/book/ch19-01-all-the-places-for-patterns.html#conditional-if-let-expressions [shadow]: https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing 이제 키보드로 숫자를 입력할 수 있습니다. diff --git a/blog/content/edition-2/posts/07-hardware-interrupts/index.md b/blog/content/edition-2/posts/07-hardware-interrupts/index.md index c3c5c427..ed3018a8 100644 --- a/blog/content/edition-2/posts/07-hardware-interrupts/index.md +++ b/blog/content/edition-2/posts/07-hardware-interrupts/index.md @@ -274,16 +274,16 @@ pub fn _print(args: fmt::Arguments) { It locks the `WRITER`, calls `write_fmt` on it, and implicitly unlocks it at the end of the function. Now imagine that an interrupt occurs while the `WRITER` is locked and the interrupt handler tries to print something too: -Timestep | _start | interrupt_handler ----------|------|------------------ -0 | calls `println!` |   -1 | `print` locks `WRITER` |   -2 | | **interrupt occurs**, handler begins to run -3 | | calls `println!` | -4 | | `print` tries to lock `WRITER` (already locked) -5 | | `print` tries to lock `WRITER` (already locked) -… | | … -_never_ | _unlock `WRITER`_ | +| Timestep | _start | interrupt_handler | +| -------- | ---------------------- | ----------------------------------------------- | +| 0 | calls `println!` |   | +| 1 | `print` locks `WRITER` |   | +| 2 | | **interrupt occurs**, handler begins to run | +| 3 | | calls `println!` | +| 4 | | `print` tries to lock `WRITER` (already locked) | +| 5 | | `print` tries to lock `WRITER` (already locked) | +| … | | … | +| _never_ | _unlock `WRITER`_ | The `WRITER` is locked, so the interrupt handler waits until it becomes free. But this never happens, because the `_start` function only continues to run after the interrupt handler returns. Thus, the entire system hangs. @@ -640,7 +640,7 @@ extern "x86-interrupt" fn keyboard_interrupt_handler( The above code translates keypresses of the number keys 0-9 and ignores all other keys. It uses a [match] statement to assign a character or `None` to each scancode. It then uses [`if let`] to destructure the optional `key`. By using the same variable name `key` in the pattern, we [shadow] the previous declaration, which is a common pattern for destructuring `Option` types in Rust. [match]: https://doc.rust-lang.org/book/ch06-02-match.html -[`if let`]: https://doc.rust-lang.org/book/ch18-01-all-the-places-for-patterns.html#conditional-if-let-expressions +[`if let`]: https://doc.rust-lang.org/book/ch19-01-all-the-places-for-patterns.html#conditional-if-let-expressions [shadow]: https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing Now we can write numbers: diff --git a/blog/content/edition-2/posts/07-hardware-interrupts/index.pt-BR.md b/blog/content/edition-2/posts/07-hardware-interrupts/index.pt-BR.md index 69377eb5..35aee20b 100644 --- a/blog/content/edition-2/posts/07-hardware-interrupts/index.pt-BR.md +++ b/blog/content/edition-2/posts/07-hardware-interrupts/index.pt-BR.md @@ -280,16 +280,16 @@ pub fn _print(args: fmt::Arguments) { Ela trava o `WRITER`, chama `write_fmt` nele, e implicitamente o destrava no final da função. Agora imagine que uma interrupção ocorre enquanto o `WRITER` está travado e o manipulador de interrupção tenta imprimir algo também: -Passo de Tempo | _start | interrupt_handler ----------|------|------------------ -0 | chama `println!` |   -1 | `print` trava `WRITER` |   -2 | | **interrupção ocorre**, manipulador começa a executar -3 | | chama `println!` | -4 | | `print` tenta travar `WRITER` (já travado) -5 | | `print` tenta travar `WRITER` (já travado) -… | | … -_nunca_ | _destravar `WRITER`_ | +| Passo de Tempo | _start | interrupt_handler | +| -------------- | ---------------------- | ----------------------------------------------------- | +| 0 | chama `println!` |   | +| 1 | `print` trava `WRITER` |   | +| 2 | | **interrupção ocorre**, manipulador começa a executar | +| 3 | | chama `println!` | +| 4 | | `print` tenta travar `WRITER` (já travado) | +| 5 | | `print` tenta travar `WRITER` (já travado) | +| … | | … | +| _nunca_ | _destravar `WRITER`_ | O `WRITER` está travado, então o manipulador de interrupção espera até que se torne livre. Mas isso nunca acontece, porque a função `_start` só continua a executar após o manipulador de interrupção retornar. Assim, o sistema inteiro trava. @@ -646,7 +646,7 @@ extern "x86-interrupt" fn keyboard_interrupt_handler( O código acima traduz pressionamentos das teclas numéricas 0-9 e ignora todas as outras teclas. Ele usa uma instrução [match] para atribuir um caractere ou `None` a cada scancode. Então usa [`if let`] para desestruturar o `key` opcional. Ao usar o mesmo nome de variável `key` no padrão, [sombreamos] a declaração anterior, que é um padrão comum para desestruturar tipos `Option` em Rust. [match]: https://doc.rust-lang.org/book/ch06-02-match.html -[`if let`]: https://doc.rust-lang.org/book/ch18-01-all-the-places-for-patterns.html#conditional-if-let-expressions +[`if let`]: https://doc.rust-lang.org/book/ch19-01-all-the-places-for-patterns.html#conditional-if-let-expressions [sombreamos]: https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing Agora podemos escrever números: @@ -737,4 +737,4 @@ Agora somos capazes de interagir com nosso kernel e temos alguns blocos fundamen Interrupções de timer são essenciais para um sistema operacional porque fornecem uma forma de interromper periodicamente o processo em execução e deixar o kernel retomar o controle. O kernel pode então mudar para um processo diferente e criar a ilusão de múltiplos processos executando em paralelo. -Mas antes de podermos criar processos ou threads, precisamos de uma forma de alocar memória para eles. As próximas postagens explorarão gerenciamento de memória para fornecer este bloco fundamental. \ No newline at end of file +Mas antes de podermos criar processos ou threads, precisamos de uma forma de alocar memória para eles. As próximas postagens explorarão gerenciamento de memória para fornecer este bloco fundamental. diff --git a/blog/content/edition-2/posts/07-hardware-interrupts/index.zh-CN.md b/blog/content/edition-2/posts/07-hardware-interrupts/index.zh-CN.md index 4e298ec1..17624caa 100644 --- a/blog/content/edition-2/posts/07-hardware-interrupts/index.zh-CN.md +++ b/blog/content/edition-2/posts/07-hardware-interrupts/index.zh-CN.md @@ -646,7 +646,7 @@ extern "x86-interrupt" fn keyboard_interrupt_handler( 以上代码可以对数字按键0-9进行转义,并忽略其他键位。具体到程序逻辑中,就是使用 [match] 匹配映射数字0-9,对于其他扫描码则返回 `None`,然后使用 [`if let`] 语句对 `key` 进行解构取值,在这个语法中,代码块中的 `key` 会 [遮蔽][shadow] 掉代码块外的同名 `Option` 型变量。 [match]: https://doc.rust-lang.org/book/ch06-02-match.html -[`if let`]: https://doc.rust-lang.org/book/ch18-01-all-the-places-for-patterns.html#conditional-if-let-expressions +[`if let`]: https://doc.rust-lang.org/book/ch19-01-all-the-places-for-patterns.html#conditional-if-let-expressions [shadow]: https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing 现在我们就可以向控制台写入数字了: diff --git a/blog/content/edition-2/posts/10-heap-allocation/index.es.md b/blog/content/edition-2/posts/10-heap-allocation/index.es.md index 31f53eca..e4b4b953 100644 --- a/blog/content/edition-2/posts/10-heap-allocation/index.es.md +++ b/blog/content/edition-2/posts/10-heap-allocation/index.es.md @@ -96,7 +96,7 @@ Pasemos por un ejemplo: Aquí la función `inner` utiliza memoria del heap en lugar de variables estáticas para almacenar `z`. Primero asigna un bloque de memoria del tamaño requerido, que devuelve un `*mut u32` [puntero bruto]. Luego usa el método [`ptr::write`] para escribir el arreglo `[1,2,3]` en él. En el último paso, utiliza la función [`offset`] para calcular un puntero al elemento `i`-ésimo y luego lo devuelve. (Nota que omitimos algunos casts requeridos y bloques unsafe en esta función de ejemplo por brevedad.) -[puntero bruto]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer +[puntero bruto]: https://doc.rust-lang.org/book/ch20-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 diff --git a/blog/content/edition-2/posts/10-heap-allocation/index.md b/blog/content/edition-2/posts/10-heap-allocation/index.md index a1ab5dd5..9bc31642 100644 --- a/blog/content/edition-2/posts/10-heap-allocation/index.md +++ b/blog/content/edition-2/posts/10-heap-allocation/index.md @@ -93,7 +93,7 @@ Let's go through an example: Here the `inner` function uses heap memory instead of static variables for storing `z`. It first allocates a memory block of the required size, which returns a `*mut u32` [raw pointer]. It then uses the [`ptr::write`] method to write the array `[1,2,3]` to it. In the last step, it uses the [`offset`] function to calculate a pointer to the `i`-th element and then returns it. (Note that we omitted some required casts and unsafe blocks in this example function for brevity.) -[raw pointer]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer +[raw pointer]: https://doc.rust-lang.org/book/ch20-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 diff --git a/blog/content/edition-2/posts/10-heap-allocation/index.pt-BR.md b/blog/content/edition-2/posts/10-heap-allocation/index.pt-BR.md index 7c9ea490..bc5ac82d 100644 --- a/blog/content/edition-2/posts/10-heap-allocation/index.pt-BR.md +++ b/blog/content/edition-2/posts/10-heap-allocation/index.pt-BR.md @@ -97,7 +97,7 @@ Vamos passar por um exemplo: Aqui a função `inner` usa memória heap em vez de variáveis estáticas para armazenar `z`. Primeiro ela aloca um bloco de memória do tamanho necessário, que retorna um [ponteiro bruto] `*mut u32`. Em seguida, usa o método [`ptr::write`] para escrever o array `[1,2,3]` nele. No último passo, usa a função [`offset`] para calcular um ponteiro para o `i`-ésimo elemento e então o retorna. (Note que omitimos alguns casts e blocos unsafe necessários nesta função de exemplo por brevidade.) -[ponteiro bruto]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer +[ponteiro bruto]: https://doc.rust-lang.org/book/ch20-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 @@ -766,4 +766,4 @@ Finalmente, adicionamos uma dependência na crate `linked_list_allocator` para a ## O que vem a seguir? -Embora já tenhamos adicionado suporte para alocação heap neste post, deixamos a maior parte do trabalho para a crate `linked_list_allocator`. O próximo post mostrará em detalhes como um alocador pode ser implementado do zero. Ele apresentará múltiplos designs de alocador possíveis, mostrará como implementar versões simples deles e explicará suas vantagens e desvantagens. \ No newline at end of file +Embora já tenhamos adicionado suporte para alocação heap neste post, deixamos a maior parte do trabalho para a crate `linked_list_allocator`. O próximo post mostrará em detalhes como um alocador pode ser implementado do zero. Ele apresentará múltiplos designs de alocador possíveis, mostrará como implementar versões simples deles e explicará suas vantagens e desvantagens. diff --git a/blog/content/edition-2/posts/10-heap-allocation/index.zh-CN.md b/blog/content/edition-2/posts/10-heap-allocation/index.zh-CN.md index b15eba41..53daf7b5 100644 --- a/blog/content/edition-2/posts/10-heap-allocation/index.zh-CN.md +++ b/blog/content/edition-2/posts/10-heap-allocation/index.zh-CN.md @@ -96,7 +96,7 @@ fn inner(i: usize) -> &'static u32 { 在此,`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 +[raw pointer]: https://doc.rust-lang.org/book/ch20-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 @@ -595,9 +595,9 @@ reference count is 1 now](qemu-alloc-showcase.png) [`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.htmlLinkedList.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.htmlBinaryHeap.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 diff --git a/blog/content/edition-2/posts/11-allocator-designs/index.es.md b/blog/content/edition-2/posts/11-allocator-designs/index.es.md index 58d70abf..c55dd4ec 100644 --- a/blog/content/edition-2/posts/11-allocator-designs/index.es.md +++ b/blog/content/edition-2/posts/11-allocator-designs/index.es.md @@ -636,7 +636,7 @@ impl LinkedListAllocator { El método utiliza una variable `current` y un [`while let`] bucle para iterar sobre los elementos de la lista. Al principio, `current` se establece en el nodo (ficticio) `head`. En cada iteración, luego se actualiza al campo `next` del nodo actual (en el bloque `else`). Si la región es adecuada para una asignación con el tamaño y alineación dados, se elimina la región de la lista y se devuelve junto con la dirección de `alloc_start`. -[`while let`]: https://doc.rust-lang.org/reference/expressions/loop-expr.html#predicate-pattern-loops +[`while let`]: https://doc.rust-lang.org/reference/expressions/loop-expr.html#while-let-patterns Cuando el puntero `current.next` se convierte en `None`, el bucle sale. Esto significa que hemos iterado sobre toda la lista, pero no se encontró ninguna región adecuada para una asignación. En ese caso, devolvemos `None`. Si una región es adecuada, es comprobada por la función `alloc_from_region`, cuya implementación se mostrará en un momento. diff --git a/blog/content/edition-2/posts/11-allocator-designs/index.ja.md b/blog/content/edition-2/posts/11-allocator-designs/index.ja.md index cd8df401..508f37ab 100644 --- a/blog/content/edition-2/posts/11-allocator-designs/index.ja.md +++ b/blog/content/edition-2/posts/11-allocator-designs/index.ja.md @@ -642,7 +642,7 @@ impl LinkedListAllocator { このメソッドは`current`変数と`while let`ループを使ってリストの各要素に関して反復を行っています。はじめ、`current`は(ダミーの)`head`ノードに設定されています。繰り返しごとに(`else`ブロックで)これは現在のノードの`next`フィールドへと更新されます。領域が与えられたサイズとアラインメントの割り当てに適しているなら、その領域がリストから取り除かれて`alloc_start`アドレスとともに返されます。 -[`while let` loop]: https://doc.rust-lang.org/reference/expressions/loop-expr.html#predicate-pattern-loops +[`while let` loop]: https://doc.rust-lang.org/reference/expressions/loop-expr.html#while-let-patterns `current.next`ポインタが`None`になった場合、ループから抜けます。これは、リスト全体を反復したものの割り当てに適した領域が見つからなかったことを意味します。その場合`None`を返します。領域が適しているか否かは`alloc_from_region`によってチェックされていますが、この関数の実装はすぐに示します。 diff --git a/blog/content/edition-2/posts/11-allocator-designs/index.md b/blog/content/edition-2/posts/11-allocator-designs/index.md index 7429a8ef..2d5bf4f9 100644 --- a/blog/content/edition-2/posts/11-allocator-designs/index.md +++ b/blog/content/edition-2/posts/11-allocator-designs/index.md @@ -641,7 +641,7 @@ impl LinkedListAllocator { The method uses a `current` variable and a [`while let` loop] to iterate over the list elements. At the beginning, `current` is set to the (dummy) `head` node. On each iteration, it is then updated to the `next` field of the current node (in the `else` block). If the region is suitable for an allocation with the given size and alignment, the region is removed from the list and returned together with the `alloc_start` address. -[`while let` loop]: https://doc.rust-lang.org/reference/expressions/loop-expr.html#predicate-pattern-loops +[`while let` loop]: https://doc.rust-lang.org/reference/expressions/loop-expr.html#while-let-patterns When the `current.next` pointer becomes `None`, the loop exits. This means we iterated over the whole list but found no region suitable for an allocation. In that case, we return `None`. Whether a region is suitable is checked by the `alloc_from_region` function, whose implementation will be shown in a moment. diff --git a/blog/content/edition-2/posts/11-allocator-designs/index.pt-BR.md b/blog/content/edition-2/posts/11-allocator-designs/index.pt-BR.md index a6ea9bab..a929cd12 100644 --- a/blog/content/edition-2/posts/11-allocator-designs/index.pt-BR.md +++ b/blog/content/edition-2/posts/11-allocator-designs/index.pt-BR.md @@ -647,7 +647,7 @@ impl LinkedListAllocator { O método usa uma variável `current` e um [loop `while let`] para iterar sobre os elementos da lista. No início, `current` é definido como o nó `head` (dummy). Em cada iteração, ele é então atualizado para o campo `next` do nó atual (no bloco `else`). Se a região for adequada para uma alocação com o tamanho e alinhamento fornecidos, a região é removida da lista e retornada junto com o endereço `alloc_start`. -[loop `while let`]: https://doc.rust-lang.org/reference/expressions/loop-expr.html#predicate-pattern-loops +[loop `while let`]: https://doc.rust-lang.org/reference/expressions/loop-expr.html#while-let-patterns Quando o ponteiro `current.next` se torna `None`, o loop sai. Isso significa que iteramos sobre toda a lista mas não encontramos nenhuma região adequada para uma alocação. Nesse caso, retornamos `None`. Se uma região é adequada é verificado pela função `alloc_from_region`, cuja implementação será mostrada em um momento. @@ -1251,4 +1251,4 @@ Com este post, concluímos nossa implementação de gerenciamento de memória po [_threads_]: https://en.wikipedia.org/wiki/Thread_(computing) [_processos_]: https://en.wikipedia.org/wiki/Process_(computing) [_multiprocessamento_]: https://en.wikipedia.org/wiki/Multiprocessing -[_async/await_]: https://rust-lang.github.io/async-book/01_getting_started/04_async_await_primer.html \ No newline at end of file +[_async/await_]: https://rust-lang.github.io/async-book/01_getting_started/04_async_await_primer.html diff --git a/blog/content/edition-2/posts/11-allocator-designs/index.zh-CN.md b/blog/content/edition-2/posts/11-allocator-designs/index.zh-CN.md index 5aecbae3..bc01e7f8 100644 --- a/blog/content/edition-2/posts/11-allocator-designs/index.zh-CN.md +++ b/blog/content/edition-2/posts/11-allocator-designs/index.zh-CN.md @@ -672,7 +672,7 @@ impl LinkedListAllocator { 此方法使用一个 `current` 变量和一个 [`while let` 循环] 来遍历链表元素。在开始时,`current` 被设置为(虚拟)`head` 节点。在每次迭代中,它都会被更新为当前节点的 `next` 字段(在 `else` 块中)。如果该区域适用于给定大小和对齐方式的分配,该区域会从链表中移除并与 `alloc_start` 地址一起返回。 -[`while let` loop]: https://doc.rust-lang.org/reference/expressions/loop-expr.html#predicate-pattern-loops +[`while let` loop]: https://doc.rust-lang.org/reference/expressions/loop-expr.html#while-let-patterns 当 `current.next` 指针变成 `None` 时,循环退出。这意味着我们遍历了整个链表,但没有找到合适的区域进行分配。在这种情况下,我们返回 `None`。内存区域是否合适是由 `alloc_from_region` 函数检查的,它的实现将在稍后展示。 diff --git a/blog/content/edition-2/posts/12-async-await/index.es.md b/blog/content/edition-2/posts/12-async-await/index.es.md index 9c8ddf6c..0c7b1ef9 100644 --- a/blog/content/edition-2/posts/12-async-await/index.es.md +++ b/blog/content/edition-2/posts/12-async-await/index.es.md @@ -125,7 +125,7 @@ pub trait Future { } ``` -El tipo [asociado](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#specifying-placeholder-types-in-trait-definitions-with-associated-types) `Output` especifica el tipo del valor asíncrono. Por ejemplo, la función `async_read_file` en el diagrama anterior devolvería una instancia de `Future` con `Output` configurado a `File`. +El tipo [asociado](https://doc.rust-lang.org/book/ch20-02-advanced-traits.html#associated-types) `Output` especifica el tipo del valor asíncrono. Por ejemplo, la función `async_read_file` en el diagrama anterior devolvería una instancia de `Future` con `Output` configurado a `File`. El método [`poll`] permite comprobar si el valor ya está disponible. Devuelve un enum [`Poll`], que se ve de la siguiente manera: @@ -807,7 +807,7 @@ La estructura `Task` es un envoltorio nuevo alrededor de un futuro pinzado, asig - Como aprendimos en la [sección sobre pinning], el tipo `Pin` asegura que un valor no puede moverse en memoria al colocarlo en el heap y prevenir la creación de referencias `&mut` a él. Esto es importante porque los futuros generados por async/await podrían ser auto-referenciales, es decir, contener punteros a sí mismos que se invalidarían cuando el futuro se moviera. [_trait object_]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html -[_despachados dinámicamente_]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#trait-objects-perform-dynamic-dispatch +[_despachados dinámicamente_]: https://doc.rust-lang.org/book/ch18-02-trait-objects.html#trait-objects-perform-dynamic-dispatch [sección sobre pinning]: #pinning Para permitir la creación de nuevas estructuras `Task` a partir de futuros, creamos una función `new`: 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 7aa7761a..33cb790a 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 @@ -130,7 +130,7 @@ pub trait Future { [関連型] `Output` は非同期値の型を指定します。例えば、上の図の `async_read_file` 関数は、`Output` を `File` に設定した `Future` インスタンスを返します。 -[関連型]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#specifying-placeholder-types-in-trait-definitions-with-associated-types +[関連型]: https://doc.rust-lang.org/book/ch20-02-advanced-traits.html#associated-types [`poll`]メソッドは、その値がすでに利用可能かどうかをチェックすることができます。このメソッドは、以下のような [`Poll`] 列挙体を返します。 @@ -818,7 +818,7 @@ pub struct Task { - [pinningについて]で学んだように、`Pin` 型は、値をheap上に配置し、その値への `&mut` 参照の作成を防ぐことで、メモリ内で値が移動できないようにします。これは、async/awaitによって生成されたfutureが自己参照構造体である可能性があるため、重要です。つまり、futureが移動されたときに無効になるような自分自身へのポインタを含む可能性があります。 [_trait object_]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html -[_dynamically dispatched_]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#trait-objects-perform-dynamic-dispatch +[_dynamically dispatched_]: https://doc.rust-lang.org/book/ch18-02-trait-objects.html#trait-objects-perform-dynamic-dispatch [pinningについて]: #pinliu-me future から新しい `Task` 構造体を作成できるように、`new` 関数を作成します: 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 4ff78197..ae11ec72 100644 --- a/blog/content/edition-2/posts/12-async-await/index.md +++ b/blog/content/edition-2/posts/12-async-await/index.md @@ -125,7 +125,7 @@ pub trait Future { The [associated type] `Output` specifies the type of the asynchronous value. For example, the `async_read_file` function in the diagram above would return a `Future` instance with `Output` set to `File`. -[associated type]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#specifying-placeholder-types-in-trait-definitions-with-associated-types +[associated type]: https://doc.rust-lang.org/book/ch20-02-advanced-traits.html#associated-types The [`poll`] method allows to check if the value is already available. It returns a [`Poll`] enum, which looks like this: @@ -811,7 +811,7 @@ The `Task` struct is a newtype wrapper around a pinned, heap-allocated, and dyna - As we learned in the [section about pinning], the `Pin` type ensures that a value cannot be moved in memory by placing it on the heap and preventing the creation of `&mut` references to it. This is important because futures generated by async/await might be self-referential, i.e., contain pointers to themselves that would be invalidated when the future is moved. [_trait object_]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html -[_dynamically dispatched_]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#trait-objects-perform-dynamic-dispatch +[_dynamically dispatched_]: https://doc.rust-lang.org/book/ch18-02-trait-objects.html#trait-objects-perform-dynamic-dispatch [section about pinning]: #pinning To allow the creation of new `Task` structs from futures, we create a `new` function: @@ -1578,7 +1578,7 @@ Let's look into some of the implementation details of this `run_ready_tasks` met - To avoid the performance overhead of creating a waker on each poll, we use the `waker_cache` map to store the waker for each task after it has been created. For this, we use the [`BTreeMap::entry`] method in combination with [`Entry::or_insert_with`] to create a new waker if it doesn't exist yet and then get a mutable reference to it. For creating a new waker, we clone the `task_queue` and pass it together with the task ID to the `TaskWaker::new` function (implementation shown below). Since the `task_queue` is wrapped into an `Arc`, the `clone` only increases the reference count of the value, but still points to the same heap-allocated queue. Note that reusing wakers like this is not possible for all waker implementations, but our `TaskWaker` type will allow it. -[_destructuring_]: https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html#destructuring-to-break-apart-values +[_destructuring_]: https://doc.rust-lang.org/book/ch19-03-pattern-syntax.html#destructuring-to-break-apart-values [RFC 2229]: https://github.com/rust-lang/rfcs/pull/2229 [RFC 2229 impl]: https://github.com/rust-lang/rust/issues/53488 diff --git a/blog/content/edition-2/posts/12-async-await/index.pt-BR.md b/blog/content/edition-2/posts/12-async-await/index.pt-BR.md index 562d267b..950eea96 100644 --- a/blog/content/edition-2/posts/12-async-await/index.pt-BR.md +++ b/blog/content/edition-2/posts/12-async-await/index.pt-BR.md @@ -129,7 +129,7 @@ pub trait Future { O [tipo associado] `Output` especifica o tipo do valor assíncrono. Por exemplo, a função `async_read_file` no diagrama acima retornaria uma instância `Future` com `Output` definido como `File`. -[tipo associado]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#specifying-placeholder-types-in-trait-definitions-with-associated-types +[tipo associado]: https://doc.rust-lang.org/book/ch20-02-advanced-traits.html#associated-types O método [`poll`] permite verificar se o valor já está disponível. Ele retorna um enum [`Poll`], que se parece com isto: @@ -816,7 +816,7 @@ A struct `Task` é um tipo newtype wrapper em torno de uma future fixada, alocad - Como aprendemos na [seção sobre pinning], o tipo `Pin` garante que um valor não pode ser movido na memória colocando-o no heap e impedindo a criação de referências `&mut` a ele. Isso é importante porque futures gerados por async/await podem ser auto-referenciais, ou seja, conter ponteiros para si mesmos que seriam invalidados quando a future é movida. [_trait object_]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html -[_dinamicamente despachados_]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#trait-objects-perform-dynamic-dispatch +[_dinamicamente despachados_]: https://doc.rust-lang.org/book/ch18-02-trait-objects.html#trait-objects-perform-dynamic-dispatch [seção sobre pinning]: #pinning Para permitir a criação de novas structs `Task` a partir de futures, criamos uma função `new`: @@ -1586,7 +1586,7 @@ Nota do tradutor ([Richard Alves](https://github.com/richarddalves)): Na data de - Para evitar a sobrecarga de desempenho de criar um waker em cada poll, usamos o mapa `waker_cache` para armazenar o waker para cada tarefa após ter sido criado. Para isso, usamos o método [`BTreeMap::entry`] em combinação com [`Entry::or_insert_with`] para criar um novo waker se ele ainda não existir e então obter uma referência mutável a ele. Para criar um novo waker, clonamos a `task_queue` e a passamos junto com o ID da tarefa para a função `TaskWaker::new` (implementação mostrada abaixo). Como a `task_queue` está encapsulada em um `Arc`, o `clone` apenas incrementa a contagem de referência do valor, mas ainda aponta para a mesma fila alocada em heap. Note que reutilizar wakers assim não é possível para todas as implementações de waker, mas nosso tipo `TaskWaker` permitirá isso. -[_desestruturação_]: https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html#destructuring-to-break-apart-values +[_desestruturação_]: https://doc.rust-lang.org/book/ch19-03-pattern-syntax.html#destructuring-to-break-apart-values [RFC 2229]: https://github.com/rust-lang/rfcs/pull/2229 [RFC 2229 impl]: https://github.com/rust-lang/rust/issues/53488 @@ -1824,4 +1824,4 @@ Para utilizar as notificações de waker da tarefa de teclado, criamos um novo t Usando async/await, agora temos suporte básico para multitarefa cooperativa em nosso kernel. Embora multitarefa cooperativa seja muito eficiente, ela leva a problemas de latência quando tarefas individuais continuam executando por muito tempo, impedindo assim outras tarefas de executar. Por esta razão, faz sentido também adicionar suporte para multitarefa preemptiva ao nosso kernel. -No próximo post, introduziremos _threads_ como a forma mais comum de multitarefa preemptiva. Além de resolver o problema de tarefas de longa execução, threads também nos prepararão para utilizar múltiplos núcleos de CPU e executar programas de usuário não confiáveis no futuro. \ No newline at end of file +No próximo post, introduziremos _threads_ como a forma mais comum de multitarefa preemptiva. Além de resolver o problema de tarefas de longa execução, threads também nos prepararão para utilizar múltiplos núcleos de CPU e executar programas de usuário não confiáveis no futuro. diff --git a/blog/content/edition-2/posts/12-async-await/index.ru.md b/blog/content/edition-2/posts/12-async-await/index.ru.md index 051c9be3..9bc40eca 100644 --- a/blog/content/edition-2/posts/12-async-await/index.ru.md +++ b/blog/content/edition-2/posts/12-async-await/index.ru.md @@ -131,7 +131,7 @@ pub trait Future { [Ассоциированный тип][associated type] `Output` определяет тип асинхронного значения. Например, функция `async_read_file` на приведенной выше диаграмме вернет экземпляр `Future` с `Output`, установленным как `File`. -[associated type]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#specifying-placeholder-types-in-trait-definitions-with-associated-types +[associated type]: https://doc.rust-lang.org/book/ch20-02-advanced-traits.html#associated-types Метод [`poll`] позволяет проверить, доступно ли значение. Он возвращает перечисление [`Poll`], которое выглядит следующим образом: @@ -829,7 +829,7 @@ pub struct Task { - Как мы узнали в [разделе о закреплении], тип `Pin` обеспечивает, что значение не может быть перемещено в памяти, помещая его в кучу и предотвращая создание `&mut` ссылок на него. Это важно, потому что футуры, генерируемые async/await, могут быть самоссылающимися, т.е. содержать указатели на себя, которые станут недействительными, если футура будет перемещена. [_trait object_]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html -[_dynamically dispatched_]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#trait-objects-perform-dynamic-dispatch +[_dynamically dispatched_]: https://doc.rust-lang.org/book/ch18-02-trait-objects.html#trait-objects-perform-dynamic-dispatch [разделе о закреплении]: #pinning Чтобы разрешить создание новых структур `Task` из футур, мы создаём функцию `new`: @@ -1599,7 +1599,7 @@ impl Executor { - Чтобы избежать накладных расходов на создание waker при каждом опросе, мы используем дерево `waker_cache` для хранения waker для каждой задачи после ее создания. Для этого мы используем метод [`BTreeMap::entry`] в сочетании с [`Entry::or_insert_with`] для создания нового waker, если он ещё не существует, а затем получаем на него мутабельную ссылку. Для создания нового waker мы клонируем `task_queue` и передаём его вместе с идентификатором задачи в функцию `TaskWaker::new` (реализация ниже). Поскольку `task_queue` обёрнута в `Arc`, `clone` только увеличивает счётчик ссылок на значение, но всё равно указывает на ту же выделенную в куче очередь. Обратите внимание, что повторное использование wakers таким образом, невозможно для всех реализаций waker, но наш тип `TaskWaker` это позволит. -[_destructuring_]: https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html#destructuring-to-break-apart-values +[_destructuring_]: https://doc.rust-lang.org/book/ch19-03-pattern-syntax.html#destructuring-to-break-apart-values [RFC 2229]: https://github.com/rust-lang/rfcs/pull/2229 [RFC 2229 impl]: https://github.com/rust-lang/rust/issues/53488 diff --git a/blog/content/edition-2/posts/12-async-await/index.zh-CN.md b/blog/content/edition-2/posts/12-async-await/index.zh-CN.md index 9749365b..571a6403 100644 --- a/blog/content/edition-2/posts/12-async-await/index.zh-CN.md +++ b/blog/content/edition-2/posts/12-async-await/index.zh-CN.md @@ -138,7 +138,7 @@ pub trait Future { [关联类型][associated type] `Output` 用于指定异步值的类型。例如, 上图中的 `async_read_file` 函数将返回一个 `Future` 实例,其 `Output` 被设置为 `File` 。 -[associated type]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#specifying-placeholder-types-in-trait-definitions-with-associated-types +[associated type]: https://doc.rust-lang.org/book/ch20-02-advanced-traits.html#associated-types [`poll`] 方法可用于检查值是否已就绪。它返回一个 [`Poll`] 枚举,其定义如下: @@ -826,7 +826,7 @@ pub struct Task { * 正如我们在 [固定 相关章节][section about pinning] 中学到的, `Pin` 类型通过将值放在堆上并组织创建 `&mut` 引用来确保它不会在内存中被移动。这一点很重要,因为由 async/await 生成的 future 可能是自引用的。也就是说会包含指向自己的指针,这些指针会在 future 移动过程中失效。 [_trait object_]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html -[_dynamically dispatched_]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#trait-objects-perform-dynamic-dispatch +[_dynamically dispatched_]: https://doc.rust-lang.org/book/ch18-02-trait-objects.html#trait-objects-perform-dynamic-dispatch [section about pinning]: #pinning 为了从 future 创建新的 `Task` 结构体,我们创建了一个 `new` 函数: @@ -1593,7 +1593,7 @@ impl Executor { * 对于每个弹出的任务 ID,我们从 `tasks` map 中获取对应任务的可变引用。由于我们的 `ScancodeStream` 实现在检查任务是否需要进入休眠状态前会先注册唤醒器,可能会出现一个已不存在的任务被唤醒的情况。这种情况下,我们只需忽略这次唤醒并继续处理队列里的下一个 ID。 * 为了避免每次轮询时创建唤醒器带来的性能开销,我们使用了 `waker_cache` map 用于存储每个任务创建后对应的唤醒器。为此,我们使用 [`BTreeMap::entry`] 方法结合 [`Entry::or_insert_with`] ,来在唤醒器不存在时创建新实例,然后获取其可变引用。为了创建新的唤醒器,我们克隆 `task_queue` 并将其与任务 ID 一同传递给 `TaskWaker::new` 函数(具体实现如下所示)。由于 `task_queue` 被封装在 `Arc` 中,克隆操作仅会增加该值的引用计数,但仍指向同一个堆分配的队列。请注意,并非所有唤醒器的实现都能像这样重复使用,不过我们的 `TaskWaker` 类型可以做到。 -[_destructuring_]: https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html#destructuring-to-break-apart-values +[_destructuring_]: https://doc.rust-lang.org/book/ch19-03-pattern-syntax.html#destructuring-to-break-apart-values [RFC 2229]: https://github.com/rust-lang/rfcs/pull/2229 [RFC 2229 impl]: https://github.com/rust-lang/rust/issues/53488 diff --git a/blog/content/edition-2/posts/12-async-await/index.zh-TW.md b/blog/content/edition-2/posts/12-async-await/index.zh-TW.md index e50491ce..0f9b9094 100644 --- a/blog/content/edition-2/posts/12-async-await/index.zh-TW.md +++ b/blog/content/edition-2/posts/12-async-await/index.zh-TW.md @@ -160,7 +160,7 @@ pub trait Future { 那個 關聯類型 [`associated type`] `Output` 指定了異步值的類型。 例如,上面的示例中的 `async_read_file` 函數將返回一個 `Output` 設置為 `File` 的 `Future` 實例。 -[associated type]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#specifying-placeholder-types-in-trait-definitions-with-associated-types +[associated type]: https://doc.rust-lang.org/book/ch20-02-advanced-traits.html#associated-types 那個 [`poll`] 方法允許檢查值是否已經可用。它返回一個 [`Poll`] 枚舉,看起來像這樣: @@ -966,7 +966,7 @@ pub struct Task { - 正如我們在 釘住操作 部分 [section about pinning] 學到的,`Pin` 類型通過將值放在堆上並防止創建對它的 `&mut` 引用來確保值在內存中不會被移動。這一點很重要,因為由 async/await 生成的 future 可能是自引用的,即包含指向自己的指針,當 future 被移動時這些指針將失效。 [_trait object_]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html -[_dynamically dispatched_]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#trait-objects-perform-dynamic-dispatch +[_dynamically dispatched_]: https://doc.rust-lang.org/book/ch18-02-trait-objects.html#trait-objects-perform-dynamic-dispatch [section about pinning]: #pinning 爲允許從 future 創建新的 `Task` 結構體,我們創建一個 `new` 函數: @@ -1836,7 +1836,7 @@ impl Executor { - 爲避免在每次輪詢時創建喚醒器的性能開銷,我們使用 `waker_cache` map 來存儲每個任務的喚醒器。爲此,我們使用了 [`BTreeMap::entry`] 方法和 [`Entry::or_insert_with`] 來在它不存在時創建一個新的喚醒器,然後獲取一個對它的可變引用。爲創建一個新的喚醒器,我們克隆了 `task_queue`,並將它與任務 ID 一起傳遞給 `TaskWaker::new` 函數(下面展示了實現)。由於 `task_queue` 被包裝在一個 `Arc` 中,`clone` 只增加了值的引用計數,但仍然指向同一個堆分配的隊列。請注意,像這樣重用喚醒器對於所有的喚醒器實現來說都是不可能的,但我們的 `TaskWaker` 類型將允許它。 -[_destructuring_]: https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html#destructuring-to-break-apart-values +[_destructuring_]: https://doc.rust-lang.org/book/ch19-03-pattern-syntax.html#destructuring-to-break-apart-values [RFC 2229]: https://github.com/rust-lang/rfcs/pull/2229 [RFC 2229 impl]: https://github.com/rust-lang/rust/issues/53488 diff --git a/blog/content/edition-2/posts/deprecated/05-integration-tests/index.md b/blog/content/edition-2/posts/deprecated/05-integration-tests/index.md index 900ec34d..f1cf4418 100644 --- a/blog/content/edition-2/posts/deprecated/05-integration-tests/index.md +++ b/blog/content/edition-2/posts/deprecated/05-integration-tests/index.md @@ -55,7 +55,7 @@ Such an automated test framework needs to know whether a test succeeded or faile The chips implementing a serial interface are called [UARTs]. There are [lots of UART models] on x86, but fortunately the only differences between them are some advanced features we don't need. The common UARTs today are all compatible to the [16550 UART], so we will use that model for our testing framework. [UARTs]: https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter -[lots of UART models]: https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter#UART_models +[lots of UART models]: https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter#Models [16550 UART]: https://en.wikipedia.org/wiki/16550_UART ### Port I/O diff --git a/blog/content/edition-2/posts/deprecated/10-advanced-paging/index.md b/blog/content/edition-2/posts/deprecated/10-advanced-paging/index.md index 2009dadf..d92d4a7f 100644 --- a/blog/content/edition-2/posts/deprecated/10-advanced-paging/index.md +++ b/blog/content/edition-2/posts/deprecated/10-advanced-paging/index.md @@ -143,13 +143,13 @@ We can now calculate virtual addresses for the page tables of all four levels. W The table below summarizes the address structure for accessing the different kinds of frames: -Virtual Address for | Address Structure ([octal]) -------------------- | ------------------------------- -Page | `0o_SSSSSS_AAA_BBB_CCC_DDD_EEEE` -Level 1 Table Entry | `0o_SSSSSS_RRR_AAA_BBB_CCC_DDDD` -Level 2 Table Entry | `0o_SSSSSS_RRR_RRR_AAA_BBB_CCCC` -Level 3 Table Entry | `0o_SSSSSS_RRR_RRR_RRR_AAA_BBBB` -Level 4 Table Entry | `0o_SSSSSS_RRR_RRR_RRR_RRR_AAAA` +| Virtual Address for | Address Structure ([octal]) | +| ------------------- | -------------------------------- | +| Page | `0o_SSSSSS_AAA_BBB_CCC_DDD_EEEE` | +| Level 1 Table Entry | `0o_SSSSSS_RRR_AAA_BBB_CCC_DDDD` | +| Level 2 Table Entry | `0o_SSSSSS_RRR_RRR_AAA_BBB_CCCC` | +| Level 3 Table Entry | `0o_SSSSSS_RRR_RRR_RRR_AAA_BBBB` | +| Level 4 Table Entry | `0o_SSSSSS_RRR_RRR_RRR_RRR_AAAA` | [octal]: https://en.wikipedia.org/wiki/Octal @@ -369,7 +369,7 @@ When we run it, we see the same result as with our handcrafted translation funct Our `memory::init` function is an [unsafe function], which means that an `unsafe` block is required for calling it because the caller has to guarantee that certain requirements are met. In our case, the requirement is that the passed address is mapped to the physical frame of the level 4 page table. -[unsafe function]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#calling-an-unsafe-function-or-method +[unsafe function]: https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html#calling-an-unsafe-function-or-method The second property of unsafe functions is that their complete body is treated as an `unsafe` block, which means that it can perform all kinds of unsafe operations without additional unsafe blocks. This is the reason that we didn't need an `unsafe` block for dereferencing the raw `level_4_table_ptr`: