Linuxの備忘録とか・・・(目次へ)Linuxの備忘録とか・・・(目次へ)ハイメモリX86-32の場合でページ4Kの時、搭載できる物理メモリは4GBです。リニアアドレスも4GBと有することができます。それぜは、何故ハイメモ領域が必要か?ということですが、1リニアアドレス空間をユーザプロセスとカーネルが使っていることにあります。ユーザプロセスは0から0xC0000000までの3GB、カーネルはそれ以降の1GBです。カーネルは同時(?)に使えるリニア空間は1GMということです。カーネルはストレートマップしています。なると物理アドレスの0から1GBまでしか、マッピングすることはできないからです。カーネルもユーザプロセスと独立したリニアアドレス空間を有しているなら、ハイメモリというものは必要ありません。カーネルは4GB空間をストレートマップすることが可能だからです。ユーザプロセスは頻度にカーネルモードと行き来します。カーネルが独自のページテーブルを有していると、そのつど、TBLの無効化が発生します。これはパフォーマンスによくありません。64ビットのプラットフォームではハイメモリというものはありません。搭載する物理メモリより十分なリニアアドレス空間を確保できるからです。ハイメモリのページを使用する方法ですが、1GBを有するカーネル空間で、それは896MBとして、残りの128MB空間内にハイメモ領域のページを割り当てるようにエントリーを予約しています。このアロケート手法として永続的カーネルマッピング/一時的カーネルマッピングおよび非連続カーネルマッピングと方法があるようです。前者の2つはページ単位の割り当ては、後者は複数ページの割り当てる物のようです。ここでは前者の永続的カーネルマッピングについてです。永続的/一時的という表現ですが、これは狭義的な意味合いで、概念的には遅延が許されるプロセスでの利用/そうでないプロセスでの利用と解釈した方が、理解しやすいのではと思います。用はワークキューとタスクレットの関係みたいなものでしょうか?マップするpageを引数としてkmap関数がコールされます。そのページがハイメモリ領域のページであるなら、kmap_high関数でハイメモとしてマップ処理が行われます。pageがハイメモ領域かどうかのチェックは、PageHighMemマクロでpage->flagsをチェックすることで行っています。zoneのZONE_HIGHMEMから取得したものは、page->flagsにPG_highmemがセットされており、そのビットをチェックすることで判定できます。ZONE_HIGHMEMでないなら、そのページはストレートマップ内のページであり、リニアアドレスを有している事になります。従ってpage_address関数でリニアアドレスを取得してそれを返します。ないならハイメモリーで、リニアアドレスを有していません。kmap_high関数でその割り当てを行います。kmap_high(page);}kmap_high関数では、再度page_address関数でリニアアドレスの取得を試みます。ハイメモページはリニアアドレスを有していないのにと・・・。他のプロセスが割り当てている場合があります。それと実はハイメモページを解放する時、ただ単に解放した旨のフラグの設定するだけです。MMUがページアドレス変換を行うのは、TBLキャッシュを通してであり、従って目的としているページが解放されたからと言っても、プロセスとして参照しないだけで、アドレス的には有効となっているケースがあるわけです。ページがマップされていないなら、map_new_virtual関数により、ハイメモ領域のページをマップすることになります。vaddr;}ページをマップすると言うことですが、これはページの物理アドレスを、ページテーブルエントリー(最終的にはTBLに読み込ませる。)に設定する事を意味します。このためカーネルはこのエントリーをPKMAP_BASEからページサイズが4Kの場合、1024個すなわち領域として4MB予約しています。(言い換えると永続的マップとして一度に使えるサイズは4MBと言う事。LAST_PKMAPは、このマップするために予約しているエントリー数です。従ってここでの処理は、空いているエントリーを探し出して、そのページテーブルエントリーに、ページの物理アドレスを設定する処理になるわけです。このエントリーを管理しているのが、pkmap_count[LAST_PKMAP]配列です。ここから空いているエントリを探します。last_pkmap_nrは、前回割り当てたpkmap_count[]内のインデックスです。LAST_PKMAP_MASKとし、その次から検索します。LAST_PKMAP_MASKでマスクしているのは、LAST_PKMAP個超えたらlast_pkmap_nrを0とするためです(たぶん)。以降空いているエントリが無かった。この場合、flush_all_zero_pkmaps関数で、pkmap_count[]=1のマップとして有効となっているが使用していないエントリをpkmap_count[]=0として、TBLを無効化し再度検索します。pkmap_count[]はそのエントリを参照しているカウンターです。して参照し、プロセスからは参照されていない。プロセスがそのページを解放する時、いきなり0とするのでなく、1とします。することで再度同じページをマップする場合、TBLを無効化する必要がないということです。再度検索で、(!pkmap_count[last_pkmap_nr])はそのエントリは使用可能ということです。その場合ループを抜けます。(--count)で、countが0になったということは、すべてのエントリーを使用できません。ここからが永続的カーネルマップの味噌となります。この場合、このハイメモページを使おうとするタスクをウエイトさせています。current)で初期化したwaitキューを繋いでいます。schedule関数で実行権を放棄し、ハイメモページを所有しているカーネルタスクが、ハイメモページを解放する時に、このwaitキューをウエイクアップし、再度実行して、ループすることで、pkmap_count[]が0のエントリーを取得できると言うことです。1でTBLが参照している旨の設定をし、set_page_address関数で、page->viatualにそのリニアアドレスを設定します。kmap_high関数に戻ると、pkmap_count[PKMAP_NR(vaddr)]++でこれを呼んだプロセスが参照している旨の設定を施します。vaddr;}従って、カーネルはmap_new_virtual関数で取得した仮想メモリ(まだページが割り当てていないなら。)で、ハイメモ領域から取得したページをアクセスすることが可能となるわけです。使用しなくなったらそのページを管理しているpkmap_count[]に0を設定することで、その仮想アドレスを解放し、他のカーネルスレッドのハイメモページをアロケイトを可能にさせているわけです。昔でいうところの、バンク切り替えみたいなものです。ノースAdsense広告情報が設定されていません。