Lions' 本読み (48)

まごろくさんからフォロー頂いた部分を確認。

まず malloc から

まごろくさん tweet が以下。

mallocは今のmalloc(3)とは違って、OS層の領域を管理します。メモリとDISK上のswap域を共通のロジックで管理してます。これにswapmapを渡すとswap領域の獲得。coremapを渡すとメモリ領域の獲得を行う事ができます。

https://twitter.com/magoroku15/status/211103131990376448 より引用
297p あたりに解説あり。ここも宿題ということにしといて別途確認の方向。

xalloc 手続き

ざっくりベースで確認を。まず unix/text.h で定義されてる構造体が以下。

4300 /*
4301  * text 構造体
4302  * スワップデバイス上の純粋な
4303  * 手続きごとに 1 つづつ割り当てられる。
4304  * text.c が操作する。
4305  */
4306 struct text
4307 {
4308  int       x_daddr;  /* セグメントのディスクアドレス */
4309  int       x_caddr;  /* ロードされている場合コアアドレス */
4310  int       x_size;   /* サイズ (64 バイト単位) */
4311  int       *x_iptr;  /* プロトタイプの i ノード */
4312  char      x_count;  /* 参照カウント */
4313  char      x_ccount; /* ロードされている参照数 */
4314 } text[NTEXT];

xalloc 手続きではこの構造体の配列をナメて一番アタマの x_iptr 属性が NULL な要素を取り出し候補にしておきつつ、引数の i-node なポインタと x_iptr 属性の値が同じ要素があるかどうかを確認してます。
同値な要素があれば共用可能な text セグメントがロードされておる、という判断をしている模様。

4441    for(xp = &text[0]; xp < &text[NTEXT]; xp++)
4442            if(xp->x_iptr == NULL) {
4443                    if(rp == NULL)
4444                            rp = xp;
4445            } else
4446                    if(xp->x_iptr == ip) {
4447                            xp->x_count++;
4448                            u.u_procp->p_textp = xp;
4449                            goto out;

x_ccount という属性はコアにいるのかどうかを判断する属性な模様。out なラベル以降の処理の記述が以下になってて

4474 out:
4475    if(xp->x_ccount == 0) {
4476            savu(u.u_rsav);
4477            savu(u.u_ssav);
4478            xswap(u.u_procp, 1, 0);
4479            u.u_procp->p_flag =| SSWAP;
4480            swtch();
4481            /* リターンしない */
4482    }
4483    xp->x_ccount++;

値が 0 ならスワップアウトしとります。また、共用できる text セグメントが無かった場合、この属性の値は 0 で初期化されてて、見つからなかった場合は必ず一回スワップアウトする、という事、で良いのかな。

核心部分

とりあえず順に確認。

4452    xp->x_count = 1;
4453    xp->x_ccount = 0;
4454    xp->x_iptr = ip;
4455    ts = ((u.u_arg[1]+63)>>6) & 01777;
4456    xp->x_size;

このあたりは text 構造体な配列要素の初期化ですね。

  • 参照カウント 1 にして
  • コアにはロードされておらず
  • 引数で渡された i-node オブジェクトを x_iptr 属性に格納
  • テキストセグメントのサイズを x_size 属性に格納

で、スワップ領域確保してるのか。

4457    if((xp->x_daddr = malloc(swapmap, (ts+7)/8)) == NULL)
4458            panic("out of swap space");

で、

が以下二行で

4459    expand(USIZE+ts);
4460    estabur(0, ts, 0, 0);

次にユーザーアドレス空間の 0 番地にテキストセグメントなデータを読み込んでいます。

4461    u.u_count = u.u_arg[1];
4462    u.u_offset[1] = 020;
4463    u.u_base = 0;
4464    readi(ip);

readi 手続きは 6221 以降で定義されててコメントの一部を以下に

6216  * u_base   転送先のコアアドレス
6217  * u_offset ファイル中のバイトオフセット
6218  * u_count  読み出すべきバイト数
6219  * u_segflg カーネル/ユーザーへの読み出し

これってデータセグメントにテキストセグメントを読み出した、という記述になってますが、swap を簡単に済ませるための便宜的措置と見てよいのかな。
んで、プロセス構造体を取り出してロックして

4465    rp = u.u_procp;
4466    rp->p_flag =| SLOCK;

swap 手続き呼び出してます。

4467    swap(xp->x_daddr, rp->p_addr+USIZE, ts, 0);

これ、順番に

  • 確保したスワップ領域のアドレス
  • ユーザ構造体のアドレスに USIZE を加えた値 (データセグメントのアドレス ?)
  • テキストセグメントのサイズ
  • 最後の引数が現時点で意味不明

という事なのか。で、後は諸々の後始末をして

4468    rp->p_flag =& ~SLOAD;
4469    rp->p_textp = xp;
4470    rp = ip;
4471    rp->i_flag =| ITEXT;
4472    rp->i_count++;
4473    expand(USIZE);

終わり、なんですがなんつーか凄いな。ポインタ型が何でも指せちゃうので何でもあり的記述になってて笑う。4469 まではプロセス構造体の後始末で、4470 から 4472 までは引数で渡ってきた i-node なオブジェクトの後始末なのか。
で、最後にスワップアウトされた領域を解放して out: 以降の処理、になるのか。

あら?

swap 手続きはスワップインが云々という記述がありますね。あ、でも 374p な記述は以下なのか。

4467: (プロセス固有データを除いた) データセグメントをテキストセグメントのために確保されたディスクスワップ領域にスワップアウトする。

Lions' Commentary on UNIX より引用
xalloc のコメントも以下に引用してみます。

4418 /* 共有テキストセグメントをアタッチする。
4419  * 共有テキストがなければ、リターンする。
4420  * あれば、それをつかまえる。
4421  * もしそれが現在使われていなければ、i ノード (ip) から
4422  * 読み込んで、スワップ空間に作る必要がある
4423  * 使われていても、現在コアにないなら、
4424  * それを戻すために swap を呼び出さなければならない。

ついでに xswap も見てみる

と思ったらここでも swap 呼び出してますね。どうも swap に渡す四番目の引数が読み書きなフラグになっている模様。sched とかでは 1 を渡してスワップイン、みたい。
ということで勉強会がそろそろ始まるので続きは別途。