今日はipc/shm.cにあるshmget()の実装部分であるnewseg()を読んでみる。昨日の「 Linuxカーネル:SystemV IPC get系操作の共通実装部分を読む - φ(・・*)ゞ ウーン カーネルとか弄ったりのメモ」でも書いた通りSystemV IPCのxxxget()はipcget()という共通関数を使い、固有の部分はstruct ipc_opsのメンバ変数の関数ポインタに関数を登録する形で行っています。
まずは最初はパラメーターのチェックなので特にこれと言った処理は無いですね。
482 static int newseg(struct ipc_namespace *ns, struct ipc_params *params) 483 { 484 key_t key = params->key; 485 int shmflg = params->flg; 486 size_t size = params->u.size; 487 int error; 488 struct shmid_kernel *shp; 489 size_t numpages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; 490 struct file *file; 491 char name[13]; 492 int id; 493 vm_flags_t acctflag = 0; 494 495 if (size < SHMMIN || size > ns->shm_ctlmax) 496 return -EINVAL; 497 498 if (numpages << PAGE_SHIFT < size) 499 return -ENOSPC; 500 501 if (ns->shm_tot + numpages < ns->shm_tot || 502 ns->shm_tot + numpages > ns->shm_ctlall) 503 return -ENOSPC; 504
次にstruct shmid_kernel型の変数のメモリ確保。ぱっと見た感じだとstruct shmid_kernelのサイズ分を確保しているように見えますが、実際にはsizeof(struct ipc_rcu)+sizeof(*shp)のサイズで確保されます。
505 shp = ipc_rcu_alloc(sizeof(*shp)); 506 if (!shp) 507 return -ENOMEM; 508
これはどういう事かというと、確保したのは下図のような形になり、呼び出し元がrcu関連のことを気にしないようになってます。
|--------------| <--- allocした領域の先頭アドレス |struct | | ipc_rcu | | | |--------------| <--- ipc_rcu_alloc()が返すのはここのアドレス |struct | | shmid_kernel | | | | | | | | | |--------------|
shmid_kernelはこのような構造体です。これがSystemV IPCの共有メモリを管理する構造体です。
9 struct shmid_kernel /* private to the kernel */ 10 { 11 struct kern_ipc_perm shm_perm; 12 struct file *shm_file; 13 unsigned long shm_nattch; 14 unsigned long shm_segsz; 15 time_t shm_atim; 16 time_t shm_dtim; 17 time_t shm_ctim; 18 pid_t shm_cprid; 19 pid_t shm_lprid; 20 struct user_struct *mlock_user; 21 22 /* The task created the shm object. NULL if the task is dead. */ 23 struct task_struct *shm_creator; 24 struct list_head shm_clist; /* list by creator */ 25 };
shpに値を設定。
509 shp->shm_perm.key = key; 510 shp->shm_perm.mode = (shmflg & S_IRWXUGO); 511 shp->mlock_user = NULL; 512 513 shp->shm_perm.security = NULL;
security_shm_alloc()はselinux等のセキュリティ機能を使って無ければ関係ないので飛ばします。
514 error = security_shm_alloc(shp); 515 if (error) { 516 ipc_rcu_putref(shp, ipc_rcu_free); 517 return error; 518 }
名前の設定。
520 sprintf(name, "SYSV%08x", key);
この名前はlsofで見た時に↓のように見えます。IPC_PRIVATEを指定しているので数値は全部0ですね。
41997 a.out 29678 root DEL REG 0,4 27394068 /SYSV00000000
次はフラグにSHM_HUGETLBが指定されたかどうかでの場合分けで、最終的にはhugetlb_file_setup()を呼ぶかshmem_file_setup()を呼ぶかの違いです。shmem_file_setup()を後で読んでみます。
521 if (shmflg & SHM_HUGETLB) { 522 struct hstate *hs; 523 size_t hugesize; 524 525 hs = hstate_sizelog((shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK); 526 if (!hs) { 527 error = -EINVAL; 528 goto no_file; 529 } 530 hugesize = ALIGN(size, huge_page_size(hs)); 531 532 /* hugetlb_file_setup applies strict accounting */ 533 if (shmflg & SHM_NORESERVE) 534 acctflag = VM_NORESERVE; 535 file = hugetlb_file_setup(name, hugesize, acctflag, 536 &shp->mlock_user, HUGETLB_SHMFS_INODE, 537 (shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK); 538 } else { 539 /* 540 * Do not allow no accounting for OVERCOMMIT_NEVER, even 541 * if it's asked for. 542 */ 543 if ((shmflg & SHM_NORESERVE) && 544 sysctl_overcommit_memory != OVERCOMMIT_NEVER) 545 acctflag = VM_NORESERVE; 546 file = shmem_file_setup(name, size, acctflag); 547 }
ここからはまたshpのメンバ変数に対する値の設定です。
558 shp->shm_cprid = task_tgid_vnr(current); 559 shp->shm_lprid = 0; 560 shp->shm_atim = shp->shm_dtim = 0; 561 shp->shm_ctim = get_seconds(); 562 shp->shm_segsz = size; 563 shp->shm_nattch = 0; 564 shp->shm_file = file; 565 shp->shm_creator = current;
shpはstruct task_structのメンバ変数にあるsysvshm構造体のshm_clistにリストでつなぎます。これでカレントタスクにある共有メモリはshm_clistから辿れるということですね。
566 list_add(&shp->shm_clist, ¤t->sysvshm.shm_clist); 567
上のほうで作ったfile構造体のf_inodeに値を設定。
568 /* 569 * shmid gets reported as "inode#" in /proc/pid/maps. 570 * proc-ps tools use this. Changing this will break them. 571 */ 572 file_inode(file)->i_ino = shp->shm_perm.id; 573
ipc_namespacesにあるshm_totにはコメントが無いので何の略なのかさっぱりわからないのですが、include/uapi/linux/shm.hにあるstruct shm_infoを見るとtotal allocated shm と書かれています。何はともあれ、このipc_namespacesで使用しているページ数が入ります。
574 ns->shm_tot += numpages;
返り値にshp->shm_perm.idを設定して不要になったオブジェクトとロックを解放してnewseg()は終了です。
575 error = shp->shm_perm.id; 576 577 ipc_unlock_object(&shp->shm_perm); 578 rcu_read_unlock(); 579 return error; 580
ここからはエラー発生時の終了処理なので省略。
581 no_id: 582 if (is_file_hugepages(file) && shp->mlock_user) 583 user_shm_unlock(size, shp->mlock_user); 584 fput(file); 585 no_file: 586 ipc_rcu_putref(shp, shm_rcu_free); 587 return error; 588 }
では先ほど飛ばしたshmem_file_setup()を見ます。hugetlbのほうは見ません。shmem_file_setup()を見ると言ってもこれは__shmem_file_setup()を呼ぶだけです。
そして、この関数は本当にファイルを作るだけなのでそんなに読まなくても良い気が。
やっているのは擬似ファイル用のdentryの取得、inodeの確保(これはshmem_get_inode()でやってます)、ファイルの実体(struct file)の作成と言ったところです。このファイルの操作は通常通りstruct file_operationsにて設定し、共有メモリ用の操作関数はshmem_file_operationsにまとまっています。
内容はこのような構造体です。
3084 static const struct file_operations shmem_file_operations = { 3085 .mmap = shmem_mmap, 3086 #ifdef CONFIG_TMPFS 3087 .llseek = shmem_file_llseek, 3088 .read = new_sync_read, 3089 .write = new_sync_write, 3090 .read_iter = shmem_file_read_iter, 3091 .write_iter = generic_file_write_iter, 3092 .fsync = noop_fsync, 3093 .splice_read = shmem_file_splice_read, 3094 .splice_write = iter_file_splice_write, 3095 .fallocate = shmem_fallocate, 3096 #endif 3097 };
Amazon Web Services 基礎からのネットワーク&サーバー構築
- 作者: 玉川憲、片山暁雄、今井雄太
- 出版社/メーカー: 日経BP社
- 発売日: 2014/07/16
- メディア: 単行本
- この商品を含むブログ (3件) を見る