System V セマフォを使用して条件変数を実装しようとしています。簡単にするために、一度に 1 つのプロセスだけが条件変数を待機できると仮定しましょう。概念は単純に見えるので、ミューテックスと条件付き変数ラッパーの私の実装を見てください:
- Mutex は、1 がフリー、0 がロックされたバイナリ セマフォです。
- 条件変数はバイナリ セマフォであり、1 は誰かが待機していることを意味し、0 はそれ以外の場合を意味します。
ここに行きます:
//Sets value of 'num' semaphore from 'semid' semaphore set to be 'val'.
// Returns 0 on success and -1 on failure.
int set_value(int semid, int num, int val)
{
union semun setval_semun;
setval_semun.val = val;
return semctl(semid, num, SETVAL, setval_semun);
}
// Returns the value of semaphore 'num' from 'semid' semaphore set
// or -1 on failure.
int get_value(int semid, int num)
{
return semctl(semid, num, GETVAL);
}
// Locks 'mutex'. Returns 0 on success and 1 if 'mutex' is invalid.
// Returns -1 on other error.
int lock_mutex(int semid, int mutex)
{
struct sembuf op;
if (get_value(semid, mutex) > 1) return 1;
op.sem_num = mutex;
op.sem_op = -1;
op.sem_flg = 0;
return semop(semid, &op, 1);
}
// Unlocks 'mutex'. Returns 0 on success and 1 ig the 'mutex' isn't locked.
// Returns -1 on different error.
int unlock_mutex(int semid, int mutex)
{
struct sembuf op;
if (get_value(semid, mutex) != 0) return 1;
op.sem_num = mutex;
op.sem_op = 1;
op.sem_flg = 0;
return semop(semid, &op, 1);
}
// Waits on condition 'cond' and frees 'mutex'.
// Returns 0 on success and 1 if either some other process is waiting on 'cond'
// or 'mutex' is not locked. Returns -1 on other failure.
int wait_cond(int semid, int cond, int mutex)
{
struct sembuf ops[2];
if (get_value(semid, cond) != 0 || get_value(semid, mutex) != 0)
return 1;
ops[0].sem_num = cond;
ops[0].sem_op = 1;
ops[0].sem_flg = 0;
semop(semid, ops, 1);
ops[0].sem_num = mutex;
ops[0].sem_op = 1;
ops[0].sem_flg = 0;
ops[1].sem_num = cond;
ops[1].sem_op = 0;
ops[1].sem_flg = 0;
return semop(semid, ops, 2);
}
// Wakes up process waiting for 'cond'. Returns 0 on success. Returns 1 when
// no process is waiting for 'cond' and -1 on other error.
int wake_cond(int semid, int cond)
{
struct sembuf op;
if (semctl(semid, cond, GETVAL) != 1)
return 1;
op.sem_num = cond;
op.sem_op = -1;
op.sem_flg = 0;
return semop(semid, &op, 1);
}
次に、最小限のテスト プログラムを確認してください。これは、2 つのセマフォを共有する 2 つのプロセスで構成されます。1 つはミューテックスとして機能し、もう 1 つは条件変数として機能します。どちらも同じフローを持っています:
- ミューテックスをロックします。
- 何かをしてください。
- 条件変数待ちのウェイクアップ処理。
- ミューテックスを使用して条件変数を待ちます。
私がこのすべてを正しければ、彼らは交互に「何か」をしているはずです.
int main(int argc, char** argv)
{
key_t key = 0;
pid_t pid = 0;
int semid = 0;
int const mutex = 0;
int const cond = 1;
int repeat = 5;
char const* name = NULL;
union semun setval_semun;
// Create unique key.
key = get_unique_key(argv[0], 47);
if (key == -1)
return 1;
// Create and intialize semaphore set.
semid = semget(key, 2, 0666 | IPC_CREAT);
if (semid == -1)
return 1;
if (set_value(semid, mutex, 1) != 0 || set_value(semid, cond, 1) != 0)
{
semctl(semid, 0, IPC_RMID);
return 1;
}
// Fork.
pid = fork();
if (fork() < 0)
return 1;
else if(pid > 0)
name = "parent";
else
name = " child";
while(repeat--)
{
printf("%s: (0), mutex: %d, cond: %d, repeat: %d\n", name,
get_value(semid, mutex), get_value(semid, cond), repeat);
if (lock_mutex(semid, mutex) != 0) return 1;
sleep(1);
printf("%s: (1), mutex: %d, cond: %d, repeat: %d\n", name,
get_value(semid, mutex), get_value(semid, cond), repeat);
if (wake_cond(semid, cond) != 0) return 1;
printf("%s: (2), mutex: %d, cond: %d, repeat: %d\n", name,
get_value(semid, mutex), get_value(semid, cond), repeat);
if (repeat == 0)
{
if (unlock_mutex(semid, mutex) != 0) return 1;
}
else if (wait_cond(semid, cond, mutex) != 0) return 1;
printf("%s: (3), mutex: %d, cond: %d, repeat: %d\n", name,
get_value(semid, mutex), get_value(semid, cond), repeat);
}
//Wait for child and clear resources.
if (pid > 0)
{
wait(NULL);
semctl(semid, 0, IPC_RMID);
}
return 0;
}
そして、ここに私が得た出力がありますが、これは非常に奇妙です:
parent: (0), mutex: 1, cond: 1, repeat: 4
parent: (0), mutex: 0, cond: 1, repeat: 4
child: (0), mutex: 0, cond: 1, repeat: 4
child: (0), mutex: 0, cond: 1, repeat: 4
parent: (1), mutex: 0, cond: 1, repeat: 4
parent: (2), mutex: 0, cond: 0, repeat: 4
^C
特に最初の 2 行を見てください。それらは、通知番号 (0) が異なる値で while ループの同じ実行で 2 回出力されたことを示しています! もちろんデッドロックに陥るので^Cします。
get_unique_key() 関数について気にしないでください。名前が示すとおりのことを行います。
この作業を行うため、または奇妙な出力を説明するための助けをいただければ幸いです。ありがとうございました。