IPCセマフォの取り消し(2)


ユーザプロセスが異常終了を含めて終了する場合、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]とすることで、そのプロセスが行ったセマフォ処理の取り消し処理を行い、1つのIPC識別子に対する取り消し処理が終了する毎に、update_queue()でこの取り消しによって起床できるプロセスが無いかチェックし、必要なら起床させる事になります。
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);
}

備考

取り消し処理において、if (semaphore->semval < 0)ならsemaphore->semval = 0、if (semaphore->semval > SEMVMX)ならsemaphore->semval = SEMVMXとキャップしています。このあたりコメントにあるようにSUS(POSIXを拡張した物らしい。)では定義されてなく、システムによって実装が異なるようです。まあ雑知識として・・・。まあカーネルリーディングそのものが、雑知識と言えなくはないですが。

最終更新 2011/09/19 15:33:51 - north
(2011/09/19 15:26:01 作成)


検索

アクセス数
3689288
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。