主記憶装置(メインメモリ)
メインメモリ
wikiの説明です。
コンピュータのメインバスなどに直接接続されている記憶装置で、レイテンシやスループットは速いが比較すると小容量である。
ウィキペディアです。
勉強机で説明すると引出しから別のノートを取り出している作業時間のことです。
探している間は勉強は止まってしまいます。レスポンスタイムとも呼ばれます。
スループット
単位時間当たりの計算量です。
1秒回に何回計算できるかみたいなことです。
スループットが多ければ多いほど計算処理を早くを終わらせられます。
簡単に説明するとメインメモリはCPUが処理する情報を広げておく領域です。HDDなどの補助記憶装置よりCPUに近いところにあり、高速に情報をやり取りして処理を行うことができます。
メインメモリにはメモリアドレスと呼ばれる番号がふられていて、その番号を元にデータへアクセスします。
メインメモリの役割を説明するため、実際に補助記憶装置に保存されたアプリケーションを起動して処理を実行するまでの一連の流れを簡単に説明します。
OSはユーザーにこのアプリケーションを起動しろと命令されるとそのアプリケーションが保存されている補助記憶装置(HDDなど)へアクセスします。
そこにはアプリケーションのコードそのものやその処理に必要なデータが保存されています。
ただ補助記憶装置はデータのやり取りが低速で、そのままでは処理が大きく遅延してしまいます。
なので処理を実行する前に高速にデータを交換できるメインメモリに一旦移し替えます。
その後にそのアプリケーションの最初のコードのアドレスから実行を開始していきます。
ついでにレイテンシとかスループットについても説明します。
メインメモリは厳密さを排除するとノートや教科書を広げておく勉強机のようなものです。
少しだけ厳密に説明します。
CPUは計算に必要なデータをメインメモリに要求するのですがコンピューターの計算速度と比較するとメインメモリ応答速度はかなり遅く、この応答を待っている間コンピューターの計算能力は使われず暇を持て余します。
なのでコンピューターはこの待っている間(レイテンシ)は要求したデータが必要のない別の計算を行って暇を潰しています。
人間の感覚では目にも止まらぬ速さでいくつもの処理を切り替えながらデータを処理し続けています。
YouTubeをバックグラウンドに流しながらエクセルを使うといった処理ですね。
厳密には一つづつ高速にこなしているだけですが、私達の感覚では全ての処理が同時に行われているように見えます。
コンピューターの性能はスループットとレイテンシに依存しています。
コンピューターのデータが行き来する通路です。メインメモリは補助記憶装置のように大容量ではありませんが、メインバスと直接つながっていて高速にデータをやりとりして動かすことができます。補助記憶装置とはHDD(ハードディスクドライブ)SSD(ソリッドステートドライブ)です。
次はメインメモリの管理方法について。
仮想記憶
仮想メモリとはメモリを効率的に使う方法のことで「仮想」とは補助記憶装置(HDDやSSD)とメインメモリを組み合わせて大きさの限られたメモリをあたかも巨大なメモリが存在しているように見せる仕組みのことです。
具体的にはメモリの中の一時的に使われていないコードやデータを必要になるまで補助記憶装置に退避しておいて、必要になった時にその都度メモリに配置し直します。
例えば1GBの大きさのあるアプリケーションは決して1GBのメモリ領域を全て同時に必要としているわけではありません。
必要な時に必要なデータがメモリに置かれていればいいのです。
アプリケーションの中で使用頻度の低いメモリ領域はそのアプリケーションが使用の為に呼び出すまでの間HDDやSSDなどの補助記憶装置に退避(スワップアウト)しておきます。そしてその間は他のアプリケーションがそのメモリを使用します。
改めて必要になった時にメモリに再配置(スワップイン)し直すのです。
このメモリの管理はOSが一括し行っています。
使用頻度の低いメモリを一旦退避しておくと言ってもメモリは物理的には連続していて区分けされていません。
そこで論理的にメモリを分割します。論理的にとは目に見えない線引きをすることです。
物理的には日本列島は繋がっていますが、論理的に線引きを行いその線の内側の管理を各都道府県が行っています。
同じことをメモリでも行います。
しかしもし小さな領域づつ細かく分けて退避すると、作業が煩雑になってしまいCPUがそのメモリからデータを出し入れするのに追われてしまって他の処理をこなすことが出来ません。
その解決策としてある程度の大きさをまとめてメモリに退避することにしました。
どこからどこまでをメモリに退避すればいいのかの区切り方を予め決めておきます。
その分割方法の違いがセグメント(セグメンテーション)方式とページング方式の違いです。
厳密さを省いて説明しています。
セグメント(セグメンテーション)方式
メモリは物理的には繋がっています。
セグメント方式はそのメモリを「セグメント」という論理的には分割された空間として解釈します。
具体的にはプログラムには大まかに「コード」と「データ」に分けられます。CPUに対する命令が「コード」でその命令によって処理を施す対象が「データ」です。
「セグメント」とはプログラムを「コード」や「データ」といった用途に合わた大きさにメモリを区切って配置することです。
そしてプログラムはその必要とされる機能によってコードの長さや扱うデータの量は異なります。
そのためセグメントの大きさは一つのプログラム大きさに合わせて可変長で決定します。
セグメント方式はメモリにあるセグメントを一つの単位として補助記憶装置に退避させます。
同時に各プロセス同士を論理的に切り離しその境界線をOSが見張ることによってプロセスAがプロセスBの領域に勝手に侵入しようとするのを防ぐことができます。
この保護機能と仮想記憶を実装することがセグメント方式の主な目的となります。
可変長なのでメモリを必要な分だけ使用することができますが、スワップインとアウトを繰り返すうちに上の画像のように未使用領域が断片化しセグメントを割り当てられないメモリ領域が出現してきます。
仮に未使用領域の合計が使用したいセグメントより大きくてもメモリが断片化してしまっていては配置することができません。
断片化されたファイルを読み取るには、ディスクヘッド(物理的に動き回りデータを読み取る)を通常より多く動かすことになるため繰り返すごとにアクセス効率は普通は悪くなり処理が遅くなります。
このメモリの断片化のことをエクスターナルフラグメンテーション(外部断片化)と呼びます。
保護機能が実装できる。
外部断片化によって空き領域があっても使用できないメモリ領域が出来てしまう。
断片化しすぎて処理が遅くなる
プロテクトモードとリアルモードのセグメント
セグメント方式はプロテクトモードとリアルモードで異なります。
まずはプロテクトモードから説明します。
プロテクトモード
プロテクトモードのセグメント方式ではセグメントはそのメモリ上のアドレス情報や保護機能(アクセス権限、サイズ、属性など)をひとまとめにしてメモリ上に配置して管理します。
この一覧をディスクリプタ・テーブルと呼び、それぞれのセグメントの情報のことをディスクリプタと呼びます。
ディスクリプタは8バイトのデータ構造体で定義されています。
以下の画像はセグメンテーション機構の概略図です。
プロテクトモードを起動する前にディスクリプタテーブルをメモリの好きな場所に読み込みそのアドレスをGDTR(レジスタ)に伝えます。
各セグメントの情報を保存したディスクリプタはディスクリプタテーブル内の番号で管理されています。
セグメントのメモリアドレスへのアクセスはGDTRとセグメントレジスタに保存されたディスクリプタの番号(セレクタ値)を入力されたアドレス変換回路が行ってくれます。
ディスクリプタに到着するとセグメントディスクリプタの中の保護用の情報とセグメントへアクセスしてきたプロセスの属性を比較します。
保護機能のチェックをパスするとディスクリプタからセグメントのベースアドレスを取り出し、オフセットを持つレジスタと足し合わせて目的のセグメントのアドレスを特定します。
もしプロセスのリミットや属性を比較してセグメントの領域を超えていたり、権限を超えたアクセスしようとするとOSに処理が移り、不正を防ぎます。
保護機能までやると時間がかかるのでこの辺で割愛しておきます。
リアルモード
リアルモードのセグメント方式はプロテクトとは異なります。
別物と言った方がいいかもしれません。
一つのセグメントが使用できるメモリの大きさは16ビットのアドレスで表現できる64KBが限界となります。
加えてアドレスバスは20ビットまでしか認識しないので使えるメモリ空間の大きさは1Mまでとなります。
どんなOSだろうがなんだろうがこの制約を受けます。
さらにメモリを保護する機能も実装されていないため、プログラマーが自分自身で注意しOSやほかのプロセスのメモリにアクセスしない努力をしなくてはなりません。
プロテクトモードでcs、ds、ss、dsなどのセグメントレジスタはディスクリプタテーブルに置かれているディスクリプタの番号を格納していました。
リアルモードではセグメントレジスタはセグメントの先頭アドレスを直接指定する為に用いられます。
cs(コードセグメント)は命令、ds(データセグメント)にはデータ、ss(スタックセグメント)はスタック、es(エクストラセグメント)は予備みたいなものです。
セグメントレジスタに格納されるセグメントの先頭アドレスのベースにオフセットを指定してアドレスの一点を指定することが出来ます。
ページング
ページングはメモリを「固定長」のおおきさに「ページ」に分割して管理する方式です。
このページ単位でメモリと補助記憶装置間でデータのやり取りを行います。
固定長でメモリに割り当てるのできっちり収納出来てセグメント方式のようにメモリが空いているのに使えないという状況が減らせます。
しかしどんなプログラムに対しても固定長でめもりを割り当てるので小さなプログラムに余分にメモリを割り当てるということも在り得ます。
ページ内部で余った使われていないメモリが発生することをインターナルフラグメンテーション(内部断片化)と呼びます。
逆にページの単位が小さすぎると頻繁に補助記憶装置とメモリの間を行き来しメモリの管理に手間取るため処理が遅くなります。
ページの大きさによっては効率的な仮想記憶を実現できる。
ページ単位によってはインターナルフラグメンテーション(内部断片化)が大きくなる
セグメント方式はON-OFFを切り替えることができませんが、ページング方式はコンピューターにON-OFFのスイッチがついておりセグメント方式とページング方式を切り替えることができます。
デフォルトではOFFです。
OSが起動する時にそのスイッチを切り替えています。
LinuxやWindowsなどの最新のOSでは基本的に「フラットメモリモデル(基本フラットモデル)」を採用しています。
フラットメモリモデルとは論理メモリ空間全体を連続した一つのセグメントとして利用することです。
簡単に説明するとセグメント方式はOFFにできないのでディスクリプタのベースレジスタを常に0にします。
これで論理メモリ空間はオフセットのアドレスだけで指定することができます。セグメント方式をOFFにするのと同義です。
詳しくは調べていませんがこうすることで移植における煩雑さを回避している見たいです。
ページング方式の概略図です。
セグメント機構はディスクリプタを格納したディスクリプタテーブルを通して、ページング機構はページングテーブルを通して物理アドレスを指定します。
ページング方式におけるアドレスの指定の流れはこうです。
セグメントレジスタ(セレクタ値)→ディスクリプタテーブル→ベースアドレス(ディスクリプタ)+オフセット=論理アドレス(リニアアドレス)→ページテーブル→物理アドレス
セグメント方式のアドレス変換
セグメントレジスタ(セレクタ値)→ディスクリプタテーブル→ベースアドレス(ディスクリプタ)+オフセット=物理アドレス(リニアアドレス)
厳密にはメモリをさらに効率化するためにPTE(ページテーブルエントリ)にさらに細分化し仮想化しています。
ソフトウェアから見た場合(ハードウェアではない)の最終的なアドレス。
セグメント方式の場合はこれをそのまま物理アドレスとして使用します。
ページング方式の場合はプロセスに割り当てられた論理アドレス上をページテーブルで物理アドレスに変換して使用します。
ページテーブル
それぞれのプロセスに割り当てられる論理メモリと物理メモリの対応の一覧表。
リニアアドレスが少し混乱しますね。
ページディレクトリテーブル(PDT)とページテーブルエントリ(PTE)
32ビットアドレスのページング機構は厳密にはPDTとPTEによってさらにメモリを細分化して管理しています。
というのももしメモリを全てページに分けて管理しようとすると一つのページテーブルの大きさは4MBになってしまいます。
必要なページテーブルの数によってはかなりメモリを食います。
既述したように一つのプロセスが全ての仮想メモリ領域を使用するということはありません。
論理ページの全てに物理ページに割り当てる必要がないということです。
必要に応じてページテーブルの論理ページに物理ページを割り当てるだけでよいのです。
PDTとPTEはメモリをさらに細分化して管理することでメモリ領域を節約するアイディアです。
セグメント機構で計算したリニアアドレスは3つの部分に分解できます。
一つはPDT(10ビット)、次にPTE(10ビット)、最後にオフセット(12ビット)です。
パソコンのファイルをフォルダで分割する方法に似ていますね。2段階に分けることで不要なページテーブルを作ることを避けることができます。
CR3レジスタにはページディレクトリのエントリが格納されています。
48ビットアドレス
64ビットアドレス空間は16エクサバイトまで表現できますが、そこまで扱えるハードウェアを作るのは現実的ではありません。
そこでIntelでは実際のメモリ空間は48ビットまでに制限しています。
48ビットアドレスではPDTとPTEに加えてPage Map Level4(ページマップレベル4)とPage Directory Pointer(ページディレクトリポインター)にリニアアドレスを分解することで48ビットのアドレス空間を管理しています。
デマンドページング
「Demand Paging」読んで字のごとく、プロセスがメモリを必要とした時にそのページをメモリに読み込みます。
基本的にメモリんよ見込まれるプロセスにはアクセスの局所性という性質があり、短い時間で見るとメモリ上に配置されたコードやデータのある部分にだけアクセスが集中します。
短い時間で見ればプロセスに使用されているメモリは非常に限られているのです。
有限のメモリを効率的に使用する為に必要になるまでプロセスが使用するコードやデータは物理ページには配置せず、要求(ページフォルト)が起きた時に補助記憶装置からメモリに読み込みます。
こうすることでメモリを節約できます。
例えば大きなメモリ空間が必要なプログラムを大量に動かそうとすると物理的なメモリ空間はすぐに足りなくなってしまいます。
その解決策としてそれぞれのプロセスに仮想的なメモリの空間(論理アドレス)を割り当てます。
プロセスから要求が発生し物理アドレスが割り当てられるまでの間は論理アドレスが物理アドレスの代わりとして振舞っています。
論理アドレスは物理アドレスが使える権利のようなものです。
それぞれのプロセスはメモリが実際に必要になった時にだけその権利を行使し物理アドレスを割り当ててもらいます。
プロセス1とプロセス2は論理ページ(ページテーブル)を割り当てられてられています。
そしてプロセス1と2に割り当てられた論理ページは同じ物理メモリのページ4に紐付けられています。
これはプロセス1と2にとってはとメモリ全体を占有しているのと同じ意味を持ちます。
メモリの大きさは限られています。
そのため各プロセスかメモリを実際に必要とするまでの間は仮想のページ(ページテーブル)を割り当てるのです。
それぞれのプロセスは物理メモリを使う権利だけ保有し必要になったときだけ行使する。
プロセス間で物理メモリを共有させているのです。
短い時間で見るとプロセスの実行している部分は限られています。
例えば勉強するときに数学をやる時には国語は引き出しにしまっておくようなものです。
数学が一通り終わってから国語の教科書を取り出すように、プロセスもある処理が終わってから次の処理を開始します。
ページフォルト
プロセスがアクセスしたページがまだ物理メモリに配置されていない時に発生する例外処理です。
一旦アプリケーションからOSに処理が移り、要求されたページを読み込んだ後にアプリケーションに処理を戻します。
論理アドレス
物理アドレスと紐づけられプロセスが認識するアドレス。
実際にはプロセスが占有していなくても全てのメモリ空間を占有していると認識させる。
簡単に説明すると物理アドレスを要求する権利のようなもの。メモリが必要になる度に行使する。
物理アドレス
CPUが認識する実際のメモリアドレス。
アドレッシングモード
アドレッシングモード(Addressing Mode)は、CPUの命令セットアーキテクチャ(ISA)の一部を構成する。プロセッサの命令には操作対象をオペランドで指定するものがあり、その指定方法の詳細がアドレッシングモードと呼ばれるものである。
また小難しい表現です。
ユーザーがコンピューターを操作する時にそれを実現する為の体系化された規則。
具体的にはレジスタや命令の種類、アドレッシング、アドレスマネージメント、割り込みや例外処理の実現方法。
レジスタ
CPUに内蔵された記憶回路。
16ビットや32ビット、62ビットと極小さな数値の記憶しかできませんが、計算などを司りコンピューターの頭脳としての役割があります。
メモリよりかなり小さいものの高速で動作し実行中のプロセスの次の命令が保存されているメモリへのアドレスやコードやデータが保存されているメモリのアドレスを記憶しています。
高級言語では基本的に操作できないので意識することは殆どありませんが、アセンブリといった低級言語だと直接操作しなければなりません。
コンピューター側がユーザーから受けた命令をどんな風にハードウェアに実行させるかということです。
ユーザーがハードウェアを直接操作するのはとても煩雑になります。
いわゆるプログラミングですらその煩雑さを大幅に取り除いています。
例えばメモリのアドレスをプログラミングで直接指定することは普通ありません。このメモリアドレスにこのデータを保存して〜などです。
ただ実際にはその作業を行わなければなりません。
ハードウェアの面倒な操作をユーザーの代わりにハードウェアに伝える規則がプログラムです。
そしてプログラムが伝える命令と実際の動作を結び付けて定めたものが命令セットアーキテクチャです。
アドレッシングとは命令を実行する時にメモリアドレスをどのように指定するのかを定めたものです。
色んな方法があります。
絶対(直接)アドレス方式
メインメモリに0から振り分けられて番号を直接指定してデータのアドレスを指定します。
最もシンプルで分かりやすい方法です。
「じゃあ全部これでいいじゃん」と思いますが、他のシステムとの兼ね合いもあってほとんど使われません。
間接アドレス方式
指定されたアドレスに格納されているアドレスへ再度アクセスして使用するデータを指定します。
メモリに2回アクセスするので実行速度が遅いという側面があります。
メモリに格納されているアドレスを実効アドレスと呼びます。
レジスタで指定すると16や32ビットというように表現できる限界の数値の制約がありますが、メインメモリを使ってアドレスを表現できるので大きなメモリ空間を使うことができます。
ベースアドレス指定方式
アドレスを基底部(ベース)とオフセットに分解して表現します。
ベースに1000を入れてオフセットに200を入れます。
この場合1000から200番離れたアドレスにデータが格納されています。
レジスタのデータを使うので関節アドレス方式より速く、またメモリを論理的に意味のある区画に分割することができます。
つまりベースによって1000(データ)、2000(コード)のようにまとまりのある区画に分けて綺麗に管理することが出来ます。
他のメモリのページ

コメント