業務で、セマフォ操作時のデバッグ方法について質問があったので、記事ネタとして作成してみました。
該当処理のサンプル
以下のソースコードをテストするものとします。
なお、OSはlinux(CentOS )を前提としています。
int RETRY_MAX = 5;
int RET_OK = 0;
int RET_NG_SEMOP = -1;
int RET_NG_RETRY_OVER = -2;
int semLock(int semid){
int ret;
int retry_cnt;
struct sembuf sops;
sops.sem_num = 0;
sops.sem_op = -1;
sops.sem_flg = SEM_UNDO;
for (retry_cnt = 0; retry_cnt < RETRY_MAX; retry_cnt++){
ret = semop(semid, &sops, 1);
if (ret == 0) {
return RET_OK;
}
if ( errno == EINTR ) {
continue;
}
return RET_NG_SEMOP;
}
return RET_NG_RETRY_OVER;
}
今回取り扱うのが23行目「if ( errno == EINTR )」の条件を満たしたテストが実施したい、とします。
本プログラムの簡単な説明は、以下となります。
- セマフォロックを行う関数「semLock」
- セマフォロックに成功した場合には、戻り値として「RET_OK」を返す。
- セマフォロックに失敗した場合、かつ、割込みが発生(EINTR)した場合は、リトライ(RETRY_MAXを上限)する。
- セマフォロックに失敗、かつ、割込み以外は、戻り値として「RET_NG_SEMOP」を返す。
- リトライ回数を超過した場合、戻り値として「RET_NG_RETRY_OVER」を返す。
テスト時に困った事
質問された際に、
semop実行が一瞬で終了してしまい、semop実行中にシステム割込みさせる事が出来ないけど、どうしたら良い?
というものでした。
テスト方法
方法の1つとして、セマフォをロックしたままにするプログラムを別途準備する事です。
(ここでは、便宜上、テスト対象のプログラムを「プログラムA」、セマフォをロックしたままにするプログラムを「プログラムB」とします。)
以下の流れでテストするイメージです。
- プログラムAをgdbで起動し、該当箇所(semopの行)でbreak pointを設定、semop実行の直前まで処理を進める。
- 別ターミナルを開き、プログラムBを起動し、セマフォロックを掛ける。(掛けたまま解除しない)
- プログラムAを1行実行(semopを実行)する。(この時、応答待ちになる)
- 別ターミナルを開き、killコマンドを発行する。(例えば、「kill -INT プログラムAのプロセスID」のように実行する)
- プログラムAの応答待ちが解除されているはずなので、次行まで処理を動かし、errnoの値を確認する。(上手く動作してれば、errno=4(EINTR)(※)が設定されている)
※実際に設定される値については「/usr/include/asm-generic/errno-base.h」を参照。
まとめ
今回はセマフォロック時に関するテスト方法を纏めてみました。
この辺りは、経験しているか、否かで難易度が変わってくる内容なのかな、と感じています。
似たようなテストを実施する際の参考になれば幸いです。
コメント