sleepとwakeupの例として、生産者/消費者モデルが紹介されている。
- 生産者 : キューにデータを書き込んでいく。一定数までキューにデータを書き込むとスリープする。
- 消費者 : キューからデータを読み込む。キューが空になるまで読み込むとスリープする。
このときに問題になるのがキューの管理である。キューに対して生産者プロセスと消費者プロセスが同時にアクセスをするため、矛盾の無いように処理をする必要がある。
201 void* 202 send(struct q *q, void *p) 203 { 204 while(q->ptr != 0) 205 ; 206 q->ptr = p; 207 wakeup(q); /* wake recv */ 208 } 209 210 void* 211 recv(struct q *q) 212 { 213 void *p; 214 215 while((p = q->ptr) == 0) 216 sleep(q); 217 q->ptr = 0; 218 return p; 219 }
安直に作った場合、
- 生産者 : キューが空で無い間は待つ。キューが空になると、データを挿入してwakeupにより自分はスリープ状態に入り、recvプロセスを起こす。
- 消費者 : キューが空の間は待つ。この間sleep状態になる。生産者によって起こされると、キューを消費する。
さて、このモデルでデッドロックが発生するケースというのは、
消費者がキューの状態を確認して、キューが空なのでスリープ状態に入るが、実はその直後に生産者がキューへデータを書き込んでしまったという状態。 消費者がスリープ状態に入る前に生産者がwakeupを実行しても、スリープ状態のプロセスはまだ存在していないためどのプロセスも起こすことができない。 従って、消費者はスリープ状態に入り、生産者は何も出来ずにデッドロックが発生してしまう。
そもそもこの問題は、消費者がキューを確認してから、スリープに入るまでの間に生産者が動作できてしまったのが問題。では、その間にロックを挿入してみるとどうか。
300 struct q { 301 struct spinlock lock; 302 void *ptr; 303 }; 304 305 void* 306 send(struct q *q, void *p) 307 { 308 acquire(&q->lock); 309 while(q->ptr != 0) 310 ; 311 q->ptr = p; 312 wakeup(q); 313 release(&q->lock); 314 } 315 316 void* 317 recv(struct q *q) 318 { 319 void *p; 320 321 acquire(&q->lock); 322 while((p = q->ptr) == 0) 323 sleep(q); 324 q->ptr = 0; 325 release(&q->lock); 326 return p; 327 }
そもそも安直にロックを挿入してしまうと、消費者はロックを獲得したままスリープ状態に入ってしまうため、生産者はもうどうしようもできなくなる。
では、スリープに細工を加えよう。スリープする直前に、任意のロックを解放できるようにする。ここで任意のロックは、引数で指定する。
400 struct q { 401 struct spinlock lock; 402 void *ptr; 403 }; 404 405 void* 406 send(struct q *q, void *p) 407 { 408 acquire(&q->lock); 409 while(q->ptr != 0) 410 ; 411 q->ptr = p; 412 wakeup(q); 413 release(&q->lock); 414 } 415 416 void* 417 recv(struct q *q) 418 { 419 void *p; 420 421 acquire(&q->lock); 422 while((p = q->ptr) == 0) 423 sleep(q, &q->lock); 424 q->ptr = 0; 425 release(&q->lock); 426 return p; 427 }
スリープ状態に入る直前にロックを解放することで、wakeupが動作できるようにする。 生産者はこれによりロックを獲得し、キューにデータを書き込んでwakeupを実行し、その後ロックを解放する。 wakeupにより起き上がった消費者プロセスは、解放されたロックを獲得し、データを消費したと後に解放する。
これにより、全体をロックさせることなく生産者と消費者はデータを自由に書き込むことができるようになる。