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