semctl()を読むのは良いけどフラグによって操作が違うのでshmctl()の時と同じくIPC_RMIDの場合を読みます。
まずはsemctl()からでshmctl()と同じくIPC_SETとIPC_RMIDは同じ関数が使われます。
1569 SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg) 1570 { 〜略〜 1596 case IPC_RMID: 1597 case IPC_SET: 1598 return semctl_down(ns, semid, cmd, version, p);
ではsemctl_down()を見ていきます。と思ったんですが、shmctl()の場合とほぼ同じなので確実に違う部分だけ読みます。
それはどこかというと、セマフォでコマンドがIPC_RMIDの場合のfreeary()のところです。
1543 case IPC_RMID: 1544 sem_lock(sma, NULL, -1); 1545 /* freeary unlocks the ipc object and rcu */ 1546 freeary(ns, ipcp); 1547 goto out_up;
freeary()を見ていきます。最初は変数宣言とセマフォを管理しているstruct sem_arrayの取得です。
1069 static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) 1070 { 1071 struct sem_undo *un, *tu; 1072 struct sem_queue *q, *tq; 1073 struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm); 1074 struct list_head tasks; 1075 int i; 1076
まずはロックを取り、次にリストを辿っていきます。ここでたどるのはundo処理をする必要があるもののリストです。
1077 /* Free the existing undo structures for this semaphore set. */ 1078 ipc_assert_locked_object(&sma->sem_perm); 1079 list_for_each_entry_safe(un, tu, &sma->list_id, list_id) { 1080 list_del(&un->list_id); 1081 spin_lock(&un->ulp->lock); 1082 un->semid = -1; 1083 list_del_rcu(&un->list_proc); 1084 spin_unlock(&un->ulp->lock); 1085 kfree_rcu(un, rcu); 1086 } 1087
次はコメントに書いてありますね。wake_up_sem_queue_prepare()の処理は、qのメンバ変数statusをIN_WAKEUPにしてtasksリストにつなぎます。
1088 /* Wake up all pending processes and let them fail with EIDRM. */ 1089 INIT_LIST_HEAD(&tasks); 1090 list_for_each_entry_safe(q, tq, &sma->pending_const, list) { 1091 unlink_queue(sma, q); 1092 wake_up_sem_queue_prepare(&tasks, q, -EIDRM); 1093 } 1094
こちらも上と同じですが手繰るリストが違うだけです。
1095 list_for_each_entry_safe(q, tq, &sma->pending_alter, list) { 1096 unlink_queue(sma, q); 1097 wake_up_sem_queue_prepare(&tasks, q, -EIDRM); 1098 }
次はセマフォの数だけループで、セマフォごとに持っているリストを辿ってwakeupしていきます。
1099 for (i = 0; i < sma->sem_nsems; i++) { 1100 struct sem *sem = sma->sem_base + i; 1101 list_for_each_entry_safe(q, tq, &sem->pending_const, list) { 1102 unlink_queue(sma, q); 1103 wake_up_sem_queue_prepare(&tasks, q, -EIDRM); 1104 } 1105 list_for_each_entry_safe(q, tq, &sem->pending_alter, list) { 1106 unlink_queue(sma, q); 1107 wake_up_sem_queue_prepare(&tasks, q, -EIDRM); 1108 } 1109 } 1110
sem_rmid()はipc_rmid()を呼ぶだけです。ipc_rmid()はこちらに。
1111 /* Remove the semaphore set from the IDR */ 1112 sem_rmid(ns, sma);
sem_unlock()は後ほど。ざっくりというとshmのpending_alterリストの内容を別のリストに移して、このリストは空にします。
1113 sem_unlock(sma, -1); 1114 rcu_read_unlock(); 1115
1116 wake_up_sem_queue_do(&tasks);
カレントプロセスのipc namespaceで使用していたセマフォ数を減算してロックを解除して終了。
1117 ns->used_sems -= sma->sem_nsems; 1118 ipc_rcu_putref(sma, sem_rcu_free); 1119 }
sem_unlock()はIPC_RMIDの場合、locknumは-1なのでunmerge_queues()が呼ばれます。
364 static inline void sem_unlock(struct sem_array *sma, int locknum) 365 { 366 if (locknum == -1) { 367 unmerge_queues(sma); 368 ipc_unlock_object(&sma->sem_perm); 369 } else { 370 struct sem *sem = sma->sem_base + locknum; 371 spin_unlock(&sem->lock); 372 } 373 }
unmerge_queues()を見ましょう。まず、shmのcomplex_countが0でない場合は何もしません。
206 static void unmerge_queues(struct sem_array *sma) 207 { 208 struct sem_queue *q, *tq; 209 210 /* complex operations still around? */ 211 if (sma->complex_count) 212 return;
次にpending_alterリストを辿っていきます。そして、qをcurr->pending_alterにaddしてループが終わったらsmaのpending_alterリストを空にします。
213 /* 214 * We will switch back to simple mode. 215 * Move all pending operation back into the per-semaphore 216 * queues. 217 */ 218 list_for_each_entry_safe(q, tq, &sma->pending_alter, list) { 219 struct sem *curr; 220 curr = &sma->sem_base[q->sops[0].sem_num]; 221 222 list_add_tail(&q->list, &curr->pending_alter); 223 } 224 INIT_LIST_HEAD(&sma->pending_alter); 225 }
最後にwake_up_sem_queue_do()を見ましょう。これに引数で渡ってくるリストはundo処理をするリストでwake_up_sem_queue_prepare()で作ったものですね。
697 static void wake_up_sem_queue_do(struct list_head *pt) 698 { 699 struct sem_queue *q, *t; 700 int did_something; 701
リストが空か?のチェック。list_empty()はリストが空なら1が返ります。
702 did_something = !list_empty(pt);
リストをたどりつつwake_up_process()でプロセスを起こしていきます。
703 list_for_each_entry_safe(q, t, pt, list) { 704 wake_up_process(q->sleeper); 705 /* q can disappear immediately after writing q->status. */ 706 smp_wmb(); 707 q->status = q->pid; 708 }
リストが空でなかった場合はpreempt_enable()でpreemptを有効にします。
709 if (did_something) 710 preempt_enable(); 711 }
The Art of Readable Code (Theory in Practice)
- 作者: Dustin Boswell,Trevor Foucher
- 出版社/メーカー: O'Reilly Media
- 発売日: 2011/11/03
- メディア: Kindle版
- 購入: 1人 クリック: 3回
- この商品を含むブログを見る