デスマーチ中につき、一時休止。
隙をみて読む予定・・・・。
.
メモ代わり。てきとーに。 いや、ですからてきとーですって。 2年前ぐらいにPythonあたりでメールくれた方、ごめんなさい。メール紛失してしまい無視した形になってしまいました。。。
2008年3月8日土曜日
[Apache][CodeReading] Apache2.2.8コードリーディング -- 一時休止
投稿者
atkonn
時刻:
14:54:00
0
コメント
ラベル: Apache, CodeReading
2008年3月1日土曜日
[Apache][CodeReading] Apache2.2.8コードリーディング30日目
今日もApache2.2.8コードリーディング。
今日はちょっとだけ。
今日やったところは、
- apr_cvt()/Apache2.2.8
apr版printfの不動小数点数を文字列に変換するところ。
apr_cvt()
この関数は少々難しい。いかに自分が力不足か確認できた。。。
何をする関数かというと、与えられた浮動小数点数値を
与えられた精度で、文字列化するのだが、ここで小数点は文字列化しない。
小数点の位置は引数で関数に渡すパラメータにセットされ、呼び出し元に返される。
正負も引数で関数に渡すパラメータにセットされ、呼び出し元に返される。
たとえば、1234.5678という数値があったとする。
そして、eflags == 0、ndigits == 10でapr_cvt()をコールしたとする。
このときの関数の呼び出し結果は、
123456780000000
という文字列と、
4
という小数点の位置と、
0
という正負フラグ
になる。
あと、たとえば、0.9999という数値があったとして、
精度が4以下の場合、1に丸められる。
とりあえず、以下コード。
96 static char *apr_cvt(double arg, int ndigits, int *decpt, int *sign,
97 int eflag, char *buf)
98 {
99 register int r2;
100 double fi, fj;
101 register char *p, *p1;
102
103 if (ndigits >= NDIG - 1)
104 ndigits = NDIG - 2;
105 r2 = 0;
106 *sign = 0;
107 p = &buf[0];
108 if (arg < 0) {
109 *sign = 1;
110 arg = -arg;
111 }
112 arg = modf(arg, &fi);
113 p1 = &buf[NDIG];
114 /*
115 * Do integer part
116 */
117 if (fi != 0) {
118 p1 = &buf[NDIG];
119 while (p1 > &buf[0] && fi != 0) {
120 fj = modf(fi / 10, &fi);
121 *--p1 = (int) ((fj + .03) * 10) + '0';
122 r2++;
123 }
124 while (p1 < &buf[NDIG])
125 *p++ = *p1++;
126 }
127 else if (arg > 0) {
128 while ((fj = arg * 10) < 1) {
129 arg = fj;
130 r2--;
131 }
132 }
133 p1 = &buf[ndigits];
134 if (eflag == 0)
135 p1 += r2;
136 if (p1 < &buf[0]) {
137 *decpt = -ndigits;
138 buf[0] = '\0';
139 return (buf);
140 }
141 *decpt = r2;
142 while (p <= p1 && p < &buf[NDIG]) {
143 arg *= 10;
144 arg = modf(arg, &fj);
145 *p++ = (int) fj + '0';
146 }
147 if (p1 >= &buf[NDIG]) {
148 buf[NDIG - 1] = '\0';
149 return (buf);
150 }
151 p = p1;
152 *p1 += 5;
153 while (*p1 > '9') {
154 *p1 = '0';
155 if (p1 > buf)
156 ++ * --p1;
157 else {
158 *p1 = '1';
159 (*decpt)++;
160 if (eflag == 0) {
161 if (p > buf)
162 *p = '0';
163 p++;
164 }
165 }
166 }
167 *p = '\0';
168 return (buf);
169 }
引数は、
- arg 変換対象の浮動小数点数値
- ndigits 精度
- decpt 出力パラメータ。小数点の位置
- sign 出力パラメータ。正負。
- eflag フォーマットが'e'か'E'の場合は、0以外の値を指定する。
- buf 出力パラメータ。変換後文字列格納領域。
さて、ややこしいので1つづつ見ていく。
99行目の
register int r2;
は、小数点の位置の算出に利用される。
103 if (ndigits >= NDIG - 1)
104 ndigits = NDIG - 2;
105 r2 = 0;
106 *sign = 0;
107 p = &buf[0];
あたりは特に難しくない。精度が、上限値(NDIG==80)を超えていたらセットしなおしている。
r2(小数点の位置)は0クリア。
sign(正負フラグ)も0クリア。
pを出力領域の先頭アドレスにセット。
108 if (arg < 0) {
109 *sign = 1;
110 arg = -arg;
111 }
ここは、変換対象の浮動小数点数が負の値で合った場合の処理。
signに1をセットし、argは正の値にセットしなおす。
112 arg = modf(arg, &fi);
ここで整数部と小数部を分離。
fiには整数部、argには小数部が入る。
113 p1 = &buf[NDIG];
ここで、p1に出力領域(buf)の最後尾のアドレスをセットしている。
117 if (fi != 0) {
118 p1 = &buf[NDIG];
119 while (p1 > &buf[0] && fi != 0) {
120 fj = modf(fi / 10, &fi);
121 *--p1 = (int) ((fj + .03) * 10) + '0';
122 r2++;
123 }
124 while (p1 < &buf[NDIG])
125 *p++ = *p1++;
126 }
ここで、整数部を文字列に変換する。
118行目は必要なコードでは無いと思う。
whileループは整数部が無くなるまで繰り返す。
120行目で10分の1にすることで、整数部の1の位を取り出している。
1の位はfjに10分の1の値で設定される。
そして、面白いのがここ。
121 *--p1 = (int) ((fj + .03) * 10) + '0';
10分の1にして取り出した1の位の値に0.03を加算後、10倍しているのだが、
この0.03が無いと結果が変わってしまう。
たとえば、1234.0という値を変換しようとした場合、0.03を加算しないと
p1には1134という値がセットされてしまう。
0.03を加算することで、doubleやfloatの問題を回避している。
124行目からは、121行目で作成した文字列をbufの先頭領域にコピーしている。
127 else if (arg > 0) {
128 while ((fj = arg * 10) < 1) {
129 arg = fj;
130 r2--;
131 }
132 }
は、整数部が無かった場合の処理で、
小数点以下第一位の位に0以外の数値を持ってきている。
位を移動した分だけr2も更新。
133 p1 = &buf[ndigits];
134 if (eflag == 0)
135 p1 += r2;
136 if (p1 < &buf[0]) {
137 *decpt = -ndigits;
138 buf[0] = '\0';
139 return (buf);
140 }
p1に指定精度になるようにbuf[ndigits]へのアドレスをセット。
135行目で、整数部の桁数分p1を後ろにずらしている。
141 *decpt = r2;
142 while (p <= p1 && p < &buf[NDIG]) {
143 arg *= 10;
144 arg = modf(arg, &fj);
145 *p++ = (int) fj + '0';
146 }
ここで、decptをセット。r2には小数点の位置(整数部の桁数)がセットされているので
その値をそのままdecptの指す先へセットしている。
142から146行目の処理では小数点以下の値を文字列化している。
仮にndigitsの値分の精度が必要なかった場合、あまった領域には'0'がセットされる。
147 if (p1 >= &buf[NDIG]) {
148 buf[NDIG - 1] = '\0';
149 return (buf);
150 }
で、領域を壊して突き進んでいたら、出力領域の最後尾にヌル文字をセットして
呼び出し元へ返る。
151 p = p1;
152 *p1 += 5;
153 while (*p1 > '9') {
154 *p1 = '0';
155 if (p1 > buf)
156 ++ * --p1;
157 else {
158 *p1 = '1';
159 (*decpt)++;
160 if (eflag == 0) {
161 if (p > buf)
162 *p = '0';
163 p++;
164 }
165 }
166 }
ここは、簡単に言うと、丸め処理をしている部分。
出力精度が4桁で、渡された値が99999であった場合、
このループに入るときのp1は、最後の'9'を指している。
そして、その'9'に5を加算する。('>'になる)
'>'は'9'より大きいので、このループが実行される。
このループの実行が終わると
99999であった値は100000に変わる。
それに伴ってdepctの値も更新。
最後にbuf[ndigits]にヌル文字をセットし、
bufを返す。
多分大体こんな感じの処理であっていると思う・・・。
うーむ。
ややこしいー。
おしまい。
.
投稿者
atkonn
時刻:
23:49:00
0
コメント
ラベル: Apache, CodeReading
2008年2月29日金曜日
[Apache][CodeReading] Apache2.2.8コードリーディング29日目
今日もApache2.2.8コードリーディング。
ひらメソッドでリーディング。
今日はapr_vformatterがコールしている関数群を中心に読んだ。
で、
- NDIGローカルマクロ(apr_snprintf)/Apache2.2.8
- HAVE_ISINFマクロ/Apache2.2.8
- HAVE_ISNANマクロ/Apache2.2.8
- S_NULL_LENローカルマクロ(apr_snprintf)/Apache2.2.8
- S_NULLローカルマクロ(apr_snprintf)/Apache2.2.8
- conv_p2_quad()/Apache2.2.8
- conv_p2()/Apache2.2.8
S_NULLは"(null)"っていう文字列。
S_NULL_LENはその文字列長。
メインはconv_p2_quad()とconv_p2()あたりか。
conv_p2_quad()
conv_p2_quad()は、64ビット整数を、8進表記文字列か16進表記文字列へ変換する関数。
以下、その関数。
638 static char *conv_p2_quad(apr_uint64_t num, register int nbits,
639 char format, char *buf_end, register apr_size_t *len)
640 {
641 register int mask = (1 << nbits) - 1;
642 register char *p = buf_end;
643 static const char low_digits[] = "0123456789abcdef";
644 static const char upper_digits[] = "0123456789ABCDEF";
645 register const char *digits = (format == 'X') ? upper_digits : low_digits;
646
647 if (num <= APR_UINT32_MAX)
648 return(conv_p2((apr_uint32_t)num, nbits, format, buf_end, len));
649
650 do {
651 *--p = digits[num & mask];
652 num >>= nbits;
653 }
654 while (num);
655
656 *len = buf_end - p;
657 return (p);
658 }
パラメータのnumを、nbits、formatの値に従って文字列に変換する。
nbitsが3の場合は8進に、nbitsが4の場合は16進表記になる。
nbitsが4の場合で、formatが'X'(大文字のX)である場合は、
0xa~0xfまでの数字に大文字が使われる。
numがINT32の範囲内であれば、conv_p2()を使用して変換する。
conv_p2()
ほとんどconv_p2_quad()と一緒。
で、以下コード。
619 static char *conv_p2(register apr_uint32_t num, register int nbits,
620 char format, char *buf_end, register apr_size_t *len)
621 {
622 register int mask = (1 << nbits) - 1;
623 register char *p = buf_end;
624 static const char low_digits[] = "0123456789abcdef";
625 static const char upper_digits[] = "0123456789ABCDEF";
626 register const char *digits = (format == 'X') ? upper_digits : low_digits;
627
628 do {
629 *--p = digits[num & mask];
630 num >>= nbits;
631 }
632 while (num);
633
634 *len = buf_end - p;
635 return (p);
636 }
こちらは32ビット整数を8進表記文字列か16進表記文字列に変換する関数になっている。
型が違うだけでconv_p2_quad()と一緒。
あとは特になしな感じ。
おしまい。
.
投稿者
atkonn
時刻:
23:53:00
0
コメント
ラベル: Apache, CodeReading
2008年2月28日木曜日
[Apache][CodeReading] Apache2.2.8コードリーディング28日目
今日もApache2.2.8コードリーディング。
今日も、昨日と同じくDEBUGログまわり。
printf書式を実装している部分。
今日読んだところは、
- FIX_PRECISIONローカルマクロ(apr_snprintf)/Apache2.2.8
- conv_10_quad()/Apache2.2.8
- conv_10()/Apache2.2.8
- FALSEローカルマクロ(apr_snprintf)/Apache2.2.8
- INT32_MINローカルマクロ(apr_snprintf)/Apache2.2.8
- INT32_MAXローカルマクロ(apr_snprintf)/Apache2.2.8
- APR_INT64_T_FMTマクロ/Apache2.2.8
これらはapr_vformatterという関数からコールされている。
conv_10_quad()とconv_10()は、数値を数字列に変換する関数。
10進表記の文字列に変換する。
conv_10_quad()とconv_10()の違いは、64ビットか32ビットかの違い。
その名のとおり、quadは64ビットに対応する。
conv_10_quad()
この関数は"%lld"フォーマットに対応する。
で、以下、そのソース。
383 static char *conv_10_quad(apr_int64_t num, register int is_unsigned,
384 register int *is_negative, char *buf_end,
385 register apr_size_t *len)
386 {
387 register char *p = buf_end;
388 apr_uint64_t magnitude = num;
389
390 /*
391 * We see if we can use the faster non-quad version by checking the
392 * number against the largest long value it can be. If <=, we
393 * punt to the quicker version.
394 */
395 if ((magnitude <= APR_UINT32_MAX && is_unsigned)
396 || (num <= INT32_MAX && num >= INT32_MIN && !is_unsigned))
397 return(conv_10((apr_int32_t)num, is_unsigned, is_negative, buf_end, len));
398
399 if (is_unsigned) {
400 *is_negative = FALSE;
401 }
402 else {
403 *is_negative = (num < 0);
404
405 /*
406 * On a 2's complement machine, negating the most negative integer
407 * results in a number that cannot be represented as a signed integer.
408 * Here is what we do to obtain the number's magnitude:
409 * a. add 1 to the number
410 * b. negate it (becomes positive)
411 * c. convert it to unsigned
412 * d. add 1
413 */
414 if (*is_negative) {
415 apr_int64_t t = num + 1;
416 magnitude = ((apr_uint64_t) -t) + 1;
417 }
418 }
419
420 /*
421 * We use a do-while loop so that we write at least 1 digit
422 */
423 do {
424 apr_uint64_t new_magnitude = magnitude / 10;
425
426 *--p = (char) (magnitude - new_magnitude * 10 + '0');
427 magnitude = new_magnitude;
428 }
429 while (magnitude);
430
431 *len = buf_end - p;
432 return (p);
433 }
とりたてて面白いことをやっているわけではない。
あえて言うなら、
元の値から、10で割った後、10でかけて1桁目を0にした値を差し引いて
1桁目を取得していることぐらいか。
値そのものが、32ビットの範囲内であれば、conv_10()をコールする。
conv_10()
conv_10は大体conv_10_quad()と一緒。
やっていることもほぼ一緒。
APR_INT64_T_FMT
これは、"lld"と記述されている。それだけ。
他は特になし。
今日の分はここまで。
おしまい。
.
投稿者
atkonn
時刻:
23:33:00
0
コメント
ラベル: Apache, CodeReading
2008年2月27日水曜日
[Apache][CodeReading] Apache2.2.8コードリーディング27日目
今日もApache2.2.8コードリーディング。
ひらメソッドでリーディング。
さて、まだApache起動直後の初期化中。
デバッグログを出力しようとしているところで、
printfのAPR版部分で、そのコア部分。
今日やったところは以下のとおり。
- INS_CHARローカルマクロ(apr_snprintf)/Apache2.2.8
- boolean_e列挙型/Apache2.2.8
- NUM_BUF_SIZEローカルマクロ(apr_snprintf)/Apache2.2.8
- apr_uint64_t/Apache2.2.8
- apr_islowerマクロ/Apache2.2.8
- NULローカルマクロ(apr_snprintf)/Apache2.2.8
- apr_isdigitマクロ/Apache2.2.8
- NUMローカルマクロ(apr_snprintf)/Apache2.2.8
- STR_TO_DECローカルマクロ(apr_snprintf)/Apache2.2.8
INS_CHARマクロ
バッファに1文字セットするマクロ。
中で、file_printf_flushがコールされる。
コードは、
255 #define INS_CHAR(c, sp, bep, cc) \
256 { \
257 if (sp) { \
258 if (sp >= bep) { \
259 vbuff->curpos = sp; \
260 if (flush_func(vbuff)) \
261 return -1; \
262 sp = vbuff->curpos; \
263 bep = vbuff->endpos; \
264 } \
265 *sp++ = (c); \
266 } \
267 cc++; \
268 }
な感じ。flush_funcは、関数へのポインタで、今回の場合は
file_printf_flush。
バッファがいっぱいになったら、file_printf_flushがコールされる。
file_printf_flush内で、バッファを出力すると、vbuffのcurposは
バッファの先頭をさすように更新されるので、262行目でspの値を
更新している。file_printf_flush内でバッファを出力しなかった場合は
curposは259行目で代入した値のままで、262行目で代入しても
値は同じ。
で、spのさしている先にc(文字)を代入している。
代入後sp++。ccの値も更新。
ccが何なのかは今のところ良く分からない。が、多分出力文字数ではないかと
思う。多分。
263行目の
263 bep = vbuff->endpos; \
の部分。
endposか、bepがどこかで更新されているのであれば、
ここでendposの値をbepに入れるのは分かるのだが、
少なくともINS_CHARマクロ、apr_vformatter関数では
endposも、bepも触っていないようだ。
このマクロは別のところでも使っているのだろうか・・・?
後のために、一応
- INS_CHARマクロの263行目は必要なのか?
file_printf_flushでは触っていないが、snprintf用とかvnprintf用とかで
endposかbepどちらかが更新されるのだろうか。
apr_islower、apr_isdigit
この辺はctype.hのislower、isdigitそのまま。
#defineしているだけ。
NUMマクロ
'0'を差し引いて数字を数値にしているだけ。
STR_TO_DECマクロ
数字列をNUMマクロを使用して数値にしているだけ。
今日のところはここまで。
apr_vformatterは時間がかかりそう。。
おしまい。
.
投稿者
atkonn
時刻:
22:18:00
0
コメント
ラベル: Apache, CodeReading
[Apache][CodeReading] Apache2.2.8コードリーディング26日目
昨日はさぼり。でもちょっと読んだ。
読んだところは、
- file_printf_flush()/Apache2.2.8
- apr_file_write_full()/Apache2.2.8
apr_file_write_full()は、apr_file_writeを使って、指定したバイト数を
がんばって出力する関数。
apr_file_writeはwriteシステムコールでEAGAINとかが返ってくると、
出力バイト数を減らしてリトライする。
apr_file_write_fullは、apr_file_writeで書き込めたバイト数を保持していて、
当初予定していた指定バイト数が全て出力されるまでがんばる。
file_printf_flushの方は、
apr_file_printf関数の中でも呼ばれているが、
多分主はapr_vformatter関数の中で呼ばれる方。
apr_vformatter関数は未読なので今のところ不明。
な感じ。
.
投稿者
atkonn
時刻:
11:37:00
0
コメント
ラベル: Apache, CodeReading
2008年2月25日月曜日
[Apache][CodeReading] Apache2.2.8コードリーディング25日目
今日もApache2.2.8コードリーディング。
やっとこさっとこデバッグログ実出力完了。
場所はApache起動直後の初期化部分。
allocator、poolの初期化が終わって、
apr_thread_mutex_createをしようかというところ。
その中で、apr_pcallocという関数がコールされているんだけど、
APR_POOL_DEBUGを有効にしてコンパイルすると
apr_pcallocの代わりにapr_pcalloc_debugという関数が呼ばれるようになる。
んで、この関数の中をいくと、デバッグログを出力するところがあるんだけど、
そこ。
今日読んだところは、
- apr_file_write()/Apache2.2.8
- apr_wait_for_io_or_timeout()/Apache2.2.8
- APR_STATUS_IS_EINTRマクロ(その他)/Apache2.2.8
- APR_STATUS_IS_EINTRマクロ(NETWARE)/Apache2.2.8
- APR_STATUS_IS_EINTRマクロ(WIN32)/Apache2.2.8
- APR_STATUS_IS_EINTRマクロ(OS2)/Apache2.2.8
- SOCEINTRマクロ/Apache2.2.8
- SOCBASEERRマクロ/Apache2.2.8
- APR_EINTRマクロ/Apache2.2.8
- apr_pollset_poll()(select)/Apache2.2.8
- apr_pollset_poll()(poll)/Apache2.2.8
- get_revent()(poll)/Apache2.2.8
- apr_pollset_poll()(port)/Apache2.2.8
- get_revent()(port)/Apache2.2.8
- APR_EGENERALマクロ/Apache2.2.8
- apr_pollset_poll()(kqueue)/Apache2.2.8
- apr_time_usecマクロ/Apache2.2.8
- apr_time_secマクロ/Apache2.2.8
- APR_USEC_PER_SECマクロ/Apache2.2.8
- APR_TIME_Cマクロ/Apache2.2.8
- APR_INT64_Cマクロ/Apache2.2.8
- get_kqueue_revent()/Apache2.2.8
やっとapr_pollset_poll全種類読破。
そして、apr_pollset_pollが読み終わったことで、apr_wait_for_io_or_timeout()も終了。
今日のメインはapr_file_write。
apr_file_writeは、簡単に言うと、バッファリングありの出力と、バッファリングなしの出力をこなす。
バッファリングありかどうかは、apr_file_t構造体にそういう設定があるので、
その設定値によって決まる。
書き込み時にEAGAINやEWOULDBLOCKを伴ってエラーになった場合、
apr_wait_for_io_or_timeout()をコールする。
リソースが一時的に使用不可になっているので、使用可能になるまで待つ。
使用可能になるまで待つのだが、その後使用可能になったのにもかかわらず
また一時的にリソースが使用不可になると、出力バイト数を半分にしてしまう。
半分にしては書き込み、また半分にしては書き込み、というのを、書き込めるか、エラーが
発生するか、writeシステムコールが0を返すまで繰り返される。
通常のファイルの場合、出力予定バイト数が0にさえなれば、
writeシステムコールは0を返すのだが、特殊ファイルの場合は
下手をすると無限ループってことも有り得るじゃないかなー。
確か、特殊ファイルの場合の出力バイト数が0だったときの動作って
未定義だった気がする。
まぁ、それは置いといて、
apr_file_writeを使って書き込みをした場合、
書き込めたバイト数が、書き込みたいバイト数(パラメータに渡したバイト数)と
違っていた場合、リソースが頻繁に「一時的に使用不可」になっている
ということか。
明日はこのapr_file_writeを使っているところから読む予定。
おしまい。
.
投稿者
atkonn
時刻:
23:29:00
0
コメント
ラベル: Apache, CodeReading
2008年2月24日日曜日
[Apache][CodeReading] Apache2.2.8コードリーディング24日目
今日もひらメソッドにてApache2.2.8コードリーディング。
今日読んだところは
- apr_pollset_poll()(epoll)/Apache2.2.8
- APR_RING_CONCATマクロ/Apache2.2.8
- get_epoll_revent()/Apache2.2.8
- apr_get_netos_errorマクロ(その他)/Apache2.2.8
- apr_get_netos_errorマクロ(netware)/Apache2.2.8
- apr_get_netos_errorマクロ(win32)/Apache2.2.8
- apr_get_netos_errorマクロ(os2)/Apache2.2.8
- apr_pollset_poll()(os2)/Apache2.2.8
- sock_errnoマクロ/Apache2.2.8
- apr_os2_sock_errno()/Apache2.2.8
- APR_FROM_OS_ERRORマクロ/Apache2.2.8
- APR_OS_START_EAIERRマクロ/Apache2.2.8
- make_pollset()/Apache2.2.8
pollしているところ。
場所はApacheの初期化部。
APR_POOL_DEBUGでAPR_POOL_DEBUG_VERBOSE_ALLOCをしている場合でDEBUGログを出力しようとしているところ。出力終わるのをpollして待っている感じ(だっけ?)。
apr_pollset_poll()(epoll版)
今日読んだ中では、まあ一番面白いかなぁ。
といっても、epoll_waitしているだけ。
面白いのはepollじゃなくて、やはりリング部。
apr_pollset_addなんかで、ファイルディスクリプタを監視対象に追加するんだけど、
その追加に失敗した場合、pfd_elem_t領域をdead_ringにつないでおく。
このapr_pollset_pollの中で、epoll_waitが終わると、
dead_ringにつないでいた失敗時に作成したpfd_elem_tをfree_ringにつなぎ、
当のdead_ringは空にセット。
うーむ。なるほど。
ここでdead_ringをfree_ringにつなぎなおすのね。
次に同じapr_pollset_tを使用して
apr_pollset_add等をすると、
このfree_ringからpfd_elem_t領域が取得される。
ふーん。
今日はこんだけ。
おしまい。
.
投稿者
atkonn
時刻:
18:55:00
0
コメント
ラベル: Apache, CodeReading
2008年2月23日土曜日
[Apache][CodeReading] Apache2.2.8コードリーディング23日目
今日もApache2.2.8コードリーディング。
ちょっと気になったので、今どのぐらいこなしたのか大雑把に計算してみた。
Apacheには、ざっと20000シンボルあって、で読破した分は294シンボル。
23日間で約1.3%終えたことになる。
すると、ざっと4.5年かかる計算。
今のところ毎日できているが、もうじき毎日読めなくなりそうなので
4.5年以上はかかることに。
まぁ、面白いから別にいいや。
で、今日は
- apr_pollset_add()(select)/Apache2.2.8
- HAS_SOCKETマクロ/Apache2.2.8
- HAS_PIPESマクロ/Apache2.2.8
- APR_FILES_AS_SOCKETSマクロ/Apache2.2.8
- get_event()(port)/Apache2.2.8
- get_epoll_event()(epoll)/Apache2.2.8
- apr_pollset_add()(poll)/Apache2.2.8
- get_event()(poll)/Apache2.2.8
- APR_POLLNVALマクロ/Apache2.2.8
- APR_POLLHUPマクロ/Apache2.2.8
- APR_POLLERRマクロ/Apache2.2.8
- APR_POLLPRIマクロ/Apache2.2.8
昨日読んだapr_pollset_add()の別バージョン。
処理内容はほぼ一緒。
apr_pollset_add()のselectバージョンは、
リングを持っていない。その代わりreadset、writeset、exceptsetを持っていて、
リングに追加する代わりにそれらのビットをオン・オフする感じ。
まぁ、昨日やったところなんで、特に面白いところは無い感じ。
おしまい
.
投稿者
atkonn
時刻:
21:47:00
0
コメント
ラベル: Apache, CodeReading
2008年2月22日金曜日
[Apache][CodeReading] Apache2.2.8コードリーディング22日目
今日もApache2.2.8コードリーディング。
今日やったところは、
- apr_pollset_add()(kqueue)/Apache2.2.8
- apr_pollset_add()(epoll)/Apache2.2.8
- APR_EBADFマクロ/Apache2.2.8
- apr_pollset_remove()/Apache2.2.8
- apr_os_sock_t/Apache2.2.8
- NETWAREマクロ/Apache2.2.8
- __BEOS__マクロ/Apache2.2.8
- OS2マクロ/Apache2.2.8
- WIN32マクロ/Apache2.2.8
- pollset_unlock_rings()/Apache2.2.8
- APR_RING_INSERT_TAILマクロ/Apache2.2.8
- APR_RING_SPLICE_TAILマクロ/Apache2.2.8
- APR_RING_SPLICE_BEFOREマクロ/Apache2.2.8
で、まだ初期化部分。
DEBUGログを出力後にIOWAITするところ。
IOWAITするためにpollするんだけど、それを実行しようとしている部分。
リングの構造はさらっと19日目ぐらいにやった。
apr_pollset_tは環境により5種類の実装のうちひとつがconfigureなりで選択される。
今日読んだapr_pollset_add()もapr_pollset_remove()も5種類。
apr_pollset_remove()
apr_pollset_removeは、指定されたポールセット(apr_pollset_t)から指定されたディスクリプタ(apr_pollfd_t)を削除する。
ringをapr_pollset_t構造体に持つパターン(epoll、kqueue、portが使える環境)の場合は、
query_ringというリングから対象ディスクリプタを削除し、dead_ringというリングに対象ディスクリプタを登録する、
ということをやっている。
もちろん、epoll、kqueue、portからも削除する。
epoll、kqueue、portのいずれも使えず、poll関数が使える場合はringを保持しない。
代わりにquery_setというapr_pollfd_t構造体へのポインタの配列を保持する。
その配列から、対象ディスクリプタを削除する。
epoll、kqueue、portのいずれも使えず、さらにpoll関数も使用不可の場合も、ringを保持しないapr_pollset_tが使用される。
またpoll関数が使える場合と同様に、query_setほ保持し、apr_pollfd_t構造体へのポインタの配列から対象ディスクリプタを削除する。
apr_pollset_add()
まだ、epoll版とkqueue版しか読んでいない。
まず最初にfree_ringから空き領域を取得する。
空きが無ければapr_palloc()で領域を確保後、その領域を初期化(APR_RING_ELEM_INIT)する。
その領域にパラメータで指定されたディスクリプタをセットし、epoll_ctlを使用してepollにイベントを追加する。
うまく追加できればquery_ringにfree_ringから取得した領域を追加し、
うまく追加できなければfree_ringに取得した領域を返す。
で、そのソースが以下。
138 APR_DECLARE(apr_status_t) apr_pollset_add(apr_pollset_t *pollset,
139 const apr_pollfd_t *descriptor)
140 {
141 struct epoll_event ev;
142 int ret = -1;
143 pfd_elem_t *elem;
144 apr_status_t rv = APR_SUCCESS;
145
146 pollset_lock_rings();
147
148 if (!APR_RING_EMPTY(&(pollset->free_ring), pfd_elem_t, link)) {
149 elem = APR_RING_FIRST(&(pollset->free_ring));
150 APR_RING_REMOVE(elem, link);
151 }
152 else {
153 elem = (pfd_elem_t *) apr_palloc(pollset->pool, sizeof(pfd_elem_t));
154 APR_RING_ELEM_INIT(elem, link);
155 }
156 elem->pfd = *descriptor;
157
158 ev.events = get_epoll_event(descriptor->reqevents);
159 ev.data.ptr = elem;
160 if (descriptor->desc_type == APR_POLL_SOCKET) {
161 ret = epoll_ctl(pollset->epoll_fd, EPOLL_CTL_ADD,
162 descriptor->desc.s->socketdes, &ev);
163 }
164 else {
165 ret = epoll_ctl(pollset->epoll_fd, EPOLL_CTL_ADD,
166 descriptor->desc.f->filedes, &ev);
167 }
168
169 if (0 != ret) {
170 rv = APR_EBADF;
171 APR_RING_INSERT_TAIL(&(pollset->free_ring), elem, pfd_elem_t, link);
172 }
173 else {
174 pollset->nelts++;
175 APR_RING_INSERT_TAIL(&(pollset->query_ring), elem, pfd_elem_t, link);
176 }
177
178 pollset_unlock_rings();
179
180 return rv;
181 }
APR_RING_EMPTYで空かどうかチェックし、空でなければ先頭の領域を取得する。
空だった場合は、領域確保後、APR_RING_ELEM_INITマクロで初期化するのだが、
この、APR_RING_ELEM_INITが良くわからない。
やっていることは、elem->link->nextとelem->link->prevにelemを代入しているだけ。
その初期化自体は良いのだが、epoll_ctl後、free_ringか、query_ringにすぐに追加してしまうので
APR_RING_ELEM_INIT自体無意味に思える。
最初はAPR_RING_INSERT_TAILマクロでnextとprevに何かしらのポインタが必要なのかと思ったが、
そうでもない。
APR_RING_INSERT_TAILは、
#define APR_RING_SPLICE_BEFORE(lep, ep1, epN, link) do { \
APR_RING_NEXT((epN), link) = (lep); \
APR_RING_PREV((ep1), link) = APR_RING_PREV((lep), link); \
APR_RING_NEXT(APR_RING_PREV((lep), link), link) = (ep1); \
APR_RING_PREV((lep), link) = (epN); \
} while (0)
こんな感じに置き換わる。
で、
APR_RING_NEXT((epN), link) = (lep)
は、
elem->link = APR_SENTINEL(hp)
に、そして、
APR_RING_PREV((ep1), link = APR_RING_PREV((lep), link);
は、
elem->link->prev = APR_SENTINEL(hp)->link->prev;
に置き換わる。
つまり、せっかく初期化してもすぐに上書きしてしまうので、
無意味な気がする・・・・。
理由があるとすれば、RINGを構成する構造体に何かしらの変更が加わった場合に対処しやすいように
といった感じか。
といってもそれも考えにくい・・・。
うーむ。
わからん。
とりあえず、今度APR_RING_ELEM_INITをコメントアウトして動かしてみようかと思う。
いつかやるタスク。
- APR_RING_ELEM_INITをapr_pollset_add関数から削除して動かしてみる。
apr_palloc()関数も。
apr_palloc()は空きノードが無かった場合allocator_alloc()をつかってmallocするんだが、
このときNULLが返ってきた場合apr_palloc()はNULLを返す。
なのにもかかわらず、apr_palloc()を使用しているところのほとんどがNULLチェックをしていない。
うーむ。
Segmentation Faltですか??
ということで、
- malloc失敗するパターンで、apr_palloc()を読んでみる。
おしまい。
.
投稿者
atkonn
時刻:
23:31:00
0
コメント
ラベル: Apache, CodeReading
[Apache][CodeReading] Apache2.2.8コードリーディング21日目
昨日もApache2.2.8コードリーディング。
昨日はメモするの忘れた。
昨日やった分は、
- APR_RING_REMOVEマクロ/Apache2.2.8
- APR_RING_UNSPLICEマクロ/Apache2.2.8
- APR_RING_PREVマクロ/Apache2.2.8
- APR_RING_EMPTYマクロ/Apache2.2.8
- APR_NOTFOUND定数/Apache2.2.8
- APR_POLL_SOCKET定数/Apache2.2.8
マクロだけしかページは作ってない。
で、これはどの辺かというと、
Apacheの初期化部分・・・。
まだ初期化。
まだListenしてない。。
詳細は今日の分も合わせてメモ予定。
.
投稿者
atkonn
時刻:
11:47:00
0
コメント
ラベル: Apache, CodeReading
2008年2月20日水曜日
[Apache][CodeReading] Apache2.2.8コードリーディング20日目
今日もApache2.2.8コードリーディング。
今日はapr_pollset_create()周り。
作ったページは
- apr_pollset_create()/Apache2.2.8
- APR_NO_DESC定数/Apache2.2.8
- APR_EINVAL定数/Apache2.2.8
らへん。
apr_pollset_createは、環境毎に定義が異なり、apr_pollset_t構造体と同じく5種類ある。
やっていることは、ほぼ一緒。apr_pollset_t用の領域を確保し、
epollが使える環境であれば、epollの初期化を、
kqueueが使える環境であれば、kqueueの初期化を、
portが使える環境であれば、portの初期化を、
上記どれでもなく、pollが使える環境であればpoll用の初期化処理を、
さらにpollも使えない場合はselect用の初期化処理を行う。
5種類のうちどれを使うかはconfigureにより静的に決定される。
apr_pollset_t構造体のリング以外は特に面白くはない。
一応それぞれ見ておく。
epollが使える環境の場合
httpd-2.2.8/srclib/apr/poll/unix/epoll.cに記述されているapr_pollset_create関数とapr_pollset_t構造体が選択される。
apr_pollset_t構造体は、
59 struct apr_pollset_t
60 {
61 apr_pool_t *pool;
62 apr_uint32_t nelts;
63 apr_uint32_t nalloc;
64 int epoll_fd;
65 struct epoll_event *pollset;
66 apr_pollfd_t *result_set;
67 apr_uint32_t flags;
70 apr_thread_mutex_t *ring_lock;
73 APR_RING_HEAD(pfd_query_ring_t, pfd_elem_t) query_ring;
75 APR_RING_HEAD(pfd_free_ring_t, pfd_elem_t) free_ring;
78 APR_RING_HEAD(pfd_dead_ring_t, pfd_elem_t) dead_ring;
79 };
となっていて、epoll特有の構造体epoll_eventが入っている。
まだ、初期化部分しか見ていないのでどのように使われるのか、さっぱりわからないリングももっている。
epollの時のapr_pollset_create()は以下な感じ。
88 APR_DECLARE(apr_status_t) apr_pollset_create(apr_pollset_t **pollset,
89 apr_uint32_t size,
90 apr_pool_t *p,
91 apr_uint32_t flags)
92 {
93 apr_status_t rv;
94 int fd;
95
96 fd = epoll_create(size);
97 if (fd < 0) {
98 *pollset = NULL;
99 return errno;
100 }
101
102 *pollset = apr_palloc(p, sizeof(**pollset));
104 if (flags & APR_POLLSET_THREADSAFE &&
105 ((rv = apr_thread_mutex_create(&(*pollset)->ring_lock,
106 APR_THREAD_MUTEX_DEFAULT,
107 p) != APR_SUCCESS))) {
108 *pollset = NULL;
109 return rv;
110 }
117 (*pollset)->nelts = 0;
118 (*pollset)->nalloc = size;
119 (*pollset)->flags = flags;
120 (*pollset)->pool = p;
121 (*pollset)->epoll_fd = fd;
122 (*pollset)->pollset = apr_palloc(p, size * sizeof(struct epoll_event));
123 apr_pool_cleanup_register(p, *pollset, backend_cleanup, backend_cleanup);
124 (*pollset)->result_set = apr_palloc(p, size * sizeof(apr_pollfd_t));
125
126 APR_RING_INIT(&(*pollset)->query_ring, pfd_elem_t, link);
127 APR_RING_INIT(&(*pollset)->free_ring, pfd_elem_t, link);
128 APR_RING_INIT(&(*pollset)->dead_ring, pfd_elem_t, link);
129
130 return APR_SUCCESS;
131 }
やっていることは、apr_pollset_tの領域を確保し、そのメンバの領域も確保する。
apr_pollset_tの各要素の値を初期化し、apr_pool_cleanup_registerでpool破棄時の動作を登録。
epollのディスクリプタを作成し、epoll_event構造体用の領域も確保する。
apr_pool_cleanup_registerで引数として渡されているbackend_cleanup関数では、
epollのディスクリプタをクローズしているだけ。
kqueueが使える環境の場合
httpd-2.2.8/srclib/apr/poll/unix/kqueue.cのapr_pollset_create関数とapr_pollset_t構造体が
使用される。
apr_pollset_t構造体は、
37 struct apr_pollset_t
38 {
39 apr_pool_t *pool;
40 apr_uint32_t nelts;
41 apr_uint32_t nalloc;
42 int kqueue_fd;
43 struct kevent kevent;
44 struct kevent *ke_set;
45 apr_pollfd_t *result_set;
46 apr_uint32_t flags;
49 apr_thread_mutex_t *ring_lock;
52 APR_RING_HEAD(pfd_query_ring_t, pfd_elem_t) query_ring;
54 APR_RING_HEAD(pfd_free_ring_t, pfd_elem_t) free_ring;
57 APR_RING_HEAD(pfd_dead_ring_t, pfd_elem_t) dead_ring;
58 };
epollのときとほとんど一緒。違うのは、
42 int kqueue_fd;
43 struct kevent kevent;
44 struct kevent *ke_set;
だけ。
apr_pollset_createは以下な感じ。
67 APR_DECLARE(apr_status_t) apr_pollset_create(apr_pollset_t **pollset,
68 apr_uint32_t size,
69 apr_pool_t *p,
70 apr_uint32_t flags)
71 {
72 apr_status_t rv = APR_SUCCESS;
73 *pollset = apr_palloc(p, sizeof(**pollset));
75 if (flags & APR_POLLSET_THREADSAFE &&
76 ((rv = apr_thread_mutex_create(&(*pollset)->ring_lock,
77 APR_THREAD_MUTEX_DEFAULT,
78 p) != APR_SUCCESS))) {
79 *pollset = NULL;
80 return rv;
81 }
88 (*pollset)->nelts = 0;
89 (*pollset)->nalloc = size;
90 (*pollset)->flags = flags;
91 (*pollset)->pool = p;
92
93 (*pollset)->ke_set =
94 (struct kevent *) apr_palloc(p, size * sizeof(struct kevent));
95
96 memset((*pollset)->ke_set, 0, size * sizeof(struct kevent));
97
98 (*pollset)->kqueue_fd = kqueue();
99
100 if ((*pollset)->kqueue_fd == -1) {
101 return APR_ENOMEM;
102 }
103
104 apr_pool_cleanup_register(p, (void *) (*pollset), backend_cleanup,
105 apr_pool_cleanup_null);
106
107 (*pollset)->result_set = apr_palloc(p, size * sizeof(apr_pollfd_t));
108
109 APR_RING_INIT(&(*pollset)->query_ring, pfd_elem_t, link);
110 APR_RING_INIT(&(*pollset)->free_ring, pfd_elem_t, link);
111 APR_RING_INIT(&(*pollset)->dead_ring, pfd_elem_t, link);
112
113 return rv;
114 }
やっていることも、epollの場合とほとんど一緒。
違うのはepollの代わりにカーネルキューになっていること。
backend_cleanupではカーネルキューのfdをクローズしているだけ。
portが使える環境の場合
これはまったく知らないのだが、恐らくSolarisの場合。/dev/poll?
httpd-2.2.8/srclib/apr/poll/unix/port.cのapr_pollset_create関数とapr_pollset_t構造体が使用される。
portのときのapr_pollset_t構造体は以下のとおり。
62 struct apr_pollset_t
63 {
64 apr_pool_t *pool;
65 apr_uint32_t nelts;
66 apr_uint32_t nalloc;
67 int port_fd;
68 port_event_t *port_set;
69 apr_pollfd_t *result_set;
70 apr_uint32_t flags;
73 apr_thread_mutex_t *ring_lock;
76 APR_RING_HEAD(pfd_query_ring_t, pfd_elem_t) query_ring;
77 APR_RING_HEAD(pfd_add_ring_t, pfd_elem_t) add_ring;
79 APR_RING_HEAD(pfd_free_ring_t, pfd_elem_t) free_ring;
82 APR_RING_HEAD(pfd_dead_ring_t, pfd_elem_t) dead_ring;
83 };
これも、epollの場合とほとんど一緒。
違うのは、
67 int port_fd;
68 port_event_t *port_set;
のport用構造体と変数。
あと、リング要素がひとつ追加になっているところ。
apr_pollset_create関数は以下な感じ。
92 APR_DECLARE(apr_status_t) apr_pollset_create(apr_pollset_t **pollset,
93 apr_uint32_t size,
94 apr_pool_t *p,
95 apr_uint32_t flags)
96 {
97 apr_status_t rv = APR_SUCCESS;
98 *pollset = apr_palloc(p, sizeof(**pollset));
100 if (flags & APR_POLLSET_THREADSAFE &&
101 ((rv = apr_thread_mutex_create(&(*pollset)->ring_lock,
102 APR_THREAD_MUTEX_DEFAULT,
103 p) != APR_SUCCESS))) {
104 *pollset = NULL;
105 return rv;
106 }
113 (*pollset)->nelts = 0;
114 (*pollset)->nalloc = size;
115 (*pollset)->flags = flags;
116 (*pollset)->pool = p;
117
118 (*pollset)->port_set = apr_palloc(p, size * sizeof(port_event_t));
119
120 (*pollset)->port_fd = port_create();
121
122 if ((*pollset)->port_fd < 0) {
123 return APR_ENOMEM;
124 }
125
126 apr_pool_cleanup_register(p, (void *) (*pollset), backend_cleanup,
127 apr_pool_cleanup_null);
128
129 (*pollset)->result_set = apr_palloc(p, size * sizeof(apr_pollfd_t));
130
131 APR_RING_INIT(&(*pollset)->query_ring, pfd_elem_t, link);
132 APR_RING_INIT(&(*pollset)->add_ring, pfd_elem_t, link);
133 APR_RING_INIT(&(*pollset)->free_ring, pfd_elem_t, link);
134 APR_RING_INIT(&(*pollset)->dead_ring, pfd_elem_t, link);
135
136 return rv;
137 }
これもepollとやっていることはほとんど一緒。
違うのはepollの代わりにport用の初期化処理が入ったというのと、
add_ringの初期化が入っただけ。
このadd_ringはなんとなく興味あり。
backend_cleanup関数でもport_fdをクローズしているだけ。
pollが使える環境の場合
今どきのlinuxでは使うことはないが、自分の開発環境は2.4系なのでこれが選択されてしまう。
httpd-2.2.8/srclib/apr/poll/unix/poll.cのapr_pollset_create関数とapr_pollset_t構造体が使用
される。
apr_pollset_t構造体は以下のとおり。
150 struct apr_pollset_t
151 {
152 apr_pool_t *pool;
153 apr_uint32_t nelts;
154 apr_uint32_t nalloc;
155 struct pollfd *pollset;
156 apr_pollfd_t *query_set;
157 apr_pollfd_t *result_set;
158 };
ずいぶんと単純になる。
基本的にはepollと同じだが、リングもなければmutexももっていない。
代わりにapr_pollfd_tのポインタが1つ増え、poll用の構造体が入っている。
で、apr_pollset_create関数は以下のとおり。
160 APR_DECLARE(apr_status_t) apr_pollset_create(apr_pollset_t **pollset,
161 apr_uint32_t size,
162 apr_pool_t *p,
163 apr_uint32_t flags)
164 {
165 if (flags & APR_POLLSET_THREADSAFE) {
166 *pollset = NULL;
167 return APR_ENOTIMPL;
168 }
169
170 *pollset = apr_palloc(p, sizeof(**pollset));
171 (*pollset)->nelts = 0;
172 (*pollset)->nalloc = size;
173 (*pollset)->pool = p;
174 (*pollset)->pollset = apr_palloc(p, size * sizeof(struct pollfd));
175 (*pollset)->query_set = apr_palloc(p, size * sizeof(apr_pollfd_t));
176 (*pollset)->result_set = apr_palloc(p, size * sizeof(apr_pollfd_t));
177 return APR_SUCCESS;
178 }
poll用apr_pollset_t構造体同様、より簡単になった。
backend_cleanup関数もない。
selectしか使えない環境
最後に、pollすらも使えない環境用。
監視・通知機構にselectを使用する。
httpd-2.2.8/srclib/apr/poll/unix/select.cのapr_pollset_create関数とapr_pollset_t構造体が使用
される。
apr_pollset_t構造体は以下のとおり。
struct apr_pollset_t
{
apr_pool_t *pool;
apr_uint32_t nelts;
apr_uint32_t nalloc;
fd_set readset, writeset, exceptset;
int maxfd;
apr_pollfd_t *query_set;
apr_pollfd_t *result_set;
};
pollの場合と同じく単純になっている。
select用に、fd_setをもつ。
apr_pollset_create関数は以下のとおり。
187 APR_DECLARE(apr_status_t) apr_pollset_create(apr_pollset_t **pollset,
188 apr_uint32_t size,
189 apr_pool_t *p,
190 apr_uint32_t flags)
191 {
192 if (flags & APR_POLLSET_THREADSAFE) {
193 *pollset = NULL;
194 return APR_ENOTIMPL;
195 }
202 *pollset = apr_palloc(p, sizeof(**pollset));
203 (*pollset)->nelts = 0;
204 (*pollset)->nalloc = size;
205 (*pollset)->pool = p;
206 FD_ZERO(&((*pollset)->readset));
207 FD_ZERO(&((*pollset)->writeset));
208 FD_ZERO(&((*pollset)->exceptset));
209 (*pollset)->maxfd = 0;
213 (*pollset)->query_set = apr_palloc(p, size * sizeof(apr_pollfd_t));
214 (*pollset)->result_set = apr_palloc(p, size * sizeof(apr_pollfd_t));
215
216 return APR_SUCCESS;
217 }
FD_ZEROぐらいか。
この辺は、selectによる監視・通知が効率が悪いってんで、各環境でいろいろな代替手段が
開発されてしまった話の被害者部分か。。
とにかくこの辺はリングのデータ構造がどのように使われるのか、
その辺が面白そうなところ。
.
投稿者
atkonn
時刻:
17:59:00
0
コメント
ラベル: Apache, CodeReading
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
な感じ。本当はもう一段階階層が深いけど。
おしまい。
.
投稿者
atkonn
時刻:
23:41:00
0
コメント
ラベル: Apache, CodeReading
[Apache][CodeReading] Apache2.2.8コードリーディング19日目(1)
昨日読んだapr_thread_mutex_createの中で、
pthread_mutexattr関連を使っているところでちょっとひっかかった。
確かpthread_mutexattr_t構造体の領域はdestroy後触っちゃいけなかった気が・・・。
manによるとLinuxThreadでは何もしないと書いてあるが。
apr_thread_mutex_create()の中で、
54 if (flags & APR_THREAD_MUTEX_NESTED) {なんていうふうに使われている。
55 pthread_mutexattr_t mattr;
56
57 rv = pthread_mutexattr_init(&mattr);
58 if (rv) return rv;
59
60 rv = pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE);
61 if (rv) {
62 pthread_mutexattr_destroy(&mattr);
63 return rv;
64 }
65
66 rv = pthread_mutex_init(&new_mutex->mutex, &mattr);
67
68 pthread_mutexattr_destroy(&mattr);
69
mattrは自動変数。
つまりdestroyした後、スタックの積み具合によって対象の領域を触ってしまうのでは?
と思ってしまった。
で、気になったんでphtreadのソースを拝見。
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
{
if (!mutex) return EINVAL;
if (attr && attr->pshared == PTHREAD_PROCESS_SHARED) return ENOSYS;
mutex->lock = 0;
mutex->recursion = 0;
mutex->kind = attr ? attr->kind : PTHREAD_MUTEX_DEFAULT;
mutex->owner = NOHANDLE;
mutex->event = mkevent(0, 0);
if (mutex->event < 0) return ENOSPC;
return 0;
}
見てみると、なーんてことは無い。
attrが指定されていたら、そのkindをコピーして
後は用無し。
なーんだ。
つまらん。
.
ラベル: Apache, CodeReading
2008年2月18日月曜日
[Apache][CodeReading] Apache2.2.8コードリーディング18日目(2)
今日読んだところは、
- apr_thread_mutex_create()/Apache2.2.8
- apr_pool_cleanup_null()/Apache2.2.8
- thread_mutex_cleanup()/Apache2.2.8
- apr_pcalloc()/Apache2.2.8
- APR_POOL__FILE_LINE__マクロ/Apache2.2.8
- apr_pcalloc_debug()/Apache2.2.8
- APR_POOL_DEBUG_VERBOSE_ALLOC定数/Apache2.2.8
- pool_alloc()/Apache2.2.8
- SIZEOF_DEBUG_NODE_Tマクロ/Apache2.2.8
- debug_node_t構造体/Apache2.2.8
- DOXYGEN定数/Apache2.2.8
- HAVE_PTHREAD_MUTEX_RECURSIVE定数/Apache2.2.8
- APR_THREAD_MUTEX_UNNESTED定数/Apache2.2.8
- APR_THREAD_MUTEX_DEFAULT定数/Apache2.2.8
- APR_THREAD_MUTEX_NESTED定数/Apache2.2.8
- APR_ENOTIMPL定数/Apache2.2.8
- APR_POLLSET_THREADSAFE定数/Apache2.2.8
- APR_RING_ENTRYマクロ/Apache2.2.8
まぁまぁ大きめのところは、
apr_pcallocとapr_thread_mutex_createぐらい。
といってもたいしたことをしていないが・・・。
apr_pcalloc()
apr_pcalloc関数はapr_pallocでサイズを確保後memsetで0クリアしているだけ。
APR_POOL_DEBUGが定義されていると、apr_pcallocの実態がapr_pcalloc_debug関数になる。
apr_pcalloc_debugはapr_pallocは使わずに、mallocを使用して領域を確保する。
確保する際はalloc数をカウントし、またパラメータに渡されたpoolの親子関係をチェックする。
APR_POOL_DEBUGにAPR_POOL_DEBUG_VERBOSE_ALLOCのビットがOnになっていると、
apr_pcalloc_debugの中で詳細なログを出力するようになる。
apr_thread_mutex_create()
apr_thread_mutex_create関数はパラメータで渡されたpoolを使用し、pthread_mutexを作成、初期化する。
APR_THREAD_MUTEX_NESTEDがflagsに指定された場合でpthreadがネストに対応していない場合は、APR_ENOTIMPLエラーを返す。
APR_THREAD_MUTEX_NESTEDがflagsに指定された場合でpthreadがネストに対応している場合は、
mutexattrをRECURSIVEに設定し、pthread_mutexを初期化する。
APR_THREAD_MUTEX_NESTEDがflagsに指定されていない場合は、
mutexattrは指定せずに、pthread_mutexを初期化する。
apr_pcalloc関数の方はcallocなだけに0クリアしているだけだし、
apr_thread_mutex_createの方は、pthreadを初期化しているだけ
な感じ。
うーむ。
まだ序章・・・、いや前書きかも。
.
投稿者
atkonn
時刻:
23:02:00
0
コメント
ラベル: Apache, CodeReading
[Apache][CodeReading] Apache2.2.8コードリーディング18日目(1)
今日もApache2.2.8コードリーディング。
どうでもいいことだが、
何も考えずにctagsでタグファイルを作ると、ジャンプできない関数がいっぱい。
だもんで、
$ ctags -R Apacheのソースディレクトリ \
--regex-c=/^APR_DECLARE\([a-zA-Z0-9_]+\)[ \t]*(ap[ru]_[a-zA-Z0-9_]+)\(.*$/\1/i
とやってます。
もっといい方法あるのかな???
--
これじゃだめだー。
APR_DECLARE_NONSTDとかも必要か。
--
doxygenがキーワードか???
--
doxygenがtagsファイル作ってくれると期待したけどだめっぽい。
とりあえず、doxygen.confの内容を参考に、
--regex-c=/^APR_[A-Z_]+\([a-zA-Z0-9_]+\)[ \t]*(ap[ru]_[a-zA-Z0-9_]+)\(.*$/\1/i
でやることに。。。
--追記:2008/2/24
APUシリーズもあるんで、
--regex-c=/^AP[RU]_[A-Z_]+\([a-zA-Z0-9_]+\)[ \t]*(ap[ru]_[a-zA-Z0-9_]+)\(.*$/\1/i
な感じか。
.
投稿者
atkonn
時刻:
16:24:00
0
コメント
ラベル: Apache, CodeReading
2008年2月17日日曜日
[Apache][CodeReading] Apache2.2.8コードリーディング17日目
今日も引き続きApache2.2.8をコードリーディング。
今日は昨日見つけた間違えを修正すべく、
apr_pollset_t構造体周りを読み直し。
今日作ったページは
- APR_RING_HEADマクロ/Apache2.2.8
- POLLSET_USES_SELECT定数/Apache2.2.8
- POLLSET_USES_KQUEUE定数/Apache2.2.8
- POLLSET_USES_EPOLL定数/Apache2.2.8
- POLLSET_USES_POLL定数/Apache2.2.8
- HAVE_EPOLL定数/Apache2.2.8
- HAVE_PORT_CREATE定数/Apache2.2.8
- HAVE_KQUEUE定数/Apache2.2.8
APR_RING_HEADで定義される構造体を使っている部分を見ないとなんともいえないが、
ちょっとわくわくな感じか。それとも期待はずれか。。。
結局今日はあんまり腰をすえてじっくり読むことができなかった。。。
うーむ。残念。
.
投稿者
atkonn
時刻:
23:19:00
0
コメント
ラベル: Apache, CodeReading
2008年2月16日土曜日
[Apache][CodeReading] Apache2.2.8コードリーディング16日目
今日もApache2.2.8コードリーディング。
今日はapr_wait_for_io_or_timeout()という関数を読んでいる途中で終わった。。
今日作ったページは、
- HAVE_POLL定数/Apache2.2.8
- APR_POLL_FILE定数/Apache2.2.8
- APR_POLLIN定数/Apache2.2.8
- APR_TIMEUP定数/Apache2.2.8
- WAITIO_USES_POLL定数/Apache2.2.8
あと、apr_pollset_tの構造体の定義が実は5つあったのを見落としていた。
もちろん、configureによってどれを使うかが決定されるんだけど。
.
投稿者
atkonn
時刻:
23:48:00
0
コメント
ラベル: Apache, CodeReading
2008年2月15日金曜日
[Apache][CodeReading] Apache2.2.8コードリーディング15日目
今日もApacheコードリーディング。
今日は昨日の続きから読み始めたんだけど、apr_atomic_init()から先に進めず。
apr_atomic_init()の中でapr_pool_cleanup_register()に入り、
さらにapr_pool_check_integrity()に入って、今DEBUGログ出力部分を読んでいるところ。
今日読めた部分は以下のとおり。
- HAVE_BONE_VERSION定数/Apache2.2.8
- BEOS定数/Apache2.2.8
- file_unlockマクロ/Apache2.2.8
- apr_file_flush_locked()/Apache2.2.8
- apr_ssize_t/Apache2.2.8
- APR_FILE_BUFSIZE定数/Apache2.2.8
- HUGE_STRING_LEN定数/Apache2.2.8
- apr_file_printf_data構造体/Apache2.2.8
- apr_vformatter_buff_t構造体/Apache2.2.8
- APR_DECLARE_NONSTDマクロ/Apache2.2.8
- APR_POOL_DEBUG_VERBOSE_ALLローカル定数/Apache2.2.8
- APR_POOL_DEBUG_VERBOSE_ALLOCローカル定数/Apache2.2.8
- APR_POOL_DEBUG_VERBOSEローカル定数/Apache2.2.8
- apr_pool_is_child_of()/Apache2.2.8
- pool_is_child_of()/Apache2.2.8
- apr_pool_walk_tree()/Apache2.2.8
- APR_POOL_DEBUG_LIFETIMEローカル定数/Apache2.2.8
- NUM_ATOMIC_HASHローカル定数/Apache2.2.8
BeOSではsys/socket.hでBONE_VERSIONという定数が定義されているとのこと。
環境がBeOSの場合、configure側ではこの定数を調べてBONEかどうか判断している。
BONE_VERSION定数がない場合はBeOS R5と認識するっぽい。
さて、今日読んだものをひとつづつ見ていく。PukiWikiに書き込めなかったようなことをつらつらと
書く感じでいきまっす。
file_unlockマクロ・file_lockマクロ
名前から察するにflockかfcntlでも使うのだろうかと思ったが、実はそうではなくて、
pthreadのmutexを使ってApache内部でapr_file_t構造体を1スレッドだけで使いたい
ときに使っている模様。
定義は
118 #define file_lock(f) do { \
119 if ((f)->thlock) \
120 apr_thread_mutex_lock((f)->thlock); \
121 } while (0)
122 #define file_unlock(f) do { \
123 if ((f)->thlock) \
124 apr_thread_mutex_unlock((f)->thlock); \
125 } while (0)
のとおり。
これとは別にflockやらfcntlを使うロック関数もあって(そりゃあるだろうが)、それは
apr_file_lock()という紛らわしい名前が付いている。
apr_file_flush_locked()
これは上記file_lockでロックされた状態のapr_file_tを使って、
バッファ内のデータを全てファイル/ソケットに吐き出す関数。
309 apr_status_t apr_file_flush_locked(apr_file_t *thefile)
310 {
311 apr_status_t rv = APR_SUCCESS;
312
313 if (thefile->direction == 1 && thefile->bufpos) {
314 apr_ssize_t written;
315
316 do {
317 written = write(thefile->filedes, thefile->buffer, thefile->bufpos);
318 } while (written == -1 && errno == EINTR);
319 if (written == -1) {
320 rv = errno;
321 } else {
322 thefile->filePtr += written;
323 thefile->bufpos = 0;
324 }
325 }
326
327 return rv;
328 }
APR_DECLAREをつけてないので、一応APRの外に公開していないことになっている。
mutexロックされているのが前提っぽい名前なので直接使わないでほしいのに違いない。
で、やっていることはパラメータで渡されたthefileが書き込み用途でなければ何もしないで終了。
書き込み用途(direction==1)で且つバッファにデータがたまっている(bufpos > 0)のであれば
書き込み処理を行う。
その後、filePtrを書き込めたバイト数分加算し、bufposは0にする。
もうちょっと先を読まないとわからないが、多分、
filePtrは現在の論理的なファイルのオフセットを保持、
bufposは現在バッファに持っているデータのバイト数
であっているんじゃないかなー。
pool_is_child_of()
これはAPR_POOL_DEBUGに0x02ビットを立てないと定義されない。
0x02はAPR_POOL_DEBUG_LIFETIMEとなっている。
多分だけど、親プールが死んでいるのに子プールが存在していたり、バグで切り離されちゃったプール
なんかを発見する手段なんじゃないかな?
そんなときはCPPFLAGS=-DAPRPOOL_DEBUG=0x02とかでconfigureする感じか。
その「ちゃんとプール同士がつながっている?」をチェックするのがapr_pool_check_integrity()
という関数なんだけど、とりあえず置いといて、以下がpool_is_child_of関数のコード。
1174 static int pool_is_child_of(apr_pool_t *parent, void *data)
1175 {
1176 apr_pool_t *pool = (apr_pool_t *)data;
1177
1178 return (pool == parent);
1179 }
この関数は名前と処理にずれがある感じがする。処理はパラメータの値を直接比較しているだけ。
なんとなく変なのは、次のapr_poll_walk_tree()関数と、apr_pool_is_child_of()関数を見ればわかる。
実はこの関数はis_child_of関係の関数の内部からコールバックされるもので、
parentには本当はparentじゃなくてchildがわたってくる。(parentがわたってくる場合もあるが)
apr_pool_walk_tree()
プールの子を辿っていって、パラメータで渡された関数fnが真のときか、子がいなくなったときに
処理を終了する。
コードは、
1082 static int apr_pool_walk_tree(apr_pool_t *pool,
1083 int (*fn)(apr_pool_t *pool, void *data),
1084 void *data)
1085 {
1086 int rv;
1087 apr_pool_t *child;
1088
1089 rv = fn(pool, data);
1090 if (rv)
1091 return rv;
1092
1094 if (pool->mutex) {
1095 apr_thread_mutex_lock(pool->mutex);
1096 }
1099 child = pool->child;
1100 while (child) {
1101 rv = apr_pool_walk_tree(child, fn, data);
1102 if (rv)
1103 break;
1104
1105 child = child->sibling;
1106 }
1107
1109 if (pool->mutex) {
1110 apr_thread_mutex_unlock(pool->mutex);
1111 }
1113
1114 return rv;
1115 }
な感じ。見にくいのでコメントと#if/#endifは削除した。
再帰的にapr_pool_walk_treeが呼ばれる。ま、なんてことは無いtreeな処理。
apr_pool_is_child_of()
1181 static int apr_pool_is_child_of(apr_pool_t *pool, apr_pool_t *parent)
1182 {
1183 if (parent == NULL)
1184 return 0;
1185
1186 return apr_pool_walk_tree(parent, pool_is_child_of, pool);
1187 }
pool_is_child_of関数がパラメータ2つをただ単に比較していただけなのも、
この関数と、前のapr_pool_walk_treeを見れば一目瞭然。
やっていることは、パラメータのparentがNULLの場合、そのままapr_pool_walk_treeに渡してしまう
と、運が良くてSegmentation Faltしちゃうので、NULLチェックして、apr_pool_walk_treeを呼ぶだけ。
APR_POOL_DEBUGのフラグが立っているときのみ、この関数は呼ばれるようだが、
ここで偽(0)を返すとApacheはabort()する。
とりあえず本日読んだところはここまで。
おしまい。
.
投稿者
atkonn
時刻:
21:59:00
0
コメント
ラベル: Apache, CodeReading
2008年2月14日木曜日
[Apache][CodeReading] Apache2.2.8コードリーディング14日目(2)
本日2回目のApacheコードリーディング。
本日片付けたものは以下のとおり。
- apr_palloc()/Apache2.2.8
- list_insertマクロ/Apache2.2.8
- list_removeマクロ/Apache2.2.8
- hash_mutexスタティック変数/Apache2.2.8
- apr_pool_tag()/Apache2.2.8
- apr_allocator_destroy()/Apache2.2.8
- MAX_INDEXローカル定数/Apache2.2.8
- apr_pool_create_ex()/Apache2.2.8
- apr_allocator_mutex_get()/Apache2.2.8
- NETWARE定数/Apache2.2.8
- SIZEOF_POOL_Tローカルマクロ/Apache2.2.8
list_insert、list_remove、apr_pool_create_exは先ほどやったので、その続きから。
hash_mutexについては存在を知っただけ。これがいったい何に使われるのかは今のところ不明。
恐らくapr_atomic_initを読むとわかると思う。
apr_allocator_destroy()
125 APR_DECLARE(void) apr_allocator_destroy(apr_allocator_t *allocator)
126 {
127 apr_uint32_t index;
128 apr_memnode_t *node, **ref;
129
130 for (index = 0; index < MAX_INDEX; index++) {
131 ref = &allocator->free[index];
132 while ((node = *ref) != NULL) {
133 *ref = node->next;
134 free(node);
135 }
136 }
137
138 free(allocator);
139 }
特筆すべきことはない。
アロケータのfree[]を全て解放し、アロケータ自身も解放しているだけ。
apr_pool_tag()
1888 APR_DECLARE(void) apr_pool_tag(apr_pool_t *pool, const char *tag)
1889 {
1890 pool->tag = tag;
1891 }
これも特になし。pool->tagのアクセサ。
apr_allocator_mutex_get()
148 APR_DECLARE(apr_thread_mutex_t *) apr_allocator_mutex_get(
149 apr_allocator_t *allocator)
150 {
151 return allocator->mutex;
152 }
これも同様。
apr_palloc()
やはりメインはこれ。
623 APR_DECLARE(void *) apr_palloc(apr_pool_t *pool, apr_size_t size)
624 {
625 apr_memnode_t *active, *node;
626 void *mem;
627 apr_size_t free_index;
628
629 size = APR_ALIGN_DEFAULT(size);
630 active = pool->active;
631
632 /* If the active node has enough bytes left, use it. */
633 if (size < (apr_size_t)(active->endp - active->first_avail)) {
634 mem = active->first_avail;
635 active->first_avail += size;
636
637 return mem;
638 }
639
640 node = active->next;
641 if (size < (apr_size_t)(node->endp - node->first_avail)) {
642 list_remove(node);
643 }
644 else {
645 if ((node = allocator_alloc(pool->allocator, size)) == NULL) {
646 if (pool->abort_fn)
647 pool->abort_fn(APR_ENOMEM);
648
649 return NULL;
650 }
651 }
652
653 node->free_index = 0;
654
655 mem = node->first_avail;
656 node->first_avail += size;
657
658 list_insert(node, active);
659
660 pool->active = node;
661
662 free_index = (APR_ALIGN(active->endp - active->first_avail + 1,
663 BOUNDARY_SIZE) - BOUNDARY_SIZE) >> BOUNDARY_INDEX;
664
665 active->free_index = (APR_UINT32_TRUNC_CAST)free_index;
666 node = active->next;
667 if (free_index >= node->free_index)
668 return mem;
669
670 do {
671 node = node->next;
672 }
673 while (free_index < free_index);
674
675 list_remove(active);
676 list_insert(active, node);
677
678 return mem;
679 }
これを見ればわかるとおり、poolのactiveメンバがnodeのリストになっていて、そのnodeリストのnodeから
メモリ領域を確保している。
開き領域がnodeリスト中になければ新しくnodeを作成し、activeリストにつなぐ。
[Apache][CodeReading] Apache2.2.8の13日目に挙げた疑問のひとつもここで解決する。
[疑問]endpの使い道は何か。
ずばり答えは、
サイズを取得するために使う。
である。
まぁ、そうだろうな。
634行目~635行目、655行目~656行目にあるとおり、メモリ割り当てを行うたびに
first_availの値は増えていく。
これも、[Apache][CodeReading] Apache2.2.8の13日目に、
何も考えずに使うとするとfree[1]ばかりが出来そうだ
との考えを否定してくれている。
allocator_alloc()で割り当てた最小8192バイトの領域は、このapr_palloc()で細切れに使用される。
また、使用領域は常に8バイトアライン。
そして、pool->activeのリストにも順番があって、その辺の順番を維持しているのが、
662行目~676行目のところ。
ここからpool->activeリストは空き容量が大きいもの(free_indexの値が大きいもの)から降順にならんでいることが分かる。
降順に並べることで、pool->activeの持っているリストの中に空き容量があるものが入っているかどうかを、先頭の要素を調べるだけで判断できるようになっている。
今日はここまで。
まだcurrent_free_indexが何なのか不明なのが気持ち悪いが、
メモリ周りは面白いねー。
次はapr_atomic_initを読む、と思う
.
投稿者
atkonn
時刻:
16:14:00
0
コメント
ラベル: Apache, CodeReading