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

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による監視・通知が効率が悪いってんで、各環境でいろいろな代替手段が
開発されてしまった話の被害者部分か。。

とにかくこの辺はリングのデータ構造がどのように使われるのか、
その辺が面白そうなところ。







.

0 コメント: