System V 共享内存(二)

tech2022-12-06  101

上一篇

参考文章: tmpfs-基于内存的文件系统 浅析Linux的共享内存与tmpfs文件系统

文章目录

一、System V 共享内存1. shmget 源码分析2. shmat 源码分析 二、System V 信号量1. semget 源码分析2. semctl 源码分析2.1 SETALL --> semctl_main2.2 SETVAL --> semctl_setval 3. semop 源码分析

一、System V 共享内存

1. shmget 源码分析

// linux-4.13.16\ipc\shm.c SYSCALL_DEFINE3(shmget, key_t, key, size_t, size, int, shmflg) { struct ipc_namespace *ns = current->nsproxy->ipc_ns;; static const struct ipc_ops shm_ops = { .getnew = newseg, // 2. 该函数创建新的共享内存结构 ... ... }; struct ipc_params shm_params; shm_params.key = key; ... ... return ipcget(ns, ...); // 1. 查无此shm, 则调用 newseg } static int newseg(struct ipc_namespace *ns, struct ipc_params *params) { struct shmid_kernel *shp; // 描述共享内存的结构体 ... ... shp = kvmalloc(sizeof(*shp), GFP_KERNEL); // 1. 在内核空间-直接映射区分配 struct shmid_kernel // shmid_kernel 结构保存有 key 和权限等信息 file = shmem_kernel_file_setup(name, size, acctflag); // 2. 在 shmem 文件系统创建一个文件 // shmem 文件系统即 struct file_system_type shmem_fs_type ... ... error = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni); // 3. 此处将 kvmalloc 分配的 shmid_kernel 挂到 ipc_ids 的基数树上,返回对应id list_add(&shp->shm_clist, &current->sysvshm.shm_clist); // 4. 将 shmid_kernel 挂到当前进程的 sysvshm队列上 ... ... return error; }

2. shmat 源码分析

SYSCALL_DEFINE3(shmat, int, shmid, char __user *, shmaddr, int, shmflg) { // linux-4.13.16\ipc\shm.c ... ... err = do_shmat(shmid, shmaddr, shmflg, &ret, SHMLBA); } long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr, unsigned long shmlba) { shp = shm_obtain_object_check(ns, shmid); // 1. 依据 shmid 获取 ipc_ids 基数树上对应的 shm 数据结构 shmid_kernel ... ... sfd = kzalloc(sizeof(*sfd), GFP_KERNEL); // 2. 新建shm_file_data 结构体 file = alloc_file(&path, f_mode, is_file_hugepages(shp->shm_file) ? &shm_file_operations_huge : &shm_file_operations); // 3. 新建当前进程 struct file addr = do_mmap_pgoff(file, addr, size, prot, flags, 0, &populate, NULL); // 4. 分配虚拟内存对应的 vm_area_struct,最后调用shm_file_operations.mmap建立 file 与虚拟内存的映射关系 // 5. 当触发缺页中断时最终会调用 shmem 文件系统的 shmem_vm_ops.fault找到空闲物理页面 }

二、System V 信号量

1. semget 源码分析

// linux-4.13.16\ipc\sem.c SYSCALL_DEFINE3(semget, key_t, key, int, nsems, int, semflg) { struct ipc_namespace *ns; static const struct ipc_ops sem_ops = { .getnew = newary, }; ... ... return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params); } static int newary(struct ipc_namespace *ns, struct ipc_params *params) { ... ... sma = sem_alloc(nsems); // 1. 使用 kvmalloc 在直接映射区分配 sem_array ... ... for (i = 0; i < nsems; i++) { // 2. 初始化 pending_alter 等链表 INIT_LIST_HEAD(&sma->sems[i].pending_alter); INIT_LIST_HEAD(&sma->sems[i].pending_const); spin_lock_init(&sma->sems[i].lock); } ... ... retval = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni); // 3. 将新创建的 sem_array 挂到 sem_ids的基数树上,并返回semid ... ... }

2. semctl 源码分析

// linux-4.13.16\ipc\sem.c SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg) { int version; struct ipc_namespace *ns; void __user *p = (void __user *)arg; ... ... switch (cmd) { ... ... case SETALL: return semctl_main(ns, semid, semnum, cmd, p); case SETVAL: return semctl_setval(ns, semid, semnum, arg); ... ... }

2.1 SETALL --> semctl_main

union semun { // 专用于 SETALL的union,是semctl的最后一个参数 int val; struct semid_ds *buf; unsigned short int *array; struct seminfo *__buf; }; static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, int cmd, void __user *p) { struct sem_array *sma; sma = sem_obtain_object_check(ns, semid); // 1. 获取信号量对应的 sem_array ... ... case SETALL: { if (copy_from_user(sem_io, p, nsems*sizeof(ushort))) { // 2. 参数数据拷贝到内核空间 ipc_rcu_putref(&sma->sem_perm, sem_rcu_free); err = -EFAULT; goto out_free; } for (i = 0; i < nsems; i++) { // 3. 更改对应的 sem 值 sma->sems[i].semval = sem_io[i]; sma->sems[i].sempid = task_tgid_vnr(current); } } ... ...

2.2 SETVAL --> semctl_setval

static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum, unsigned long arg) { struct sem_array *sma; struct sem *curr; int err, val; val = arg; sma = sem_obtain_object_check(ns, semid); // 1. 获取 sem_array curr = &sma->sems[semnum]; // 2. get semaphore curr->semval = val; // 3. set value curr->sempid = task_tgid_vnr(current); ... ... }

3. semop 源码分析

struct sembuf { unsigned short sem_num; /* semaphore index in array */ short sem_op; /* semaphore operation */ short sem_flg; /* operation flags */ }; // linux-4.13.16\ipc\sem.c SYSCALL_DEFINE3(semop, int, semid, struct sembuf __user *, tsops, unsigned, nsops) { return sys_semtimedop(semid, tsops, nsops, NULL); } SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, unsigned, nsops, const struct timespec __user *, timeout) { struct sem_array *sma; struct sembuf fast_sops[SEMOPM_FAST]; struct sembuf *sops = fast_sops, *sop; struct sem_queue queue; if (copy_from_user(sops, tsops, nsops * sizeof(*tsops))) { // 1. copy sembuf 到内核空间 ... ... if (timeout) { // 2. 对于 P操作是否要设置一个超时时间 ... ... sma = sem_obtain_object_check(ns, semid); // 3. get sem_array struct by semid queue.sops = sops; // 4. queue is sem_queue struct, 表示当前信号量操作 queue.nsops = nsops; ... ... error = perform_atomic_semop(sma, &queue); // 5. 实施信号量操作,返回值:1表示需要阻塞等待,0表示成功 /// 获取到了信号量 // if (error == 0) { /* non-blocking succesfull path */ DEFINE_WAKE_Q(wake_q); // 1. 声明一个 wake_q do_smart_update(sma, sops, nsops, 1, &wake_q); // 2. 看这次对于信号量的值的改变,可以影响并可以激活等待队列中的哪些 struct sem_queue,然后把它们都放在 wake_q 里面 wake_up_q(&wake_q); // 3. 调用 wake_up_q 唤醒这些进程 ... ... goto out_free; } /// 未获取到信号量 // /* * We need to sleep on this operation, so we put the current * task into the pending queue and go to sleep. */ if (nsops == 1) { // 对一个信号量进行操作 struct sem *curr; curr = &sma->sems[sops->sem_num]; if (alter) { if (sma->complex_count) { list_add_tail(&queue.list, &sma->pending_alter); ... ... } else { // 对于整个信号量集合的 ... ... }
最新回复(0)