44
SystemV IPC Masami Ichikawa (@masami256)

SystemV IPC

Embed Size (px)

DESCRIPTION

Linux SystemV IPC

Citation preview

Page 1: SystemV IPC

SystemV IPCMasami Ichikawa (@masami256)

Page 2: SystemV IPC

overview• libc側

• kernel側

• 主な構造体

• IPC全般の共通処理

• 共通処理(shmget、semget、msgget共通部)

• 共有メモリ

Page 3: SystemV IPC

overview• libc側

• kernel側

• 主な構造体

• IPC全般の共通処理

• 共通処理(shmget、semget、msgget共通部)

• 共有メモリ

Page 4: SystemV IPC

system calls

• msgctl, msgget, msgrcv, msgsnd

• semctl, semget, semop,

• shmctl, shmget, shmat, shmdt

Page 5: SystemV IPC

ipc(2) - libc

• SystemV IPCのsystem callの窓口

• socket関連にあるsocketcall(2)みたいなもの

Page 6: SystemV IPC

ipc(2)の呼び出し• sysdeps/unix/sysv/linux/shmat.c 41 resultvar = INTERNAL_SYSCALL (ipc, err, 5, IPCOP_shmat, 42 shmid, shmflg, 43 (long int) &raddr, 44 (void *) shmaddr);

• sysdeps/unix/sysv/linux/x86_64/sysdep.h 220 # define INTERNAL_SYSCALL_NCS(name, err, nr, args...) \ 221 ({ \ 222 unsigned long int resultvar; \ 223 LOAD_ARGS_##nr (args) \ 224 LOAD_REGS_##nr \ 225 asm volatile ( \ 226 "syscall\n\t" \ 227 : "=a" (resultvar) \ 228 : "0" (name) ASM_ARGS_##nr : "memory", "cc", "r11", "cx"); \ 229 (long int) resultvar; }) 230 # undef INTERNAL_SYSCALL 231 # define INTERNAL_SYSCALL(name, err, nr, args...) \ 232 INTERNAL_SYSCALL_NCS (__NR_##name, err, nr, ##args)

Page 7: SystemV IPC

ipc(2) - kernel

• ipc/syscall.c

• 対象のsystem callに応じた関数を呼んで結果を返す

Page 8: SystemV IPC

overview• libc側

• kernel側

• 主な構造体

• IPC全般の共通処理

• 共通処理(shmget、semget、msgget共通部)

• 共有メモリ

Page 9: SystemV IPC

kernel側実装• コードはipc/以下に

• 主に以下のファイルを参照

• ipc/syscall.c

• ipc/shm.c

• ipc/util.c

Page 10: SystemV IPC

主な構造体• IPC objectの管理

• セマフォ、共有メモリ、メッセージキュー固有の構造体

• パーミッションの管理

• IPC Identifier

• 1つのIPC Objecdtに対して一つ設定

• IPC namespace単位で管理

Page 11: SystemV IPC

共有メモリ管理オブジェクト• include/linux/shm.h 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 };

Page 12: SystemV IPC

セマフォ管理オブジェクト• 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 };

Page 13: SystemV IPC

message queue管理オブジェクト

• include/linux/msg.h 18 struct msg_queue { 19 struct kern_ipc_perm q_perm; 20 time_t q_stime; /* last msgsnd time */ 21 time_t q_rtime; /* last msgrcv time */ 22 time_t q_ctime; /* last change time */ 23 unsigned long q_cbytes; /* current number of bytes on queue */ 24 unsigned long q_qnum; /* number of messages in queue */ 25 unsigned long q_qbytes; /* max number of bytes on queue */ 26 pid_t q_lspid; /* pid of last msgsnd */ 27 pid_t q_lrpid; /* last receive pid */ 28 29 struct list_head q_messages; 30 struct list_head q_receivers; 31 struct list_head q_senders; 32 };

Page 14: SystemV IPC

IPCパーミッション管理• include/linux/ipc.h 11 struct kern_ipc_perm 12 { 13 spinlock_t lock; 14 bool deleted; 15 int id; 16 key_t key; 17 kuid_t uid; 18 kgid_t gid; 19 kuid_t cuid; 20 kgid_t cgid; 21 umode_t mode; 22 unsigned long seq; 23 void *security; 24 };

kuid_t・kgid_tはコンテナ型仮想化において、host側のuid/gitとguest側のuid/pidをマッピングするための型。 参照:http://www.slideshare.net/masamiichikawa/linux-namespace

Page 15: SystemV IPC

プロセスからIPCオブジェクトの参照

• セマフォ/共有メモリを作成すると、struct task_structのメンバ変数よりリストで繋がる

1385 #ifdef CONFIG_SYSVIPC1386 /* ipc stuff */1387 struct sysv_sem sysvsem;1388 struct sysv_shm sysvshm;1389 #endif

• メッセージキューは無し

Page 16: SystemV IPC

IPC Identifier• IPC Namespace単位で管理

• 管理する構造体はstruct ipc_ids

• struct ipc_namespaceのメンバ変数ids[3]にて管理 29 struct ipc_namespace { 30 atomic_t count; 31 struct ipc_ids ids[3];

• 初期化のタイミングはIPC namespace作成時のcreate_ipc_ns()

• ipc_addid()にてidを設定

Page 17: SystemV IPC

struct ipc_ids

• include/linux/ipc_namespace.h 21 struct ipc_ids { 22 int in_use; 23 unsigned short seq; 24 struct rw_semaphore rwsem; 25 struct idr ipcs_idr; 26 int next_id; 27 };

IPC Object作成時に+1 IPC_RMIDの操作時に-1

通常は-1を設定し、この番号を使いたい!という場合にsysctlで設定可能。 カーネルのconfigでCONFIG_CHECKPOINT_RESTOREが設定されている必要あり。 https://github.com/torvalds/linux/commit/03f595668017f1a1fb971c02fc37140bc6e7bb1c

idを振るときに使用

Page 18: SystemV IPC

struct ipc_idsの初期化

• 共有メモリの場合(セマフォ、メッセージも同様の流れ)

create_ipc_ns() --> shm_init_ns() --> ipc_init_ids() 

初期化対象のstruct ipc_idsを渡す

Page 19: SystemV IPC

ipc_init_ids()

• 引数で渡されたstruct ipc_idsの初期化

• 処理内容は各変数に初期値を設定する程度

Page 20: SystemV IPC

ipc_addid()• 関数プロトタイプ int ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int size)

• IPC Identifierはnew->idに設定される

• 戻り値のintはidr_alloc()の戻り値

• idr_alloc()の戻り値もidだけど、ipc_addid()の呼び出し元はこの変数はエラーチェックにしか使用しない

Page 21: SystemV IPC

ipc_addid()処理概要• IPC Identifierの使用数(in_use)のチェック

• 最大で32768

• IDR APIによるidの取得

• in_useをインクリメント

• kern_ipc_perm構造体にeuid、egidを設定

• next_idの値に応じてseq変数を設定

• ipc_buildid()でidになる値を計算して返す

Page 22: SystemV IPC

管理用オブジェクトのalloc• メモリ確保はすべてipc_rcu_alloc()にて実施 477 void *ipc_rcu_alloc(int size)478 {479 /*480 * We prepend the allocation with the rcu struct481 */482 struct ipc_rcu *out = ipc_alloc(sizeof(struct ipc_rcu) + size);483 if (unlikely(!out))484 return NULL;485 atomic_set(&out->refcount, 1);486 return out + 1;487 }

Page 23: SystemV IPC

ipc_rcu_allocが返すデータ構造• メモリ上の構造は先頭にipc_rcu構造体が置かれ、その後ろに本来のデータを置く形

struct ipc_rcustruct

shmid_kenel とか

[kv]malloc()の返したアドレス

ipc_rcu_alloc()が返すアドレス

Page 24: SystemV IPC

xxxget()共通処理

• shmget, semget, msggetはipcget()を共通の処理として使用

• 機能固有の処理は関数ポインタを渡し、それを実行してもらう

• ipc_addid()でIPC 識別子を設定

Page 25: SystemV IPC

機能固有処理の登録• ipc/util.hにあるstruct ipc_opsに関数をセット 80 struct ipc_ops { 81 int (*getnew)(struct ipc_namespace *, struct ipc_params *); 82 int (*associate)(struct kern_ipc_perm *, int); 83 int (*more_checks)(struct kern_ipc_perm *, struct ipc_params *); 84 };

• getnew()

• 新規にIPCのオブジェクトを作成

• associate()

• パーミッションのチェック

• more_checks()

• その他のチェック

• optional

Page 26: SystemV IPC

ipcget()• keyにIPC_PRIVATEが設定されているかで分岐

• ipcget_new()

• IPC_PRIVATE指定時

• ipcget_public()

• IP_PRIVATE以外のキーが指定された場合

Page 27: SystemV IPC

ipcget()の流れipcget() --> ipcget_new() // if key is IPC_PRIVATE --> ipcget_public() // if key is not IPC_PRIVATE --> ipc_findkey() // find key --> ipcget_new() // if key is not found --> ipc_check_perms() // if found key --> (struct ips_ops *)->more_checks() --> (struct ips_ops *)->associate() ipcget_new() --> (struct ipc_ops *)->getnew()

Page 28: SystemV IPC

ipcget_new()

• struct ipc_opsに設定されているgetnew()を呼ぶ程度

Page 29: SystemV IPC

ipcget_public()• keyがnamespace中にあるかチェック

• keyが見つからなかったらipcget_new()を呼ぶ

• keyが見つかった

• flagのチェック

• ipc_opsのmore_checksがセットされていればそれを呼ぶ

• ipc_check_perms()でパーミッションのチェックとipc_opsのassociate()呼び出し

• ipc_check_permsがシステムコール(xxxget())の戻り値となるidを返す

Page 30: SystemV IPC

overview• libc側

• kernel側

• 主な構造体

• IPC全般の共通処理

• 共通処理(shmget、semget、msgget共通部)

• 共有メモリ

Page 31: SystemV IPC

shmget• 共有メモリのセグメントを作成

• ipcget()からnewseg()が呼ばれる

• struct ips_opsのgetnewにnewseg()を設定

• IPCの管理オブジェクトはstruct shmid_kernel

• 作成したオブジェクトはcurrent->sysvshm.shm_clistにつながる

• セグメントは擬似ファイルとして作る

• SYSVxxxxxxxx(xはkey)というファイル名

• lsofすると以下のように表示される 41997 a.out 29678 root DEL REG 0,4 27394068 /SYSV00000000

• flagにSHM_HUGETLBによりファイルの作成方法が変わる

• hugetlb_file_setup() or shmem_file_setup()

Page 32: SystemV IPC

セグメント作成の流れ• shmem_file_setup()

• mm/shmem.cの__shmem_file_setup()が本体

__shmem_file_setup() --> mntget() // path to mount directory --> d_alloc_pseudo() // allocate a dentry --> shmem_get_inode() // get an inode --> alloc_file() // allocate a file object

Page 33: SystemV IPC

shmat()• 実際の処理はdo_shmat()

• 共有メモリ固有データ作成

• struct fileのprivate_dataへ設定する

• ファイルオブジェクト(struct file)の作成

• ファイルはshmget()で作成した擬似ファイルを使用

• ファイルをmmap()でメモリにマップ

• 使用するのはdo_mmap_pgoff()

• do_mmap_pgoff()の返り値がshmat(2)の返り値になる

Page 34: SystemV IPC

セグメントattachの流れ

do_shmat() --> path_get() // get a file path for shmem --> alloc_file() // allocate a file object --> do_mmap_pgoff() // map the file object --> shm_may_destroy() // check if other process destroying this shmem --> shm_destroy() // other process destorying this shmem --> shm_unlock() // no one destorys this shmem

Page 35: SystemV IPC

共有メモリ固有データ 50 struct shm_file_data { 51 int id; 52 struct ipc_namespace *ns; 53 struct file *file; 54 const struct vm_operations_struct *vm_ops; 55 };

• 以下のデータを設定

• idにはkey

• nsにはカレントプロセスのipc namespace

• ipc namespaceのリファレンスカウンタをインクリメントする

• fileにはnewseg()で作成したファイルオブジェクト

• vm_opsにはNULL

Page 36: SystemV IPC

shmdt()• do_shmat()でmmap(do_mmap_pgoff)した領域を解放

• アドレスに対して単純にdo_munmap()を呼べない

• アドレスがmlock()やmunmap()で変更されている可能性がある

Page 37: SystemV IPC

セグメントdetatchの流れ

shmdt() --> find_vma() // find a vm_area_struct from address --> file_inode() // if vma is found, find an inode to get file(segment) size --> do_munmap() // unmap the address --> do_munmap()

Page 38: SystemV IPC

shmdt()• 処理はCONFIG_MMUがdefineされているかで違うが、ここではdefineされている場合を見る

• do_unmap()は 複数回呼ばれる可能性がある

• find_vma()でvmaが見つかった場合

• unmap対象のアドレスを探し、unmap()する

• これは1回だけ

• vmaがNULLでなく、vma->vm_end - addrの結果がセグメントサイズより小さい間

• ここでは複数回do_unmap()を呼ぶ可能性がある

Page 39: SystemV IPC

shmctl()• セグメントに関して各種操作ができるが、破棄の場合を見ていきます

• 破棄

• swap禁止・禁止解除

• 情報取得

• etc…

Page 40: SystemV IPC

セグメント破棄の流れshmctl() --> shmctl_down() --> do_shm_rmid() --> shm_unlock() // someone using this segment --> shm_destroy() // anyone using it --> shm_rmid() // remove id from namespace and segment's list --> shm_unlock() --> shm_lock() // if shmflg is not SHM_HUGETLB --> user_shm_unlock() // else if (struct shmid *)->mlock_user isn't 0 --> fput() // remove file object

Page 41: SystemV IPC

shmctl_down()• shmctl(2)でcmdにIPC_RMIDもしくはIPC_SETを指定した時に呼ばれる

• ロックを取るなどの部分はIPC_RMID、IPC_SETで共通

• cmdがIPC_RMIDの場合はdo_shm_rmid()を呼ぶ

Page 42: SystemV IPC

do_shm_rmid()• まだ誰かがセグメントにattachしている場合

• struct shmid_kenrnelのshm_perm.modeにSHM_DESTをセット

• 削除予定フラグ

• keyをIPC_PRIVATEに設定し、他から参照できないようにする

• 他にattachしているプロセスはいない

• shm_destroy()を呼ぶ

Page 43: SystemV IPC

shm_destroy()• shm_rmid()を呼ぶ

• namespaceからidの削除

• shm_clistのリストから管理オブジェクトを削除

• セグメントがhugetlbを使っていない場合

• shmem_lock()でセグメントがswap禁止になっていたら許可するように設定

• もしくはmlockされている場合

• 共有メモリで使用するmlock対象のページ数をstruct userのlocked_shmから減らす

• fput()でnewseg()で作成したセグメント用のファイルオブジェクトを削除

Page 44: SystemV IPC

Reference

• LXR

• http://lxr.free-electrons.com/

• glibc git

• https://sourceware.org/git/?p=glibc.git