System V 共享内存(二)

上一篇

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

一、System V 共享内存

dqEaGR.png

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 信号量

wCJ2ZR.png

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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值