mirror of
https://github.com/phil-opp/blog_os.git
synced 2025-12-16 22:37:49 +00:00
@@ -179,18 +179,18 @@ test-args = ["-device", "isa-debug-exit,iobase=0xf4,iosize=0x04"]
|
||||
|
||||
به جای فراخوانی دستی دستورالعمل های اسمبلی `in` و `out`، ما از انتزاعات ارائه شده توسط کریت [`x86_64`] استفاده میکنیم. برای افزودن یک وابستگی به آن کریت، آن را به بخش `dependencies` در `Cargo.toml` اضافه میکنیم:
|
||||
|
||||
[`x86_64`]: https://docs.rs/x86_64/0.13.2/x86_64/
|
||||
[`x86_64`]: https://docs.rs/x86_64/0.14.2/x86_64/
|
||||
|
||||
```toml
|
||||
# in Cargo.toml
|
||||
|
||||
[dependencies]
|
||||
x86_64 = "0.13.2"
|
||||
x86_64 = "0.14.2"
|
||||
```
|
||||
|
||||
اکنون میتوانیم از نوع [`Port`] ارائه شده توسط کریت برای ایجاد عملکرد `exit_qemu` استفاده کنیم:
|
||||
|
||||
[`Port`]: https://docs.rs/x86_64/0.13.2/x86_64/instructions/port/struct.Port.html
|
||||
[`Port`]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/port/struct.Port.html
|
||||
|
||||
```rust
|
||||
// in src/main.rs
|
||||
|
||||
@@ -183,18 +183,18 @@ CPUと<ruby>周辺機器<rp> (</rp><rt>ペリフェラル</rt><rp>) </rp></ruby>
|
||||
|
||||
`in`と`out`のアセンブリ命令を手動で呼び出す代わりに、[`x86_64`]クレートによって提供される<ruby>abstraction<rp> (</rp><rt>抽象化されたもの</rt><rp>) </rp></ruby>を使います。このクレートへの依存を追加するため、`Cargo.toml`の`dependencies`セクションにこれを追加しましょう:
|
||||
|
||||
[`x86_64`]: https://docs.rs/x86_64/0.13.2/x86_64/
|
||||
[`x86_64`]: https://docs.rs/x86_64/0.14.2/x86_64/
|
||||
|
||||
```toml
|
||||
# in Cargo.toml
|
||||
|
||||
[dependencies]
|
||||
x86_64 = "0.13.2"
|
||||
x86_64 = "0.14.2"
|
||||
```
|
||||
|
||||
これで、このクレートによって提供される[`Port`]型を使って`exit_qemu`関数を作ることができます。
|
||||
|
||||
[`Port`]: https://docs.rs/x86_64/0.13.2/x86_64/instructions/port/struct.Port.html
|
||||
[`Port`]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/port/struct.Port.html
|
||||
|
||||
```rust
|
||||
// in src/main.rs
|
||||
|
||||
@@ -173,18 +173,18 @@ test-args = ["-device", "isa-debug-exit,iobase=0xf4,iosize=0x04"]
|
||||
这里我们使用 [`x86_64`] crate提供的抽象,而不是手动调用`in`或`out`指令。为了添加对该crate的依赖,我们可以将其添加到我们的 `Cargo.toml`中的 `dependencies` 小节中去:
|
||||
|
||||
|
||||
[`x86_64`]: https://docs.rs/x86_64/0.7.5/x86_64/
|
||||
[`x86_64`]: https://docs.rs/x86_64/0.14.2/x86_64/
|
||||
|
||||
```toml
|
||||
# in Cargo.toml
|
||||
|
||||
[dependencies]
|
||||
x86_64 = "0.11.0"
|
||||
x86_64 = "0.14.2"
|
||||
```
|
||||
|
||||
现在我们可以使用crate中提供的[`Port`] 类型来创建一个`exit_qemu` 函数了:
|
||||
|
||||
[`Port`]: https://docs.rs/x86_64/0.7.0/x86_64/instructions/port/struct.Port.html
|
||||
[`Port`]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/port/struct.Port.html
|
||||
|
||||
```rust
|
||||
// in src/main.rs
|
||||
|
||||
@@ -90,7 +90,7 @@ Bits | Name | Description
|
||||
|
||||
به جای ایجاد نوع IDT خود ، از [ساختمان `InterruptDescriptorTable`] کرت `x86_64` استفاده خواهیم کرد که به این شکل است:
|
||||
|
||||
[ساختمان `InterruptDescriptorTable`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html
|
||||
[ساختمان `InterruptDescriptorTable`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html
|
||||
|
||||
``` rust
|
||||
#[repr(C)]
|
||||
@@ -121,10 +121,10 @@ pub struct InterruptDescriptorTable {
|
||||
|
||||
فیلدها از نوع [`<idt::Entry<F`] هستند ، این ساختمانی است که فیلد های یک عنصر IDT را نشان می دهد (به جدول بالا مراجعه کنید). پارامتر نوع `F`، نوع تابع کنترل کننده مورد انتظار را تعریف می کند. می بینیم که برخی از عناصر به یک [`HandlerFunc`] و برخی دیگر به [`HandlerFuncWithErrCode`] نیاز دارند. خطای صفحه حتی نوع خاص خود را دارد: [`PageFaultHandlerFunc`].
|
||||
|
||||
[`<idt::Entry<F`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/struct.Entry.html
|
||||
[`HandlerFunc`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/type.HandlerFunc.html
|
||||
[`HandlerFuncWithErrCode`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/type.HandlerFuncWithErrCode.html
|
||||
[`PageFaultHandlerFunc`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/type.PageFaultHandlerFunc.html
|
||||
[`<idt::Entry<F`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.Entry.html
|
||||
[`HandlerFunc`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/type.HandlerFunc.html
|
||||
[`HandlerFuncWithErrCode`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/type.HandlerFuncWithErrCode.html
|
||||
[`PageFaultHandlerFunc`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/type.PageFaultHandlerFunc.html
|
||||
|
||||
بیایید ابتدا به نوع `HandlerFunc` نگاه کنیم:
|
||||
|
||||
@@ -201,7 +201,7 @@ _callee-saved_ | _caller-saved_
|
||||
|
||||
در کرت `x86_64` ، فریم پشته وقفه توسط ساختمان [`InterruptStackFrame`] نشان داده می شود. این ساختمان به عنوان `&mut` به کنترل کننده وقفه منتقل می شود و می تواند برای دریافت اطلاعات بیشتر در مورد علت استثنا استفاده شود. ساختمان بدون فیلد کد خطا است ، زیرا فقط برخی از استثناها کد خطا را پوش میکنند. این استثناها از نوع تابع جداگانه [`HandlerFuncWithErrCode`] استفاده میکنند ، که دارای یک آرگومان اضافی `error_code` است.
|
||||
|
||||
[`InterruptStackFrame`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/struct.InterruptStackFrame.html
|
||||
[`InterruptStackFrame`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.InterruptStackFrame.html
|
||||
|
||||
### پشت صحنه
|
||||
قرارداد فراخوانی `x86-interrupt` یک انتزاع قدرتمند است که تقریباً تمام جزئیات پیچیده فرآیند مدیریت استثناها را پنهان می کند. با این حال ، گاهی اوقات مفید است که بدانیم پشت پرده چه اتفاقی می افتد. در اینجا یک مرور کوتاه از مواردی که قرارداد فراخوانی `x86-interrupt` انجام میدهد را میبینید:
|
||||
@@ -283,7 +283,7 @@ error[E0658]: x86-interrupt ABI is experimental and subject to change (see issue
|
||||
برای اینکه پردازنده از جدول توصیف کننده وقفه جدید ما استفاده کند ، باید آن را با استفاده از دستورالعمل [`lidt`] بارگیری کنیم. ساختمان `InterruptDescriptorTable` از کرت ` x86_64` متد [`load`][InterruptDescriptorTable::load] را برای این کار فراهم می کند. بیایید سعی کنیم از آن استفاده کنیم:
|
||||
|
||||
[`lidt`]: https://www.felixcloutier.com/x86/lgdt:lidt
|
||||
[InterruptDescriptorTable::load]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html#method.load
|
||||
[InterruptDescriptorTable::load]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html#method.load
|
||||
|
||||
```rust
|
||||
// in src/interrupts.rs
|
||||
@@ -462,7 +462,7 @@ blog_os::interrupts::test_breakpoint_exception... [ok]
|
||||
قرارداد فراخوانی `x86-interrupt` و نوع [`InterruptDescriptorTable`] روند مدیریت استثناها را نسبتاً سر راست و بدون درد ساختهاند. اگر این برای شما بسیار جادویی بود و دوست دارید تمام جزئیات مهم مدیریت استثنا را بیاموزید، برای شما هم مطالبی داریم: مجموعه ["مدیریت استثناها با توابع برهنه"] ما، نحوه مدیریت استثناها بدون قرارداد فراخوانی`x86-interrupt` را نشان می دهد و همچنین نوع IDT خاص خود را ایجاد می کند. از نظر تاریخی، این پستها مهمترین پستهای مدیریت استثناها قبل از وجود قرارداد فراخوانی `x86-interrupt` و کرت `x86_64` بودند. توجه داشته باشید که این پستها بر اساس [نسخه اول] این وبلاگ هستند و ممکن است قدیمی باشند.
|
||||
|
||||
["مدیریت استثناها با توابع برهنه"]: @/edition-1/extra/naked-exceptions/_index.md
|
||||
[`InterruptDescriptorTable`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html
|
||||
[`InterruptDescriptorTable`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html
|
||||
[نسخه اول]: @/edition-1/_index.md
|
||||
|
||||
## مرحله بعدی چیست؟
|
||||
|
||||
@@ -88,7 +88,7 @@ u32 | 予約済 |
|
||||
## IDT型
|
||||
自前でIDTの型を作る代わりに、`x86_64`クレートの[`InterruptDescriptorTable`構造体][`InterruptDescriptorTable` struct]を使います。こんな見た目をしています:
|
||||
|
||||
[`InterruptDescriptorTable` struct]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html
|
||||
[`InterruptDescriptorTable` struct]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html
|
||||
|
||||
``` rust
|
||||
#[repr(C)]
|
||||
@@ -119,10 +119,10 @@ pub struct InterruptDescriptorTable {
|
||||
|
||||
この構造体のフィールドは[`idt::Entry<F>`]という型を持っています。これはIDTのエントリのフィールド(上の表を見てください)を表す構造体です。型パラメータ`F`は、期待されるハンドラ関数の型を表します。エントリの中には、[`HandlerFunc`]型を要求するものや、[`HandlerFuncWithErrCode`]型を要求するものがあることがわかります。ページフォルトに至っては、[`PageFaultHandlerFunc`]という自分専用の型を要求していますね。
|
||||
|
||||
[`idt::Entry<F>`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/struct.Entry.html
|
||||
[`HandlerFunc`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/type.HandlerFunc.html
|
||||
[`HandlerFuncWithErrCode`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/type.HandlerFuncWithErrCode.html
|
||||
[`PageFaultHandlerFunc`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/type.PageFaultHandlerFunc.html
|
||||
[`idt::Entry<F>`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.Entry.html
|
||||
[`HandlerFunc`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/type.HandlerFunc.html
|
||||
[`HandlerFuncWithErrCode`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/type.HandlerFuncWithErrCode.html
|
||||
[`PageFaultHandlerFunc`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/type.PageFaultHandlerFunc.html
|
||||
|
||||
まず`HandlerFunc`型を見てみましょう:
|
||||
|
||||
@@ -199,7 +199,7 @@ _callee-saved_ | _caller-saved_
|
||||
|
||||
`x86_64`クレートにおいては、割り込み時のスタックフレームは[`InterruptStackFrame`]構造体によって表現されます。これは割り込みハンドラに`&mut`として渡されるため、これを使うことで例外の原因に関して追加で情報を手に入れることができます。例外のすべてがエラーコードをプッシュするわけではないので、この構造体にはエラーコードのためのフィールドはありません。これらの例外は[`HandlerFuncWithErrCode`]という別の関数型を使いますが、これらは追加で`error_code`引数を持ちます。
|
||||
|
||||
[`InterruptStackFrame`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/struct.InterruptStackFrame.html
|
||||
[`InterruptStackFrame`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.InterruptStackFrame.html
|
||||
|
||||
### 舞台裏では何が
|
||||
`x86-interrupt`呼び出し規約は、この例外<ruby>処理<rp> (</rp><rt>ハンドル</rt><rp>) </rp></ruby>プロセスのややこしいところをほぼ全て隠蔽してくれる、強力な抽象化です。しかし、その後ろで何が起こっているのかを知っておいたほうが良いこともあるでしょう。以下に、`x86-interrupt`呼び出し規約がやってくれることを簡単なリストにして示しました。
|
||||
@@ -281,7 +281,7 @@ error[E0658]: x86-interrupt ABI is experimental and subject to change (see issue
|
||||
CPUがこの割り込みディスクリプタテーブル(IDT)を使用するためには、[`lidt`]命令を使ってこれを読み込む必要があります。`x86_64`の`InterruptDescriptorTable`構造体には、そのための[`load`][InterruptDescriptorTable::load]というメソッド関数が用意されています。それを使ってみましょう:
|
||||
|
||||
[`lidt`]: https://www.felixcloutier.com/x86/lgdt:lidt
|
||||
[InterruptDescriptorTable::load]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html#method.load
|
||||
[InterruptDescriptorTable::load]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html#method.load
|
||||
|
||||
```rust
|
||||
// in src/interrupts.rs
|
||||
@@ -460,7 +460,7 @@ blog_os::interrupts::test_breakpoint_exception... [ok]
|
||||
`x86-interrupt`呼び出し規約と[`InterruptDescriptorTable`]型のおかげで、例外処理のプロセスは比較的わかりやすく、面倒なところはありませんでした。「これではさすがに簡単すぎる、例外処理の闇をすべて学び尽くしたい」というあなた向けの記事もあります:私達の[Handling Exceptions with Naked Functions][“Handling Exceptions with Naked Functions”]シリーズ(未訳)では、`x86-interrupt`呼び出し規約を使わずに例外を処理する方法を学び、さらには独自のIDT型を定義します。`x86-interrupt`呼び出し規約や、`x86_64`クレートが存在する前は、これらの記事が主な例外処理に関する記事でした。なお、これらの記事はこのブログの[第1版][first edition]をもとにしているので、内容が古くなっている可能性があることに注意してください。
|
||||
|
||||
[“Handling Exceptions with Naked Functions”]: @/edition-1/extra/naked-exceptions/_index.md
|
||||
[`InterruptDescriptorTable`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html
|
||||
[`InterruptDescriptorTable`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html
|
||||
[first edition]: @/edition-1/_index.md
|
||||
|
||||
## 次は?
|
||||
|
||||
@@ -242,7 +242,7 @@ I/O Map Base Address | `u16`
|
||||
|
||||
بیایید یک TSS جدید ایجاد کنیم که شامل یک پشته خطای دوگانه جداگانه در جدول پشته وقفه خود باشد. برای این منظور ما به یک ساختار TSS نیاز داریم. خوشبختانه کریت `x86_64` از قبل حاوی [ساختار `TaskStateSegment`] است که میتوانیم از آن استفاده کنیم.
|
||||
|
||||
[ساختار `TaskStateSegment`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/tss/struct.TaskStateSegment.html
|
||||
[ساختار `TaskStateSegment`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/tss/struct.TaskStateSegment.html
|
||||
|
||||
ما TSS را در یک ماژول جدید به نام `gdt` ایجاد میکنیم (نام این ماژول بعداً برایتان معنا پیدا میکند):
|
||||
|
||||
@@ -391,8 +391,8 @@ pub fn init() {
|
||||
|
||||
ما با استفاده از [`set_cs`] ثبات کد سگمنت را بارگذاری مجدد میکنیم و برای بارگذاری TSS با از [`load_tss`] استفاده میکنیم. توابع به عنوان `unsafe` علامت گذاری شدهاند، بنابراین برای فراخوانی آنها به یک بلوک `unsafe` نیاز داریم. چون ممکن است با بارگذاری انتخابگرهای نامعتبر، ایمنی حافظه از بین برود.
|
||||
|
||||
[`set_cs`]: https://docs.rs/x86_64/0.13.2/x86_64/instructions/segmentation/fn.set_cs.html
|
||||
[`load_tss`]: https://docs.rs/x86_64/0.13.2/x86_64/instructions/tables/fn.load_tss.html
|
||||
[`set_cs`]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/segmentation/fn.set_cs.html
|
||||
[`load_tss`]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/tables/fn.load_tss.html
|
||||
|
||||
اکنون که یک TSS معتبر و جدول پشته وقفه را بارگذاری کردیم، میتوانیم اندیس پشته را برای کنترل کننده خطای دوگانه در IDT تنظیم کنیم:
|
||||
|
||||
|
||||
@@ -232,7 +232,7 @@ I/Oマップベースアドレス | `u16`
|
||||
### TSSをつくる
|
||||
割り込みスタックテーブルにダブルフォルト用のスタックを含めた新しいTSSをつくってみましょう。そのためにはTSS構造体が必要です。幸いにも、すでに`x86_64`クレートに[`TaskStateSegment`構造体]が含まれているので、これを使っていきます。
|
||||
|
||||
[`TaskStateSegment`構造体]: https://docs.rs/x86_64/0.12.1/x86_64/structures/tss/struct.TaskStateSegment.html
|
||||
[`TaskStateSegment`構造体]: https://docs.rs/x86_64/0.14.2/x86_64/structures/tss/struct.TaskStateSegment.html
|
||||
|
||||
新しい`gdt`モジュール内でTSSをつくります(名前の意味は後でわかるでしょう):
|
||||
|
||||
@@ -378,8 +378,8 @@ pub fn init() {
|
||||
|
||||
[`set_cs`]を使ってコードセグメントレジスタを再読み込みして、[`load_tss`]を使ってTSSを読み込んでいます。これらの関数は`unsafe`とマークされているので、呼び出すには`unsafe`ブロックが必要です。`unsafe`なのは、不正なセレクタを読み込むことでメモリ安全性を壊す可能性があるからです。
|
||||
|
||||
[`set_cs`]: https://docs.rs/x86_64/0.12.1/x86_64/instructions/segmentation/fn.set_cs.html
|
||||
[`load_tss`]: https://docs.rs/x86_64/0.12.1/x86_64/instructions/tables/fn.load_tss.html
|
||||
[`set_cs`]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/segmentation/fn.set_cs.html
|
||||
[`load_tss`]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/tables/fn.load_tss.html
|
||||
|
||||
これで正常なTSSと割り込みスタックテーブルを読み込みこんだので、私達はIDT内のダブルフォルトハンドラにスタックインデックスをセットすることができます:
|
||||
|
||||
|
||||
@@ -80,29 +80,29 @@ Secondary ATA ----> |____________| Parallel Port 1----> |____________|
|
||||
|
||||
پیکربندی پیش فرض PIC ها قابل استفاده نیست، زیرا اعداد بردار وقفه را در محدوده 15-0 به پردازنده می فرستد. این اعداد در حال حاضر توسط استثناهای پردازنده اشغال شدهاند ، به عنوان مثال شماره 8 مربوط به یک خطای دوگانه است. برای رفع این مشکل همپوشانی، باید وقفه های PIC را به اعداد دیگری تغییر دهیم. دامنه واقعی مهم نیست به شرطی که با استثناها همپوشانی نداشته باشد ، اما معمولاً محدوده 47-32 انتخاب می شود، زیرا اینها اولین شماره های آزاد پس از 32 اسلات استثنا هستند.
|
||||
|
||||
پیکربندی با نوشتن مقادیر ویژه در پورت های فرمان و داده PIC ها اتفاق می افتد. خوشبختانه قبلا کرتای به نام [`pic8259_simple`] وجود دارد، بنابراین نیازی نیست که توالی راه اندازی اولیه را خودمان بنویسیم. در صورت علاقهمند بودن به چگونگی عملکرد آن، [کد منبع آن][pic crate source] را بررسی کنید، نسبتاً کوچک و دارای مستند خوبی است.
|
||||
پیکربندی با نوشتن مقادیر ویژه در پورت های فرمان و داده PIC ها اتفاق می افتد. خوشبختانه قبلا کرتای به نام [`pic8259`] وجود دارد، بنابراین نیازی نیست که توالی راه اندازی اولیه را خودمان بنویسیم. در صورت علاقهمند بودن به چگونگی عملکرد آن، [کد منبع آن][pic crate source] را بررسی کنید، نسبتاً کوچک و دارای مستند خوبی است.
|
||||
|
||||
[pic crate source]: https://docs.rs/crate/pic8259_simple/0.2.0/source/src/lib.rs
|
||||
[pic crate source]: https://docs.rs/crate/pic8259/0.10.1/source/src/lib.rs
|
||||
|
||||
برای افزودن کرت به عنوان وابستگی ، موارد زیر را به پروژه خود اضافه می کنیم:
|
||||
|
||||
[`pic8259_simple`]: https://docs.rs/pic8259_simple/0.2.0/pic8259_simple/
|
||||
[`pic8259`]: https://docs.rs/pic8259/0.10.1/pic8259/
|
||||
|
||||
```toml
|
||||
# in Cargo.toml
|
||||
|
||||
[dependencies]
|
||||
pic8259_simple = "0.2.0"
|
||||
pic8259 = "0.10.1"
|
||||
```
|
||||
|
||||
انتزاع اصلی ارائه شده توسط کرت، ساختمان [`ChainedPics`] است که نمایانگر طرح اولیه/ثانویه PIC است که در بالا دیدیم. برای استفاده به روش زیر طراحی شده است:
|
||||
|
||||
[`ChainedPics`]: https://docs.rs/pic8259_simple/0.2.0/pic8259_simple/struct.ChainedPics.html
|
||||
[`ChainedPics`]: https://docs.rs/pic8259/0.10.1/pic8259/struct.ChainedPics.html
|
||||
|
||||
```rust
|
||||
// in src/interrupts.rs
|
||||
|
||||
use pic8259_simple::ChainedPics;
|
||||
use pic8259::ChainedPics;
|
||||
use spin;
|
||||
|
||||
pub const PIC_1_OFFSET: u8 = 32;
|
||||
@@ -130,7 +130,7 @@ pub fn init() {
|
||||
|
||||
ما از تابع [`initialize`] برای انجام مقداردهی اولیه PIC استفاده می کنیم. مانند تابع `ChainedPics::new`، این تابع نیز ایمن نیست زیرا در صورت عدم پیکربندی صحیح PIC می تواند باعث رفتار نامشخص شود.
|
||||
|
||||
[`initialize`]: https://docs.rs/pic8259_simple/0.2.0/pic8259_simple/struct.ChainedPics.html#method.initialize
|
||||
[`initialize`]: https://docs.rs/pic8259/0.10.1/pic8259/struct.ChainedPics.html#method.initialize
|
||||
|
||||
اگر همه چیز خوب پیش برود ، باید هنگام اجرای `cargo run` پیام "It did not crash" را ببینیم.
|
||||
|
||||
@@ -213,7 +213,7 @@ extern "x86-interrupt" fn timer_interrupt_handler(
|
||||
|
||||
`timer_interrupt_handler` ما دارای امضای مشابه کنترل کننده های استثنای ما است ، زیرا پردازنده به طور یکسان به استثناها و وقفه های خارجی واکنش نشان می دهد (تنها تفاوت این است که برخی از استثناها کد خطا را در پشته ذخیره میکنند). ساختمان [`InterruptDescriptorTable`] تریت [`IndexMut`] را پیاده سازی می کند، بنابراین می توانیم از طریق سینتکس ایندکسدهی آرایه، به ایتم های جداگانه دسترسی پیدا کنیم.
|
||||
|
||||
[`InterruptDescriptorTable`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html
|
||||
[`InterruptDescriptorTable`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.InterruptDescriptorTable.html
|
||||
[`IndexMut`]: https://doc.rust-lang.org/core/ops/trait.IndexMut.html
|
||||
|
||||
در کنترل کننده وقفه تایمر، یک نقطه را روی صفحه چاپ می کنیم. همانطور که وقفه تایمر به صورت دوره ای اتفاق می افتد ، انتظار داریم که در هر تیک تایمر یک نقطه ظاهر شود. با این حال، هنگامی که آن را اجرا می کنیم می بینیم که فقط یک نقطه چاپ می شود:
|
||||
@@ -338,7 +338,7 @@ pub fn _print(args: fmt::Arguments) {
|
||||
|
||||
تابع [`without_interrupts`] یک [کلوژر] را گرفته و آن را در یک محیط بدون وقفه اجرا می کند. ما از آن استفاده می کنیم تا اطمینان حاصل کنیم که تا زمانی که `Mutex` قفل شده است ، هیچ وقفه ای رخ نمی دهد. اکنون هنگامی که هسته را اجرا می کنیم ، می بینیم که آن بدون هنگ کردن به کار خود ادامه می دهد. (ما هنوز هیچ نقطه ای را مشاهده نمی کنیم ، اما این به این دلیل است که سرعت حرکت آنها بسیار سریع است. سعی کنید سرعت چاپ را کم کنید، مثلاً با قرار دادن `for _ in 0..10000 {}` در داخل حلقه.)
|
||||
|
||||
[`without_interrupts`]: https://docs.rs/x86_64/0.13.2/x86_64/instructions/interrupts/fn.without_interrupts.html
|
||||
[`without_interrupts`]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/interrupts/fn.without_interrupts.html
|
||||
[کلوژر]: https://doc.rust-lang.org/book/second-edition/ch13-01-closures.html
|
||||
|
||||
ما می توانیم همین تغییر را در تابع چاپ سریال نیز اعمال کنیم تا اطمینان حاصل کنیم که هیچ بنبستی در آن رخ نمی دهد:
|
||||
@@ -585,7 +585,7 @@ extern "x86-interrupt" fn keyboard_interrupt_handler(
|
||||
|
||||
ما برای خواندن یک بایت از پورت داده صفحه کلید از نوع [`Port`] کرت `x86_64` استفاده میکنیم. این بایت [_اسکن کد_] نامیده می شود و عددی است که کلید فشرده شده / رها شده را نشان می دهد. ما هنوز کاری با اسکن کد انجام نمی دهیم ، فقط آن را روی صفحه چاپ می کنیم:
|
||||
|
||||
[`Port`]: https://docs.rs/x86_64/0.13.2/x86_64/instructions/port/struct.Port.html
|
||||
[`Port`]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/port/struct.Port.html
|
||||
[_اسکن کد_]: https://en.wikipedia.org/wiki/Scancode
|
||||
|
||||

|
||||
|
||||
@@ -77,11 +77,11 @@ The default configuration of the PICs is not usable, because it sends interrupt
|
||||
|
||||
The configuration happens by writing special values to the command and data ports of the PICs. Fortunately there is already a crate called [`pic8259`], so we don't need to write the initialization sequence ourselves. In case you are interested how it works, check out [its source code][pic crate source], it's fairly small and well documented.
|
||||
|
||||
[pic crate source]: https://docs.rs/crate/pic8259/0.10.0/source/src/lib.rs
|
||||
[pic crate source]: https://docs.rs/crate/pic8259/0.10.1/source/src/lib.rs
|
||||
|
||||
To add the crate as dependency, we add the following to our project:
|
||||
|
||||
[`pic8259`]: https://docs.rs/pic8259/0.10.0/pic8259/
|
||||
[`pic8259`]: https://docs.rs/pic8259/0.10.1/pic8259/
|
||||
|
||||
```toml
|
||||
# in Cargo.toml
|
||||
|
||||
@@ -239,8 +239,8 @@ Bit(s) | Name | Meaning
|
||||
|
||||
کریت `x86_64` انواع مختلفی را برای [جدولهای صفحه] و [ورودیهای] آنها فراهم میکند، بنابراین نیازی نیست که خودمان این ساختارها را ایجاد کنیم.
|
||||
|
||||
[جدولهای صفحه]: https://docs.rs/x86_64/0.13.2/x86_64/structures/paging/page_table/struct.PageTable.html
|
||||
[ورودیهای]: https://docs.rs/x86_64/0.13.2/x86_64/structures/paging/page_table/struct.PageTableEntry.html
|
||||
[جدولهای صفحه]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/page_table/struct.PageTable.html
|
||||
[ورودیهای]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/page_table/struct.PageTableEntry.html
|
||||
|
||||
### بافر ترجمه Lookaside
|
||||
|
||||
@@ -249,7 +249,7 @@ Bit(s) | Name | Meaning
|
||||
برخلاف سایر حافظههای پنهان پردازنده، TLB کاملاً شفاف نبوده و با تغییر محتوای جدولهای صفحه، ترجمهها را بهروز و حذف نمیکند. این بدان معنی است که هسته هر زمان که جدول صفحه را تغییر میدهد باید TLB را به صورت دستی بهروز کند. برای انجام این کار، یک دستورالعمل ویژه پردازنده وجود دارد به نام [`invlpg`] ("صفحه نامعتبر") که ترجمه برای صفحه مشخص شده را از TLB حذف میکند، بنابراین دوباره از جدول صفحه در دسترسی بعدی بارگیری میشود. TLB همچنین میتواند با بارگیری مجدد رجیستر `CR3`، که یک تعویض فضای آدرس را شبیهسازی میکند، کاملاً فلاش (کلمه: flush) شود. کریت `x86_64` توابع راست را برای هر دو نوع در [ماژول `tlb`] فراهم میکند.
|
||||
|
||||
[`invlpg`]: https://www.felixcloutier.com/x86/INVLPG.html
|
||||
[ماژول `tlb`]: https://docs.rs/x86_64/0.13.2/x86_64/instructions/tlb/index.html
|
||||
[ماژول `tlb`]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/tlb/index.html
|
||||
|
||||
مهم است که به یاد داشته باشید که TLB را روی هر جدول صفحه فلاش کنید، زیرا در غیر اینصورت پردازنده ممکن است از ترجمه قدیمی استفاده کند، که میتواند منجر به باگهای غیرقطعی شود که اشکالزدایی آن بسیار سخت است.
|
||||
|
||||
@@ -304,8 +304,8 @@ extern "x86-interrupt" fn page_fault_handler(
|
||||
ثبات [`CR2`] بهطور خودکار توسط CPU روی خطای صفحه تنظیم میشود و حاوی آدرس مجازی قابل دسترسی است که باعث رخ دادن خطای صفحه شده است. ما برای خواندن و چاپ آن از تابع [`Cr2::read`] کریت ` x86_64` استفاده میکنیم. نوع [`PageFaultErrorCode`] اطلاعات بیشتری در مورد نوع دسترسی به حافظهای که باعث خطای صفحه شده است، فراهم می کند، به عنوان مثال این امر به دلیل خواندن یا نوشتن بوده است. به همین دلیل ما آن را نیز چاپ میکنیم. بدون رفع خطای صفحه نمیتوانیم به اجرا ادامه دهیم، بنابراین در انتها یک [hlt_loop] اضافه میکنیم.
|
||||
|
||||
[`CR2`]: https://en.wikipedia.org/wiki/Control_register#CR2
|
||||
[`Cr2::read`]: https://docs.rs/x86_64/0.13.2/x86_64/registers/control/struct.Cr2.html#method.read
|
||||
[`PageFaultErrorCode`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/struct.PageFaultErrorCode.html
|
||||
[`Cr2::read`]: https://docs.rs/x86_64/0.14.2/x86_64/registers/control/struct.Cr2.html#method.read
|
||||
[`PageFaultErrorCode`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.PageFaultErrorCode.html
|
||||
[LLVM bug]: https://github.com/rust-lang/rust/issues/57270
|
||||
[`hlt_loop`]: @/edition-2/posts/07-hardware-interrupts/index.md#the-hlt-instruction
|
||||
|
||||
@@ -339,7 +339,7 @@ pub extern "C" fn _start() -> ! {
|
||||
|
||||
ثبات `CR2` در واقع حاوی` 0xdeadbeaf` هست، آدرسی که سعی کردیم به آن دسترسی پیدا کنیم. کد خطا از طریق [`CAUSED_BY_WRITE`] به ما میگوید که خطا هنگام تلاش برای انجام یک عملیات نوشتن رخ داده است. حتی از طریق [بیتهایی که تنظیم _نشدهاند_][`PageFaultErrorCode`] اطلاعات بیشتری به ما میدهد. به عنوان مثال، عدم تنظیم پرچم `PROTECTION_VIOLATION` به این معنی است که خطای صفحه رخ داده است زیرا صفحه هدف وجود ندارد.
|
||||
|
||||
[`CAUSED_BY_WRITE`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/struct.PageFaultErrorCode.html#associatedconstant.CAUSED_BY_WRITE
|
||||
[`CAUSED_BY_WRITE`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.PageFaultErrorCode.html#associatedconstant.CAUSED_BY_WRITE
|
||||
|
||||
میبینیم که اشارهگر دستورالعمل فعلی `0x2031b2` میباشد، بنابراین میدانیم که این آدرس به یک صفحه کد اشاره دارد. صفحات کد توسط بوتلودر بصورت فقط خواندنی نگاشت میشوند، بنابراین خواندن از این آدرس امکانپذیر است اما نوشتن باعث خطای صفحه میشود. میتوانید این کار را با تغییر اشارهگر `0xdeadbeaf` به `0x2031b2` امتحان کنید:
|
||||
|
||||
@@ -363,7 +363,7 @@ println!("write worked");
|
||||
|
||||
میبینیم که پیام _"read worked"_ چاپ شده است، که نشان میدهد عملیات خواندن هیچ خطایی ایجاد نکرده است. با این حال، به جای پیام _"write worked"_ خطای صفحه رخ میدهد. این بار پرچم [`PROTECTION_VIOLATION`] علاوه بر پرچم [`CAUSED_BY_WRITE`] تنظیم شده است، که نشاندهنده وجود صفحه است، اما عملیات روی آن مجاز نیست. در این حالت نوشتن در صفحه مجاز نیست زیرا صفحات کد به صورت فقط خواندنی نگاشت میشوند.
|
||||
|
||||
[`PROTECTION_VIOLATION`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/struct.PageFaultErrorCode.html#associatedconstant.PROTECTION_VIOLATION
|
||||
[`PROTECTION_VIOLATION`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.PageFaultErrorCode.html#associatedconstant.PROTECTION_VIOLATION
|
||||
|
||||
### دسترسی به جدولهای صفحه
|
||||
|
||||
@@ -389,9 +389,9 @@ pub extern "C" fn _start() -> ! {
|
||||
|
||||
تابع [`Cr3::read`] از ` x86_64` جدول صفحه سطح 4 که در حال حاضر فعال است را از ثبات `CR3` برمیگرداند. یک تاپل (کلمه: tuple) از نوع [`PhysFrame`] و [`Cr3Flags`] برمیگرداند. ما فقط به قاب علاقهمَندیم، بنابراین عنصر دوم تاپل را نادیده میگیریم.
|
||||
|
||||
[`Cr3::read`]: https://docs.rs/x86_64/0.13.2/x86_64/registers/control/struct.Cr3.html#method.read
|
||||
[`PhysFrame`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/paging/frame/struct.PhysFrame.html
|
||||
[`Cr3Flags`]: https://docs.rs/x86_64/0.13.2/x86_64/registers/control/struct.Cr3Flags.html
|
||||
[`Cr3::read`]: https://docs.rs/x86_64/0.14.2/x86_64/registers/control/struct.Cr3.html#method.read
|
||||
[`PhysFrame`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/frame/struct.PhysFrame.html
|
||||
[`Cr3Flags`]: https://docs.rs/x86_64/0.14.2/x86_64/registers/control/struct.Cr3Flags.html
|
||||
|
||||
هنگامی که آن را اجرا میکنیم، خروجی زیر را مشاهده میکنیم:
|
||||
|
||||
@@ -401,7 +401,7 @@ Level 4 page table at: PhysAddr(0x1000)
|
||||
|
||||
بنابراین جدول صفحه سطح 4 که در حال حاضر فعال است در آدرس `0x100` در حافظه _فیزیکی_ ذخیره میشود، همانطور که توسط نوع بستهبندی [`PhysAddr`] نشان داده شده است. حال سوال این است: چگونه میتوانیم از هسته خود به این جدول دسترسی پیدا کنیم؟
|
||||
|
||||
[`PhysAddr`]: https://docs.rs/x86_64/0.13.2/x86_64/addr/struct.PhysAddr.html
|
||||
[`PhysAddr`]: https://docs.rs/x86_64/0.14.2/x86_64/addr/struct.PhysAddr.html
|
||||
|
||||
دسترسی مستقیم به حافظه فیزیکی در هنگام فعال بودن صفحهبندی امکان پذیر نیست، زیرا برنامهها به راحتی میتوانند محافظت از حافظه (ترجمه: memory protection) را دور بزنند و در غیر اینصورت به حافظه سایر برنامهها دسترسی پیدا میکنند. بنابراین تنها راه دسترسی به جدول از طریق برخی از صفحههای مجازی است که به قاب فیزیکی در آدرس`0x1000` نگاشت شده. این مشکل ایجاد نگاشت برای قابهای جدول صفحه یک مشکل کلی است، زیرا هسته به طور مرتب به جدولهای صفحه دسترسی دارد، به عنوان مثال هنگام اختصاص پشته برای یک نخِ (ترجمه: thread) جدید.
|
||||
|
||||
|
||||
@@ -240,8 +240,8 @@ pub struct PageTable {
|
||||
|
||||
`x86_64`クレートが[ページテーブル][page tables]とその[エントリ][entries]のための型を提供してくれているので、これらの構造体を私達自身で作る必要はありません。
|
||||
|
||||
[page tables]: https://docs.rs/x86_64/0.13.2/x86_64/structures/paging/page_table/struct.PageTable.html
|
||||
[entries]: https://docs.rs/x86_64/0.13.2/x86_64/structures/paging/page_table/struct.PageTableEntry.html
|
||||
[page tables]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/page_table/struct.PageTable.html
|
||||
[entries]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/page_table/struct.PageTableEntry.html
|
||||
|
||||
### トランスレーション・ルックアサイド・バッファ
|
||||
|
||||
@@ -256,7 +256,7 @@ pub struct PageTable {
|
||||
</div>
|
||||
|
||||
[`invlpg`]: https://www.felixcloutier.com/x86/INVLPG.html
|
||||
[`tlb` module]: https://docs.rs/x86_64/0.13.2/x86_64/instructions/tlb/index.html
|
||||
[`tlb` module]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/tlb/index.html
|
||||
|
||||
ページテーブルを修正したときは毎回TLBをflushしないといけないということはしっかりと覚えておいてください。これを行わないと、CPUは古い変換を使いつづけるかもしれず、これはデバッグの非常に難しい、予測不能なバグに繋がるかもしれないためです。
|
||||
|
||||
@@ -311,8 +311,8 @@ extern "x86-interrupt" fn page_fault_handler(
|
||||
[`CR2`]レジスタは、ページフォルト時にCPUによって自動的に設定されており、その値はアクセスされページフォルトを引き起こした仮想アドレスになっています。`x86_64`クレートの[`Cr2::read`]関数を使ってこれを読み込み出力します。[`PageFaultErrorCode`]型は、ページフォルトを引き起こしたメモリアクセスの種類についてより詳しい情報を提供します(例えば、読み込みと書き込みのどちらによるものなのか、など)。そのため、これも出力します。ページフォルトを解決しない限り実行を継続することはできないので、最後は[`hlt_loop`]に入ります。
|
||||
|
||||
[`CR2`]: https://en.wikipedia.org/wiki/Control_register#CR2
|
||||
[`Cr2::read`]: https://docs.rs/x86_64/0.13.2/x86_64/registers/control/struct.Cr2.html#method.read
|
||||
[`PageFaultErrorCode`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/struct.PageFaultErrorCode.html
|
||||
[`Cr2::read`]: https://docs.rs/x86_64/0.14.2/x86_64/registers/control/struct.Cr2.html#method.read
|
||||
[`PageFaultErrorCode`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.PageFaultErrorCode.html
|
||||
[LLVM bug]: https://github.com/rust-lang/rust/issues/57270
|
||||
[`hlt_loop`]: @/edition-2/posts/07-hardware-interrupts/index.md#the-hlt-instruction
|
||||
|
||||
@@ -346,7 +346,7 @@ pub extern "C" fn _start() -> ! {
|
||||
|
||||
`CR2`レジスタは確かに私達がアクセスしようとしていたアドレスである`0xdeadbeaf`を格納しています。エラーコードが[`CAUSED_BY_WRITE`]なので、この<ruby>障害<rp> (</rp><rt>フォルト</rt><rp>) </rp></ruby>は<ruby>write<rp> (</rp><rt>書き込み</rt><rp>) </rp></ruby>操作の実行中に発生したのだと分かります。更に、[1にセットされていないビット][`PageFaultErrorCode`]からも情報を得ることができます。例えば、`PROTECTION_VIOLATION`フラグが1にセットされていないことから、ページフォルトは対象のページが存在しなかったために発生したのだと分かります。
|
||||
|
||||
[`CAUSED_BY_WRITE`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/struct.PageFaultErrorCode.html#associatedconstant.CAUSED_BY_WRITE
|
||||
[`CAUSED_BY_WRITE`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.PageFaultErrorCode.html#associatedconstant.CAUSED_BY_WRITE
|
||||
|
||||
ページフォルトを起こした時点での命令ポインタは`0x2031b2`であるので、このアドレスはコードページを指しているとわかります。コードページはブートローダによって読み込み専用に指定されているので、このアドレスからの読み込みは大丈夫ですが、このページへの書き込みはページフォルトを起こします。`0xdeadbeaf`へのポインタを`0x2031b2`に変更して、これを試してみましょう。
|
||||
|
||||
@@ -370,7 +370,7 @@ println!("write worked");
|
||||
|
||||
"read worked"というメッセージが表示されますが、これは読み込み操作が何のエラーも発生させなかったことを示しています。しかし、"write worked"のメッセージではなく、ページフォルトが発生してしまいました。今回は[`CAUSED_BY_WRITE`]フラグに加えて[`PROTECTION_VIOLATION`]フラグがセットされています。これは、ページは存在していたものの、それに対する今回の操作が許可されていなかったということを示します。今回の場合、ページへの書き込みは、コードページが読み込み専用に指定されているため許可されていませんでした。
|
||||
|
||||
[`PROTECTION_VIOLATION`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/idt/struct.PageFaultErrorCode.html#associatedconstant.PROTECTION_VIOLATION
|
||||
[`PROTECTION_VIOLATION`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/idt/struct.PageFaultErrorCode.html#associatedconstant.PROTECTION_VIOLATION
|
||||
|
||||
### ページテーブルへのアクセス
|
||||
|
||||
@@ -396,9 +396,9 @@ pub extern "C" fn _start() -> ! {
|
||||
|
||||
`x86_64`クレートの[`Cr3::read`]関数は、現在有効なレベル4ページテーブルを`CR3`レジスタから読みとって返します。この関数は[`PhysFrame`]型と[`Cr3Flags`]型のタプルを返します。私達はフレームにしか興味がないので、タプルの2つ目の要素は無視しました。
|
||||
|
||||
[`Cr3::read`]: https://docs.rs/x86_64/0.13.2/x86_64/registers/control/struct.Cr3.html#method.read
|
||||
[`PhysFrame`]: https://docs.rs/x86_64/0.13.2/x86_64/structures/paging/frame/struct.PhysFrame.html
|
||||
[`Cr3Flags`]: https://docs.rs/x86_64/0.13.2/x86_64/registers/control/struct.Cr3Flags.html
|
||||
[`Cr3::read`]: https://docs.rs/x86_64/0.14.2/x86_64/registers/control/struct.Cr3.html#method.read
|
||||
[`PhysFrame`]: https://docs.rs/x86_64/0.14.2/x86_64/structures/paging/frame/struct.PhysFrame.html
|
||||
[`Cr3Flags`]: https://docs.rs/x86_64/0.14.2/x86_64/registers/control/struct.Cr3Flags.html
|
||||
|
||||
これを実行すると、以下の出力を得ます:
|
||||
|
||||
@@ -408,7 +408,7 @@ Level 4 page table at: PhysAddr(0x1000)
|
||||
|
||||
というわけで、現在有効なレベル4ページテーブルは、[`PhysAddr`]ラッパ型が示すように、 **物理** メモリのアドレス`0x1000`に格納されています。ここで疑問が生まれます:このテーブルに私達のカーネルからアクセスするにはどうすればいいのでしょう?
|
||||
|
||||
[`PhysAddr`]: https://docs.rs/x86_64/0.13.2/x86_64/addr/struct.PhysAddr.html
|
||||
[`PhysAddr`]: https://docs.rs/x86_64/0.14.2/x86_64/addr/struct.PhysAddr.html
|
||||
|
||||
ページングが有効なとき、物理メモリに直接アクセスすることはできません。もしそれができたとしたら、プログラムは容易くメモリ保護を回避して他のプログラムのメモリにアクセスできてしまうだろうからです。ですので、テーブルにアクセスする唯一の方法は、アドレス`0x1000`の物理フレームに対応づけられているような仮想ページにアクセスすることです。ページテーブルの存在するフレームへの対応づけは(実用上も必要になる)一般的な問題です。なぜなら、例えば新しいスレッドのためにスタックを割り当てるときなど、カーネルは日常的にページテーブルにアクセスする必要があるためです。
|
||||
|
||||
|
||||
@@ -1756,8 +1756,8 @@ impl Executor {
|
||||
|
||||
`sleep_if_idle`は、`task_queue`が空になるまでループする`run_ready_tasks`の直後に呼び出されるので、キューを再度チェックする必要はないと思われるかもしれません。しかし、`run_ready_tasks` がリターンしてきた直後にハードウェア割り込みが発生する可能性があるため、`sleep_if_idle` 関数が呼ばれた時点ではキューに新しいタスクがあるかもしれません。キューがまだ空であった場合のみ、[`x86_64`]クレートが提供する[`instructions::hlt`]ラッパー関数を介して`hlt`命令を実行することで、CPUをスリープさせます。
|
||||
|
||||
[`instructions::hlt`]: https://docs.rs/x86_64/0.13.2/x86_64/instructions/fn.hlt.html
|
||||
[`x86_64`]: https://docs.rs/x86_64/0.13.2/x86_64/index.html
|
||||
[`instructions::hlt`]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/fn.hlt.html
|
||||
[`x86_64`]: https://docs.rs/x86_64/0.14.2/x86_64/index.html
|
||||
|
||||
残念ながら、この実装には微妙な競合状態が残っています。割り込みは非同期であり、いつでも発生する可能性があるため、`is_empty` のチェックと `hlt` の呼び出しの間に割り込みが発生する可能性があります:
|
||||
|
||||
@@ -1772,7 +1772,7 @@ if self.task_queue.is_empty() {
|
||||
|
||||
その答えは、チェックの前にCPUの割り込みを無効にし、`hlt`命令と一緒にアトミックに再度有効にすることです。この方法では、その間に発生するすべての割り込みが `hlt` 命令の後に遅延されるため、wakeupが失敗することはありません。この方法を実装するには、[`x86_64`]クレートが提供する[`interrupts::enable_and_hlt`][`enable_and_hlt`]関数を使用します。
|
||||
|
||||
[`enable_and_hlt`]: https://docs.rs/x86_64/0.13.2/x86_64/instructions/interrupts/fn.enable_and_hlt.html
|
||||
[`enable_and_hlt`]: https://docs.rs/x86_64/0.14.2/x86_64/instructions/interrupts/fn.enable_and_hlt.html
|
||||
|
||||
更新された `sleep_if_idle` 関数の実装は次のようになります:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user