C++などのソースコードをコンパイルすると、コンパイラからの出力はマシンコード(実行ファイル)になりますが、これはCPUへの命令だと思っていました。最近カーネルについて調べていて、プログラムはハードウェアに直接アクセスすることができず、カーネルを経由しなければならないことを知りました
では、単純なソースコードをコンパイルしたとき、例えば printf()
関数だけでコンパイルして、そのコンパイルが実行可能なマシンコードを生成したとき、このマシンコードの各命令はメモリから直接実行されるのでしょうか (一旦 OS によってメモリにロードされると) それともマシンコードの各命令は実行されるために OS (カーネル) を経由する必要があるのでしょうか?
似たような質問を読んだことがあります。それはコンパイル後に生成されるマシンコードが直接CPUへの命令なのか、それともCPUへの正しい命令を生成するために再度カーネルを経由する必要があるのかについては説明していませんでした。つまり、マシンコードがメモリにロードされた後はどうなるのでしょうか?カーネルを経由するのか、それとも直接プロセッサに話しかけるのか
55 GRANZER 2018-07-27
OSなしで実行するプログラムを書いたことがある者として、私は決定的な答えを提示します
実行ファイルを実行するにはOSカーネルが必要ですか?
それは、そのプログラムがどのように書かれ、どのように構築されたかによります。 OSを全く必要としないプログラムを(知識があれば)書くことができます。 そのようなプログラムはスタンドアロンと呼ばれます。 スタンドアロンプログラムの代表的な用途としては、ブートローダーや診断プログラムなどがあります
しかし、いくつかのホストOS環境で書かれ、構築された典型的なプログラムは、デフォルトではそのホストOS環境で実行されます。 スタンドアロンのプログラムを書いてビルドするには、非常に明確な決定とアクションが必要です
… コンパイラからの出力はマシンコード(実行ファイル)で、CPUへの直接の命令だと思っていました
Correct.
最近カーネルを読み漁っていて、プログラムはハードウェアに直接アクセスできないが、カーネルを経由しなければならないことがわかった
これは、OSがプログラムを実行するために使用するCPUモードによって課せられる制限であり、コンパイラやライブラリなどの特定のビルドツールによって促進されます。 これまでに書かれたすべてのプログラムに内在する制限ではありません
では、単純なソースコードを、例えば printf() 関数だけでコンパイルして、そのコンパイルが実行可能なマシンコードを生成した場合、このマシンコードの各命令はメモリから直接実行されるのでしょうか (一旦 OS によってメモリにロードされると) それともマシンコードの各命令は実行されるために OS (カーネル) を経由する必要があるのでしょうか?
すべての命令はCPUによって実行されます。 サポートされていない命令や不正な命令(プロセスの特権が不十分な場合など)は、直ちに例外が発生し、CPUはこの異常な状態を処理するためのルーチンを実行します
printf()関数を「単純なソースコード」の例として使うべきではありません。 オブジェクト指向の高レベルプログラミング言語からマシンコードへの変換は、あなたが暗示するほど些細なものではないかもしれません。 そして、データ変換やI/Oを行うランタイムライブラリから、最も複雑な関数の一つを選ぶのです
あなたの質問は、OS(とランタイムライブラリ)のある環境を規定していることに注意してください。 システムが起動され、OSがコンピュータを制御するようになると、プログラムができることに制限が課せられます(例:I/OはOSが実行しなければなりません)。 スタンドアロンのプログラムを実行したい場合(つまりOSなしで)は、OSを実行するためにコンピュータを起動してはいけません
… マシンコードがメモリにロードされた後は?
それは環境によります
スタンドアロンのプログラムの場合は、それを実行することができ、すなわち、プログラムの開始アドレスにジャンプして制御を引き継ぐことができます
OSがロードしたプログラムは、そのプログラムが依存している共有ライブラリと動的にリンクされていなければなりません。OSは、プログラムを実行するプロセスのための実行空間を作成しなければならない
カーネルを経由するのか、プロセッサに直接話しかけるのか
マシンコードはCPUによって実行されます。 カーネルを通る」のではなく、「プロセッサに話しかける」のでもありません。 マシンコード(オペランドとオペコードで構成される)は、CPUへの命令をデコードして演算を行うものです
おそらく、次に調査すべきトピックは CPUモード です
87 sawdust 2018-07-27
カーネルは「単なる」より多くのコードです。そのコードはシステムの最下位部分と実際のハードウェアの間に存在する層であるというだけのことです
それはすべてCPU上で直接実行され、あなたは何かをするためにそれの層を介して移行するだけです
あなたのプログラムは、printf
コマンドを使うために標準のCライブラリを必要とするのと同じように、カーネルを「必要としている」のです
あなたのプログラムの実際のコードはCPU上で実行されますが、画面に何かを印刷するためのコードの分岐は、Cのprintf
関数のコードを通り、他の様々なシステムやインタプリタを通り、それぞれが独自の処理をしてhello world!
が実際にどのように画面に印刷されるのかを調べます
デスクトップのウィンドウマネージャ上でターミナルプログラムを実行していて、カーネル上で実行していて、ハードウェア上で実行しているとします
他にもたくさんのことがありますが、シンプルにしておきましょう
- ターミナルプログラムで、
hello world!
を印刷するプログラムを実行します - ターミナルは、プログラムが(C出力ルーチンを介して)
hello world!
をコンソールに書き込んだことを確認します - ターミナルプログラムはデスクトップのウィンドウマネージャに “I got
hello world!
written at me, can you put it at positionx
,y
please? “と言って上がってくる - デスクトップウィンドウマネージャがカーネルに「私のプログラムの一つがあなたのグラフィックデバイスにこの位置にテキストを配置したいと思っています
- カーネルはグラフィックデバイスドライバにリクエストを渡し、グラフィックカードが理解できるようにフォーマットします
- グラフィックカードがどのように接続されているかに応じて、他のカーネルデバイスドライバを呼び出して、PCIeのような物理デバイスバス上でデータをプッシュし、正しいデバイスが選択されていることを確認したり、データが関連するブリッジやコンバータを通過できるようにするなどの処理を行う必要があります
- ハードウェアは何かを表示します
説明だけのために大げさな言い方をしています。ここにはドラゴンがいます
事実上、ハードウェアアクセスを必要とするすべての作業は、ディスプレイ、メモリブロック、ファイルのビット、またはそのようなものは、カーネル内のデバイスドライバを通過しなければならず、関連するデバイスと通信する方法を正確に理解しなければなりません。それがSATAハードディスクコントローラドライバの上にあるファイルシステムドライバであっても、PCIeブリッジデバイスの上にあるファイルシステムドライバであっても構いません
カーネルはこれらすべてのデバイスを結びつける方法を知っており、プログラムがこれらすべてのことを自分で行う方法を知らなくてもできるようにするための比較的シンプルなインターフェースを提供しています
デスクトップのウィンドウマネージャは、プログラムがウィンドウを描画する方法を知らなくても、同時に物事を表示しようとする他のプログラムとうまく遊ぶことを意味するレイヤーを提供します
最後に、ターミナルプログラムは、あなたのプログラムがウィンドウを描画する方法や、カーネルグラフィックスカードドライバとの通信方法、画面バッファや表示タイミングの扱いや、実際にデータラインを表示するための複雑な操作のすべてを知る必要がないことを意味します
それはすべて、コードを何層にも重ねて処理されます
39 Mokubai 2018-07-27
環境によります。IBM 1401のような古い(もっとシンプルな!)コンピュータの多くでは、答えは「ノー」でしょう。コンパイラとリンカはスタンドアロンの “バイナリ “を出力していて、OSなしで動作していたとします。プログラムの実行が停止すると、別のものをロードします
現代の環境では、一度に一つのプログラムを実行しているわけではないので、オペレーティング・システムが必要です。CPUコア、RAM、大容量記憶装置、キーボード、マウス、ディスプレイなどを複数のプログラム間で一度に共有するには、協調性が必要です。OSはそれを提供します。現代の環境では、あなたのプログラムはディスクやSSDを読み書きすることはできません。OSは、ストレージデバイスにアクセスしたいすべてのプログラムからそのような要求を取得し、アクセス制御のようなものを実装し(一般ユーザがOSのファイルに書き込むことはできません)、デバイスにそれらをキューに入れ、返された情報を正しいプログラム(プロセス)に振り分けます
さらに、現代のコンピュータは(例えば1401とは異なり)昔のIBMが販売していたものだけでなく、非常に多様なI/Oデバイスの接続をサポートしています。コンパイラやリンカはすべての可能性を知ることはできません。例えば、キーボードはPS/2やUSBを介して接続されているかもしれません。OSはあなたがそれらのデバイスに話をする方法を知っているデバイス固有の “デバイスドライバ “をインストールすることができます, しかし、OSにデバイスクラスのための共通のインターフェイスを提示します。だから、あなたのプログラム, さらにはOS, USB対PS/2キーボードからキーストロークを取得するための別の何かを行う必要はありません, またはアクセスするために, 例えば, ローカルSATAディスク対USBストレージデバイス対NASやSAN上のどこかオフにあるストレージ.これらの詳細は、様々なデバイスコントローラのデバイスドライバによって処理されます
大容量ストレージデバイスの場合、OSはそれらのすべての上にファイルシステムドライバを提供し、ストレージがどこでどのように実装されているかに関係なく、ディレクトリとファイルへの同じインタフェースを提示します。また、OSはアクセス制御とシリアル化を心配しています。一般的には、例えば、同じファイルは、いくつかのフープを飛び越えることなく、一度に複数のプログラムで書き込みのために開かれるべきではありません(しかし、同時読み取りは一般的にOKです)
だから、現代の汎用環境では、そうですね、本当にOSが必要です。しかし、今でもリアルタイムコントローラのようなコンピュータは、OSを必要とするほど複雑ではありません
例えばArduino環境では、OSは存在しません。確かに、ビルド環境がビルドするすべての「バイナリ」に組み込まれたライブラリコードの束があります。しかし、そのコードは次から次へと永続化されないので、OSではありません
21 Jamie Hanrahan 2018-07-27
多くの回答は、この質問を誤解していると思います
コンパイラはマシンコードを出力します。このマシンコードはCPUによって直接実行されるのか、それともカーネルによって「解釈」されるのか
基本的にはCPUが直接マシンコードを実行します。カーネルがすべてのアプリケーションを実行するのはかなり遅いでしょう。ただし、いくつかの注意点があります
OSが存在する場合、アプリケーションプログラムは通常、特定の命令の実行や特定のリソースへのアクセスを制限されます。例えば、アプリケーションがシステムの割り込みテーブルを変更する命令を実行した場合、CPUは代わりにOSの例外ハンドラにジャンプし、問題のあるアプリケーションを終了させます。また、アプリケーションは通常、デバイスメモリへの読み書きを許可されていません。これらの特別なメモリ領域にアクセスすることは、OSがグラフィックカードやネットワークインターフェース、システムクロックなどのデバイスと通信する方法です
OSがアプリケーションに与える制限は、特権モード、メモリ保護、割り込みなどのCPUの特殊な機能によって実現されています。スマートフォンやPCに搭載されているCPUには、これらの機能が搭載されていますが、特定のCPUには搭載されていないものもあります。これらの CPU は、必要な機能を実現するために、アプリケーションコードを「解釈」する特別なカーネルを必要とします。非常に興味深い例として、ギガトロンがあります
Java のようないくつかの言語は、バイトコードと呼ばれるものに「コンパイル」しますが、これは実際にはマシンコードではありません。過去にはプログラムを実行するために解釈されていましたが、最近では ジャストインタイムコンパイル と呼ばれるものが通常使用されているため、マシンコードとしてCPU上で直接実行されてしまいます
仮想マシンでソフトウェアを実行するには、ハイパーバイザーと呼ばれるプログラムによってマシンコードを「解釈」する必要がありました。VMに対する膨大な業界の需要のため、CPUメーカーは、ゲストシステムのほとんどの命令がCPUによって直接実行されるように、VTxのような機能をCPUに追加してきました。しかし、互換性のないCPU用に設計されたソフトウェアを仮想マシンで実行する場合(例えば、ファミコンのエミュレート)、マシンコードを解釈する必要があります
10 Artelius 2018-07-28
あなたのコードをコンパイルするとき、(ほとんどの場合)システムライブラリ(例えばprintf
など)に依存する、いわゆる “オブジェクト “コードを作成し、あなたのコードは、あなたの特定のオペレーティングシステムが認識できるプログラムローダのようなものを追加するリンカによってラップされます。だから、あなたのプログラムはサンドイッチの中の肉のようなものであり、全体では、バンドルとしてのみ食べることができます
最近、カーネルを読んでいて、プログラムはハードウェアに直接アクセスできないが、カーネルを経由しなければならないことを知りました
あなたのプログラムがカーネルモードのドライバであれば、ハードウェアと「話す」方法を知っていれば、実際にはハードウェアに直接アクセスすることができますが、通常は (特に文書化されていない、あるいは複雑なハードウェアの場合は) カーネルライブラリのドライバを使用しています。この方法では、アドレスやレジスタ、タイミングや他の多くのことを知る必要がなく、ほとんど人間が読める方法でハードウェアと話す方法を知っているAPI関数を見つけることができます
このマシンコードの各命令はメモリから直接実行されるのか(一旦OSがメモリにロードした後)、それともマシンコードの各命令はOS(カーネル)を経由して実行される必要があるのか
まあ、カーネルはウェイトレスとして、その責任はあなたをテーブルに案内し、あなたにサービスを提供することです。カーネルができない唯一のことは、あなたのために食事をすることです。カーネルはあなたのプログラムをメモリに展開し、CPUが直接実行するマシンコードであるあなたのコードを起動します。カーネルは、あなたが何をしていいのか、何をしてはいけないのか、あなたを監督する必要があるだけです
コンパイル後に生成されるマシンコードが直接CPUへの命令なのか、それともCPUへの正しい命令を生成するために再度カーネルを経由する必要があるのかは説明されていないのでしょうか?
コンパイル後に生成されるマシンコードは、CPUへの直接命令です。これは間違いありません。唯一心に留めておく必要があるのは、コンパイルされたファイルのすべてのコードが実際のマシン/CPUのコードであるとは限らないということです。リンカは、あなたのプログラムをどうするかの手がかりとして、カーネルだけが解釈できるメタデータであなたのプログラムを包んでいます
マシンコードがメモリにロードされた後はどうなるのでしょうか?カーネルを経由するのか、プロセッサに直接話しかけるのか
もしあなたのコードが2つのレジスタの追加のような単純なオペコードであれば、カーネルの助けを借りずにCPUが直接実行しますが、ライブラリの関数を使ったコードであれば、そのような呼び出しはカーネルの助けを借りて実行されます
まあ、コメントでの炎上を防ぐために – それは本当に単純化されすぎたモデルであり、私はOPが基本的なことを理解するのに役立つことを願っていますが、この答えを改善するための良い提案は歓迎します
5 Alex 2018-07-27
では、単純なソースコードを、例えばprintf()関数だけでコンパイルして実行可能なマシンコードを生成した場合、このマシンコードの各命令はメモリから直接実行されるのでしょうか、それともマシンコードの各命令はOS(カーネル)を経由して実行される必要があるのでしょうか?
基本的には、システムコールのみがカーネルに送られます。I/O やメモリの割り当て/解放に関係するものは、通常、最終的にはシステムコールになります。いくつかの命令はカーネルモードでしか実行できず、CPUが例外を誘発します。例外はカーネルモードへの切り替えとカーネルコードへのジャンプを引き起こします
カーネルはプログラムのすべての命令を処理するわけではありません。システムコールを行い、実行中のプログラムを切り替えてCPUを共有するだけです
ユーザモードで(カーネルなしで)メモリ割り当てを行うことはできません、アクセス許可のないメモリにアクセスすると、以前にカーネルによってプログラムされていたMMUが気付いてCPUレベルの「セグメンテーションフォルト」例外を発生させ、カーネルがトリガーとなり、カーネルがプログラムをキルします
ユーザモードで (カーネルなしで) I/O を行うことはできません。デバイスの I/O ポートやレジスタ、デバイスに接続されたアドレス (I/O を実行するために必要な 1 つまたは両方) にアクセスすると、同じように例外が発生します
実行ファイルを実行するにはOSカーネルが必要ですか?
実行ファイルの種類に依存します
カーネルは、RAMやハードウェアへの共有アクセスを仲介するだけでなく、ローダー機能も果たします
ELF や PE のような多くの「実行形式」は、実行ファイルにはコードに加えてメタデータが含まれており、それを処理するのがローダーの仕事です。詳細については、Microsoft の PE フォーマットの詳細を読んでください
これらの実行ファイルは、ライブラリ(Windows .dll
や Linux の共有オブジェクト .so
ファイル)も参照しています – それらのコードを含める必要があります
コンパイラがオペレーティング・システム・ローダで処理されるファイルを生成していて、そのローダが存在しない場合、それは動作しません
- ローダの仕事をするコードを含めることはできますか?
メタデータを処理せずに生のコードをどうにかして実行するように OS を説得する必要があります。あなたのコードがカーネルAPIを 呼び出しても、それは動作しません
- カーネルAPIを呼ばないとどうなるの?
この実行ファイルを何らかの方法でオペレーティングシステムからロードした場合(つまり、生のコードをロードして実行することを許可している場合)、それはまだユーザモードのままです。あなたのコードがカーネルモードではなくユーザモードで禁止されているもの、例えば未割り当てのメモリや I/O デバイスのアドレス/レジスタにアクセスした場合、特権やセグメント違反でクラッシュします (ここでも例外はカーネルモードで処理されます)
- カーネルモードから実行するとどうなるか
そうすれば、うまくいくでしょう
3 LawrenceC 2018-07-27
TL;DR No.
OSがない現在の環境として、Arduinoの開発が思い浮かびます。信じてください、these babiesには、OSを搭載するスペースがありません
同じように、セガジェネシスのゲームには、セガが提供するOSを呼び出すことはありませんでした。あなたは68Kのアセンブラでゲームを作り、むき出しの金属に直接書き込みをしていただけです
または、私はIntel 8051の組み込み作業をして、私の歯を切ったところ。再び、すべてのあなたが持っているときに2k * 8フットプリントで2716 epromです, あなたは、オペレーティングシステムのための部屋を持っていません
もちろん、これはアプリケーションという言葉の非常に広い意味での使い方を前提としています。修辞的な質問として、Arduinoのスケッチが実際にアプリケーションなのかどうかを自問自答する価値があります
3 dgnuff 2018-07-27
他の回答が正しいとは言いませんが、あまりにも多くの詳細を提供してくれているので、残念ながら、あなたにとってはまだ非常に曖昧なものになっています
基本的な答えは、コードはプロセッサ上で直接実行されるということです。そして、マシンのコードは誰とも「話をする」ことはありません。プロセッサはアクティブなコンポーネントであり、あなたがコンピュータで行うすべてのことは、そのプロセッサによって行われます(ここでは少し単純化していますが、今のところはそれで構いません)。プロセッサはコードを読み込んで実行し、結果を吐き出しますが、機械のコードはプロセッサの餌に過ぎません
混乱の原因は、ハードウェアという言葉の使い方にあります。以前ほど明確な区分はありませんが、すべてを単にハードウェアと呼ぶよりも、周辺機器という観点から考えた方が良いでしょう。つまり、あなたのマシンにオペレーティングシステムなどがある場合、あなたのプログラムはそのサービスを使って周辺機器にアクセスしなければなりませんが、プロセッサ自体は周辺機器ではなく、あなたのプログラムが直接実行されるメインの処理装置なのです
カーネル、オペレーティングシステム、および同様の介在層は、通常、複数のプログラムが実行されることが予想され、これらのプログラムがコンピュータの周辺機器をどのように使用できるかをシステムが管理する必要があるような大規模なシステムでのみ使用されます(同時に使用されることがよくあります)。このような場合、実行中のプログラムは、どのように周辺機器を共有するかを決定し、競合がないことを確認するシステムを使用して、これらの周辺機器にのみアクセスすることができます。競合するプログラム間で管理する必要がない小規模なシステムでは、基盤となるシステムが全くないことが多く、これらのシステム上で通常実行されている単一のプログラムが、周辺機器を使って好きなことをすることが多かれ少なかれ自由にできます
3 Gábor 2018-07-27
電源投入時にコンピュータで実行されるBIOSは、ROMに格納された実行可能なコードです。これはマシンの命令とデータから構成されています。このBIOSをソースコードから組み立てるコンパイラ(またはアセンブラ)があります。これは特殊なケースです
他の特殊なケースとしては、カーネルをロードするブートストラッププログラムやカーネル自体があります。これらの特殊なケースは、一般的に C++ 以外の言語でコーディングされています
一般的なケースでは、カーネルやライブラリルーチンによって提供されるシステムサービスを呼び出す命令をコンパイラに生成させる方がはるかに実用的です。これにより、コンパイラがより軽量になります。また、コンパイルされたコードの軽量化にもつながります
もう一方の端にはJavaがあります。Javaでは、この用語が一般的に理解されているように、コンパイラはソースコードを機械命令に翻訳しません。代わりに、ソースコードは、Java仮想マシンと呼ばれる架空のマシンのための「マシン命令」に翻訳されます。Javaプログラムを実行する前に、Java仮想マシン用のインタプリタを含むJavaランタイムと組み合わせる必要があります
2 Walter Mitty 2018-07-28
古き良き時代には、プログラムの実行中に必要なことはすべて、自分で行うか、他の人が書いたライブラリコードをプログラムに追加することで、プログラムが責任を持って実行していました。 コンピュータの中でその横で動いているのは、コンパイルされたプログラムの中に読み込まれるコードだけでした。 いくつかのコンピュータでは、それ以上のことができるようになる前に、スイッチを通してコードを入力しなければなりませんでした (オリジナルの「ブートストラップ」プロセス) あるいは、プログラム全体がこのように入力されました
プログラムをロードして実行できるコードを実行しているのはいいことだとすぐにわかりました。 後に、コンピュータは、特にハードウェアが助けてくれるのであれば、CPUがそれらの間を切り替えることによって、複数のプログラムを同時に実行することをサポートするのに十分なパワーを持っていることがわかりましたが、プログラムがお互いのつま先を踏まないようにするための複雑さが追加されました(例えば、プリンタにデータを一度に送ろうとする複数のプログラムをどのように扱うか?)
この結果、大量のヘルパーコードが個々のプログラムから「オペレーティングシステム」に移動され、ユーザプログラムからヘルパーコードを呼び出す標準化された方法で実行されるようになりました
そして、それが今日の状況です。あなたのプログラムはフルスピードで動作しますが、オペレーティングシステムが管理する何かが必要なときは、オペレーティングシステムが提供するヘルパールーチンを呼び出し、そのコードは必要とされず、ユーザープログラム自体には存在しません。これには、ディスプレイへの書き込み、ファイルの保存、ネットワークへのアクセスなどが含まれていました
マイクロカーネルは、あるプログラムを完全なオペレーティングシステムなしで実行するために必要なものだけを提供するように書かれています。これは、経験豊富なユーザにとってはいくつかの利点がありますが、他のほとんどの利点はありません。 https://en.wikipedia.org/wiki/Microkernel – もっと知りたければ、ウィキペディアのページを読んでみてください
Java仮想マシンを実行できるマイクロカーネルで実験してみましたが、そのスイートスポットがDockerであることが後でわかりました
2 Thorbjørn Ravn Andersen 2018-07-28
一般的なデスクトップOSでは、カーネル自体が実行ファイルです。(Windowsはntoskrnl.exe
、Linuxはvmlinux
など) 実行ファイルを実行するためにカーネルが必要な場合、それらのOSは存在し得ません
あなたがカーネルを必要としているのは、カーネルが行うことを行うためです。複数の実行ファイルを同時に実行できるようにしたり、それらの間を参照したり、ハードウェアを抽象化したり。ほとんどのプログラムはこれらのことを自分自身で有能に行うことはできませんし、できたとしてもそうしてほしくはないでしょう。DOSの時代には — オペレーティングシステムそのものとは呼べなかった — ゲームはしばしばOSをローダ以上のものとして使用し、カーネルと同じようにハードウェアに直接アクセスしていました。しかし、ゲームを買う前に、自分のマシンにどのようなブランドやモデルのハードウェアがあるのかを知っておく必要がありました。多くのゲームは特定のビデオカードやサウンドカードのファミリーしかサポートしておらず、競合するブランドでは動作が非常に悪かったのです。これは、通常カーネルを介して提供される抽象化ではなく、プログラムがハードウェアを直接制御するときに得られるものです。)
1 cHao 2018-07-31