【Linux内核】以共享内存的方式实现进程间通信

现在有很多进程间通信的模式,我们选择一个简单的IPC机制(共享内存)来实现,并让它工作起来。

简单来讲我们实现了两个系统调用(不可避免地需要我们完善IDT),发送方查看接受方是否接收,并选择是否发送消息。本质是修改目标进程的页表,实现同一块物理内存的映射。

这两个函数命为sys_ipc_recv()和sys_ipc_try_send(),顾名思义,一个负责发送,一个负责接收。

发送函数sys_ipc_try_send

static int
sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm)  // 接收消息方进程为envid
{
	// LAB 4: Your code here.
	struct Env *env;
	if (envid2env(envid, &env, 0) < 0) return -E_BAD_ENV;					 // 指定进程要存在,并将其赋值给env
	if (!env->env_ipc_recving) return -E_IPC_NOT_RECV;						 // 对方是否要接收

	if ((size_t) srcva < UTOP) {
		if (((size_t) srcva % PGSIZE) != 0) {
			return -E_INVAL;
		}

		if ((perm & PTE_U) != PTE_U || (perm & PTE_P) != PTE_P) return -E_INVAL;  // 权限检查

		pte_t *pte;
		struct PageInfo *pp = page_lookup(curenv->env_pgdir, srcva, &pte);
		if (!pp) return -E_INVAL;

		if ((perm & PTE_W) && ((size_t) *pte & PTE_W) != PTE_W) return -E_INVAL;  // 权限检查
		if ((size_t) env->env_ipc_dstva < UTOP) {								  // 接收消息方虚拟地址要合理
			if (page_insert(env->env_pgdir, pp, env->env_ipc_dstva, perm) < 0) return -E_NO_MEM;  // 补充目标进程的页表
			env->env_ipc_perm = perm;
		}
	} else {
		env->env_ipc_perm = 0;
	}

	env->env_ipc_from = curenv->env_id;
	env->env_ipc_recving = 0;		// 不再等待
	env->env_ipc_value = value;		// 消息值是多少
	env->env_status = ENV_RUNNABLE; // 就绪态
	env->env_tf.tf_regs.reg_eax = 0;
	return 0;
}

接收函数sys_ipc_recv

static int
sys_ipc_recv(void *dstva)
{
	// LAB 4: Your code here.
	// 要映射的目的虚拟地址必须小于UTOP
	if ((size_t) dstva < UTOP && ((size_t) dstva % PGSIZE) != 0) return -E_INVAL;
	curenv->env_ipc_recving = 1;			// 我正在等待接收消息
	curenv->env_ipc_dstva = dstva;			// 希望将页面映射到这个虚拟地址中
	curenv->env_status = ENV_NOT_RUNNABLE;  // 因为我在等待,所以我阻塞自己,这样做不浪费cpu资源
	sys_yield();							// 主动阻塞自己,操作系统调度其他进程
											// 内核函数当然可以直接调用内核函数
	return 0;
}

除此之外需要包装两个函数

int32_t
ipc_recv(envid_t *from_env_store, void *pg, int *perm_store)
{
	// LAB 4: Your code here.
	if (pg == NULL) {
		pg = (void *)-1;
	}
	int r = sys_ipc_recv(pg);
	if (r < 0) {				//系统调用失败
		if (from_env_store) *from_env_store = 0;
		if (perm_store) *perm_store = 0;
		return r;
	}
	if (from_env_store)
		*from_env_store = thisenv->env_ipc_from;
	if (perm_store)
		*perm_store = thisenv->env_ipc_perm;
	return thisenv->env_ipc_value;
}

void
ipc_send(envid_t to_env, uint32_t val, void *pg, int perm)
{
	// LAB 4: Your code here.
	if (pg == NULL) {
		pg = (void *)-1;
	}
	int r;
	while(1) {
		r = sys_ipc_try_send(to_env, val, pg, perm);
		if (r == 0) {		//发送成功
			return;
		} else if (r == -E_IPC_NOT_RECV) {	//接收进程没有准备好
			sys_yield();
		} else {			//其它错误
			panic("ipc_send():%e", r);
		}
	}
}

本质其实就是实现地址映射,在网上看到一个很好的图,记录一下
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值