Apacheモジュールで共有メモリを使うには
訳あって,Apacheモジュールを書くことに.
Apacheモジュール開発の導入に関してはDSAS開発者の部屋:apache module 開発事始めが大変参考になりました.
ここではAPR(Apache Portable Runtime)の機能を使って,共有メモリを使う方法を書きます.
このモジュールはApache(複数プロセス)が,アクセスを受けた回数をエラーログに出力するという単純なもの.
なお簡単のためにディレクティブは使用しません.(キリッ
排他制御もやってません.(キリッ
でも排他制御はそのうち触れたい.
では早速ソースを...
#include "httpd.h" #include "http_config.h" #include "http_protocol.h" #include "ap_config.h" #include "http_log.h" #include "apr_time.h" #include "apr_shm.h" #define USER_DATA_KEY "shm_counter" #define DEBUGLOG(...) ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, NULL, __VA_ARGS__) module AP_MODULE_DECLARE_DATA shm_counter_module; typedef struct ServerConfig{ char *shm_path; apr_shm_t *shm_data; int *access_count; }sconfig; static void *create_server_config(apr_pool_t *p, server_rec *s){ sconfig *config; config = (sconfig *)(apr_palloc(p, sizeof(sconfig))); config->shm_path = "/tmp/shm_counter"; config->shm_data = NULL; return config; } static int post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s){ sconfig *config; void *user_data; apr_status_t status; apr_pool_userdata_get(&user_data, USER_DATA_KEY, s->process->pool); if(user_data == NULL){ apr_pool_userdata_set((const void *)1, USER_DATA_KEY, apr_pool_cleanup_null, s->process->pool); return OK; } do{ config = (sconfig *)(ap_get_module_config(s->module_config, &shm_counter_module)); DEBUGLOG("%s", config->shm_path); //raise(SIGTRAP); status = apr_shm_remove(config->shm_path, p); if (status == APR_SUCCESS) { DEBUGLOG("removed the existing shared memory segment name"); } status = apr_shm_create(&(config->shm_data), sizeof(int), config->shm_path, p); if(status != APR_SUCCESS){ return HTTP_INTERNAL_SERVER_ERROR; } config->access_count = (int *)(apr_shm_baseaddr_get(config->shm_data)); *(config->access_count) = 0; }while((s=s->next) != NULL); return OK; } static void init_child(apr_pool_t *p, server_rec *s) { sconfig *config; do{ config = (sconfig *)(ap_get_module_config(s->module_config, &shm_counter_module)); //raise(SIGTRAP); if(!config->shm_data){ if(apr_shm_attach(&(config->shm_data), config->shm_path, p) != APR_SUCCESS){ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0 , 0, "shm attached!"); return; } } }while((s = s->next) != NULL); } static int shm_counter_handler(request_rec *r) { sconfig *config; config = (sconfig *)(ap_get_module_config(r->server->module_config, &shm_counter_module)); (*config->access_count)++; DEBUGLOG("ACCESS_COUNT=%d", *config->access_count); return OK; } static void shm_counter_register_hooks(apr_pool_t *p) { ap_hook_access_checker(shm_counter_handler, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_post_config(post_config, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_child_init(init_child, NULL, NULL, APR_HOOK_MIDDLE); } /* Dispatch list for API hooks */ module AP_MODULE_DECLARE_DATA shm_counter_module = { STANDARD20_MODULE_STUFF, NULL, /* create per-dir config structures */ NULL, /* merge per-dir config structures */ create_server_config, /* create per-server config structures */ NULL, /* merge per-server config structures */ NULL, /* table of config file commands */ shm_counter_register_hooks /* register hooks */ };
Apacheがこのモジュールを読み込むと,module構造体のcreate per-server config structuresに登録されているcreate_server_configを実行し,サーバのこのモジュールに関する設定を行います.
sconfig構造体のshm_pathは,共有メモリで使用するファイルを指定します.
ファイルは,実際に読み書き可能なディレクトリに置く必要があります.私はここでハマりました.
apacheはpost_configステージに達すると,post_configを呼び出します.
post_configでは,apr_shm_createで共有メモリを作成し,apr_shm_baseaddr_getで実際に使用するデータ領域のアドレスを取得します.
apr_shm_createの前のapr_shm_removeは,サーバが異常終了するなど,共有メモリで使用していたファイルが削除されずに残った場合,これを削除するためのものです.
child_initステージでは,自分が共有メモリにアタッチしているか否かを判定し,アタッチしていない場合にapr_shm_attachを使ってアタッチします.
これで,起動時の処理はおしまいです.
ブラウザからアクセスされるたびにshm_counter_handlerが呼ばれます.
errorログにアクセスカウントが記録されているはずです.
参考:
DSAS開発者の部屋:apache module 開発事始め
http://acapulco.dyndns.org/mod_uploader/module_dev.htm