shmctl()はセグメントの削除だけ見ておけば良いかなというところで。
shmctl()はipc()よりsys_shmctl()の呼び出しという形で呼ばれます。 そして、shmctl()の第二引数にあたるcmdの値を使ったswitchがあり、IPC_RMIDとIPC_SETの場合はshmctl_down()が呼ばれます。
963 case IPC_RMID: 964 case IPC_SET: 965 return shmctl_down(ns, shmid, cmd, buf, version);
ではshmctl_down()を見ていきます。
最初は変数定義とIPC_SETの場合の処理なので飛ばします。
775 static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd, 776 struct shmid_ds __user *buf, int version) 777 { 778 struct kern_ipc_perm *ipcp; 779 struct shmid64_ds shmid64; 780 struct shmid_kernel *shp; 781 int err; 782 783 if (cmd == IPC_SET) { 784 if (copy_shmid_from_user(&shmid64, buf, version)) 785 return -EFAULT; 786 }
次にロックを取ります。shm_ids()はマクロで、namespaceにあるSystemV IPCのidのうち共有メモリのIDを取得します。
788 down_write(&shm_ids(ns).rwsem); 789 rcu_read_lock();
ipcctl_pre_down_nolock()はカレントプロセスのipc namespaceからshmidに該当するものがあるかチェックし、存在した場合はeuid、capabilityによるパーミッションのチェックを行います。
91 ipcp = ipcctl_pre_down_nolock(ns, &shm_ids(ns), shmid, cmd, 792 &shmid64.shm_perm, 0); 793 if (IS_ERR(ipcp)) { 794 err = PTR_ERR(ipcp); 795 goto out_unlock1; 796 }
ipcpよりstruct shmid_kernelのデータを取得。
797 798 shp = container_of(ipcp, struct shmid_kernel, shm_perm);
ここは毎度ながらselinux関連なので無視。
800 err = security_shm_shmctl(shp, cmd); 801 if (err) 802 goto out_unlock1; 803
IPC_RMIDの場合はdo_shm_rmid()を呼びます。ここは後で見ましょう。
804 switch (cmd) { 805 case IPC_RMID: 806 ipc_lock_object(&shp->shm_perm); 807 /* do_shm_rmid unlocks the ipc object and rcu */ 808 do_shm_rmid(ns, ipcp); 809 goto out_up; 810 case IPC_SET: 811 ipc_lock_object(&shp->shm_perm); 812 err = ipc_update_perm(&shmid64.shm_perm, ipcp); 813 if (err) 814 goto out_unlock0; 815 shp->shm_ctim = get_seconds(); 816 break; 817 default: 818 err = -EINVAL; 819 goto out_unlock1; 820 }
後はロックを解除して終了です。
821 822 out_unlock0: 823 ipc_unlock_object(&shp->shm_perm); 824 out_unlock1: 825 rcu_read_unlock(); 826 out_up: 827 up_write(&shm_ids(ns).rwsem); 828 return err; 829 }
do_shm_rmid()ですが、もし他のプロセスなりがアタッチしている場合は削除できないので後で削除できるようにフラグを設定します。そうじゃなければ削除に行きます。この辺はman Man page of SHMCTLにあるとおりの挙動です。
89 static void do_shm_rmid(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) 90 { 91 struct shmid_kernel *shp; 92 shp = container_of(ipcp, struct shmid_kernel, shm_perm); 93 94 if (shp->shm_nattch) { 95 shp->shm_perm.mode |= SHM_DEST; 96 /* Do not find it any more */ 97 shp->shm_perm.key = IPC_PRIVATE; 98 shm_unlock(shp); 99 } else 100 shm_destroy(ns, shp); 101 }
ところで、shm_nattachが0じゃない場合はshm_unlock()を呼んでますがshmctl()の流れではこのロックは取っていません。じゃあどこでロックしたのかというとdo_shmat()の最後のほうで取っているこれじゃないかと思います。shmdt()ではロックの解除してませんし。
1198 down_write(&shm_ids(ns).rwsem); 1199 shp = shm_lock(ns, shmid); 1200 BUG_ON(IS_ERR(shp));
それはさておき、shm_destroy()を見ましょう。
最初にshpに設定されているfileオブジェクトがNULLに設定されます。
210 static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp) 211 { 212 struct file *shm_file; 213 214 shm_file = shp->shm_file; 215 shp->shm_file = NULL;
次に削除するセグメントで使っていたページ数をトータルのページ数から減らします。
216 ns->shm_tot -= (shp->shm_segsz + PAGE_SIZE - 1) >> PAGE_SHIFT;
shm_rmid()は後で見ます。
217 shm_rmid(ns, shp);
shpがホールドしてるロックを解除。
218 shm_unlock(shp);
使っていたページがhugetlbなのか普通のなのかで処理が変わりますが、やることはロックの解除です。
219 if (!is_file_hugepages(shm_file)) 220 shmem_lock(shm_file, 0, shp->mlock_user); 221 else if (shp->mlock_user) 222 user_shm_unlock(file_inode(shm_file)->i_size, shp->mlock_user);
共有メモリの擬似ファイルを削除して、最後にロックを解除して完了です。
223 fput(shm_file); 224 ipc_rcu_putref(shp, shm_rcu_free); 225 }
先ほど飛ばしたshm_rmid()はこのような関数です。 sをリストから消すのとipc_rmid()でidの削除をします。
179 static inline void shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *s) 180 { 181 list_del(&s->shm_clist); 182 ipc_rmid(&shm_ids(ns), &s->shm_perm); 183 } 184
ipc_rmid()はこんな関数です。
428 void ipc_rmid(struct ipc_ids *ids, struct kern_ipc_perm *ipcp) 429 { 430 int lid = ipcid_to_idx(ipcp->id); 431 432 idr_remove(&ids->ipcs_idr, lid); 433 ids->in_use--; 434 ipcp->deleted = true; 435 }
新装改訂版 Linuxのブートプロセスをみる (アスキー書籍)
- 作者: 白崎博生
- 出版社/メーカー: KADOKAWA / アスキー・メディアワークス
- 発売日: 2014/10/02
- メディア: Kindle版
- この商品を含むブログ (1件) を見る