読者です 読者をやめる 読者になる 読者になる

Linux SystemV IPC: semgetを読む

kernel linux

semget()は以前のエントリ(Linuxカーネル:SystemV IPC get系操作の共通実装部分を読む - φ(・・*)ゞ ウーン カーネルとか弄ったりのメモ)で書いたようにSystemV IPC共通部分と機能固有部分に分かれていて、semget()の場合はnewary()が使われます。
というわけでnewary()を見ていきます。

最初は特筆することはないですね。

477 static int newary(struct ipc_namespace *ns, struct ipc_params *params)
478 {
479         int id;
480         int retval;
481         struct sem_array *sma;
482         int size;
483         key_t key = params->key;
484         int nsems = params->u.nsems;
485         int semflg = params->flg;
486         int i;

最初にsemget(2)の2番目の引数をチェック。次のチェックは現在のセマフォ数にnsemsを足した結果が、maxのセマフォ数を超えるかどうかのチェックです。ちなみにsc_semmnsは構造体のメンバではなくてsem_ctls[1]のdefineです。

488         if (!nsems)
489                 return -EINVAL;
490         if (ns->used_sems + nsems > ns->sc_semmns)
491                 return -ENOSPC;

セマフォを管理するstruct sem_array用にメモリを確保。これはshmget()の時と同様です。気になる方はLinux SystemV IPC: shmgetの実装を読む - φ(・・*)ゞ ウーン カーネルとか弄ったりのメモをどぞ。

493         size = sizeof(*sma) + nsems * sizeof(struct sem);
494         sma = ipc_rcu_alloc(size);
495         if (!sma)
496                 return -ENOMEM;
497 
498         memset(sma, 0, size);

パーミッション関連の設定。

500         sma->sem_perm.mode = (semflg & S_IRWXUGO);
501         sma->sem_perm.key = key;

毎度おなじみのselinux関連部分なので気にしません。

503         sma->sem_perm.security = NULL;
504         retval = security_sem_alloc(sma);
505         if (retval) {
506                 ipc_rcu_putref(sma, ipc_rcu_free);
507                 return retval;
508         }

ここはstruct idrを使ったidの取得です。

510         id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni);
511         if (id < 0) {
512                 ipc_rcu_putref(sma, sem_rcu_free);
513                 return id;
514         }

確保したセマフォ数を使用済みの数に追加。

515         ns->used_sems += nsems;

セマフォのベースアドレスを設定していますが、0番目の要素を渡していません。ここだけだとなんで1番目と思いますよね。

517         sma->sem_base = (struct sem *) &sma[1];
518 

これはsmaをallocする時のサイズが以下のように計算されていて、本当のデータはsizeof(*sma)以降の要素でインデックスとして1なんじゃないかと思います。

493         size = sizeof(*sma) + nsems * sizeof(struct sem);

smaの型はstruct sem_arrayです。struct sem_arrayはinclude/linux/sem.hにて以下のようの定義されています。

 12 struct sem_array {
 13         struct kern_ipc_perm    ____cacheline_aligned_in_smp
 14                                 sem_perm;       /* permissions .. see ipc.h */
 15         time_t                  sem_ctime;      /* last change time */
 16         struct sem              *sem_base;      /* ptr to first semaphore in array */
 17         struct list_head        pending_alter;  /* pending operations */
 18                                                 /* that alter the array */
 19         struct list_head        pending_const;  /* pending complex operations */
 20                                                 /* that do not alter semvals */
 21         struct list_head        list_id;        /* undo requests on this array */
 22         int                     sem_nsems;      /* no. of semaphores in array */
 23         int                     complex_count;  /* pending complex operations */
 24 };
 

ここは見た通りの処理ですね。

519         for (i = 0; i < nsems; i++) {
520                 INIT_LIST_HEAD(&sma->sem_base[i].pending_alter);
521                 INIT_LIST_HEAD(&sma->sem_base[i].pending_const);
522                 spin_lock_init(&sma->sem_base[i].lock);
523         }

ここから最後のreturn文までも特にこれといったことはしていないですね。戻り値はsma->sem_perm.idですが、この値は最初にsmaをmemsetして以降は誰も触っていないはずなので0が返るはずです。

525         sma->complex_count = 0;
526         INIT_LIST_HEAD(&sma->pending_alter);
527         INIT_LIST_HEAD(&sma->pending_const);
528         INIT_LIST_HEAD(&sma->list_id);
529         sma->sem_nsems = nsems;
530         sma->sem_ctime = get_seconds();
531         sem_unlock(sma, -1);
532         rcu_read_unlock();
533 
534         return sma->sem_perm.id;

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)