OSを実行せずにオペレーティングシステムを実行するにはどうすればよいですか?


167

私は今、本当に興味があります。私はPythonプログラマーであり、この質問に戸惑いました。あなたはOSを書いています。どのように実行しますか?どういうわけか実行する必要があり、その方法は別のOS内ですか?

OSにいなくてもアプリケーションを実行するにはどうすればよいですか?コンピューターに、たとえばCを実行し、実行するOSがない場合にこれらのコマンドを画面に対して実行するように指示するにはどうすればよいですか?

UNIXカーネルと関係がありますか?もしそうなら、Unixカーネルとは何ですか、それともカーネル全般ですか?

OSはそれよりも複雑なはずですが、どのように機能しますか?


14
私はそれがBIOSの目的であることをかなり確信しています-それはより大きなOSの実行をブートストラップする本当に小さなOSです。
sevenseacat

64
OSは便利ですが、コンピューター上でプログラムを実行するためにOSは必要ありません。
アンドレスF.

10
OSなしでOS以外のソフトウェアを作成することも完全に可能です。多くのForthインタープリターは、OSなしで実行されていました(または、OSであると言えます)。そんなに難しいことでもありません。Cを知っているなら、学習プログラムとしてそのようなプログラム(おそらく小さなゲーム)を書くのを楽しむかもしれません。
最大

44
この混乱は、我々は、これらの日を使用して素晴らしい、安全で、高度に抽象化コンピューティング・システムのコストの一つである:人々がすることができ、非常に良いと有能なプログラマとどのようにコンピュータの動作についても、基本を知っていません。どれだけ低くしたいですか?非常に低いが、それでも物理学を超えている場合は、最初のマイクロプロセッサのプログラミング方法を参照してくださいElectronics.SEで。
dmckee

2
プログラミングは、現在のOSコンセプトが発明される前に行われました。明らかに、そのレベルの何かがOSを開始します。OSはブートストラップされます。ほとんどの場合、オペレーティングシステムコースのコンピューター理論が必要なため、これは通常、少なくともある時点でCS 4年プログラムで言及されています。
リグ

回答:


263

ブートプロセスを実行する多くのWebサイトがあります(How Computers Boot Upなど)。一言で言えば、それは最終的にOSプロセスを開始できるまで、少しずつシステムを構築し続けるマルチステージプロセスです。

CPUを起動して実行しようとするマザーボード上のファームウェアから開始します。次に、他のハードウェアを起動して実行するミニオペレーティングシステムのようなBIOSをロードします。それが完了すると、ブートデバイス(ディスク、CDなど)を探し、MBR(マスターブートレコード)を見つけてメモリにロードし、実行します。オペレーティングシステム(または、より複雑になった他のブートローダー)を初期化して起動する方法を知っているのは、この小さなコードです。この時点で、カーネルのようなものがロードされ、実行が開始されます。

それがまったく機能することはかなり信じられません!


108
最後の文に+1。
CVn

39
「ブート」と呼ばれる理由があります。この用語は、「ブートストラップで自分を引き上げる」などの「ブートストラップ」の略です。
キース

5
誰かがブートストラップコードをキー入力したり切り替えたりしなければならなかった時代がありました。ROMのプログラムの最初の命令への単純なジャンプである場合もありました。また、デバイスから読み取り、読み取られたデータ内の最初のプログラム命令にジャンプするコードである場合もありました。物事は今、はるかに簡単です。
BillThor


15
@BillThor:「はるかに単純」とは、もちろん「はるかに複雑」を意味します。それらは使用するのがより簡単です。
ラファエルシュバイケルト

173

「ベアメタル」オペレーティングシステムは、何も実行さません。物理マシン上で完全な命令セットを実行し、すべての物理メモリ、すべてのデバイスレジスタ、および仮想メモリサポートハードウェアを制御するものを含むすべての特権命令にアクセスできます。

(オペレーティングシステムが仮想マシンで実行されている場合、上記と同じ状況にあると考えられる可能性があります。違いは、特定のものがハイパーバイザーによってエミュレートされるか、他の方法で処理されることです。 )

とにかく、OSは(たとえば)Cで実装されるかもしれませんが、通常のCライブラリをすべて使用できるわけではありません。特に、通常の「stdio」ライブラリはありません。むしろ、(たとえば)ディスクブロックの読み取りと書き込みを可能にするディスクデバイスドライバーを実装します。ディスクブロック層の上にファイルシステムを実装し、その上に、ユーザーアプリケーションのランタイムライブラリが(たとえば)ファイルの作成、読み取り、書き込みなどを行うシステムコールを実装します。

OSにいなくてもアプリケーションを実行するにはどうすればよいですか?

I / Oハードウェアなどと直接対話する方法を知っている特別な種類のアプリケーション(オペレーティングシステムなど)である必要があります。

コンピューターに、たとえばCを実行し、実行するOSがない場合にこれらのコマンドを画面に対して実行するように指示するにはどうすればよいですか?

あなたはしません。

アプリケーション(Cで記述された引数のため)は、ネイティブコードイメージを提供するために、他のマシンでコンパイルおよびリンクされます。次に、BIOSが見つけられる場所のイメージがハードドライブに書き込まれます。BIOSはイメージをメモリにロードし、アプリケーションのエントリポイントにジャンプする命令を実行します。

本格的なオペレーティングシステムでない限り、アプリケーションには(通常) "Cを実行してコマンドを実行する"というものはありません。その場合、それを実現するために必要なすべてのインフラストラクチャを実装するのはオペレーティングシステムの責任です。魔法はありません。たくさんのコード。

Billの答えは、電源がオフになっているマシンから通常のオペレーティングシステムが稼働しているマシンに移行するプロセスであるブートストラップについて説明しています。ただし、BIOSがタスクを完了すると、(通常)メインオペレーティングシステムにハードウェアの完全な制御を渡し、次のシステムが再起動するまでそれ以上の役割を果たさないことに注意してください。メインOSは、確かに従来の意味でBIOS内で実行されていません。

UNIXカーネルと関係がありますか?もしそうなら、Unixカーネル、または一般的なカーネルとは何ですか?

はい、そうです。

UNIXカーネルは、UNIXオペレーティングシステムの中核です。上記の「ベアメタル」のすべてを行うのはUNIXの一部です。

「カーネル」の考え方は、システムソフトウェアをコアのもの(物理デバイスアクセス、すべてのメモリなどを必要とするもの)と非コアのものに分離しようとすることです。カーネルはコア部分で構成されています。

実際には、カーネル/コアと非カーネル/非コアの区別はそれよりも複雑です。そして、実際にカーネルに属するものとそうでないものについては、多くの議論がありました。(たとえば、マイクロカーネルを調べます。)


6
驚異的な答え。可能であれば、さらにいくつかの賛成票を差し上げます。
weberc2

7
ここにはたくさんの良い答えがありますので、コメントとしてこれを追加します。これまで誰もこれについて言及していませんでした。プロセスをスケジュールし、プロセスが互いの動作を変更しないように保護する機能は通常、ファームウェアまたはBIOSではなく、OSでのみ見られる機能です。
ショーンバルボー

2
The idea of a "kernel" is that you try to separate the system software into core stuffこの用語kernelはドイツ語Kernに由来するものであり、コア/ニュークリアスを意味することに注意することで覚えやすくなります。
deed02392

1
それはそれはないC.実行するバイナリコードにコンパイルとリンクされていることを言及しているので、ここでこの答えを愛する
トラヴィスPessetto

3
「それから逃げることで、PCユーザーの頭が良くなくなった」-それほど賢くはない...コンピュータの知識が少ない。また、PCユーザーの数が増えたとも言えます。
スティーブンC

62

最初はCPUに電力がありませんでした。

そして、男は「電源を入れて」と言い、CPUはメモリ内の指定されたアドレスから読み取りを開始し、そこにあった命令を実行しました。その後、電源が切れるまで次のように続きます。

これがブートアップでした。そのタスクは、別のソフトウェアをロードして、メインソフトウェアが存在する環境にアクセスし、それをロードすることでした。

最後に、わかりやすい画面にログオンするように求められました。


58
この回答はchristianity.stackexchange.comに移動する必要があります
Coomie

6
「与えられた住所」とは何ですか?ここでチャールズダーウィンをプレイしてすみません。
ミッドハット

7
@Midhat-CPUによって取得される最初のアドレスは、その内部に組み込まれています。通常0です
。– mouviciel

17
...そして7日目に、男は彼のゲームで休んだ
カナダのルーク

9
メモリ内@mouvicielアドレスは、0x7C00いずれかのx86互換性のあるアーキテクチャと最初の通常それが好むブート可能なあらゆるデバイスの最初のセクタを読み込み、BIOSによって埋められていなければならない...ニースの答えかかわら:-7
トビアスKienzler

29

遅れてすみませんが、そのように説明します:

  • マザーボードに電力が供給されます。

  • タイミング回路は、電気的特性のみに基づいて、必要に応じて開始および安定します。一部の新しいデバイスでは、実際には非常に限られたマイクロプロセッサまたはシーケンサを使用する場合があります。

    「タイミング回路が開始され、必要に応じて安定する」などの多くのことは、ハードウェアでは実際に起こらないことに注意してください。その膨大な量の作業は、実際には非常に限られたサブプロセッサー/シーケンサーで実行されている非常に特殊なソフトウェアです。

    - jkerian10月25日5時20

  • CPUとRAMに電力が供給されます。

  • CPUはBIOSからデータを(その内部配線に基づいて)ロードします。一部のマシンでは、BIOSがRAMにミラーリングされ、そこから実行される場合がありますが、これはまれなIIRCです。

    オンにすると、x86互換CPUはアドレス空間のアドレス0xFFFFFFF0から始まります...

    -Micheal Steil、マイクロソフトがXboxセキュリティシステムで行った17の間違いアーカイブ

  • BIOSは、マザーボードがディスクおよびその他のハードウェアIOに使用するハードウェアポートとアドレスを呼び出し、ディスクをスピンアップし、残りのRAMを動作させます。

  • BIOSコード(ハー​​ドウェアに保存されたCMOS設定による)は、低レベルのIDEまたはSATAコマンドを使用して、CMOSで指定された順序で、またはメニューでユーザーがオーバーライドした順に、各ディスクのブートセクターを読み取ります。

  • ブートセクターを持つ最初のディスクは、ブートセクターを実行します。このブートセクターは、ディスクからより多くのデータをロードし、より大きなNTLDR、後のステージをロードするGRUBなどの命令を持つアセンブリです。

  • 最後に、OSマシンコードは、ブートローダーによって、直接的または間接的に、代替またはオフセットの場所からブートセクターをロードするチェーンローディングによって実行されます。

その後、友好的なカーネルパニック、窒息したペンギン、またはヘッドクラッシュのためにディスクが停止してしまいます。=)別のシナリオでは、カーネルはプロセステーブル、メモリ内構造をセットアップし、ディスクをマウントし、ドライバー、モジュール、GUIまたはサービスのセット(サーバー上にある場合)をロードします。次に、ヘッダーが読み取られるとプログラムが実行され、アセンブリがメモリに取り込まれ、それに応じてマップされます。


2
「タイミング回路が開始され、必要に応じて安定する」などの多くのことは、ハードウェアでは実際には発生しません。その膨大な量の作業は、実際には非常に限られたサブプロセッサー/シーケンサーで実行されている非常に特殊なソフトウェアです。-フレンドリーな近所のファームウェアエンジニア
jkerian

@jkerianあなたのコメントを私の投稿に引用したことを気にしますか?
–ζ

へえ、まったくありません。
jkerian

BIOSはオペレーティングシステムではありません。BIOSは、Basic Input / Output Systemの省略形であり、これ BIOSの機能です。プログラマーは、メーカー提供のドライバーで低レベルのリソースを使用できます。OSが保護(32ビット)またはロング(64ビット)モードに入ると、BIOSは使用できなくなり、OSはBIOSが「下位」レベルで提供した機能を基本的に置き換える独自のドライバーを使用します。LinuxやWindowsなどの最新のオペレーティングシステムは、使用可能なRAMセクションを検出し、必要なドライバーをロードできる独自のより高度なローダーをロードするためにのみBIOSを使用します。
ハンネスカルピーラ

1
@HannesKarppila更新; これは約4年前ですが、このサイトではもう活動していません。
–ζ

15

多くの良い答えがありますが、私はこれを追加したかったです:あなたはPythonのバックグラウンドから来たと述べました。Pythonは、インタプリタ化されていない(少なくとも「一般的なCPythonのユースケースでは」「インターパイル」されている)言語です。これは、他のソフトウェア(Pythonインタープリター)がソースを見て何らかの方法で実行していることを意味します。これは素晴らしいモデルであり、実際のハードウェアから十分に抽象化された非常に優れた高レベル言語を可能にします。欠点は、このインタープリターソフトウェアが常に最初に必要になることです。

通常、このようなインタープリターソフトウェアは、CやC ++などのマシンコードにコンパイルされる言語で記述されています。マシンコードは、CPUが処理できるものです。CPUでできることは、メモリからいくつかのバイトを読み取り、バイト値に応じて特定の操作を開始することです。したがって、1つのバイトシーケンスは、メモリからレジスタにデータをロードするコマンドであり、2つの値を追加する別のシーケンス、レジスタからの値をメインメモリに戻す別のシーケンス、そしてすぐに(レジスタは一部である特別なメモリ領域です最適に動作するCPUの)、これらのコマンドのほとんどはそのレベルでは非常に低いです。これらのマシンコード命令を人間が読み取れるのは、アセンブラーコードです。このマシンコードは、基本的に、Windows上の.exeまたは.comファイルまたはLinux / Unixバイナリ内に保存されているものです。

これでコンピューターが起動した場合、それは馬鹿げていますが、そのようなマシンコードの指示を読み取る配線があります。PC上では、これは通常(現在)BIOS(基本入力出力システム)を含むメインボード上のEEPROMチップであり、このシステムは多くのことを行うことができず、ハードウェアなどへのアクセスを容易にしてからキー操作を行うことができます:ブートし、最初の数バイト(別名マスターブートレコード、MBR)をメモリにコピーし、CPUに「ここにプログラムがあります」と伝え、CPUはそれらのバイトをマシンコードとして処理して実行します。通常、これはいくつかのパラメーターでカーネルをロードし、そのカーネルに制御を渡すオペレーティングシステムローダーです。その後、すべてのドライバーをロードしてすべてのハードウェアにアクセスし、デスクトップまたはシェルプログラムなどをロードし、ユーザーがログインできるようにします。システムを使用します。


6
「インターパイル」?その言葉を聞いたことがありません。
ブライアンオークリー

3
この用語は5年ほど前に使用され、実行とは別の別個のコンパイルフェーズを持つ「モダン」インタープリターを表します。この用語がどこでも生き延びたかどうかは
わかり

1
「解釈されない」?その言葉を聞いたことがありません。
コールジョンソン14

12

「OSを使用せずにアプリケーションを実行する方法」を尋ねます。簡単な答えは、「OSはアプリケーションではありません」です。OSはアプリケーションと同じツールで作成でき、同じ素材で作成できますが、同じものではありません。OSは、アプリケーションと同じルールで遊ぶ必要はありません。

OTOH、実際のハードウェアとファームウェアは、OS「アプリケーション」が実行される「OS」と考えることができます。ハードウェアは非常にシンプルなOSです-マシンコードで記述された命令の実行方法を知っており、起動時に最初の命令について非常に特定のメモリアドレスを調べる必要があることを知っています。そのため、起動してすぐに最初の命令を実行し、次に2番目の命令を実行します。

そのため、OSは、既知の場所に存在し、ハードウェアと直接対話できる単なるマシンコードです。


1
+1これがベストアンサーだと思います。抽象化に関しては、適切なレベルで釘付けにされていると思います。
プリエトサンガ

6

質問に答えるには、ネイティブ(CPU用)コードがどのように見え、CPUによってどのように解釈されるかについての知識が必要です。

通常、コンパイルプロセス全体は、C、Pascal、またはPython(pypyを使用)およびC#で記述したものをCPUが理解できるものに変換すること、つまり「[メモリアドレス]の下に何かを保存する」、「レジスタeaxおよびebx」、「call関数foo」、「eaxを10と比較」。1つずつ実行されるこれらの命令は、コードで実行したいことを行います。

これについて考えてみましょう。このネイティブコードを実行するのにOSは本当に必要ありません!必要なのは、このコードをメモリにロードし、CPUがそこにあり、実行するように指示することです。ただし、それについてはあまり気にしないでください。これはBIOSが心配するべき仕事です-CPUが起動した直後、物理アドレス0x7C00でコード(1つと1つのセクターのみ)をロードします。次に、CPUがコードのこの1セクター(512 B)の実行を開始します。そして、あなたはあなたが想像する何でもできます!もちろん、OSからのサポートなし。あなたがオペレーティングシステムだからです。かっこいい?標準ライブラリ、ブースト、Python、プログラム、ドライバーはありません!すべてを自分で書く必要があります。

そして、どのようにハードウェアと通信しますか?さて、あなたには2つの選択肢があります:

  1. 「リアルモード」の内部に留まります-CPU実行モードは1 MBのメモリ(およびそれ以下)のみで、CPU拡張、メモリ保護、マルチタスクなどの高度なCPU機能はありません。16ビットの実行可能コード、古代のアドレッシングモード...しかし、単純な画面出力、キーボードサポート、ディスクI / O、電源管理など、BIOSによって提供されるいくつかのルーチンを使用します。一言で言えば、あなたはMS-DOSと16ビットCPUの時代に戻っています。
  2. CPUが持つすべての機能、インストールしたすべてのメモリなどを備えた「保護モード」になります。ただし、保護モードでは、完全に1人であり、自分ですべてを行う必要があります(また、「in」および「out」命令を使用してハードウェアと通信し、I / Oポートにデータを入出力し、割り込みを使用します。 / O)。Windows 95および最初のLinuxがこのオプションを選択してから、すべてのOSを言う必要がありますか?

今、あなたはカーネルとは何かを尋ねています。まもなく、カーネルは、あなたが直接見たり経験したりしないすべてです。キーボードからPC内のほぼすべてのハードウェアに至るまで、ドライバーとともにすべてを管理します。グラフィカルシェルまたはターミナルを使用して通信します。または、幸運なことに、OSのサポートにより、コード内の関数によって実行されます。

よりよく理解するために、私はあなたに一つのアドバイスを与えることができます:あなた自身のOSを書くようにしてください。画面に「Hello world」と書いても。


3

オペレーティングシステムの動作方法には、システムに大きく依存するいくつかの違いがあります。有用であるためには、システムは「アドレスXで実行を開始する」など、起動時に予測可能な動作が必要です。不揮発性ストレージ(フラッシュメモリなど)がプログラム空間にマッピングされているシステムの場合、起動コードをプロセッサのプログラム空間内の適切な場所に配置するだけでよいため、これは非常に簡単です。これは、マイクロコントローラーでは非常に一般的です。一部のシステムは、実行する前に他の場所から起動プログラムを取得する必要があります。これらのシステムには、いくつかの操作がハードワイヤード(またはほぼハードワイヤード)されています。別のチップからi2c経由で起動コードを取得するプロセッサがいくつかありますが、

x86ファミリのプロセッサを使用するシステムは通常、その進化と後方互換性の問題のためにかなり複雑なマルチステージブートプロセスを使用します。システムは、マザーボード上の不揮発性メモリにあるファームウェア(BIOS-Basic Input / Output System、または同様のものと呼ばれる)を実行します。場合によっては、このファームウェアの一部またはすべてをRAMにコピー(再配置)して、実行を高速化します。このコードは、どのハードウェアが存在し、ブートに使用できるかを認識して書かれています。

通常、スタートアップファームウェアは、システムにどのハードウェアが存在するかを想定して記述されています。何年も前の286マシンでは、おそらくI / OアドレスXにフロッピードライブコントローラーがあり、特定のコマンドセット(およびセクター0のコード)が与えられると、セクター0を特定のメモリ位置にロードするという仮定がありました。 BIOS独自の機能を使用してより多くのコードをロードする方法を知っており、最終的にはOSになるのに十分なコードがロードされます。マイクロコントローラでは、ブートプロセスを続行する前に、X時間から(より複雑なファームウェアを更新するために)コマンドを待機する必要がある特定の設定で動作するシリアルポートがあると想定される場合があります。

特定のシステムの正確な起動プロセスは、システムごとに異なるだけでなく、すべてに共通点があることを知っているほど重要ではありません。多くの場合、I / Oを実行する必要があるときのスタートアップ(ブートストラップ)コード内で、割り込みに依存するのではなく、I / Oデバイスがポーリングされます。これは、割り込みが複雑で、スタックRAM(まだ完全にはセットアップされていない可能性がある)を使用し、唯一の操作である場合に他の操作をブロックすることを心配する必要がないためです。

最初にロードされると、OSカーネル(カーネルはほとんどのOSの主要部分)が最初にファームウェアのように動作します。存在するハードウェアの知識または発見、プログラミング、RAMのスタックスペースとしてのセットアップ、さまざまなテストの実施、さまざまなデータ構造のセットアップ、ファイルシステムの発見とマウント、そしておそらくより多くのプログラムを起動する必要がありますあなたが書くのに慣れているプログラム(存在するOSに依存するプログラム)のように。

OSコードは通常、Cとアセンブリの混合で記述されます。OSカーネルの最初のコードは、おそらく常にアセンブリ内にあり、Cコードが依存するスタックのセットアップなどを行い、C関数を呼び出します。OSが実行する必要のある操作は、Cで表現できないことが多いため(コンテキストスイッチング/スタックのスタックなど)、他の手書きのアセンブリもそこにあります。多くの場合、特別なフラグをCコンパイラに渡して、ほとんどのCプログラムが使用する標準ライブラリに依存しないこと、およびint main(int argc, char *argv[])プログラムで。さらに、ほとんどのアプリケーションプログラマが決して使用しない特別なリンカーオプションを使用する必要があります。これらにより、カーネルコードが特定のアドレスでロードされることを期待したり、特定の場所に外部変数があるように見えるように設定したりすることがあります(これらの変数はCコードで宣言されていません他の特別なメモリ位置)。

操作全体は最初は魔法のように見えますが、それを調べてその一部を理解すると、その魔法は実装するためにより多くの計画とシステムの知識を必要とするプログラムのセットになります。ただし、それらのデバッグには手間がかかります。


3

オペレーティングシステムがどのように機能するかを理解するには、それらを2つのカテゴリに分割すると役立つ場合があります:リクエストに応じてアプリケーションにサービスを提供するだけのものと、CPUのハードウェア機能を使用してアプリケーションがすべきでないことを防ぐものです。MS-DOSは以前のスタイルでした。3.0以降のWindowsのすべてのバージョンは後者のスタイルでした(少なくとも8086より強力なものを実行している場合)。

PC-DOSまたはMS-DOSを実行する元のIBM PCは、以前のスタイルの "OS"の例でした。アプリケーションが画面にキャラクターを表示したい場合は、いくつかの方法があります。MS-DOSに「標準出力」に送信するよう求めるルーチンを呼び出すことができます。それが行われた場合、MS-DOSは出力がリダイレクトされているかどうかを確認し、リダイレクトされていない場合は、ROMに格納されているルーチン(IBMがBasic Input / Output Systemと呼ぶルーチンのコレクション内)を呼び出して、文字を表示しますカーソルの位置とカーソルを移動します(「テレタイプを書き込む」)。そのBIOSルーチンは、0xB800:0から0xB800:3999の範囲のどこかにバイトのペアを格納します。Color Graphics Adapterのハードウェアは、その範囲内のバイトペアを繰り返しフェッチします。各ペアの最初のバイトを使用して文字の形状を選択し、2番目のバイトを使用して前景色と背景色を選択します。バイトがフェッチされ、赤、緑、青の信号に処理されて、読みやすいテキスト表示が生成されます。

IBM PC上のプログラムは、DOSの「標準出力」ルーチンを使用するか、BIOSの「テレタイプを書き込む」ルーチンを使用するか、メモリに直接保存してテキストを表示できます。多くのテキストを表示する必要のある多くのプログラムは、DOSルーチンを使用するよりも文字通り何百倍も高速である可能性があるため、後者のアプローチをすぐに選択しました。これは、DOSおよびBIOSルーチンが非常に非効率的だったからではありません。ディスプレイがブランクになっていない限り、特定の時間にのみ書き込むことができます。文字を出力するBIOSルーチンは、いつでも呼び出せるように設計されています。したがって、各要求は、書き込み操作を実行するために適切な時間を待って新たに開始する必要がありました。対照的に、必要なことを知っているアプリケーションコードは、ディスプレイを作成する利用可能な機会を中心に整理できます。

ここで重要な点は、DOSとBIOSがディスプレイにテキストを出力する手段を提供する一方で、そのような機能に関して特に「魔法の」ものは何もなかったことです。ディスプレイにテキストを書き込みたいアプリケーションは、少なくともディスプレイハードウェアがアプリケーションの期待どおりに機能した場合(誰かがCGAに似ているが文字メモリを備えたモノクロディスプレイアダプタをインストールした場合) 0xB000:0000-0xB000:3999にある)、BIOSは自動的に文字を出力します。MDAまたはCGAのいずれかで動作するようにプログラムされたアプリケーションでも同様に実行できますが、CGA専用にプログラムされたアプリケーションはMDAではまったく役に立ちません)。

新しいシステムでは、状況は少し異なります。プロセッサにはさまざまな「特権」モードがあります。それらは、最も特権的なモードで開始します。このモードでは、コードは必要なことを何でも行うことができます。その後、選択した範囲のメモリまたはI / O機能のみが利用可能な制限モードに切り替えることができます。コードは制限モードから特権モードに直接切り替えることはできませんが、プロセッサは特権モードエントリポイントを定義しており、制限モードコードはプロセッサに特権モードのエントリポイントの1つでコードの実行を開始するように依頼できます。さらに、制限モードでは禁止される多くの操作に関連付けられた特権モードエントリポイントがあります。たとえば、誰かが複数のMS-DOSアプリケーションを同時に実行し、それぞれが独自の画面を持っているとします。アプリケーションが0xB800:0のディスプレイコントローラーに直接書き込むことができる場合、あるアプリケーションが別のアプリケーションの画面を上書きするのを防ぐ方法はありません。一方、OSはアプリケーションを制限モードで実行し、ディスプレイメモリへのアクセスをトラップできます。「バックグラウンド」にあるはずのアプリケーションが0xB800:160を書き込もうとしていることを発見した場合、バックグラウンドアプリケーションの画面バッファーとして取っておいたメモリにデータを保存できます。そのアプリケーションが後でフォアグラウンドに切り替えられた場合、バッファは実際の画面にコピーされる可能性があります。OSはアプリケーションを制限モードで実行し、ディスプレイメモリへのアクセスをトラップできます。「バックグラウンド」にあるはずのアプリケーションが0xB800:160を書き込もうとしていることを発見した場合、バックグラウンドアプリケーションの画面バッファーとして取っておいたメモリにデータを保存できます。そのアプリケーションが後でフォアグラウンドに切り替えられた場合、バッファは実際の画面にコピーされる可能性があります。OSはアプリケーションを制限モードで実行し、ディスプレイメモリへのアクセスをトラップできます。「バックグラウンド」にあるはずのアプリケーションが0xB800:160を書き込もうとしていることを発見した場合、バックグラウンドアプリケーションの画面バッファーとして取っておいたメモリにデータを保存できます。そのアプリケーションが後でフォアグラウンドに切り替えられた場合、バッファは実際の画面にコピーされる可能性があります。

注意すべき重要な点は、(1)テキストの表示などのさまざまな標準サービスを実行するためのルーチンの標準セットがあると便利ですが、「特権モード」で実行されていたアプリケーションが実行できなかったことは何もしませんインストールされているハードウェアを処理するように適切にプログラムされている場合。(2)今日実行されているほとんどのアプリケーションは、オペレーティングシステムによってこのようなI / Oを直接実行できませんが、特権モードで起動するプログラムは必要なことをすべて実行し、制限付きモードに必要なルールを設定できますプログラム。


2

Stephen C.が言ったように、それはオペレーティングシステムを起動することだけでなく、それがどのように動作し、その上にあるハードウェアやソフトウェアと相互作用するかについてでもあります。

彼の答えに加えて、「コンピューティングシステムの要素」をご覧ください。これは本といくつかのツールであり、コンピューター、オペレーティングシステム、コンパイラーの相互作用を説明しています。ユニークな点は、シミュレーション環境で独自のオペレーティングシステムを非常に迅速に開発するためのツールを提供し、実際のシステムに必要な多くの詳細を無視して、概念を把握できることです。木ではなく森林を見ることができるという素晴らしい仕事をします。

オペレーティングシステムとハードウェアとの相互作用について詳しく知りたい場合は、Minixを確認してください。


1

OSを作成します。どういうわけか実行する必要があり、その方法は別のOS内ですか?

アプリケーションはOS内で実行されています。このオペレーティングシステムは、ファイルを開いたり、バイトを書き込んだりするなど、アプリケーションにサービスを提供します。これらのサービスは通常、システムコールを介して提供されます。

オペレーティングシステムはハードウェア内で実行されています。ハードウェアは、シリアルポートのボーレートの設定やバイトの書き込みなど、オペレーティングシステムにサービスを提供します。これらのサービスは通常、メモリマップレジスタまたはI / Oポートを介して提供されます。


これがどのように機能するかの非常に単純化された例を与えるには:

アプリケーションは、ファイルに何かを書き込むようオペレーティングシステムに指示します。オペレーティングシステムは、アプリケーションにファイルやディレクトリなどの概念を提供します。

ハードウェアでは、これらの概念は存在しません。ハードウェアは、512バイトの固定ブロックに分割されたディスクのような概念を提供します。オペレーティングシステムは、ファイルに使用するブロックと、ファイル名、サイズ、ディスク上の場所などのメタデータの他のブロックを決定します。次に、ハードウェアに指示します。これらの512バイトを、その番号のディスク上のこの番号のセクターに書き込みます。これらの他の512バイトを、同じ番号のディスク上のこの異なる番号のセクターに書き込みます。等々。

オペレーティングシステムがそれを行うようにハードウェアに指示する方法は大きく異なります。オペレーティングシステムの機能の1つは、これらの違いからアプリケーションを保護することです。ディスクの例では、ある種のハードウェアでは、オペレーティングシステムはディスクとセクター番号をI / Oポートに書き込み、その後バイトを1つずつ個別のI / Oポートに書き込む必要があります。別の種類のハードウェアでは、オペレーティングシステムはセクターの512バイト全体をメモリの領域にコピーし、メモリのその領域の場所を特別なメモリの場所に書き込み、ディスクとセクター番号をさらに別の場所に書き込む必要があります特別なメモリの場所。


今日のハイエンドハードウェアは非常に複雑です。プログラミングの詳細をすべて記載したマニュアルは、数千ページに及ぶドアストッパーです。たとえば、最新のIntel CPUマニュアルは7ボリュームで、合計4000ページ以上あります。これはCPU専用です。他のほとんどのコンポーネントは、メモリブロックまたはI / Oポートを公開します。オペレーティングシステムは、それらのアドレススペース内のアドレスにマップするようにCPUに指示できます。これらのコンポーネントのいくつかは、いくつかのI / Oポートまたはメモリアドレスの背後にさらに多くのものを公開します。一例として、RTC(リアルタイムクロック、電源がオフの間コンピューターの時間を保持するコンポーネント)は、1組のI / Oポートの後ろに数百バイトのメモリを公開します。元のPC / AT。ハードディスクのようなものは完全に独立したプロセッサを持っています、オペレーティングシステムが標準化されたコマンドを介して通信します。GPUはさらに複雑です。

上記のコメントの何人かの人々はArduinoを提案しました。私は彼らに同意します、それは理解するのがはるかに簡単です-ATmega328はシリアルポートとしてUSBコネクタを公開することを除いてArduino Unoですべてを行います、わずか数百ページのマニュアルがあります。Arduinoでは、ハードウェア上で直接実行し、間にオペレーティングシステムはありません。ほんの数個の小さなライブラリルーチン。必要ない場合は使用する必要はありません。


1

実行可能な例

技術的には、OSなしで実行されるプログラムはOSです。それでは、ごくわずかなHello World OSを作成して実行する方法を見てみましょう。

以下のすべての例のコードは、このGitHubリポジトリにあります。

ブートセクター

x86では、ブートセクタの一種であるマスターブートセクタ(MBR)を作成して、ディスクにインストールすることができます。

ここでは、1回のprintf呼び出しで作成します。

printf '\364%509s\125\252' > main.img
sudo apt-get install qemu-system-x86
qemu-system-x86_64 -hda main.img

結果:

ここに画像の説明を入力してください

Ubuntu 18.04、QEMU 2.11.1でテスト済み。

main.img 次のものが含まれます。

  • \3648進数== 0xf416進数:hlt命令のエンコード。CPUに動作を停止するよう指示します。

    したがって、プログラムは何も行いません。開始と停止のみです。

    \x16進数はPOSIXで指定されていないため、8進数を使用します。

    このエンコードは次の方法で簡単に取得できます。

    echo hlt > a.asm
    nasm -f bin a.asm
    hd a
    

    もちろん、0xf4エンコーディングはIntelのマニュアルにも記載されています。

  • %509s509個のスペースを作成します。バイト510までファイルに入力する必要がありました。

  • \125\2528進数==の0x55後に0xaa:ハードウェアに必要なマジックバイト。バイト511および512でなければなりません。

    存在しない場合、ハードウェアはこれを起動可能なディスクとして扱いません。

何もしなくても、いくつかの文字がすでに画面に印刷されていることに注意してください。これらはファームウェアによって印刷され、システムを識別するのに役立ちます。

実際のハードウェアで実行する

エミュレータは楽しいものですが、ハードウェアは本当に重要です。

ただし、これは危険であり、誤ってディスクを消去する可能性があることに注意してください。これは、重要なデータが含まれていない古いマシンでのみ実行してください。さらに良いことには、Raspberry Piなどの開発ボードについては、以下のARMの例を参照してください。

典型的なラップトップの場合、次のようなことをする必要があります。

  • 画像をUSBスティックに書き込みます(データが破壊されます!):

    sudo dd if=main.img of=/dev/sdX
    
  • USBをコンピューターに接続します

  • それをオン

  • USBから起動するように指示します。

    これは、ファームウェアがハードディスクの前にUSBを選択することを意味します。

    それがお使いのマシンのデフォルトの動作ではない場合、電源投入後、USBから起動することを選択できる起動メニューが表示されるまで、Enter、F12、ESCなどの奇妙なキーを押し続けます。

    多くの場合、これらのメニューで検索順序を構成できます。

たとえば、古いLenovo Thinkpad T430、UEFI BIOS 1.16では、次のように表示されます。

こんにちは世界

最小限のプログラムを作成したので、こんにちは世界に移りましょう。

明らかな質問は次のとおりです。IOを行う方法は?いくつかのオプション:

  • ファームウェア(BIOSやUEFIなど)に依頼してください
  • VGA:書き込まれた場合に画面に印刷される特別なメモリ領域。保護モードで使用できます。
  • ドライバーを作成し、ディスプレイハードウェアと直接話します。これは「適切な」方法です。より強力ですが、より複雑です。
  • シリアルポート。これは、ホスト端末から文字を送受信する非常に単純な標準化されたプロトコルです。

    ソース

    残念ながら、ほとんどの最新のラップトップでは公開されていませんが、開発ボードを使用する一般的な方法です。以下のARMの例を参照してください。

    このようなインターフェースは、たとえばLinuxカーネルのデバッグに非常に役立つため、これは本当に残念です。

  • チップのデバッグ機能を使用します。例えば、ARMはセミホスティングを呼び出します。実際のハードウェアでは、追加のハードウェアとソフトウェアのサポートが必要ですが、エミュレーターでは無料の便利なオプションになります。

ここでは、x86でより簡単なBIOSの例を実行します。ただし、これは最も堅牢な方法ではないことに注意してください。

main.S

.code16
    mov $msg, %si
    mov $0x0e, %ah
loop:
    lodsb
    or %al, %al
    jz halt
    int $0x10
    jmp loop
halt:
    hlt
msg:
    .asciz "hello world"

link.ld

SECTIONS
{
    . = 0x7c00;
    .text :
    {
        __start = .;
        *(.text)
        . = 0x1FE;
        SHORT(0xAA55)
    }
}

アセンブルとリンク:

gcc -c -g -o main.o main.S
ld --oformat binary -o main.img -T linker.ld main.o

結果:

ここに画像の説明を入力してください

テスト済み:Lenovo Thinkpad T430、UEFI BIOS 1.16。Ubuntu 18.04ホストで生成されたディスク。

標準のユーザーランドアセンブリ手順の他に、次のものがあります。

  • .code16:GASに16ビットコードを出力するよう指示します

  • cli:ソフトウェア割り込みを無効にします。これらは、プロセッサがhlt

  • int $0x10:BIOS呼び出しを行います。これは、文字を1つずつ印刷するものです。

重要なリンクフラグは次のとおりです。

  • --oformat binary:生のバイナリアセンブリコードを出力します。通常のユーザーランド実行可能ファイルの場合のように、ELFファイル内でワープしないでください。

アセンブリの代わりにCを使用

Cはアセンブリにコンパイルされるため、標準ライブラリなしでCを使用するのは非常に簡単なので、基本的には次のものが必要です。

  • メモリ内の適切な場所に配置するリンカスクリプト
  • GCCに標準ライブラリを使用しないよう指示するフラグ
  • に必要なC状態を設定する小さなアセンブリエントリポイントmain、特に:
    • スタック
    • ゼロアウトBSS

TODO:GitHubでx86の例をリンクします。これは私が作成したARMの1つです

ただし、標準ライブラリを使用する場合は、POSIXを介してC標準ライブラリ機能の多くを実装するLinuxカーネルがないため、物事はさらに楽しくなります。

Linuxのような本格的なOSに移行することなく、いくつかの可能性があります。

  • ニューリブ

    詳細な例:https : //electronics.stackexchange.com/questions/223929/c-standard-libraries-on-bare-metal/223931

    Newlibでは、syscallsを自分で実装する必要がありますが、非常に最小限のシステムを取得し、それらを実装するのは非常に簡単です。

    たとえばprintf、UARTまたはARMシステムにリダイレクトしたりexit()セミホスティングで実装したりできます。

  • FreeRTOSZephyrなどの組み込みオペレーティングシステム。

    このようなオペレーティングシステムでは、通常、プリエンプティブスケジューリングをオフにできるため、プログラムのランタイムを完全に制御できます。

    それらは、事前実装されたNewlibの一種と見ることができます。

ARMでは、一般的な考え方は同じです。アップロードしました:

Raspberry Piの場合、https://github.com/dwelch67/raspberrypiは、今日入手可能な最も人気のあるチュートリアルのように見えます。

x86との違いは次のとおりです。

  • IOは、マジックアドレスに直接書き込むことで行われinout指示はありません。

    これは、メモリマップIOと呼ばれます

  • Raspberry Piなどの一部の実際のハードウェアでは、ファームウェア(BIOS)を自分でディスクイメージに追加できます。

    ファームウェアの更新がより透過的になるため、これは良いことです。

ファームウェア

実際、ブートセクタは、システムのCPUで実行される最初のソフトウェアではありません。

実際に最初に実行されるのは、ソフトウェアであるファームウェアと呼ばれるものです

  • ハードウェアメーカーによって作られた
  • 通常はクローズドソースですが、Cベースの可能性があります
  • 読み取り専用メモリに保存されるため、ベンダーの同意なしに変更することは難しく/不可能です。

よく知られているファームウェアは次のとおりです。

  • BIOS:古いすべてが存在するx86ファームウェア。SeaBIOSは、QEMUで使用されるデフォルトのオープンソース実装です。
  • UEFI:BIOSの後継で、より標準化されていますが、より機能的で、非常に肥大化しています。
  • Coreboot:高貴なクロスアーチオープンソースの試み

ファームウェアは次のようなことを行います:

  • 起動可能なものが見つかるまで、各ハードディスク、USB、ネットワークなどをループします。

    QEMUを実行する-hdaと、それmain.imgはハードウェアに接続されたハードディスクであり、

    hda 最初に試行されるものであり、使用されます。

  • 最初の512バイトをRAMメモリアドレス0x7c00にロードし、そこにCPUのRIPを配置して実行します

  • ブートメニューやBIOS印刷呼び出しなどをディスプレイに表示する

ファームウェアは、ほとんどのOSが依存するOSのような機能を提供します。たとえば、PythonサブセットがBIOS / UEFIで実行するように移植されています:https : //www.youtube.com/watch?v=bYQ_lq5dcvM

ファームウェアはOSと見分けがつかず、ファームウェアは唯一可能な「真の」ベアメタルプログラミングであると主張できます。

このCoreOS開発者が言うように

難しい部分

PCの電源を入れたとき、チップセットを構成するチップ(ノースブリッジ、サウスブリッジ、SuperIO)はまだ適切に初期化されていません。BIOS ROMは可能な限りCPUから取り外されていますが、CPUからアクセスする必要があります。そうしないと、CPUには実行する命令がありません。これは、BIOS ROMが完全にマップされることを意味するものではなく、通常はマップされます。ただし、ブートプロセスを開始するのに十分な量がマップされています。他のデバイスは、忘れてください。

QEMUでCorebootを実行すると、Corebootの上位層とペイロードを試すことができますが、QEMUは低レベルのスタートアップコードを試す機会がほとんどありません。1つには、RAMは最初から正しく機能します。

BIOS初期状態のポスト

ハードウェアの多くのものと同様に、標準化は脆弱であり、依存すべきでないことの1つは、BIOSの後にコードが実行を開始するときのレジスタの初期状態です。

だから、あなた自身に賛成して、次のようないくつかの初期化コードを使用してくださいhttps : //stackoverflow.com/a/32509555/895245

レジスタのように%dsして%es、重要な副作用を持っているので、あなたが明示的にそれらを使用していない場合でも、それらをゼロにする必要があります。

一部のエミュレーターは実際のハードウェアよりも優れており、初期状態が優れていることに注意してください。その後、実際のハードウェアで実行すると、すべてが壊れます。

GNU GRUBマルチブート

ブートセクターは単純ですが、あまり便利ではありません。

  • ディスクごとに1つのOSのみを使用できます
  • ロードコードは本当に小さく、512バイトに収まる必要があります。これは、int 0x13 BIOS呼び出しで解決できます。
  • 保護モードに移行するなど、多くのスタートアップを自分で行う必要があります

これらの理由により、GNU GRUBはマルチブートと呼ばれるより便利なファイル形式を作成しました。

最小限の作業例:https : //github.com/cirosantilli/x86-bare-metal-examples/tree/d217b180be4220a0b4a453f31275d38e697a99e0/multiboot/hello-world

また、GitHubサンプルリポジトリで使用して、USBを100万回書き込むことなく、実際のハードウェアですべてのサンプルを簡単に実行できるようにします。QEMUでは次のようになります。

ここに画像の説明を入力してください

OSをマルチブートファイルとして準備すると、GRUBは通常のファイルシステム内でそれを見つけることができます。

これは、ほとんどのディストリビューションが行うことで、OSイメージをの下に置きます/boot

マルチブートファイルは、基本的に特別なヘッダーを持つELFファイルです。GRUBで指定されています:https : //www.gnu.org/software/grub/manual/multiboot/multiboot.html

を使用して、マルチブートファイルを起動可能なディスクに変換できますgrub-mkrescue

エル・トリト

CDに書き込むことができる形式:https : //en.wikipedia.org/wiki/El_Torito_%28CD-ROM_standard%29

ISOまたはUSBで動作するハイブリッドイメージを作成することもできます。これはgrub-mkrescue)で実行できます。また、make isoimageを使用してLinuxカーネルでも実行できますisohybrid

資源

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.