Open25

[作って学ぶ]OSのしくみI──メモリ管理、マルチタスク、ハードウェア制御

dak2dak2

XQuartz

The XQuartz project is an open-source effort to develop a version of the X.Org X Window System that runs on macOS

https://www.xquartz.org/index.html

X Window System は LINUX などの UNIX 系 OS で使用されているウィンドウシステムを提供する表示プロトコル
GUI を構築するのに使われる
現在のバージョンが2012年6月6日にリリースされた X11 で、通称 X11 と呼ばれることもあるみたい

brew install

$ brew install --cask xquartz
dak2dak2
  "mounts": [
    "source=/tmp/.X11-unix,target=/tmp/.X11-unix,type=bind,consistency=cached"
  ],
  "runArgs": [
    "--privileged"
  ],
  "containerEnv": {
    "DISPLAY": "unix:0"
  }
vscode ➜ /workspace $  qemu-system-x86_64 \
  -drive file=./third_party/ovmf/RELEASEX64_OVMF.fd,if=virtio \
  -net nic,model=virtio \
  -vga virtio \
  -display gtk,grab-on-hover=on,gl=on \
  -boot once=d -no-reboot
qemu-system-x86_64: OpenGL is not supported by the display

上記の .devcontainer.json だと OpenGL is not supported by the display

macOS の場合の指定が違ってそう
https://stackoverflow.com/a/72230203

    "containerEnv": {
        "DISPLAY": "docker.for.mac.host.internal:0"
    },
vscode ➜ /workspace $  qemu-system-x86_64 \
  -drive file=./third_party/ovmf/RELEASEX64_OVMF.fd,if=virtio \
  -net nic,model=virtio \
  -vga virtio \
  -display gtk,grab-on-hover=on,gl=on \
  -boot once=d -no-reboot
Couldn't open libEGL.so.1: libEGL.so.1: cannot open shared object file: No such file or directory
[1]    794 IOT instruction  qemu-system-x86_64 -drive file=./third_party/ovmf/RELEASEX64_OVMF.fd,if=virti

libEGL 周りのパッケージを入れる

RUN .....
    libegl1-mesa \
    libglvnd0 \
    libgles2 
dak2dak2
 qemu-system-x86_64 \
  -drive file=third_party/ovmf/RELEASEX64_OVMF.fd,if=virtio \
  -net nic,model=virtio \
  -vga virtio \
  -display gtk,grab-on-hover=on \
  -boot once=d -no-reboot

https://www.qemu.org/docs/master/system/qemu-manpage.html

UEFIファームウェアのファイル (RELEASEX64_OVMF.fd) をディスクとして定義しようとしてるみたい
-pflash でファームウェアを指定するのが正しそう

-pflash file
Use file as a parallel flash image.

The kernel options were designed to work with Linux kernels although other things (like hypervisors) can be packaged up as a kernel executable image. The exact format of a executable image is usually architecture specific.

The way in which the kernel is started (what address it is loaded at, what if any information is passed to it via CPU registers, the state of the hardware when it is started, and so on) is also architecture specific. Typically it follows the specification laid down by the Linux kernel for how kernels for that architecture must be started.
qemu-system-x86_64 \
 -pflash ./third_party/ovmf/RELEASEX64_OVMF.fd \
 -net nic,model=virtio \
 -vga virtio \
 -display gtk,grab-on-hover=on \
 -no-reboot

きた

dak2dak2

DevContainer 立ち上げ前に xhost +localhost をホスト側で実行しておく必要あり
X Window System にローカルホストからのアクセスを許可する
恒常的に立ち上げておきたければ、shell 立ち上げ時に実行しておくと良いのかも

Hello World!

これでいけてそう

qemu-system-x86_64 \
 -bios ./third_party/ovmf/RELEASEX64_OVMF.fd \
 -drive format=raw,file=fat:rw:mnt \
 -net nic,model=virtio \
 -display gtk,grab-on-hover=on
dak2dak2

Rust の話

Rust の trait ってインターフェースみたいなもんなのか

トレイトは、Rustコンパイラに、特定の型に存在し、他の型と共有できる機能について知らせます。 トレイトを使用すると、共通の振る舞いを抽象的に定義できます。トレイト境界を使用すると、 あるジェネリックが、特定の振る舞いをもつあらゆる型になり得ることを指定できます。

https://doc.rust-jp.rs/book-ja/ch10-02-traits.html

dak2dak2

x86_64 は 64 bit 幅で、メモリ空間は1ビットあたり2通りだから、2^64 = 約16EB 扱える

dak2dak2

Rust の話

クレートはバイナリクレートとライブラリクレートの2種類ある

  • バイナリクレートはsrc/main.rs を頂点とするクレートで、これをもとに実行ファイルが生成される
    • cargo run をしたときに最終的に生成するもの
  • ライブラリクレートはsrc/lib.rs を頂点とするクレートで、これはほかのクレート(バイナリでもライブラリでも)から利用するためのクレートであり、実行ファイルを直接生成することはない
    • これらのクレートはあくまでも別のクレートであり、バイナリクレートをビルドする際には、wasabi というバイナリクレートがwasabi というライブラリクレートに依存する、という形でビルドされる
    • src/main.rs から呼び出す場合は、ライブラリクレートwasabi の中の関数を呼ぶた
      めwasabi:: から始まるパスを使用
    • それ以外のライブラリクレートに属するコードから自分自身のクレートの関数を呼ぶためには、自身のクレートを表すcrate:: から始まるパスを使用
dak2dak2

CPU はクロックという周期的な信号に合わせて計算
CPU のクロックは近年3GHzが多くなってきてるので、1秒あたり3 * 10^9 = 30億命令サイクルくらい回せる。
命令サイクル => CPU がメモリから命令をフェッチ・デコード・実行する一連のサイクル

大体のコンピュータの時間感覚を掴むための gist
https://gist.github.com/jboner/2841832

CPU 内部で計算するよりメモリとやりとりする方が圧倒的に時間がかかる。
それを効率化するためにキャッシュメモリかなと思ってたけど、メモリバスで1度に転送できるデータ量を増やすというのも確かに。
現代のコンピュータのメモリバスの幅は64bit幅が多い

キャッシュに記憶されるメモリ上の領域の最小単位のことをキャッシュラインと言い、その大きさのことはキャッシュラインサイズと呼ぶ

dak2dak2
方式 仕組み 利点 欠点
フルアソシエイティブ 要求されたアドレスを、キャッシュ内の全てのラインと比較する。 格納場所に制限がなく、キャッシュヒット率が最も高くなる可能性がある。 比較回路が非常に大規模で高コスト。消費電力も大きい。
ダイレクトマッピング メモリアドレスから格納先のキャッシュラインを一意に決定する。 回路が単純で高速。低コストで実装できる。 違うデータでも格納先が同じだと衝突し、上書きが頻発することがある。
セットアソシエイティブ アドレスから**グループ(セット)を特定し、その中の複数のライン(ウェイ)**から格納場所を探す。 性能とコストのバランスが良い。ダイレクトマッピングの衝突問題を緩和できる。 ダイレクトマッピングよりは回路が複雑になる。

https://eetimes.itmedia.co.jp/ee/articles/1603/04/news032_2.html

この画像分かりやすい
https://ja.wikipedia.org/wiki/キャッシュメモリ#/media/ファイル:Cache,associative-fill-both.png

CPU に近い順から、L1、L2、L3
https://ja.wikipedia.org/wiki/キャッシュメモリ

dak2dak2

キャッシュラインを跨ぐケース

struct Data {
    long long b;  // 8バイト
};

みたいなデータがあったとして、b のデータがキャッシュライン0と1にそれぞれ格納されているケースを考える。

b のデータを扱うためにキャッシュライン0と1と参照しないといけない。キャッシュライン0に格納されていれば、それだけ見れれば良い

読みたいバイトが同一のキャッシュラインに収まるようにすることをアラインメント

2バイトなら2の倍数のアドレスに、3バイトか4バイトなら4の倍数のアドレスに、5から8バイトなら8の倍数のアドレスに……というように、一まとめにアクセスしたいバイト数より大きい、最小の2のべきのバイト数の倍数になるようアドレスを調節してあげることが、CPUにとってやさしいデータ配置になる

dak2dak2

シリアルポートというのは、比較的古くからコンピューターに存在する入出力インタフェースの一つです。ほかの入出力に比べて簡単に扱うことができるため、現在でもデバッグ用に広く用いられています

https://ja.wikipedia.org/wiki/シリアルポート

dak2dak2

ページ管理

そもそもページとは?

メモリの管理単位のこと、x86_64 では4KB

https://unix.stackexchange.com/a/128218

ページテーブルにおける変換処理

x86_64 では、一つのページテーブル構造体は4KBの大きさとアライメントを持つ。
CPUがアドレス変換を行う際に起点として参照するページテーブルでは、仮想アドレス空間全体を大雑把に分割して管理。
アドレスは64ビット、つまり8バイトなので、一つのページテーブルは次の段階へのポイ
ンタを512個書き並べたものになっている

そして、分割された各部分の管理は、さらに次の段階のページテーブルによってもう少し細かく管理。これを何回か繰り返して、最終的に管理する領域の範囲がページサイズになっ
たら、そこで変換は終了

Huge Page について

仮想アドレスを物理アドレスに変換してメモリアクセスするが、そのマッピングテーブルをページテーブルと呼ぶ。Memory Management Unit が変換処理を行う

ページテーブルからメモリを読み込んで変換するのは時間がかかる => 最近使った変換情報を TLB(Translation Lookaside Buffer) にキャッシュとして保存して使う

大量のメモリが必要な情報を扱おうとすると、TLB でキャッシュヒットしない場合がある => ページテーブル参照のコスト

これを解決するのが Huge Page => 基本的には4KBのページサイズを増やして、キャッシュヒット率上げようぜということ

4KB/ページの場合、1GB のメモリを管理するには、1GB/4KB 大体 25万個のページエントリ(1つのページに対応するデータ)が必要

2MB/ページの場合、1GB/2MB = 312 ページエントリになる => TLB キャッシュヒット率が向上

hugetlbpage support

標準的な Huge Page で、事前にページサイズを上げておく方法

THP: Transparent Huge Pages

事前にページサイズを上げなくても動的にページサイズを確保する方法

https://gist.github.com/shino/5d9aac68e7ebf03d4962a4c07c503f7d
https://docs.kernel.org/admin-guide/mm/transhuge.html

dak2dak2

ページテーブルを見る

...
entry[ 63] = L3Entry @ 0x00000000bfc021f8 { 0x00000000BFC42003 PWS }
}
Some(L2Table @ 0x00000000bfc03000 {
entry[ 0] = L2Entry @ 0x00000000bfc03000 { 0x00000000000000E3 PWS }
...
entry[511] = L2Entry @ 0x00000000bfc03ff8 { 0x000000003FE00083 PWS }
}
)
None

各 Entry[0] は次のページテーブルへのアドレスが格納
L2Entry[0] の 0x00000000 から 物理アドレス 0x00000000から始まる 2MiB の領域にマッ
ピングされている

dak2dak2

割り込みについて

TSS(Task State Segment)
IST
GDT