システムプログラミング 演習7:データベースサーバ (1)

2009年11月5日

データベースサーバ (1)

本演習では,マルチスレッドのデータベースサーバの骨格部分の作成を行う.

スレッドセーフなハッシュ表

データベースサーバではスレッドセーフなハッシュ表を利用する. マルチスレッド環境では,ハッシュ表をひくところだけでなく, ハッシュ表のエントリの内容の修正,読込みに関してもデータ競合がおこる. が,ハッシュ表をひいて, データを利用し終えるまでハッシュ表全体をロックしてしまうと著しい効率低下は免れない. そのため,ハッシュ全体ではなく,エントリごとのロックも利用する.

以下のハッシュ表では,hash_get(), hash_find() において, キーに割り当てられたメモリ領域が取得できるが, その領域はその後の操作で修正されるため, 修正が終了するまでその領域をロックし保護する.修正後, hash_release() を呼び出してアンロックする.

hash_make() は,バケットのサイズ bucket_size のハッシュ表を作成する. hash_type はハッシュキーのデータ型が string の場合は,STRING_HASH_KEY を指定し, それ以外の場合は INT_HASH_KEY を指定する.

hash_operate() は,ハッシュ表に登録された全エントリに対する操作であるが, この操作を実行するためには, 全てのエントリのロックが開放されている必要がある.

以下にそれぞれの関数の型と説明を示す.

hash_t *hash_make(int bucket_size, int key_type);
bucket_sizeで指定されたバケット数のハッシュ表を作成する. key_typeは,ハッシュキーがstringのときは,STRING_HASH_KEYを 指定し,それ以外は INT_HASH_KEY を指定する.
void **hash_get(hash_t *tbl, char *key);
keyとして登録されているハッシュ表のエントリのアドレスを返す. エントリがない場合は作成される.エントリはロックされるため, データ競合なしにその内容を修正,読込みできる.
void **hash_find(hash_t *tbl, char *key);
keyとして登録されているハッシュ表のエントリのアドレスを返す. エントリがない場合はNULLが返る.エントリはロックされるため, データ競合なしにその内容を修正,読込みできる.
int hash_release(hash_t *tbl, void **data);
hash_getあるいはhash_findでロックしたエントリのロックを解放する.
void *hash_delete(hash_t *tbl, void **dataptr);
hash_getあるいはhash_findでロックしたエントリを消去する.
int hash_operate(
	hash_t *tbl, void (*func)(void *, void **, void *), void *usr_arg);
ハッシュ表の全エントリに対し,funcで指定された関数を実行する. funcには引数としてkey,エントリのアドレス,usr_argが渡される.

ハッシュ表の使ったプログラム例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <pthread.h>
#include "ll.h"
#include "hash.h"

static hash_t *h;

void
print_entry(void *k, void **d, void *a)
{
	char *key = k;
	char *data = (char *)*d;

	printf("<key:%s> <data:%s>\n", key, data);
}

int
main()
{
	void **hash_entry;
	char *data;

	/* create a hash table of a string hash key with 1117 buckets */
	h = hash_make(1117, STRING_HASH_KEY);

	/* insert data "1" as a key "first" */
	hash_entry = hash_get(h, "first");
	*hash_entry = strdup("1");
	hash_release(h, hash_entry);

	/* find a data by a key "first" */
	hash_entry = hash_find(h, "first");
	assert(hash_entry != NULL);
	printf("<%s>\n", (char *)*hash_entry);
	hash_release(h, hash_entry);

	/* dump every data */
	hash_operate(h, &print_entry, NULL);

	/* remove a data */
	hash_entry = hash_find(h, "first");
	assert(hash_entry != NULL);
	data = hash_delete(h, hash_entry);
	free(data);

	exit(0);
}
[演習7-1]
スレッドセーフ版のハッシュ表のプログラムを理解し,特に hash_get(), hash_find() がどのようにブロックされたスレッドの順番に処理を行っているか説明しなさい.
[演習7-2]
スレッドセーフ版のハッシュ表を利用するマルチスレッドのサンプルプログラムを作成し,動作の確認を行いなさい.

マルチスレッドサーバ

マルチスレッドサーバでは,あるスレッドでクライアントからの接続を待ち, 接続されたら,別スレッドを立ち上げてそのクライアントからの処理を行う.

以下はそのmainプログラムである.オプションで -d が指定されない場合, daemon(0, 0)でデーモンとなり,バックグランドで動作し, 標準入出力が利用できなくなるので注意すること.
#define DEFAULT_SERVER_PORT	10000

int
main(int argc, char **argv)
{
	char *port_number = NULL;
	int ch, sock, server_port = DEFAULT_SERVER_PORT;
	int debug_mode = 0;

	while ((ch = getopt(argc, argv, "dp:")) != -1) {
		switch (ch) {
		case 'd':
			debug_mode = 1;
			break;
		case 'p':
			port_number = optarg;
			break;
		case '?':
		default:
			usage();
		}
	}
	argc -= optind;
	argv += optind;

	if (port_number != NULL)
		server_port = strtol(port_number, NULL, 0);

	/* server_portでlistenし,socket descriptorをsockに代入 */
	sock = open_accepting_socket(server_port);

	if (!debug_mode)
		daemon(0, 0);

	/*
	 * 無限ループでsockをacceptし,acceptしたらそのクライアント用
	 * のスレッドを作成しプロトコル処理を続ける.
	 */
	main_loop(sock);

	/*NOTREACHED*/
	return (0);
}

[演習7-3]
上記のプログラムにおいて,open_accepting_socket()とmain_loop() を実装し,動作の確認を行いなさい.main_loop() が生成するクライアント用のスレッドでは,EOFを待ち,EOFとなったら disconnected と表示して終了すること. 表示は debug_mode の場合はエラー出力に,debug_mode ではない場合は syslog に書き出すこと. なお,syslog への書き出しは openlog および syslog で行う. 動作の確認は telnet で行うとよい.

シグナルスレッド

非同期割り込みのSIGINTとSIGTERMの処理を行う.処理を行うにあたり, まず main() で SIGINT と SIGTERM をブロックし, シグナルを処理するためのスレッドを生成する.

シグナルを処理するスレッドでは,対象となるシグナルを待ち, シグナルが送信されたら適切な処理を行う.

[演習7-4]
上記のプログラムにおいて,SIGINTとSIGTERMのシグナル処理を行うため のスレッドを生成し,シグナルが送信されたら bye と出力するようにすること. 出力先は EOF を受け取った場合と同様とすること. サーバを Ctrl-C あるいは kill -TERM で落とした場合に bye が表示されるか確認しなさい.

提出締切は 11/11(水) 20:00とする. 作成したプログラムだけでは無く,考察および実行結果も含めること. サブジェクトを SP7 学籍番号 とし,tatebe _at_ cs までメールすること. 本文には授業の感想を含めること.


Osamu Tatebe
Last modified: Thu Nov 5 15:44:53 JST 2009