swapリストでのCのお勉強
swapon関数内でスワップ領域のプライオリティに応じ、そのリストを作成する箇所である。pが新規にリストにつなごうとするswap_infoで、swap_info[]がそれぞれのスワップ領域を表している。swap_list_t構造対はheadとnextの2つのメンバーをもつ構造体で、swap_info[]リストのヘッドとなる。初期値は-1,-1である。headはswap_info[]内の一番高いプライオリティのインデックスを、nextはスワップアウト時の検索開始のswap_info[]のインデックスを示す。以降その先のswap_info->nextを辿ることでリストを形成している。と思う。
2回目以降はforループでチェックされ、pより小さいプライオリティのswap_infoがあると、そこでループを抜ける。そして直前のpより大きいプライオリティのswap_infoはprevに設定されており、そのswap_info.nextにp - swap_infoを設定し、p->nextにpより次に小さいプライオリティの位置となるiを設定することでリストを作成している。このリストはswap_info[]内の配列インデックスで構成していることになるのだが。で・・・・
p - swap_infoは配列インデックスを算出するわけである。実はアドレスpからアドレスswap_infoまでの差とばっかし思い込んでいたため、ここの理解に悩んでしまった。でCのお勉強。
通常/ets/fstabでプライオリティを設定しないでスワップを設定する(たぶん)、この場合0でl初期化されているスタテックな変数least_priorityから-1したものをそのプライオリティとしている。従ってデフォルトのスワップ領域のプライオリティは-1となっている。
ここで、スワップ領域としてファイルを指定するのは周知の事実。ddで一連のファイルイメージを作成し、それをmkswapでフォーマットしswaponすればよい。実はこのファイルイメージddで作成する必要はない。ためしに既存のでかいファイルをmkswapしてswaponするとswapon -sでちゃんとスワップファイルとして登録されているのが確認できる。ただスワップの領域(ページ単位)と物理的なブロックが連続している方が効率がいいだけの話だ。内部的にはswap_exten構造体にブロックとページをマップするリストを作成する。非連続のイメージだと、そのリストが複数作成されてしまう。
また、ファイルでのスワップとデバイスでのスワップの内部的差はそのブロックデバイスの取得方法だけである。ファイルの場合swap_info->bdevはinodeのスーパブロックから取得する。デバイスの場合はデバイススペシャルファイルから取得し、ブロックサイズとしてページサイズを設定する。それ以外の処理でかかる違いはない。(と思う。)従ってスワップ領域が足りなくなれば再度パーティションを切りなおして作成しなくても、ファイルでスワップを作成してもパフォーマンス的には問題無さそうである。まあ、当初のスワップが足らなくなるぐらいなら物理メモリを増設するのが今日のアプローチか? かりにスワップが足りなくなっても、ただちにシステムそのものに支障がでてくるケースは稀であり、支障がでてくるのであればシステム構成を見直すのがベストのアプローチか。
このプライオリティをスワップアウトするときのスワップ領域を決定する唯一の指針となる。これはget_swap_page関数で行う。まず優先度の一番高いものからスワップ領域として選択していく。そして同じ優先度がラウンドロビンで選択していく。get_swap_page関数ではswap_list.nextから検索するわけだが、実際のページ獲得scan_swap_mapの前に次に検索するための準備として、next = si->nextで、次のスワップ領域をnextに保存しておく。ただし、そのプライオリティが検索対象となるプライオリティと異なっている場合(次のプライオリティが現プライオリティより低いことを意味する。)next = swap_list.headとし、次に検索するのはswap_list.headとする。こうすることで同じプライオリティなら次の領域が、そうでないならheadから再検索することで再度そのプライオリティの領域が選択される。このようにして、同じプライオリティでのラウンドロビンを実現している。なお、wrapped++とすることで、再ループとならないようにしている。再ループしてしまうことはswap_list.headにも空きスペースがないからで、この場合次にプライオリティの領域が選択されるようになっている。
highest_bitはそれ以降のページには空きがないことを示す指針である。従って!si->highest_bitならそのswap_infoには空きがないことで次のスワップ領域が検索対象となる。空きがあるなら次回の検索指針のために、swap_list.next = nextとして、scan_swap_mapでページを獲得しにいく。
static struct swap_list_t swap_list = {-1, -1}; static struct swap_info_struct swap_info[MAX_SWAPFILES]; /* insert swap space into swap_list: */ prev = -1; for (i = swap_list.head; i >= 0; i = swap_info[i].next) { if (p->prio >= swap_info[i].prio) { break; } prev = i; } p->next = i; if (prev < 0) { swap_list.head = swap_list.next = p - swap_info; } else { swap_info[prev].next = p - swap_info; }初めてスワップを登録するとき、swap_list の初期値が-1であることとi >= 0条件で、forループはスキップする。従ってprev = -1で、swap_list.headとswap_list.nextにはp - swap_infoが設定される。
2回目以降はforループでチェックされ、pより小さいプライオリティのswap_infoがあると、そこでループを抜ける。そして直前のpより大きいプライオリティのswap_infoはprevに設定されており、そのswap_info.nextにp - swap_infoを設定し、p->nextにpより次に小さいプライオリティの位置となるiを設定することでリストを作成している。このリストはswap_info[]内の配列インデックスで構成していることになるのだが。で・・・・
p - swap_infoは配列インデックスを算出するわけである。実はアドレスpからアドレスswap_infoまでの差とばっかし思い込んでいたため、ここの理解に悩んでしまった。でCのお勉強。
struct test { int dummy1; int dummy2; }; struct test a[10]; main() { struct test *p; p = a; p++; p++; printf("%d\n", a); printf("%d\n", p); printf("%d\n", p - a); }結果は以下の通りで、Cのお勉強はおしまい。
[root@KURO-BOXHG kitamura]# ./a.out 268503276 268503292 2
通常/ets/fstabでプライオリティを設定しないでスワップを設定する(たぶん)、この場合0でl初期化されているスタテックな変数least_priorityから-1したものをそのプライオリティとしている。従ってデフォルトのスワップ領域のプライオリティは-1となっている。
[root@KURO-BOXHG ~]# swapon -s Filename Type Size Used Priority /dev/sda2 partition 530136 0 -1swapon -pで設定できるプライオリティは0 から 32767 の間の数値である。従ってleast_priorityはシステムのスワップ領域の一番小さいプライオリティとなる。プライオリティを設定しないでスワップ領域を追加すると、least_priorityから-1を引いたものをプライオリティとしていく。ここで新規にプライオリティを設定しないでスワップを設定すると、そのプライオリティは-2となる。
ここで、スワップ領域としてファイルを指定するのは周知の事実。ddで一連のファイルイメージを作成し、それをmkswapでフォーマットしswaponすればよい。実はこのファイルイメージddで作成する必要はない。ためしに既存のでかいファイルをmkswapしてswaponするとswapon -sでちゃんとスワップファイルとして登録されているのが確認できる。ただスワップの領域(ページ単位)と物理的なブロックが連続している方が効率がいいだけの話だ。内部的にはswap_exten構造体にブロックとページをマップするリストを作成する。非連続のイメージだと、そのリストが複数作成されてしまう。
また、ファイルでのスワップとデバイスでのスワップの内部的差はそのブロックデバイスの取得方法だけである。ファイルの場合swap_info->bdevはinodeのスーパブロックから取得する。デバイスの場合はデバイススペシャルファイルから取得し、ブロックサイズとしてページサイズを設定する。それ以外の処理でかかる違いはない。(と思う。)従ってスワップ領域が足りなくなれば再度パーティションを切りなおして作成しなくても、ファイルでスワップを作成してもパフォーマンス的には問題無さそうである。まあ、当初のスワップが足らなくなるぐらいなら物理メモリを増設するのが今日のアプローチか? かりにスワップが足りなくなっても、ただちにシステムそのものに支障がでてくるケースは稀であり、支障がでてくるのであればシステム構成を見直すのがベストのアプローチか。
このプライオリティをスワップアウトするときのスワップ領域を決定する唯一の指針となる。これはget_swap_page関数で行う。まず優先度の一番高いものからスワップ領域として選択していく。そして同じ優先度がラウンドロビンで選択していく。get_swap_page関数ではswap_list.nextから検索するわけだが、実際のページ獲得scan_swap_mapの前に次に検索するための準備として、next = si->nextで、次のスワップ領域をnextに保存しておく。ただし、そのプライオリティが検索対象となるプライオリティと異なっている場合(次のプライオリティが現プライオリティより低いことを意味する。)next = swap_list.headとし、次に検索するのはswap_list.headとする。こうすることで同じプライオリティなら次の領域が、そうでないならheadから再検索することで再度そのプライオリティの領域が選択される。このようにして、同じプライオリティでのラウンドロビンを実現している。なお、wrapped++とすることで、再ループとならないようにしている。再ループしてしまうことはswap_list.headにも空きスペースがないからで、この場合次にプライオリティの領域が選択されるようになっている。
highest_bitはそれ以降のページには空きがないことを示す指針である。従って!si->highest_bitならそのswap_infoには空きがないことで次のスワップ領域が検索対象となる。空きがあるなら次回の検索指針のために、swap_list.next = nextとして、scan_swap_mapでページを獲得しにいく。
for (type = swap_list.next; type >= 0 && wrapped < 2; type = next) { si = swap_info + type; next = si->next; if (next < 0 || (!wrapped && si->prio != swap_info[next].prio)) { next = swap_list.head; wrapped++; } if (!si->highest_bit) continue; if (!(si->flags & SWP_WRITEOK)) continue; swap_list.next = next; offset = scan_swap_map(si); if (offset) { spin_unlock(&swap_lock); return swp_entry(type, offset); } next = swap_list.next; }