メモ代わり。てきとーに。 いや、ですからてきとーですって。 2年前ぐらいにPythonあたりでメールくれた方、ごめんなさい。メール紛失してしまい無視した形になってしまいました。。。

2008年2月19日火曜日

[Apache][CodeReading] Apache2.2.8コードリーディング19日目(2)

今日もApache2.2.8コードリーディング。

今日読んだところは、

  • APR_RING_SENTINELマクロ/Apache2.2.8
  • APR_RING_LASTマクロ/Apache2.2.8
  • APR_OFFSETOFマクロ/Apache2.2.8
  • CRAY2定数/Apache2.2.8
  • LINUX定数/Apache2.2.8
  • CRAY定数/Apache2.2.8
  • APR_RING_FIRSTマクロ/Apache2.2.8
  • APR_STRINGIFY_HELPERマクロ/Apache2.2.8
  • APR_STRINGIFYマクロ/Apache2.2.8
ら辺。

apr_pollset_t構造体は環境によって定義が違う。

kqueueを使える環境用(FreeBSD、今や*BSD?)、
epoll用(Linuxカーネル2.5.44以降)、
portが使える環境用(Solarisか)、
pollが使える環境用、
selectしか使えない環境用の5種類ある。

ほとんどメンバは一緒なんだけど、poll、select用以外の環境用のapr_pollset_tは、
リングを持っている。
このリングを定義しているものが、
APR_RING_HEADマクロ、今日読んだAPR_RING_SENTINELマクロ、APR_RING_FIRSTマクロ、APR_RING_LASTマクロあたり。

このリングがなかなか素敵。

ざっと簡単に言うと
「headを持つ構造体」と「リストを構成する構造体」が別なのにもかかわらず、
「headを持つ構造体」を無理やり「リストを構成する構造体」に「キャスト」して
双方向リストを「リング」にしている
っていう感じ・・・。
うーむ・・・。

とりあえず画像を入れてみる・・・。


双方向リストは、通常右の画像のようにつながっている。







これにこのリストの先頭へのポインタと最後尾へのポインタを持つlinkと同じ構造体をheadとして入れると、右の2番目の画像のように最後尾(左側のelem)のnextの値と、先頭(右側のelem)のprevの値が決められない。
最後尾(左側のelem)のnextと先頭(右側のelem)のprevにNULLを入れておく、なんてことでも良いが、それだとhead部も含めてリングとして操作できない。

つまり、先頭(右側)→prev→prevとして最後尾(左側)を指すことができないし、最後尾(左側)→next→nextとやって先頭(右側)の指すことができない。



そこで、ファントム(幽霊、で良い?)を用意する。head用にバーチャルなelemを用意する。それが右の図。
こうしておくことでheadも含めて全ての要素をリングとして扱うことができる。



赤い矢印の指すポイントを仮にSENTINELと名づけよう。
このバーチャルなelemを用意するのにheadのアドレスからSENTINELを算出しなければならないのだが、それはAPR_RING_SENTINELというマクロでやっている。
elemのlink要素のオフセット値をheadのアドレスから差し引いた値がSENTINELになる。
こうしてSENTINELからheadまでの距離とelemの先頭アドレスからlink要素までの距離を等しくする
ことでSENTINELをあたかもelemの先頭アドレスとして扱えるようになる。

結果、最後尾→next→nextというアクセスや、先頭→prev→prevというアクセスが可能になる。

ちなみになんでSENTINELというかというと、headから算出できる値なもんで、いわゆる番兵の役割
を果たすからなのではないかと・・・・。(てきとー)

また、SENTINELを算出するときに、構造体のメンバのオフセット値を求めなくてはならないのだが、
Linuxなんかだと、offsetof関数があるんで問題ないのだが、offsetof関数が無い環境の場合はなか
なか素敵なコードが定義される。

それが下記。

((unsigned int)&(((struct xxx *)NULL)->link))


なんてことを構造体メンバのオフセット値を取得するためにやっている。(xxxは適当)
Apacheのソース以外でも見たことがあるような気がするが、それは置いといて、
ぱっと見、NULLがあって気持ちわるい。

が、Linuxでもばっちり動く。

これは、struct xxxの先頭アドレスをとりあえず0番地にして、
struct xxxのメンバのアドレスを取得してみると、あら不思議。
オフセットになっている

というテクニックだか常套手段だかなんだか良く知らない。
でこのオフセット取得をやっているのがAPR_OFFSETOFマクロ。apr_general.hに書かれている。

あとは特になしな感じ。
敢えて言うなら、

APR_STRINGIFYマクロか。

といっても、文字列化マクロなだけ。

#define APR_STRINGIFY(n) #n


な感じ。本当はもう一段階階層が深いけど。



おしまい。
.

0 コメント: