当記事をコピ&ペーストした記事が見つかりました。
当記事のコピー&ペーストはご遠慮下さい。
※現在は削除済み
https://qiita.com/ytrsfdfdfdfpapon3/items/f0f9e38f4237d7329280
当記事は、実際に高校生が単純なOS(オペレーティングシステム)を構築する記事です。
できる限り誰でもOSについて理解ができる記事を目指しています。
OS(オペレーティングシステム)
一言に「OS」と言っても、パッと具体的にどういうものであるか頭に浮かべることができる人は少ないのではないでしょうか?
今や、「プログラミング」という言葉はどこでも当たり前のように飛び交っています。
そして...
初心者がまず真っ先に使うであろう言語はやはり「Python」だと思います。
このQuiitaでも最も人気のある言語で、沢山の人々がそれに取り組んでいます。
例えばこんなコードがあって...
a, b = 5, 3
print( a + b )
実行結果は、もちろん8
です。
このような"命令"がオペレーティングシステムやハードウェアにより、どのように処理されているのかを深く考えることは普段無いと思います。
実はこれ、もの凄いことなんです。
Pythonはスクリプト言語なので、構文もカンタンですね。
僕は昔から、これらが具体的にどうなっているのか。
オペレーティングシステムは実際には何をどうしているのか。
興味がありました。
そして、実際に自分でオペレーティングシステムを構築してみたいと考えていました。
しかし、一言に「OSを作る!」と言っても、何の知識もない状態では不可能に等しいでしょう。
「俺、ちょっとコンビニ行ってくるわ。」的なノリで作るものではないですからね。
とは言え、単純なものであれば意外と簡単に作れたりします。
Windowsはすごい
「Windows」のようなOSを一人で作ることは不可能と言えるでしょう。
ちなみに、Windowsの総ソースコードは500GBを超え、ファイル数は400万とも言われています。
これ間違えて全部消したら絶対クビだ。バックアップあるだろうけど。
全て理解する頃には、おじいちゃんになってしまいますね。
前提知識
カーネルとは
カーネルには大きく分けて「モノシリックカーネル」と「マイクロカーネル」の2つがあります。
※厳密にはもっと沢山の種類のカーネルがあります。
モノリシックカーネル
OSの機能のほとんどすべてが単一のメモリ空間(カーネル空間)で行なわれるので、同一の処理を行う際に費やされるコスパにおいて優位です。
Windows NTではモノリシックカーネルが採用されています。
Windows NTではハイブリッドカーネル(マイクロカーネル)が採用されています。
マイクロカーネル
カーネルの機能を最小限にとどめたものです。
画像より、モノリシックカーネルと違い、ファイルシステムやドライバ部分がユーザモードに結合され、カーネルの機能が最小限となっていることが確認できます。
ブートローダー
これはご存じの方も多いと思いますが、パソコンに電源を入れてから実際にオペレーティングシステムが動き始めるまで、複数段階あります。
ブートローダーは、オペレーティングシステムを呼び起こす役割を担っています。
そして、そのブートローダーはファームウェアによって呼び起こされます。
これに関連した余談ですが、「xHelper」はご存知ですか?
興味のある方は是非こちらの記事をご覧になって下さい。
「Androidで工場出荷時の状態に戻しても撃退不可なマルウェア「xHelper」が発見される」
このマルウェアはブートローダに感染している為、オペレーティングシステムを再インストールしても復活してしまうのです。
このマルウェアを作り上げたプログラマーはさぞ優秀なことでしょう。会ってみたいものです。
リングプロテクション
リングプロテクションは、人間社会における「カースト制」のようなものだと言えます。
一番権限の小さいRing3から、最も権限の大きいRing0まであります。
Ring0では、CPUやGPU等のハードウェアと直接やり取りができます。
Bluetoothドライバ等はRing1で動作しています。
一方で、ユーザーモードアプリケーションが動作するRing3では限られた範囲でのみ行動できます。
万が一、どのRingもハードウェアとやり取りができてしまったら、どうなるでしょうか?
システムの重要な処理をユーザモードアプリケーションが妨げ、オペレーティングシステムとしての機能性を確保できません。
ちなみに、Ring0であるカーネルモードでのプログラミングは高い権限を持つ反面、一つのコーディングミスでブルースクリーンを引き起こします。
※体験談
OSのプログラミング言語は何が良いの?
PascalやBasicを使用してオペレーティングシステムを構築することも可能ですが、やはりC言語が良いみたいです。
そして、アセンブリ言語は必須。
先にコンパイラを自作してみるのが近道かも?
今ではC#でもOSを作り上げている「CosmosOS」というプロジェクトがあります。
個人的にかなり興味があるので、機会があったら自分でも「C#でOSを構築してみた!」やりたいです。
コンピュータの起動プロセス
コンピュータが起動してからのプロセスをより詳細に説明します。
こういった詳細な部分を解説している日本語の文献は本当に無いですね。
1.メインボードのBIOSがRAMに読み込まれる。
2.CPUの命令ポインタがBIOSに設定される。
3.BIOSがハードドライブを読み、ブートローダをRAMに読み込む。
4.ブートローダが実行され、ハードドライブからカーネルを読み、RAMにロードする。
ここまで放置されてきたCPUの「スタックポインタ」ですが、これはブートローダによって設定されません。
したがって、C++で記述されたオペレーティングシステムには、スタックポインタを設定し、プログラムのメイン関数を呼び出すメソッドが必要です。
また、カーネルにはアセンブリ言語で記述されたローダーと、C++で記述されたオペレーティングシステムが必要です。
ここでいう、.o
はオブジェクトで、.bin
はバイナリです。
ローダー
ブートローダーにカーネルであることを認識させるために、0x1badb002
というマジックナンバーを設定します。
カーネルスタック溢れ対策に、.space 2*1024*1024;
で予めスベースを確保する必要があるらしい。
※ソースコードはコピペ専用ではなく、あくまで一例として一部のみ抜粋して掲載しています。
.set MAGIC, 0x1badb002
.set FLAGS, (1<<0 | 1<<1)
.set CHECKSUM, -(MAGIC + FLAGS)
.section .multiboot
.long MAGIC
.long FLAGS
.long CHECKSUM
.section .text
.extern kernelMain
.extern callConstructors
.global loader
loader:
mov $kernel_stack, %esp
call callConstructors
push %eax
push %ebx
call kernelMain
_stop:
cli
hlt
jmp _stop
.section .bss
.space 2*1024*1024;
kernel_stack:
リンカー
※ソースコードはコピペ専用ではなく、あくまで一例として一部のみ抜粋して掲載しています。
ENTRY(loader)
OUTPUT_FORMAT(elf32-i386)
OUTPUT_ARCH(i386:i386)
SECTIONS
{
. = 0x0100000;
.text :
{
*(.multiboot)
*(.text*)
*(.rodata)
}
.data :
{
start_ctors = .;
KEEP(*(.init_array));
KEEP(*(SORT_BY_INIT_PRIORITY( .init_array.* )));
end_ctors = .;
*(.data)
}
.bss :
{
*(.bss)
}
/DISCARD/ :
{
*(.fini_array*)
*(.comment)
}
}
カーネル
以上の点を踏まえて、実際にソースを書いていきます。
ライブラリは一切利用できないので、printf
関数等は自分で定義する必要があります。
この場合、モニタがメモリ0xb8000
に入っているものを表示するようにしています。
※ソースコードはコピペ専用ではなく、あくまで一例として一部のみ抜粋して掲載しています。
/*
* プリント関数
*/
void printf(const char* txt) {
static unsigned short* vmem=(unsigned short*)0xb8000;
for(int i=0; txt[i]!='\0';++i) {
vmem[i]=(vmem[i] & 0xFF00)|txt[i];
}
}
/*
* カーネルエントリポイント
*/
extern "C" void kernelMain(void* multiboot_structure, uint32_t magicnumber) {
printf("Hello Quiita! From myOS kernel.");
while(1);
}
コンパイル
WindowsでのMinGWのインストールは下記の記事を参考にして下さい。
「windowsで快適なC/C++開発環境を作る」
kernel.cpp
gcc -c kernel.cpp
typedef char int8_t;
typedef unsigned char uint8_t;
typedef short int16_t;
typedef unsigned short unint16_t;
typedef int int32_t;
typedef unsigned int uint32_t;
typedef long long int int64_t;
typedef unsigned long long int uint64_t;
クロスコンパイラ問題
ld: unrecognised emulation mode: elf_i386
Supported emulations: i386pe
どうやらWindows上でコンパイルするにはクロスコンパイラが必要みたいです。
面倒なので、VirtualBoxでUbuntuを動かしてコンパイルします。
解決策をご存じの方がいらっしゃればコメントにてお願い致します。
LLVMがクロスコンパイルに対応しているらしい?
sudo apt install build-essential
でパッケージをインストールしてmake
します。
色々警告出てますが、無視で大丈夫。
警告は無視するもんだって死んだばあちゃんが言ってた。
VirtualBoxで実行してみる
ブートローダー(GRUB2)の画面が出るのでエンター。
無事動作を確認できました。感動。感激。
苦労したところ
アセンブリに関しては全く学んだことが無かったので、結構苦労しました。
夜中の1時半からずっとアセンブリと戦闘して、気が付けば朝の4時半。
そして、クロスコンパイル問題にも中々苦戦し、結局は折れてUbuntu3分構築必殺カップ麺作戦でゴリ押ししました。
Windowsで出来るようになれば、もっと開発がスムーズ/ストレスレスに進行できるのに。
最後に
結局、トータルで5時間程度で簡易的なOSを構築できました。
意外とあっさりできてしまって、自分でも拍子抜けしています。
「30日でできる! OS自作入門」という書籍もありますので、興味のある方は是非挑戦されてみて下さい。
分かりにくい箇所がございましたら、コメント欄にてご指摘いただければ可能な範囲で訂正致します。
最後までご覧頂きありがとうございました。
Comments