カーネル空間とユーザー空間の違いは何ですか?カーネルスペース、カーネルスレッド、カーネルプロセス、カーネルスタックは同じものですか?また、なぜこの差別化が必要なのでしょうか?
really簡略化された答えは、カーネルはカーネル空間で実行され、通常のプログラムはユーザー空間で実行されるということです。ユーザースペースは基本的にサンドボックス化の形式です-ユーザープログラムを制限するため、他のプログラムまたはOSカーネルが所有するメモリ(およびその他のリソース)を混乱させることはできません。これは、マシンをクラッシュさせるなどの悪いことをする能力を制限します(通常は完全に排除しません)。
カーネルはオペレーティングシステムの中核です。通常、すべてのメモリとマシンのハードウェア(およびマシン上のその他すべて)にフルアクセスできます。マシンをできるだけ安定に保つために、通常は最も信頼できる、十分にテストされたコードのみをカーネルモード/カーネルスペースで実行する必要があります。
スタックはメモリの別の一部にすぎないため、当然、残りのメモリと一緒に分離されます。
ランダムアクセスメモリ(RAM)は、カーネル空間とユーザー空間という2つの異なる領域に論理的に分割できます。( TheRAMの物理アドレスは、実際には仮想アドレス 、これはすべて MMU )によって実装されます
カーネルは、資格のあるメモリの一部で実行されます。カーネルはメモリのすべての部分にアクセスできるため、メモリのこの部分は通常のユーザーのプロセスから直接アクセスすることはできません。カーネルの一部にアクセスするには、ユーザープロセスは事前定義されたシステムコール(open
、read
、write
など)を使用する必要があります。また、C
のようなprintf
ライブラリ関数は、システム変数write
を順番に呼び出します。
システムコールは、ユーザープロセスとカーネルプロセス間のインターフェースとして機能します。ユーザーが知らないうちにカーネルを台無しにするのを防ぐために、アクセス権はカーネル空間に置かれます。
そのため、システムコールが発生すると、ソフトウェア割り込みがカーネルに送信されます。 CPUは、関連する割り込みハンドラルーチンに一時的に制御を渡すことができます。割り込みによって停止されたカーネルプロセスは、割り込みハンドラルーチンがジョブを終了した後に再開します。
カーネル空間と仮想空間は仮想メモリの概念です.... Ram(実際のメモリ)がカーネルとユーザー空間に分割されることを意味するものではありません。各プロセスにはカーネルとユーザーに分割される仮想メモリが与えられます。スペース
つまり、「ランダムアクセスメモリ(RAM)は、カーネル空間とユーザー空間という2つの異なる領域に分割できます。」間違っている。
&「カーネルスペースvsユーザースペース」について
プロセスが作成され、その仮想メモリがユーザースペースとカーネルスペースに分割される場合、ユーザースペース領域にはデータ、コード、スタック、プロセスのヒープ、カーネルスペースにはプロセスのページテーブルなどが含まれます、カーネルデータ構造、カーネルコードなど。カーネルスペースコードを実行するには、制御をカーネルモードに移行する必要があります(システムコールに0x80ソフトウェア割り込みを使用)。カーネルスタックは基本的にカーネルスペースで実行中のすべてのプロセスで共有されます。
CPUリングは最も明確な区別です
X86保護モードでは、CPUは常に4つのリングのいずれかになります。 Linuxカーネルは0と3のみを使用します。
これは、カーネルとユーザーランドの最もハードで高速な定義です。
Linuxがリング1および2を使用しない理由: CPU特権リング:リング1および2が使用されない理由/
現在のリングはどのように決定されますか?
現在のリングは、次の組み合わせで選択されます。
グローバル記述子テーブル:GDTエントリのメモリ内テーブル。各エントリには、リングをエンコードするフィールドPrivl
があります。
LGDT命令は、アドレスを現在の記述子テーブルに設定します。
セグメントはGDTのエントリのインデックスを指すCS、DSなどを登録します。
たとえば、CS = 0
は、GDTの最初のエントリが実行中のコードに対して現在アクティブであることを意味します。
各リングは何ができますか?
CPUチップは、次のように物理的に構築されています。
リング0は何でもできます
リング3は、複数の命令を実行して複数のレジスタに書き込むことはできません。
独自のリングを変更することはできません!そうしないと、リング自体をリング0に設定し、リングを使用できなくなります。
つまり、現在のリングを決定する現在の セグメント記述子 を変更することはできません。
ページテーブルを変更できません: x86ページングはどのように機能しますか?
つまり、CR3レジスタを変更できず、ページング自体がページテーブルの変更を妨げます。
これにより、1つのプロセスが他のプロセスのメモリをセキュリティ/プログラミングの容易さの理由で見ることができなくなります。
割り込みハンドラーを登録できません。それらはメモリ位置への書き込みによって設定されますが、これもページングによって防止されます。
ハンドラーはリング0で実行され、セキュリティモデルを破壊します。
つまり、LGDTおよびLIDT命令は使用できません。
in
やout
などのIO命令を実行できないため、任意のハードウェアアクセスが可能です。
そうしないと、たとえば、プログラムがディスクから直接読み取ることができる場合、ファイルのアクセス許可が役に立たなくなります。
Michael Petch により正確に感謝します。実際には、OSはリング3でIO命令を許可することができます。これは Taskによって実際に制御されます。状態セグメント 。
不可能なことは、リング3がそもそも持っていなかった場合に、それを許可することです。
Linuxは常にそれを許可しません。参照: LinuxがTSS経由でハードウェアコンテキストスイッチを使用しないのはなぜですか?
プログラムとオペレーティングシステムはリング間でどのように移行しますか?
cPUがオンになると、リング0で初期プログラムの実行が開始されます(それでもいいですが、これはおおよその目安です)。この初期プログラムはカーネルであると考えることができます(ただし、通常はブートローダーであり、リング0のままカーネルを呼び出します)。
ユーザーランドプロセスがカーネルにファイルへの書き込みなどの処理を行わせたい場合、 int 0x80
やsyscall
などの割り込みを生成する命令を使用してカーネルに信号を送ります。 x86-64 Linux syscall hello worldの例:
.data
hello_world:
.ascii "hello world\n"
hello_world_len = . - hello_world
.text
.global _start
_start:
/* write */
mov $1, %rax
mov $1, %rdi
mov $hello_world, %rsi
mov $hello_world_len, %rdx
syscall
/* exit */
mov $60, %rax
mov $0, %rdi
syscall
コンパイルして実行:
as -o hello_world.o hello_world.S
ld -o hello_world.out hello_world.o
./hello_world.out
これが発生すると、CPUはブート時にカーネルが登録した割り込みコールバックハンドラーを呼び出します。ハンドラを登録し、それを使用する 具体的なベアメタルの例を次に示します 。
このハンドラはリング0で実行され、カーネルがこのアクションを許可し、アクションを実行し、リング3のユーザーランドプログラムを再起動するかどうかを決定します。x86_64
exec
システムコールが使用される場合(またはカーネル が/init
を開始する場合)、カーネル は新しいユーザーランドのレジスタとメモリ を準備しますプロセス、それからエントリポイントにジャンプし、リング3にCPUを切り替えます
プログラムが(ページングのため)禁止されたレジスタまたはメモリアドレスへの書き込みのようないたずらをしようとすると、CPUはリング0でカーネルコールバックハンドラーも呼び出します。
しかし、ユーザーランドはいたずらだったので、今回はカーネルがプロセスを強制終了するか、シグナルで警告を出します。
カーネルが起動すると、一定の周波数でハードウェアクロックが設定され、定期的に割り込みが生成されます。
このハードウェアクロックは、リング0を実行する割り込みを生成し、起動するユーザーランドプロセスをスケジュールできるようにします。
このように、プロセスがシステムコールを行っていない場合でも、スケジューリングを行うことができます。
複数のリングを持つポイントは何ですか?
カーネルとユーザーランドを分離することには、2つの大きな利点があります。
それをいじる方法は?
リングを直接操作するのに適したベアメタル設定を作成しました: https://github.com/cirosantilli/x86-bare-metal-examples
残念ながらユーザーランドの例を作成する忍耐はありませんでしたが、ページングのセットアップまで行ったので、ユーザーランドを実現できるはずです。プルリクエストをご覧ください。
または、Linuxカーネルモジュールはリング0で実行されるため、それらを使用して特権操作を試すことができます。制御レジスタを読み取ります: プログラムから制御レジスタcr0、cr2、cr3にアクセスする方法は?セグメンテーション違反の取得
ホストを殺さずに試してみるのに便利な QEMU + Buildroot setup があります。
カーネルモジュールの欠点は、他のkthreadが実行されており、実験に干渉する可能性があることです。しかし、理論的には、カーネルモジュールですべての割り込みハンドラを引き継いでシステムを所有することができます。これは実際には興味深いプロジェクトです。
負のリング
ネガティブリングは実際にはIntelマニュアルでは参照されていませんが、リング0自体よりもさらに機能があるCPUモードが実際に存在するため、「ネガティブリング」名に適しています。
1つの例は、仮想化で使用されるハイパーバイザーモードです。
詳細については、 https://security.stackexchange.com/questions/129098/what-is-protection-ring-1 を参照してください。
ARM
ARMでは、リングは代わりに例外レベルと呼ばれますが、主な考え方は同じです。
ARMv8には4つの例外レベルがあり、一般に次のように使用されます。
EL0:ユーザーランド
EL1:カーネル(ARM用語の「スーパーバイザー」)。
以前はsvc
として知られていたswi
命令(SuperVisor呼び出し)と共に入力されます。これは、Unified Assembly の前で、Linuxシステム呼び出しを行うために使用される命令です。 Hello world ARMv8の例:
.text
.global _start
_start:
/* write */
mov x0, 1
ldr x1, =msg
ldr x2, =len
mov x8, 64
svc 0
/* exit */
mov x0, 0
mov x8, 93
svc 0
msg:
.ascii "hello syscall v8\n"
len = . - msg
Ubuntu 16.04でQEMUを使用してテストします。
Sudo apt-get install qemu-user gcc-arm-linux-gnueabihf
arm-linux-gnueabihf-as -o hello.o hello.S
arm-linux-gnueabihf-ld -o hello hello.o
qemu-arm hello
SVCハンドラを登録し、SVC呼び出し を実行する具体的なベアメタルの例を次に示します。
EL2: hypervisors 、たとえば Xen 。
hvc
命令(HyperVisor呼び出し)で入力します。
ハイパーバイザーはOSに対するものであり、OSはユーザーランドに対するものです。
たとえば、Xenを使用すると、LinuxやWindowsなどの複数のOSを同じシステムで同時に実行でき、Linuxがユーザーランドプログラムで行うのと同様に、セキュリティとデバッグを容易にするためにOSを互いに分離します。
ハイパーバイザーは、今日のクラウドインフラストラクチャの重要な部分です。複数のサーバーを単一のハードウェアで実行できるため、ハードウェアの使用率を常に100%近くに保ち、多くのお金を節約できます。
たとえば、AWSは2017年まで がKVMに移動してnews を作成するまでXenを使用していました。
EL3:さらに別のレベル。 TODOの例。
smc
命令を使用して入力(セキュアモードコール)
ARMv8アーキテクチャリファレンスモデルDDI 0487C.a -D1章-AArch64システムレベルプログラマーモデル-図D1-1は、これを美しく示しています。
おそらく後知恵の恩恵によるARMの特権レベルの命名規則が、負のレベルを必要とせずにx86よりも優れていることに注意してください。0は下位、3は最高です。高いレベルは、低いレベルよりも頻繁に作成される傾向があります。
現在のELはMRS
命令でクエリできます: 現在の実行モード/例外レベルなどは何ですか?
ARMでは、チップ領域を節約する機能を必要としない実装を可能にするために、すべての例外レベルが存在する必要はありません。 ARMv8の「例外レベル」には次のように記載されています。
実装には、すべての例外レベルが含まれない場合があります。すべての実装には、EL0とEL1を含める必要があります。 EL2およびEL3はオプションです。
たとえば、QEMUはデフォルトでEL1ですが、EL2とEL3はコマンドラインオプションで有効にできます: qemu-system-aarch64 a53 power up
Ubuntu 18.10でテストされたコードスニペット.
カーネル空間とユーザー空間は、特権オペレーティングシステムの機能と制限されたユーザーアプリケーションの分離です。この分離は、ユーザーアプリケーションがコンピューターを攻撃するのを防ぐために必要です。古いユーザープログラムがハードドライブへのランダムデータの書き込みを開始したり、別のユーザープログラムのメモリスペースからメモリを読み取ったりするのは悪いことです。
ユーザー空間プログラムはシステムリソースに直接アクセスできないため、アクセスはオペレーティングシステムカーネルによってプログラムに代わって処理されます。通常、ユーザー空間プログラムは、システムコールを通じてオペレーティングシステムにこのような要求を行います。
カーネルスレッド、プロセス、スタックは同じことを意味しません。これらは、ユーザー空間の対応するものとしてのカーネル空間の類似した構造です。
各プロセスには、ページテーブルを介して物理メモリにマップする独自の4GBの仮想メモリがあります。仮想メモリは、主に2つの部分に分割されます。プロセスを使用するための3 GBと、カーネルを使用するための1 GBです。作成する変数のほとんどは、アドレス空間の最初の部分にあります。その部分はユーザー空間と呼ばれます。最後の部分は、カーネルが存在する場所であり、すべてのプロセスに共通です。これはカーネルスペースと呼ばれ、このスペースのほとんどは、ブート時にカーネルイメージがロードされる物理メモリの開始位置にマップされます。
アドレス空間の最大サイズは、CPUのアドレスレジスタの長さに依存します。
32ビットのアドレスレジスタを備えたシステムでは、アドレススペースの最大サイズは2です。32 バイト、または4 GiB。同様に、64ビットシステムでは、264 バイトをアドレス指定できます。
このようなアドレス空間は仮想メモリまたは仮想アドレス空間と呼ばれます。実際には物理的なRAMサイズとは関係ありません。
Linuxプラットフォームでは、仮想アドレス空間はカーネル空間とユーザー空間に分割されます。
task size limit、またはTASK_SIZE
と呼ばれるアーキテクチャ固有の定数は、分割が発生する位置をマークします。
0からTASK_SIZE
- 1までのアドレス範囲がユーザー空間に割り当てられます。
TASK_SIZE
から2までの残り32-1(または264-1)カーネル空間に割り当てられます。
たとえば、特定の32ビットシステムでは、ユーザースペースに3 GiB、カーネルスペースに1 GiBを使用できます。
Unixライクなオペレーティングシステムの各アプリケーション/プログラムはプロセスです。これらのそれぞれには、プロセス識別子(または単にプロセスID、つまりPID)と呼ばれる一意の識別子があります。 Linuxには、プロセスを作成するための2つのメカニズムがあります:1. fork()
システムコール、または2. exec()
コール。
カーネルスレッドは軽量のプロセスであり、実行中のプログラムでもあります。単一のプロセスは、同じデータとリソースを共有する複数のスレッドで構成されている場合がありますが、プログラムコードのパスは異なります。 Linuxは、スレッドを生成するclone()
システムコールを提供します。
カーネルスレッドの使用例:RAMのデータ同期、スケジューラがCPU間でプロセスを分散するのを支援するなど。
簡単に説明すると、カーネルはカーネルスペースで実行され、カーネルスペースはすべてのメモリとリソースに完全にアクセスできます。メモリは2つの部分に分かれています。一部はカーネル用、一部はユーザー独自プロセス用です。スペースはカーネルスペースに直接アクセスできないため、カーネルにリソースの使用を要求します。 syscall(glibcの事前定義システムコール)
異なる「ユーザースペースは単なるカーネルのテストロード」を簡素化するステートメントがあります...
明確にするために:プロセッサアーキテクチャにより、CPUは2つのモードカーネルモードとユーザーモードで動作できます。ハードウェア命令により、一方のモードから他方のモードに切り替えることができます。
メモリは、ユーザー空間またはカーネル空間の一部としてマークできます。
CPUがユーザーモードで実行されている場合、CPUはユーザー空間にあるメモリのみにアクセスできますが、CPUはカーネル空間でメモリにアクセスしようとしますが、結果は「ハードウェア例外」です。カーネル空間とユーザー空間の両方に...
Linuxカーネルは、カーネルモードで実行され、いくつかの異なるレイヤーで構成されるすべてのものを指します。最下層では、カーネルはHALを介してハードウェアと対話します。中間レベルでは、UNIXカーネルは4つの異なる領域に分かれています。 4つの領域のうちの最初の領域では、キャラクターデバイス、未加工および調理済みTTY、およびターミナル処理を処理します。 2番目の領域は、ネットワークデバイスドライバー、ルーティングプロトコル、およびソケットを処理します。 3番目の領域は、ディスクデバイスドライバー、ページキャッシュとバッファーキャッシュ、ファイルシステム、仮想メモリ、ファイルの命名とマッピングを処理します。最後の4番目の領域は、プロセスのディスパッチ、スケジューリング、作成、終了、およびシグナル処理を処理します。とりわけ、システムコール、割り込み、トラップを含むカーネルの最上層があります。このレベルは、下位レベルの各機能へのインターフェースとして機能します。プログラマは、さまざまなシステムコールと割り込みを使用して、オペレーティングシステムの機能と対話します。
カーネル空間とは、カーネルがメモリ空間にのみアクセスできることを意味します。 32ビットLinuxでは、1G(仮想メモリアドレスとして0xC0000000から0xffffffffまで)です。カーネルによって作成されたすべてのプロセスもカーネルスレッドです。したがって、1つのプロセスには2つのスタックがあります。カーネルスレッド用のスペース。
カーネルスタックは2ページ(32ビットLinuxでは8k)を占有し、task_struct(約1k)と実際のスタック(約7k)を含みます。後者は、いくつかの自動変数または関数呼び出しパラメーターまたは関数アドレスをカーネル関数に保存するために使用されます。コードは次のとおりです(Processor.h(linux\include\asm-i386)):
#define THREAD_SIZE (2*PAGE_SIZE)
#define alloc_task_struct() ((struct task_struct *) __get_free_pages(GFP_KERNEL,1))
#define free_task_struct(p) free_pages((unsigned long) (p), 1)
__get_free_pages(GFP_KERNEL、1))は、メモリを2 ^ 1 = 2ページとして割り当てることを意味します。
しかし、プロセススタックは別のものであり、そのアドレスは0xC0000000(32bit linux)以下であり、そのサイズは非常に大きく、ユーザー空間の関数呼び出しに使用されます。
システムコールについて質問があります。カーネルスペースで実行されていますが、ユーザースペースのプロセスによって呼び出されました。どのように動作しますか? Linuxはカーネルスタックまたはプロセススタックにパラメーターと関数アドレスを入れますか? Linuxのソリューション:すべてのシステムコールは、ソフトウェア割り込みINT 0x80によってトリガーされます。 entry.S(linux\Arch\i386\kernel)で定義されています。以下に例を示します。
ENTRY(sys_call_table)
.long SYMBOL_NAME(sys_ni_syscall) /* 0 - old "setup()" system call*/
.long SYMBOL_NAME(sys_exit)
.long SYMBOL_NAME(sys_fork)
.long SYMBOL_NAME(sys_read)
.long SYMBOL_NAME(sys_write)
.long SYMBOL_NAME(sys_open) /* 5 */
.long SYMBOL_NAME(sys_close)
INカーネルスペースは、Linuxカーネルが実行されるメモリの部分(Linuxの場合は上部1 GBの仮想スペース)、ユーザースペースはユーザーアプリケーションが実行されるメモリの部分(Linuxの場合は下部3 GBの仮想メモリ)です。以下のリンクを参照してください:)
http://learnlinuxconcepts.blogspot.in/2014/02/kernel-space-and-user-space.html
非常に簡単な説明をしようとする
仮想メモリは、カーネル空間とユーザー空間に分けられます。カーネルスペースは、カーネルプロセスが実行される仮想メモリの領域であり、ユーザースペースは、ユーザープロセスが実行される仮想メモリの領域です。
この区分は、メモリアクセス保護に必要です。
ブートローダーがRAMの場所にカーネルをロードした後、カーネルを起動するたびに(通常ARMベースのコントローラーで)、FIQとIRQが無効になっているスーパーバイザーモードにあることを確認する必要があります。
メモリは2つの異なる領域に分けられます。
ユーザー空間で実行されているプロセスは、メモリの限られた部分にしかアクセスできませんが、カーネルはすべてのメモリにアクセスできます。ユーザー空間で実行されているプロセスも、カーネル空間にアクセスできません。ユーザー空間プロセスは、カーネルによって公開されたインターフェース(システムコール)を介してカーネルのごく一部にのみアクセスできます。プロセスがシステムコールを実行すると、ソフトウェア割り込みがカーネルに送信され、カーネルは適切な割り込みハンドラーをディスパッチして続行しますハンドラーが終了した後の動作。
カーネル空間とユーザー空間は論理空間です。
最新のプロセッサのほとんどは、異なる特権モードで実行するように設計されています。 x86マシンは、4つの異なる特権モードで実行できます。
また、特定の特権モードでは、特定の特権モードのときに特定のマシン命令を実行できます。
この設計により、システム保護を提供したり、実行環境をサンドボックス化したりします。
カーネルは、ハードウェアを管理し、システムの抽象化を提供するコードです。したがって、すべての機械命令にアクセスできる必要があります。そして、それは最も信頼できるソフトウェアです。だから私は最高の特権で実行されるべきです。そして、Ring level 0は最も特権的なモードです。そのため、Ring Level 0は、Kernel Modeとも呼ばれます。
ユーザーアプリケーションは、サードパーティベンダーから提供されるソフトウェアの一部であり、完全に信頼することはできません。悪意を持った人は、すべての機械命令に完全にアクセスできる場合、システムをクラッシュさせるコードを書くことができます。そのため、アプリケーションには、限られた一連の指示へのアクセスを提供する必要があります。また、Ring Level 3は最も特権の少ないモードです。したがって、すべてのアプリケーションはそのモードで実行されます。したがって、Ring Level 3は、User Modeとも呼ばれます。
注:リングレベル1および2を取得していません。これらは基本的に中間の特権を持つモードです。デバイスドライバーコードがこの特権で実行される場合もあります。知る限り、Linuxはカーネルコードの実行とユーザーアプリケーションにそれぞれリングレベル0と3のみを使用します。
そのため、カーネルモードで発生する操作はすべてカーネルスペースと見なすことができます。また、ユーザーモードで発生する操作はすべてユーザー空間と見なすことができます。
正しい答えは次のとおりです。カーネルスペースやユーザースペースなどはありません。プロセッサの命令セットには、ページテーブルマップのルートなどの破壊的なものを設定したり、ハードウェアデバイスのメモリにアクセスしたりする特別な権限があります。
カーネルコードには最高レベルの特権があり、ユーザーコードには最低レベルの特権があります。これにより、ユーザーコードがシステムをクラッシュさせたり、他のプログラムを変更したりすることを防ぎます。
一般に、カーネルコードはユーザーコードとは異なるメモリマップの下に保持されます(ユーザースペースが互いに異なるメモリマップに保持されるのと同じように)。これが、「カーネルスペース」と「ユーザースペース」という用語の由来です。しかし、それは難しい規則ではありません。たとえば、x86は常に間接的に割り込み/トラップハンドラーをマップする必要があるため、カーネルの一部(または一部のOSすべて)をユーザー空間にマップする必要があります。繰り返しますが、これはそのようなコードにユーザー特権があることを意味しません。
カーネル/ユーザーの分割が必要なのはなぜですか?一部の設計者は、実際にはそれが必要であることに反対しています。マイクロカーネルアーキテクチャは、コードの最高特権セクションをできるだけ小さくし、すべての重要な操作をユーザー特権コードで実行するという考えに基づいています。なぜこれが良いアイデアなのかを研究する必要があります。これは単純な概念ではありません(そして長所と短所の両方で有名です)。