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

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によってどれを使うかが決定されるんだけど。


.

[Python][お勉強] Python入門(37) - モジュールのリロード

続いてモジュールのリロードをやる。

モジュールのコードがメモリ上にロードされ、実行されるのは原則1プロセス1回。
どうしても再ロード、実行するにはreload関数を使う。
仮に2回同じモジュールをimportしてもロードされ実行されるのは初回のみ。

早速reload関数を使ってみる。

まずへぼモジュールを作成。


$ cat hebo.py
print "start loading"

X = 10
Y = 20

def aaa(x):
print "param is ", x
print "X = ", X
print "Y = ", Y


print "done"

 

こんな感じのプログラムがあったとする。
で、トップレベルからimport。

$ python
Python 2.4.4 (#2, Apr 5 2007, 20:11:18)
[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import hebo
start loading
done
>>>
 

うむ。一回ロードされた。
一応もう一度importしてみる。

>>> import hebo
>>>
 

今度は実行されなかった。

で、pythonをCTRL-Zでバックグラウンドにし、heboモジュールを変更してみる。

$ cat hebo.py
print "start loading start start"

X = 10
Y = 20

def aaa(x):
print "param is ", x
print "X = ", X
print "Y = ", Y


print "done done done"
 

・・・ちょっとメッセージが変わっただけ。
で、これをもう一度import。

$ fg
python
import hebo
import hebo
>>>
 

なにも実行されない。
ここでreload関数を使ってみる。


>>> reload(hebo)
start loading start start
done done done
<module>
>>>
 

モジュールが実行された。ちゃんと書き換えたメッセージが実行された。
これじゃロードされたのか良くわからないので、さらにhebo.pyを書き換える。

$ cat hebo.py
print "start loading start start"

X = 10
Y = 20

def aaa(x):
print "param is BOYOYON ", x
print "X = ", X
print "Y = ", Y


print "done done done"
 

aaa関数の最初のメッセージを書き換えた。
で、pythonに戻り・・・

>>> hebo.aaa(10)
param is 10
X = 10
Y = 20
>>>
 

現状は書き換えた後リロードしていないので、以前のメッセージが表示されている。
で、リロード。

>>> reload(hebo)
start loading start start
done done done
<module>
 

さらにaaa関数を呼んでみる。

>>> hebo.aaa(10)
param is BOYOYON 10
X = 10
Y = 20
>>>
 

ぼよよんって言われた。
heboがリロードされてメモリ上にロードされているっぽいことが
確認できる。


リロードと変数、名前空間の関係
  1. リロードでは、モジュールの名前空間に変更が加えられる。
  2. リロード時に代入が行われると、変数の値は新しいものに置き換えられる。
  3. リロードを行うと、同じモジュールをimportステートメントでインポートしている全てのモジュールに影響を与える。
  4. fromステートメントによってモジュールをインポートした後にリロードが行われても影響を受けない。
とのこと。
1は、リロードが行われると、モジュールの名前空間をそのままに、必要に応じて変更が加えられるということ。いったんメモリ上の名前空間を全削除して再構成されるわけではない。

2は、リロードされるモジュールのトップレベルで代入が行われているのであれば、リロード後はリロード時に代入された値になる。
日本語じゃ、よくわからないのでやってみる。

>>> import hebo
start loading start start
done done done
>>> hebo.X
10
>>>
 

いま、hebo.Xの値は10。
これを上書きしてみる。

>>> hebo.X = 100000
>>> hebo.X
100000
>>>
 

上書きされた。
で、リロード。

>>> reload(hebo)
start loading start start
done done done
<module>
>>> hebo.X
10
>>>
 

リロードすると、hebo.Xの値は10になる。
なぜかというと、モジュールのトップレベルで代入されているから。
って感じか。

3は、リロードするとメモリ上のモジュールが変更されるので、他のimportで同じモジュールを参照しているモジュールにも影響を及ぼすということか。つまり他のモジュールも同じモジュールであれば同じメモリ領域を参照する、ということを言っているに違いない。


4はfromステートメントはコピーを作るのでリロードされても関係ないよ、といっている。


なるほどねー。



おしまい。

.

[Python][お勉強] Python入門(36) - モジュールのインポート

今日もぶりぶりお勉強。

今日はモジュールのインポートについてやる。

まずは、、、
モジュールの作成
モジュールは主にテキストエディタなどで作成するのが普通とのこと。テキストファイルにPythonのコードを入力する。
モジュール内の「トップレベル」で値が代入された変数(名前)は、そのモジュールの「属性」となる。
ちなみに、モジュールをインポートする側のコードのことを「クライアント」と言う。
で、クライアントからはその「属性」を利用できる。
モジュールのファイル名は.py拡張子をつけておく。
モジュールのファイル名のうち、.py拡張子を省いた部分が「モジュールの名前」となる。
したがって、モジュールのファイル名は変数名命ルールに従ってつける必要がある。

さらにモジュールを置いておくディレクトリ名も変数命名ルールに従っておくのが無難とのこと。



モジュールを利用する。
利用するにはimportステートメントかfromステートメントを使う。
importとfromの違いは、ざっくりと

  • importステートメント -- モジュール全体を取り込む
  • fromステートメント -- モジュール中の特定の属性だけをコピーする
な感じか。

importステートメントでモジュールを利用する
モジュールを「module1.py」というファイル名で作成したとする。
中身は

$ cat module1.py
print "start loading"

def aaa(x):
print 'Hello,',x


print "loaded"

$

となっていたとする。
トップレベルからインポートしてみる。

$ python
Python 2.4.4 (#2, Apr 5 2007, 20:11:18)
[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import module1.py
start loading
loaded
Traceback (most recent call last):
File "", line 1, in ?
ImportError: No module named py


import module1.pyと入力するとエラーになる。import、fromでは.py拡張子をつけずにモジュール名
を指定しなければならない。
上記のように.py拡張子をつけてインポートすると、どういうことになるかというと、

module1モジュールのpy属性をインポートする

という意味になってしまう。
さらにimportステートメントなのでpy属性はモジュールである必要がある。

気を取り直して、

$ python
Python 2.4.4 (#2, Apr 5 2007, 20:11:18)
[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import module1
start loading
loaded
>>> module1.aaa('world')
Hello, world
>>>


.py拡張子をつけずにimportし、その後module1モジュールのaaa属性を呼び出した。
module1の属性を利用するには、利用するたびに"module1."と、モジュール名を指定する必要がある。



fromステートメントでモジュールを利用する。
次はfromステートメントでモジュールを利用してみる。
利用するモジュールは上記のmodule1.py。

$ python
Python 2.4.4 (#2, Apr 5 2007, 20:11:18)
[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from module1 import aaa
start loading
loaded
>>> aaa('world')
Hello, world
>>>


こんどはmodule1モジュールのaaa属性のみを抽出した。
importと違うのはfromでは、

カレントスコープにimport以降で指定した属性をコピーする

ということらしい。
よって、モジュール名を指定しなくてもモジュールの属性を利用できる。
コピーなので、

>>> aaa = (lambda x: 'hi,' + x)
>>> aaa('world')
'hi,world'
>>>


などとやっても、module1の方のaaa属性は影響を受けない。
ただし、aaaが仮に共有リファレンスであった場合、つまりリストなどであった場合はその要素に対する変更がモジュール側にも影響を及ぼす。

$ cat module1.py
print "start loading"

def aaa(x):
print 'Hello,',x

bbb = [1,2,3,4,5]

print "loaded"


のようにbbb属性をリストで追加した。
そしてこれを

$ python
Python 2.4.4 (#2, Apr 5 2007, 20:11:18)
[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from module1 import bbb
start loading
loaded
>>> bbb
[1, 2, 3, 4, 5]
>>> bbb[0]=10
>>> bbb
[10, 2, 3, 4, 5]


と0番目の要素を上書きする。
モジュール側はどうなっているかというと、

>>> import module1
>>> module1.bbb
[10, 2, 3, 4, 5]
>>>
 

と、bbbの0番目の要素が10に上書きされている。
つまりbbbそのものはコピーされたが、bbbの保持する各要素はリファレンスをそのまま引き継いでいる
ということ。
この辺は他の変数に対するリファレンスの扱いと一緒。


from *ステートメントでモジュールを利用する。
fromステートメントで、import以降に'*'を指定すると、「全ての属性」をコピーする。
module1は1つしか属性がないが、、、

$ python
Python 2.4.4 (#2, Apr 5 2007, 20:11:18)
[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from module1 import *
start loading
loaded
>>> aaa('hello')
Hello, hello
>>>


などと指定すると、module1の中の全属性がカレントスコープにコピーされる。



インポートしたモジュールの属性へのアクセス
インポートしたモジュールの属性を利用する際にもPython側で属性の特定ルールが存在する。



単純な変数の場合
LEGBルールによって変数がいずれかのスコープから検索される。


完全名の場合
module1.aaaのように書くと、カレントスコープでmodule1が検索され、
その後module1にaaaという属性がないか検索される。


完全パス名の場合
module1.aaa.bbbのように書くと、カレントスコープでmodule1が検索され、
その後module1にaaaという属性がないか検索され、
次にmodule1.aaaにbbbという属性がないか検索される。



な感じにルールがある。
完全名は、モジュールの他にクラス、Cの型など属性を持つ全てのオブジェクトに使用できる。





おしまい。
.

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
BONEの意味がわからず時間がとられた。BeOS Network Environmentの略らしい。
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()する。


とりあえず本日読んだところはここまで。



おしまい。
.

[Python][お勉強] Python入門(35) - インポートの処理

ひきつづき「インポートの処理」を見ていく。

インポートの処理は大きく3つのプロセスに分けることができる。

  1. インポート対象のモジュールファイルを探す
  2. ファイルのコンパイル
  3. モジュールを実行し、オブジェクトを生成
上記3つのプロセスをひとつづつ見ていく。



1. インポート対象のモジュールファイルを探す

モジュールファイルを探すのにはモジュールサーチパスなるものが利用される。
基本的にはモジュールサーチパスは自動で設定される。
モジュールサーチパスは4つの要素を持つ。その要素を以下に記す。
  1. トップレベルファイルのホームディレクトリ
  2. 環境変数PYTHONPATHで指定されたディレクトリ
  3. 標準ライブラリモジュールのディレクトリ
  4. .pthファイルの内容
モジュールサーチパスを変更するときは、通常、環境変数PYTHONPATHか.pthを変更する。
1が最初に検索され、なければ2を。最後に4を検索される。


トップレベルファイルのホームディレクトリ
ホームディレクトリとは、通常トップレベルファイルが置かれているディレクトリを指す。
モジュールインポート時には最初に検索される場所。

環境変数PYTHONPATH
環境に依存したディレクトリパスを記述しておく環境変数。
複数の値を設定でき、複数指定された場合には左端から順に検索される。
設定された値がトップレベルファイルのホームディレクトリの次に検索される場所になる。

標準ライブラリモジュールのディレクトリ
標準ライブラリがインストールされているディレクトリ。
環境変数PYTHONPATHの値の次に検索される場所。

.pthファイルの内容
.pthファイルとは.pthという拡張子の付いたテキストファイルのこと。
一行に1パスづつ複数行指定できる。このファイルを「適切な場所」に設置すると
.pthファイルの内容の最初の行から順にそれぞれの行の値の場所を検索してくれる。
「適切な場所」がどこかは今のところ不明。


上記4つのサーチパスをプログラム上から触ることもできる。
sysモジュールのpath属性の値を調べればサーチパスのリストを取得できる。

>>> import sys
>>> sys.path
['', '/usr/lib/python24.zip', '/usr/lib/python2.4', '/usr/lib/python2.4/plat-linux2',
'/usr/lib/python2.4/lib-tk', '/usr/lib/python2.4/lib-dynload',
'/usr/local/lib/python2.4/site-packages', '/usr/lib/python2.4/site-packages',
'/var/lib/python-support/python2.4']
>>>


と、こんな感じ。

また、サーチパスをカスタマイズするプログラムも、sys.pathを使用すれば作成できるとのこと。


インポートできるファイルの種類
インポートできるファイルの種類は、以下のとおり。
  • ソースファイル(.py)
  • バイトコードファイル(.pyc)
  • パッケージインポートのためのディレクトリ
  • Cエクステンションモジュール(.so)
  • フローズンバイナリに対応するメモリイメージ
  • Jythonの場合のJavaクラス
  • zipファイルコンポーネント(zipimportステートメントを利用)
同じディレクトリに同一名で同じ違う種類のファイルが存在する場合は、
優先順位にルールがあるものの、いつ変わるかわからないからなんとも言えないらしい。




2.見つかったファイルのコンパイル
.pyファイルが見つかると次は.pycファイルを生成すべくコンパイルをする。
ただしコンパイルされない場合もある。
  • .pyファイルよりも.pycファイルの方が新しい場合
  • .pycファイルしか対象ディレクトリになかった場合
が、コンパイルされない場合とのこと。
注意点としては、.pycファイルはインポートのタイミングで作成されるものである、という点。




3.モジュールの実行
コンパイルプロセスが終わると、次はモジュール中のコードが実行され、そのモジュールに対応するモジュールオブジェクトが生成される。このときモジュール中のコードが最初の行から1つづつ実行される。
仮にprintステートメントが書かれていればインポート時に出力されたりする。
defステートメントがあればdefステートメントが実行され、関数オブジェクトが生成され、関数名に代入される。

インポート処理によってモジュールが実行されるということは、コードの内容によってはインポート時点でなんらかの処理結果が得られる場合もあるということを意味する。



これらの3つのステップは既にインポート済みの場合には実行されない。
既にインポート済みの場合にはメモリ中にロードされているモジュールが再利用される。
既にインポート済みかどうかはsys.modulesを見ればわかるとのこと。


>>> import sys
>>> sys.modules.keys()
['copy_reg', '__main__', 'site', '__builtin__', 'encodings',
'posixpath', 'encodings.codecs', 'os.path', '_codecs',
'encodings.exceptions', 'stat', 'zipimport', 'warnings',
'encodings.types', 'UserDict', 'encodings.utf_8', 'sys',
'codecs', 'readline', 'types', 'signal', 'linecache', 'posix',
'encodings.aliases', 'exceptions', 'os']
>>>


Debian-Etchでデフォルト状態のPythonで起動しただけのトップレベルの場合には上記のようになった。



ふーん。


おしまい。
.

[Python][お勉強] Python入門(34) - モジュール

さて、今日もESRいわく「pointless, zero-content sludge」の某日本語訳をぶりぶり。

今日はモジュールをお勉強。

モジュールとは
プログラムやデータの集合のこと。
Pythonにおいては最上位の構成単位。
Pythonのプログラムファイルのことと思ってほぼ良いらしい。
大きく言って、中心をなす1つのファイルをトップレベルファイル、残りの補助的な役割を果たすファイルを狭義のモジュールという。


モジュールを使うには
モジュールをプログラムから使用するには「インポート」をする。
インポートするには以下のステートメントが利用できる。

  • importステートメント
  • fromステートメント
  • reload関数

importステートメントはモジュールの中身全体を使えるようにするステートメント。
fromステートメントはモジュールの特定の「名前」だけをインポートするためのステートメント。
reload関数は既にインポート済みのモジュールをリロードするための関数。

インポートするとモジュールに属する「属性」(モジュールに属するツール)を利用できるようになる。


モジュールを利用するメリット
  • コードの再利用が容易になる
  • プログラムの名前空間が分割できる
  • コードの共有ができる

標準ライブラリモジュール
プログラマが自分で作成しなくてもPython側であらかじめ用意してくれているモジュールで、
かなりの数がある。これらも「インポート」することによって利用可能になる。


そもそも「インポート」って?
他言語によくある、ただ単に「テキストファイルを別のファイルに挿入する」というだけのものではなく
プログラム実行時に行われる処理で、以下のプロセスをもって行われるもの。
  1. インポート対象のモジュールファイルを探す
  2. 見つかったファイルをコンパイルしバイトコード(.pyc)にする。(必要ない場合もある)
  3. モジュールのコードを実行し、モジュール内で定義されているオブジェクトを作成する
プログラム中で初めてimportされるときに実行される。



次回は「インポート処理」を見ていく予定。


おしまい。
.




2008年2月14日木曜日

[Apache] ApacheをAPR_POOL_DEBUGつけてコンパイル

ソース読んでて気づいたのですが・・・・、


CPPFLAGS=-DAPR_POOL_DEBUG ./configure
make
make install

とすればPOOLのデバッグが有効になるかと思ってた。

でも、これじゃだめっぽい。。
これだと

#if APR_POOL_DEBUG

なんて書かれているところにひっかからない・・・。

正しくは、

CPPFLAGS=-DAPR_POOL_DEBUG=0xff ./configure
make
make install

な感じか。

どうも0xffはそれぞれのビットがフラグになってて、何かしらのデバッグを有効にするには
対象のビットを立てないとだめなんじゃない?

apr_pool_check_integrity()という関数を今読んでいるんだけど、

#if (APR_POOL_DEBUG & APR_POOL_DEBUG_LIFETIME)

などが見つかる。

ドキュメントを読めば分かるのかもしれないけど、
多分、
httpd-2.2.8/srclib/apr/memory/unix/apr_pools.cの

418 /*
419 * Debug level
420 */
421
422 #define APR_POOL_DEBUG_GENERAL 0x01
423 #define APR_POOL_DEBUG_VERBOSE 0x02
424 #define APR_POOL_DEBUG_LIFETIME 0x04
425 #define APR_POOL_DEBUG_OWNER 0x08
426 #define APR_POOL_DEBUG_VERBOSE_ALLOC 0x10
427
428 #define APR_POOL_DEBUG_VERBOSE_ALL (APR_POOL_DEBUG_VERBOSE \
429 | APR_POOL_DEBUG_VERBOSE_ALLOC)
430

この辺を指定しなきゃだめなんじゃないかなー。
ざっと眺めた感じではいろいろとPOOL周りのログが出そうな感じ。

APR_POOL_DEBUG_GENERALを有効にしたかったら、

CPPFLAGS=-DAPR_POOL_DEBUG=0x01 ./configure

って感じか。




.

[mod_chxj][携帯] バージョンについて

3つのパートを持っているが、
とりあえず現状考えているのが
0.8.4
であった場合は、

  • 0がメジャーバージョンで何かしら大きめの変更を行った場合に上がる。
  • 8がマイナーバージョンで機能追加の場合に上がる。
  • 4がパッチレベルでバグ修正で上がる。
などとルールを決めてやってます。
そして今後も。

絵文字関連で割りと大きめの修正をしたんでルールにのっとって1.0.0にしてしまおうと
思う今日この頃。

.

[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
Apacheのコードリーディングというよりも、むしろAPRのコードリーディングな感じ。

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を読む、と思う
.

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

今日もApacheのコードリーディング。

本日分はまだ終わっていないがメモっとく。

apr_pool_create_ex関数

apr_pool_create_ex()は読み終わった。コードは以下。


810 APR_DECLARE(apr_status_t) apr_pool_create_ex(apr_pool_t **newpool,
811 apr_pool_t *parent,
812 apr_abortfunc_t abort_fn,
813 apr_allocator_t *allocator)
814 {
815 apr_pool_t *pool;
816 apr_memnode_t *node;
817
818 *newpool = NULL;
819
820 if (!parent)
821 parent = global_pool;
822
823 if (!abort_fn && parent)
824 abort_fn = parent->abort_fn;
825
826 if (allocator == NULL)
827 allocator = parent->allocator;
828
829 if ((node = allocator_alloc(allocator,
830 MIN_ALLOC - APR_MEMNODE_T_SIZE)) == NULL) {
831 if (abort_fn)
832 abort_fn(APR_ENOMEM);
833
834 return APR_ENOMEM;
835 }
836
837 node->next = node;
838 node->ref = &node->next;
839
840 pool = (apr_pool_t *)node->first_avail;
841 node->first_avail = pool->self_first_avail = (char *)pool + SIZEOF_POOL_T;
842
843 pool->allocator = allocator;
844 pool->active = pool->self = node;
845 pool->abort_fn = abort_fn;
846 pool->child = NULL;
847 pool->cleanups = NULL;
848 pool->free_cleanups = NULL;
849 pool->subprocesses = NULL;
850 pool->user_data = NULL;
851 pool->tag = NULL;
852
856
857 if ((pool->parent = parent) != NULL) {
859 apr_thread_mutex_t *mutex;
860
861 if ((mutex = apr_allocator_mutex_get(parent->allocator)) != NULL)
862 apr_thread_mutex_lock(mutex);
864
865 if ((pool->sibling = parent->child) != NULL)
866 pool->sibling->ref = &pool->sibling;
867
868 parent->child = pool;
869 pool->ref = &parent->child;
870
872 if (mutex)
873 apr_thread_mutex_unlock(mutex);
875 }
876 else {
877 pool->sibling = NULL;
878 pool->ref = NULL;
879 }
880
881 *newpool = pool;
882
883 return APR_SUCCESS;
884 }


読みにくいのでNETWARE部分は削除し、APR_HAS_THREADSの#ifdefも削除してある。

ざっと説明すると、13日分で読んだallocator_alloc関数を使ってノード領域を確保する。
確保したノード領域のnextとrefをセットし、ノードの利用可能開始アドレスにapr_pool_tコントロール領域を配置。
ノードの利用可能開始アドレスを更新後、apr_pool_tコントロールの各メンバに値をセットし、
できあがったプールを呼び出し元へ返す。

ここでまず疑問が生じた。
837行目、838行目のnextとrefをセットしている部分。

837 node->next = node;
838 node->ref = &node->next;

一見意味が無いように見える。なんじゃこりゃ。

でも先に読み進むとすぐに解決。list_insertマクロとlist_removeマクロというものが出てくるが、
これらを読めば一目瞭然。なるほど、といった感じ。

list_insertマクロとlist_removeマクロ
一度パターンを知っておくと、次見たときもすぐわかる系。
コードは以下。

606 #define list_insert(node, point) do { \
607 node->ref = point->ref; \
608 *node->ref = node; \
609 node->next = point; \
610 point->ref = &node->next; \
611 } while (0)

となっている。
なるほどねー。

一方向リストは前の要素を指すポインタを保持しない。
次の要素を指すポインタだけを保持する。ゆえに一方向リストと呼ぶ。
だが、一方向リストだと途中の要素をリストから削除するのが大変面倒になる。
なぜかというと、削除の場合では「前の要素」の「次」を、削除対象の「次」に更新したいが
削除対象だけでは「前の要素」がわからないので、リストの先頭から1つづつ「前の要素」を
探さないといけない。

そこで、「ref」が出てくる。

「ref」は何かというと、「前の要素」の「次」を指すポインタになっている。
refを持つことで「前の要素」の「次」がわかるので削除時にはrefの指している先を更新すればよい。
下記は実際の削除処理。

613 /* list_remove() removes 'node' from its list. */
614 #define list_remove(node) do { \
615 *node->ref = node->next; \
616 node->next->ref = node->ref; \
617 } while (0)


これだけでOK.
さらに素敵なのがnodeのリストは結果的に循環リストになる。
最初の要素を

837 node->next = node;
838 node->ref = &node->next;


と初期化することで、
「先頭の要素」の「前の要素」は「最後の要素」になる。・・・何言っているかよくわからなくなりそうだが。
そして、
「最後の要素」の「次の要素」は「先頭の要素」になる。

こうしておくことでいろいろな処理が楽に書けるようになる。

面白いねー。



さて、次はapr_atomic_init()を攻略する。
知らない関数のapr_palloc()あたりから片付ける。
.

[Python][お勉強] Python入門(33) - 変数がローカルスコープに属するかどうかの内部的判断

ここまではローカルスコープに属するかどうかは


「代入」した時点で決定する

とのことだったが、実際は違うとのこと。

まず例を見る。

>>> x = 100
>>> def aaa():
... print x
... x = 99
...
>>> aaa()
Traceback (most recent call last):
File "", line 1, in ?
File "", line 2, in aaa
UnboundLocalError: local variable 'x' referenced before assignment
>>>


aaa関数の中で2回グローバル変数と同じ名前の変数が使われている。
aaa関数ではx変数に代入しているのでaaa関数内部においてはxはローカル変数ということに
なる。よって上記のようにExceptionとなる。
代入のタイミングでローカル変数かどうか決まるのであれば、

print x

の時点のxはグローバル変数ということになるが、実際はそうでは無い。
その関数内で代入されているかどうかをPythonは静的に判断しローカル変数かどうかを決定する。


同じ名前の変数を、ローカル、グローバルと混在して使いたい場合には、完全名を指定すればよい。

>>> x = 100
>>> def aaa():
... import __main__
... print __main__.x
... x = 88
... print x
...
>>> aaa()
100
88
>>>


のようにする。






おしまい。

--
追記:2008/2/14
正しくは「参照」だか「結合」だかで厳密にルールがあるようだが、
「初めてのPyhthon」中の身としては、今のところ書籍に沿っていこうかと先に進もうかと思うのでした。

追記:2008/2/16
「オブジェクト参照」なるものも存在するとのこと。

追記:2008/2/27
疑問メモ: Pythonでは、代入は「結合」という言葉でよいのかどうか。ちょっといままでの言語のイメージと違うのでメモ。

追記:2008/2/27
疑問メモ: Pythonでは、代入のことを「定義」と言ってよいのかどうか。こちらはなんとなく良さそうな気がする。

追記:2008/2/27
気になったので調べた。
「Assignments do not copy data -- they just bind names to objects. 」とのこと。この部分の
世の中の日本語訳では「結合」という言葉が使われている模様。
Pythonでも「bind」で良いのね。ということで、ひと安心。

.

[Python][お勉強] Python入門(32) - イテレータ

今日もPythonのお勉強。
イテレータからやる。

イテレータオブジェクト
イテレータプロトコルをサポートするオブジェクトのこと。
必ずnext()という「ループにおいて次に処理対象となるオブジェクトを戻す」メソッドを持っている。

イテレータオブジェクトとジェネレータ。
ジェネレータはイテレータプロトコルをサポートしている。
よってジェネレータもイテレータの一種ということができる。

ビルトインオブジェクト
ビルトインオブジェクトはビルトイン関数iter()によってイテレータオブジェクトを作成できるように設計されている。

さっそくやってみる。


>>> L = [1,2,3,4,5]
>>> xx = iter(L)
>>> xx.next()
1
>>> xx.next()
2
>>> xx.next()
3
>>> xx.next()
4
>>> xx.next()
5
>>>


リストの例。

次はディクショナリ。

>>> D = {'a':1,'b':2,'c':3}
>>> xx = iter(D)
>>> xx.next()
'a'
>>> xx.next()
'c'
>>> xx.next()
'b'
>>> xx.next()
Traceback (most recent call last):
File "", line 1, in ?
StopIteration
>>>


できた。
要素が無くなるとExceptionをはく。


ふーん。


おしまい。
.

2008年2月13日水曜日

[mod_chxj] ドキュメント更新

0.9.0用にドキュメントを更新。

やたらと時間がかかった・・・。

書いてて忘れてた機能をこれから追加。
metaタグにcharsetが入ってたら書き換えないといけない。





.

[Apache][CodeReading] Apache2.2.8コードリーディング13日目

今日もApacheリーディング。

今日作ったページは・・・

  • allocator_alloc()/Apache2.2.8
  • APR_UINT32_TRUNC_CASTマクロ/Apache2.2.8
  • apr_thread_mutex_unlock()/Apache2.2.8
  • apr_thread_mutex_lock()/Apache2.2.8
  • PTHREAD_SETS_ERRNO定数/Apache2.2.8
  • APR_UINT32_MAX定数/Apache2.2.8
  • MIN_ALLOCローカル定数/Apache2.2.8
  • BOUNDARY_SIZEローカル定数/Apache2.2.8
  • APR_MEMNODE_T_SIZEマクロ/Apache2.2.8
  • apr_size_t/Apache2.2.8
  • APR_INLINEマクロ/Apache2.2.8
な感じ。


本日のメインはやっぱりallocator_alloc()。
apr_allocator_tの保持するfree[]に既にほしいサイズの領域があればそれを使い、無ければ新しくmallocするところ。

ソースコードを順に見ていく。と、その前に、前提知識としてfree[]を知っておく。
apr_allocator_tにはfreeというメンバがある。
このメンバはapr_memnode_tへのポインタの配列。

で、これはつまるところ何かというと、「malloc済みの空き領域」のリストの先頭の要素へのポインタの配列。

free[0]はsinkってコメントに書いてあるが、free[1]からfree[MAX_INDEX-1]のサイズに入りきらない大きさのメモリ領域用リストで、要は掃き溜め。ごみ。くず。

free[1]にはBOUNDARY_SIZE * (1+1)の大きさの領域のリストを保持している。つまり8192バイト。
free[2]にはBOUNDARY_SIZE * (1+2)の大きさの領域のリストを保持している。つまり12288バイト。
free[3]にはBOUNDARY_SIZE * (1+3)の大きさの領域のリストを保持している。つまり16384バイト。

・・・・。

というのがfree[MAX_INDEX-1]まであると。まだ一回もallocatorを使用していない場合にはfreeは全てNULLが入っている。

取得希望のサイズからfreeの添え字を取得するには・・

取得希望サイズ >> 12


とすれば求まる。

で、早速ソースを見ていく。


static APR_INLINE
apr_memnode_t *allocator_alloc(apr_allocator_t *allocator, apr_size_t size)
{
apr_memnode_t *node, **ref;
apr_uint32_t max_index;
apr_size_t i, index;

/* Round up the block size to the next boundary, but always
* allocate at least a certain size (MIN_ALLOC).
*/
size = APR_ALIGN(size + APR_MEMNODE_T_SIZE, BOUNDARY_SIZE);
if (size < MIN_ALLOC)
size = MIN_ALLOC;


最初の部分。
まず呼び出し元が取得したいサイズがsizeパラメータでわたってくる。
でいきなり4096バイトアライン。
sizeパラメータでわたってきてアライン調整した値がMIN_ALLOC(8192)より小さければ8192バイトにする。
つまり小さい領域はみな8192バイト使うということ。

そして、

207 /* Find the index for this node size by
208 * dividing its size by the boundary size
209 */
210 index = (size >> BOUNDARY_INDEX) - 1;
211
212 if (index > APR_UINT32_MAX) {
213 return NULL;
214 }


で、これから探そうとするサイズ用のfree[]の添え字を算出する。
index値がぶっとんだ値になってたらNULLを返しちゃう。



216 /* First see if there are any nodes in the area we know
217 * our node will fit into.
218 */
219 if (index <= allocator->max_index) {
220 #if APR_HAS_THREADS
221 if (allocator->mutex)
222 apr_thread_mutex_lock(allocator->mutex);
223 #endif /* APR_HAS_THREADS */
224
225 /* Walk the free list to see if there are
226 * any nodes on it of the requested size
227 *
228 * NOTE: an optimization would be to check
229 * allocator->free[index] first and if no
230 * node is present, directly use
231 * allocator->free[max_index]. This seems
232 * like overkill though and could cause
233 * memory waste.
234 */
235 max_index = allocator->max_index;
236 ref = &allocator->free[index];
237 i = index;
238 while (*ref == NULL && i < max_index) {
239 ref++;
240 i++;
241 }
242
243 if ((node = *ref) != NULL) {
244 /* If we have found a node and it doesn't have any
245 * nodes waiting in line behind it _and_ we are on
246 * the highest available index, find the new highest
247 * available index
248 */
249 if ((*ref = node->next) == NULL && i >= max_index) {
250 do {
251 ref--;
252 max_index--;
253 }
254 while (*ref == NULL && max_index > 0);
255
256 allocator->max_index = max_index;
257 }
258
259 allocator->current_free_index += node->index;
260 if (allocator->current_free_index > allocator->max_free_index)
261 allocator->current_free_index = allocator->max_free_index;
262
263 #if APR_HAS_THREADS
264 if (allocator->mutex)
265 apr_thread_mutex_unlock(allocator->mutex);
266 #endif /* APR_HAS_THREADS */
267
268 node->next = NULL;
269 node->first_avail = (char *)node + APR_MEMNODE_T_SIZE;
270
271 return node;
272 }
273
274 #if APR_HAS_THREADS
275 if (allocator->mutex)
276 apr_thread_mutex_unlock(allocator->mutex);
277 #endif /* APR_HAS_THREADS */
278 }


算出したindex値が現在allocatorが保持しているfree[]のmax_indexより小さければ
既にallocatorが持っている可能性が高いので上記のif文内を実行する。

allocatorのmax_indexは空きがあるfreeのindexの最大値。

221行目から222行目でロックをかける。初期化したてのallocatorはmutexがNULLなんでロックはかからない。

235行目から241行目はindexからmax_indexまでのfreeリストを1づつ見ていく。
要はfree[i] != NULLのものを探している。

243行目から272行目はfree[i] != NULLのものが見つかったときの処理。
なければunlockして319行目へいく。
見つかったらnode変数にfree[i]をセット。
そして、node->nextをfree[i]にセットする。
free[i]のリストの先頭要素をnodeに取り出しnodeの次の要素をfree[i]の先頭にしている。

249行目から257行目では、見つかったノードがfree[max_index]リストの最後の要素であった場合の処理をしている。
最後の要素であった場合には、max_indexの値を更新する。
max_index以下のfree[]が!= NULLであった場合にはその添え字をあたらしいmax_indexとしている。
もし、同じサイズでもう一度allocator_alloc()が呼ばれても、max_index値が更新されているので同じルートは通らない。その際は、free[0]かmallocをしにいく。

259行目から261行目でcurrent_free_indexを更新している。
この値はまだ良くわからないが、恐らくallocatorが貸し出し中のサイズを意味しているような気がする。
ただ、そうなると以下の疑問が生ずる。

  • [疑問]max_free_indexよりcurrent_free_indexが大きい値の場合、なぜmax_free_indexの値で更新しているのか。
  • [疑問]更新してしまっては貸し出し中のサイズがわからなくなってしまう。
きっともう少し読めばわかるとは思うが、後ほど参照できるように疑問点を挙げておく。

で、263行目から266行目でアンロック。

268行目でnode->nextをNULLでクリアし、
269行目でnode->first_availに利用できるメモリ領域の先頭アドレスを設定している。
269行目でわかるとおり、利用できるメモリアドレスはapr_memnode_tのコントロール領域の後ろに位置する。

271行目で呼び出し元へnodeを返す。



次はindexがallocator->max_indexよりも大きい場合の処理。
sink(掃き溜め)にある可能性が高いので、free[0]を探してみる処理。
そもそもfree[0]がNULLだったら319行目にいく。


280 /* If we found nothing, seek the sink (at index 0), if
281 * it is not empty.
282 */
283 else if (allocator->free[0]) {
284 #if APR_HAS_THREADS
285 if (allocator->mutex)
286 apr_thread_mutex_lock(allocator->mutex);
287 #endif /* APR_HAS_THREADS */
288
289 /* Walk the free list to see if there are
290 * any nodes on it of the requested size
291 */
292 ref = &allocator->free[0];
293 while ((node = *ref) != NULL && index > node->index)
294 ref = &node->next;
295
296 if (node) {
297 *ref = node->next;
298
299 allocator->current_free_index += node->index;
300 if (allocator->current_free_index > allocator->max_free_index)
301 allocator->current_free_index = allocator->max_free_index;
302
303 #if APR_HAS_THREADS
304 if (allocator->mutex)
305 apr_thread_mutex_unlock(allocator->mutex);
306 #endif /* APR_HAS_THREADS */
307
308 node->next = NULL;
309 node->first_avail = (char *)node + APR_MEMNODE_T_SIZE;
310
311 return node;
312 }
313
314 #if APR_HAS_THREADS
315 if (allocator->mutex)
316 apr_thread_mutex_unlock(allocator->mutex);
317 #endif /* APR_HAS_THREADS */
318 }



284行目から287行目でロックをかける。

292行目から294行目で収まるサイズを保持しているノードを探している。
十分わかりやすいのだが、もう少しへぼへぼに書くと、

for (p = free[0];
p != NULL;
p = p->next) {

if (p->index > index) break;
}

な感じか。

296行目からほしいサイズ以上のサイズをもつノードが見つかった場合の処理が続く。
297行目でリストをつなぎ直して、見つかったノードをリストからはずしている。

そして、299行目でcurrent_free_indexに取得したindex値を加算して、
303行目から306行目でアンロックと。

で、308行目、309行目でnextをNULLクリアし、
first_availに利用できるメモリ領域の先頭アドレスをセット。

で、できあがったnodeを呼び出し元に返している。


次は、free[]には持っていなかった場合。

320 /* If we haven't got a suitable node, malloc a new one
321 * and initialize it.
322 */
323 if ((node = malloc(size)) == NULL)
324 return NULL;
325
326 node->next = NULL;
327 node->index = (APR_UINT32_TRUNC_CAST)index;
328 node->first_avail = (char *)node + APR_MEMNODE_T_SIZE;
329 node->endp = (char *)node + size;
330
331 return node;
332 }


323行目でnode領域を確保し、
326行目から329行目でnext、index、first_avail、endpメンバをセットしている。

nextはNULLクリア。
indexは、これまでに算出したindex値をセット。
first_availには利用できるメモリ領域の先頭アドレスを設定している。

そして、mallocするときにだけ、endpを設定している。
mallocしたサイズを覚えておくためか???
endpは細切れにメモリ領域を使うときに使用する気がするが、
これも今のところよくわからない。

  • [疑問]endpの使い道は何か。

と一応疑問点を挙げておく。


これでallocator_alloc()は一通り読んだ。
あとは疑問点を解消すべく先を読みすすむ。

期待通り、面白い。
何も考えずに使うとするとfree[1]ばかりが出来そうだが、きっとその辺はpoolとかでいろいろと
やっているに違いない。

あと、
apr_thread_mutex_unlock()とapr_thread_mutex_lock()はarch/unix系では
pthreadを読んでいるだけ。
特に面白くない。

おしまい。
.

[Python][お勉強] Python入門(31) - ジェネレータ

Pythonではジェネレータなるものが使用できる。

通常関数はreturnステートメントで呼び出し元に戻る。再度同じ関数をコールするとまた最初から実行され、
returnステートメントで呼び出し元に戻る。
ジェネレータを使うとyeildステートメントで呼び出し元に戻る。再度同じ関数をコールすると、yeildステートメントで呼び出し元に戻ったところから実行が再開され、またyeildステートメントで呼び出し元に戻る。

ただし普通に使おうとすると(通常の関数と同じようにコールすると)、ジェネレータオブジェクトが返されるだけなので、ちょっと使い方を注意する。

ジェネレータを使用した例


>>> def aaa(x):
... for i in range(x):
... yield i ** 2
...
>>>

と定義したとする。普通の関数のようにコールすると、、、

>>> aaa(10)
<generator>
>>>
 

とジェネレータオブジェクトが返されるだけ。
なので、いったん変数に入れるなりして、、、

>>> a = aaa(10)
>>> a.next()
0
>>> a.next()
1
>>> a.next()
4
>>> a.next()
9
>>> a.next()
16
>>> a.next()
25
>>> a.next()
36
>>>
 

nextメソッドをコールしてやる。すると呼び出す度に続きから再実行される。
このnextメソッドは「イテレータオブジェクト」の機能らしい。





おしまい。
.

[Python][お勉強] Python入門(30) - リスト内包表記

filterやreduceは他の言語とほぼ一緒な感じなのでとばして、リスト内包表記をやる。

まず、リスト内包表記を使わない例。forでがんばる例。


>>> res = []
>>> for x in 'spam':
... res.append(ord(x))
...
>>> res
[115, 112, 97, 109]
>>>



これをmap関数を使うと・・・

>>> res = map(ord, 'spam')
>>> res
[115, 112, 97, 109]
>>>



なんて感じで書ける。
さらにリスト内包表記を使うと、、

>>> res = [ord(x) for x in 'spam']
>>> res
[115, 112, 97, 109]
>>>



な感じ。この例ではmap関数の方が楽であるもののいろいろと使えそう。


おしまい。




.

2008年2月12日火曜日

[Apache][CodeReading] Apache2.2.8コードリーディング12日目

今日は、ページを作っただけ。

作ったページは、

  • allocator_alloc()/Apache2.2.8
  • global_poolスタティック変数/Apache2.2.8
  • apr_pool_create_ex()/Apache2.2.8
  • apr_allocator_create()/Apache2.2.8
  • APR_ALLOCATOR_MAX_FREE_UNLIMITED定数/Apache2.2.8

だけ。

作っただけなんで何もなし。。。



.

[Python][お勉強] Python入門(29) - map関数

シーケンスに対して順に個々のデータを操作するにはmap関数が使える。

たとえば、


>>> L = [1,2,3,4,5]
>>> L
[1, 2, 3, 4, 5]
>>>


といったリストがあったとする。
各要素に1を足したリストがほしいとする。
そういう場合には、

>>> map((lambda x: x+1),L)
[2, 3, 4, 5, 6]
>>>


というように第一引数にそれぞれの要素に対する処理を渡し、繰り返し処理をさせることができる。


また、第二引数以降にもうひとつ引数を足すと、

>>> map((lambda x,y: x*y),[1,2,3,4],[2,3,4,5])
[2, 6, 12, 20]
>>>


なんてこともできる。
これは第二引数と第三引数の各要素をひとつづつ、lambdaで定義した関数に引数として渡し、計算結果を最終結果として返している。





おしまい。
.

[Python][お勉強] Python入門(28) - apply関数

apply関数についてやる。

PythonではLispのようにapply関数がある。
プログラム実行時に関数の名前、引数が決まる場合にapply関数を使える。

apply関数の例


>>> def func(x,y,z): return x + y + z
...
>>> apply(func, (2,3,4))
9
>>>




キーワード引数を渡す

apply関数の第三引数を使用する。

>>> def aaa(a,b,c): return a + b + c
...
>>> apply(aaa,(),{'c':1, 'b':2, 'a':1})
4
>>>


第三引数にディクショナリの形にして渡せばキーワード引数として渡すことができる。




おしまい。
.

2008年2月11日月曜日

[Apache][CodeReading] Apache2.2.8コードリーディング11日目

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

昨日のつづきでapr_initialize()まわりを読んだ。

つくったページは、

  • APR_ENOMEM定数/Apache2.2.8
  • APR_OS_START_CANONERR定数/Apache2.2.8
  • APR_OS_START_USERERR定数/Apache2.2.8
  • APR_OS_START_STATUS定数/Apache2.2.8
  • APR_OS_ERRSPACE_SIZE定数/Apache2.2.8
  • APR_OS_START_ERROR定数/Apache2.2.8
  • SIZEOF_ALLOCATOR_Tマクロ/Apache2.2.8
  • APR_ALIGN_DEFAULTマクロ/Apache2.2.8
  • APR_ALIGNマクロ/Apache2.2.8
  • global_allocatorスタティック変数/Apache2.2.8
  • APR_POOL_DEBUG定数/Apache2.2.8
  • apr_pools_initializedスタティック変数/Apache2.2.8

定数が定数で定義されててsのまた定数が定数で・・・という繰り返しがちょろっと。

で、今日の本丸はapr_pool_initialize()/Apache2.2.8。
結局全部読めなかった。

global_allocatorにapr_allocator_t用の領域を確保して
0クリアしたところまで。
global_allocatorはglobalってついているのにスタティック変数。
きっとシステム全体のallocatorという意味か。


領域確保できなかったらAPR_ENOMEMを返すんだけど、
errno.hのENOMEMが定義されていなかったら、APRで独自に定義する。
でその値は620007。
システムの他のエラーコードと重ならないようにしていると思うんだけど、
「へぇ」という感じ。

まだまだアロケータの中身に到達していないのでなんともいえないが、
相変わらず面白そうだ。




.

[Python][お勉強] Python入門(27) - lambda式

今日はlambda式。

Lispから来た名前だそうで。

lambda式とは
defステートメントのように関数を作成する際に使用するもの。
defステートメントとは違い「式」。よってdefステートメントでは記述できない場所に記述することが可能。
また、defステートメントでは関数の名前を記述できたが、lambda式では意図的に代入しない限り名前がない。つまり無名関数生成式ということ。

lambda式のシンタックス


lambda <arg1>,<arg2>,...<argN>: <expressions>


の形をとる。

実際に書いてみる。
まずdefステートメントから、

>>> def func(x,y,z): return x + y + z
...
>>> func(2,3,4)
9
>>>


なんてのがあったとする。
これをlambdaで書き直すと・・・、

>>> func = lambda x, y, z: x + y + z
>>> func(2,3,4)
9
>>>


な感じか。
lambdaでは名前がないので、名前に相当する変数にlambda式の結果を代入している。
defステートメントと同様にキーワード引数やデフォルト値も設定できる。


>>> func = lambda x,y=10,z=30: x + y + z
>>> func(x=1)
41
>>>




lambda式の特徴。
lambda式は「式」なので、defステートメントでは記述できない場所に記述できる。
たとえば

>>> L=[(lambda x: x**2),(lambda x: x**3),(lambda x: x**4)]
>>> for f in L:
... print f(2)
...
4
8
16
>>>


のように[]中に記述できる。なにせ「式」なので。





おしまい。
.

2008年2月10日日曜日

[Debian][Apache] 静的ファイルが送られてこない

Apache2.2にアップグレードしたばっかで気づかなかったが、
静的ファイルが一切レスポンスされない。

そういうものか??

ログレベルをdebugにして見てみると・・・


Function not implemented: core_output_filter: writing data to the network


なんてのが出力される。

とりあえず、

EnableSendfile Off


を/etc/apache2/sites-available/defaultに追記して事なきを得た。


うーむん。



.

[Python][お勉強] Python入門(26) - 引数

さて、Pythonのお時間。今日は引数を学ぶ。

引数の重要事項

  • 引数が渡されると自動的にローカルスコープの変数に代入される。
  • よって引数に渡された変数を関数内で更新しても呼び出し元では影響しない。
  • ただし、引数の内部のリファレンスを上書きした場合は呼び出し元でも影響する。
な感じ。要は変数は全てポインタっぽく動作するので、ポインタそのものはコピーされるがそのポインタが保持しているポインタを上書きすれば呼び出し元にも影響を及ぼすということか。

たとえば、

>>> L = [1,2,3,4]
>>> def func(ll):
... ll = [5,6,7,7]
...
>>> func(L)
>>> L
[1, 2, 3, 4]
>>>


としてもグローバル変数Lは更新されない。が、

>>> def func(ll):
... ll[0] = 5
...
>>> func(L)
>>> L
[5, 2, 3, 4]
>>>


と、Lの内容はコピーされているわけじゃなくて、同じリファレンスを持つので、グローバル変数の値は更新されてしまう。
この辺、要注意らしい。

仮に、上記のようにリストの中身を変更しても呼び出し元に影響されたくないとするならば、

>>> L = [1,2,3,4]
>>> def func (ll):
... ll[0] = 5
...
>>> func(L[:])
>>> L
[1, 2, 3, 4]
>>>



と、スライシングの[:]を使って全ての要素をコピーしてしまえばよい。



キーワード引数
pythonではキーワード引数なるものが使える。


>>> def func(a,b,c):
... print a, b,c
...
>>> func(c=10,b=20,a=30)
30 20 10
>>>


な感じに引数を定義順には関係なく渡すことができる。
c=10は、引数cに10をという意味。
b=20は、引数bに20をという意味。
a=30は、引数aに30という意味。
順番ではなく、引数の名前を指定して値を渡すことができる。
このことを「名前によるマッチング」という。


可変引数
*や**を使用する。


>>> def func(*a):
... print a
...
>>> func("a","b","c")
('a', 'b', 'c')
>>>


となり、*を使用した場合は引数全てがタプルとなって関数内で受け取れる。

**を使用した場合は、

>>> def func(**a):
... print a
...
>>> func(a=10,b=20,c=30)
{'a': 10, 'c': 30, 'b': 20}
>>>


となり、関数内ではディクショナリとして受け取れる。
これらは、全て自由に引数の個数を変えられる。

**を使用した場合は、引数に渡す時点で、a=10、b=20のようにキーワード引数の形で渡す必要がある。


デフォルト値
関数の定義時に、仮に引数が指定されなかったときのためにデフォルトの値を設定できる。


>>> def func(a,b=20,c=30):
... print a,b,c
...
>>> func(10)
10 20 30
>>>


呼び出し時には1つしか引数を渡していない。定義時にbとcのデフォルト値を設定してあるので、
実際に呼び出すとa=10、b=20、c=30という値で呼び出されたことになる。

仮に、

>>> def func(a=10,c,d):
... print a,b,c
...
SyntaxError: non-default argument follows default argument
>>>



と引数aにだけデフォルト値を設定すると構文エラーになる。
これは引数が2つ以上あった場合、どの引数が省略されたのかPythonは知るすべがないからである。


>>>func(10,20)

とコールした場合、10はaに設定され、20はbに設定される。
cは未定義となってしまう。




おしまい。
.

[Apache][CodeReading] Apache2.2.8コードリーディング10日目

今日もこつこつとリーディング。


今日は
apr_unix_setup_time()/Apache2.2.8周りを読破。

apr_unix_setup_timeでは、struct tmにtm_gmtoffか__tm_gmtoffを保持しないシステム用に
自力でgmtからのオフセット値を計算して保持している部分。
tm_gmtoffを保持しているシステムではコード自体がNOPになって何も処理しない。


APR_DECLARE(void) apr_unix_setup_time(void)
{
#ifdef NO_GMTOFF_IN_STRUCT_TM
/* Precompute the offset from GMT on systems where it's not
in struct tm.

Note: This offset is normalized to be independent of daylight
savings time; if the calculation happens to be done in a
time/place where a daylight savings adjustment is in effect,
the returned offset has the same value that it would have
in the same location if daylight savings were not in effect.
The reason for this is that the returned offset can be
applied to a past or future timestamp in explode_time(),
so the DST adjustment obtained from the current time won't
necessarily be applicable.

mktime() is the inverse of localtime(); so, presumably,
passing in a struct tm made by gmtime() let's us calculate
the true GMT offset. However, there's a catch: if daylight
savings is in effect, gmtime()will set the tm_isdst field
and confuse mktime() into returning a time that's offset
by one hour. In that case, we must adjust the calculated GMT
offset.

*/

struct timeval now;
time_t t1, t2;
struct tm t;

gettimeofday(&now, NULL);
t1 = now.tv_sec;
t2 = 0;

#if APR_HAS_THREADS && defined(_POSIX_THREAD_SAFE_FUNCTIONS)
gmtime_r(&t1, &t);
#else
t = *gmtime(&t1);
#endif
t.tm_isdst = 0; /* we know this GMT time isn't daylight-savings */
t2 = mktime(&t);
server_gmt_offset = (apr_int32_t) difftime(t1, t2);
#endif /* NO_GMTOFF_IN_STRUCT_TM */
}


簡単に書くと、gettimeofdayをtimezoneにNULLを伴ってコールし(マニュアルによるとtimezoneに値を指定するのは時代遅れとのこと)GMT秒を取得。
秒からstruct tmへ変換。
変換結果の夏時間をクリアしてmktimeでローカル時間に変換。
結果の差分をgmt_offsetとしてスタティック変数に保管。

な感じか。
ちなみにDebian-EtchではNOP。


次回はいよいよpoolの初期化部分に入る予定。
きっとものすごくアツイに違いない。



.

[その他] MyMiniCity ビルができてた。

わーい。

ビルが建った。

といっても小さいビルだけど。。

http://atkonn.myminicity.com/
http://atkonn.myminicity.com/ind


.