IPCセマフォの取り消し(2)
Rev.1を表示中。最新版はこちら。
ユーザプロセスが異常終了を含めて終了する場合、do_exit()がコールされ、プロセスのリソースの解放等の終了処理が行われます。その中で exit_sem()をコールする事で、IPCセマフォの取り消し処理が行われます。これはIPCセマフォの取り消し(1)のタスク構造体のtsk->sysvsem.undo_listにリストされているstruct sem_undoを参照する事で行っています。まずulp = tsk->sysvsem.undo_listで終了プロセスのundoリストをulpに設定したのち、atomic_dec_and_test()で、他のプロセスがこのundoリストを参照しているかチェックします。fork()でCLONE_SYSVSEMフラグを設定するとundoリストは継承されるからです。
次のforループが実際の取り消し処理になります。list_entryマクロでulp->list_proc(tsk->sysvsem.undo_listにリストされているノード)、struct sem_undo *unを取得します。取得したunlist_procがulp->list_procと同じなら、リストを一巡したという事でsemid = -1とし、ループを抜けることになります。そうでないなら、semid = un->semidとし、これが取り消し処理するIPC識別子です。このIPC識別子でIPCセマフォそのものを管理しているIPCネームスペースから、sem_lock_check()でstruct sem_array *smaを取得します。この構造体がポイントするstruct sem * semaphoreのセマフォカウンターを更新することになります。
なお、この識別子でもって再度lookup_undo()でtsk->sysvsem.undo_listからstruct sem_undo *unを取得していますが、理由はわかりません。(コメントありますが・・・)
list_del()でun->list_id/un->list_procをノードとするリストを削除します。list_procはタスク構造体、list_idはstruct sem_arrayでリストされている物です。
次のforループで、使用しちるセマフォ組数sma->sem_nsemsで、各struct sem * semaphoreを取得し、semaphore->semval += un->semadj[i]とすることで、そのプロセスが行ったセマフォ処理の取り消し処理を行っています。
void exit_sem(struct task_struct *tsk) { struct sem_undo_list *ulp; ulp = tsk->sysvsem.undo_list; if (!ulp) return; tsk->sysvsem.undo_list = NULL; if (!atomic_dec_and_test(&ulp->refcnt)) return; for (;;) { struct sem_array *sma; struct sem_undo *un; int semid; int i; rcu_read_lock(); un = list_entry(rcu_dereference(ulp->list_proc.next), struct sem_undo, list_proc); if (&un->list_proc == &ulp->list_proc) semid = -1; else semid = un->semid; rcu_read_unlock(); if (semid == -1) break; sma = sem_lock_check(tsk->nsproxy->ipc_ns, un->semid); /* exit_sem raced with IPC_RMID, nothing to do */ if (IS_ERR(sma)) continue; un = lookup_undo(ulp, semid); if (un == NULL) { /* exit_sem raced with IPC_RMID+semget() that created * exactly the same semid. Nothing to do. */ sem_unlock(sma); continue; } /* remove un from the linked lists */ assert_spin_locked(&sma->sem_perm.lock); list_del(&un->list_id); spin_lock(&ulp->lock); list_del_rcu(&un->list_proc); spin_unlock(&ulp->lock); /* perform adjustments registered in un */ for (i = 0; i < sma->sem_nsems; i++) { struct sem * semaphore = &sma->sem_base[i]; if (un->semadj[i]) { semaphore->semval += un->semadj[i]; /* * Range checks of the new semaphore value, * not defined by sus: * - Some unices ignore the undo entirely * (e.g. HP UX 11i 11.22, Tru64 V5.1) * - some cap the value (e.g. FreeBSD caps * at 0, but doesn't enforce SEMVMX) * * Linux caps the semaphore value, both at 0 * and at SEMVMX. * * Manfred <manfred@colorfullife.com> */ if (semaphore->semval < 0) semaphore->semval = 0; if (semaphore->semval > SEMVMX) semaphore->semval = SEMVMX; semaphore->sempid = task_tgid_vnr(current); } } sma->sem_otime = get_seconds(); /* maybe some queued-up processes were waiting for this */ update_queue(sma); sem_unlock(sma); call_rcu(&un->rcu, free_un); } kfree(ulp); }