首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >linux进程通信之共享内存原理(基于linux 1.2.13)

linux进程通信之共享内存原理(基于linux 1.2.13)

作者头像
theanarkh
发布2019-11-13 14:02:43
1.9K0
发布2019-11-13 14:02:43
举报
文章被收录于专栏:原创分享原创分享

1 有一个全局的结构体数据,每次需要一块共享的内存时(shmget),从里面取一个结构体,记录相关的信息。

struct shmid_ds {
        // 权限相关
	struct ipc_perm shm_perm;	/* operation perms */
	// 共享内存的大小
	int	shm_segsz;		/* size of segment (bytes) */
	time_t	shm_atime;		/* last attach time */
	time_t	shm_dtime;		/* last detach time */
	time_t	shm_ctime;		/* last change time */
	// 创建该结构体的进程
	unsigned short	shm_cpid;	/* pid of creator */
	unsigned short	shm_lpid;	/* pid of last operator */
	// 当前使用该共享内存的进程数
	short	shm_nattch;		/* no. of current attaches */
	/* the following are private */
	// 共享内存的页数
	unsigned short   shm_npages;	/* size of segment (pages) */
	// 指向共享的物理内存的指针
	unsigned long   *shm_pages;	/* array of ptrs to frames -> SHMMAX */
	// 使用该共享内存的进程信息
	struct vm_area_struct *attaches; /* descriptors for attaches */
}

2 调用shmat的时候传入shmget返回的id。shmat根据id找到对应的shmid_ds 结构体。新建一个vm_area_struct结构体。开始地址和结束地址根据shmid_ds 中的信息计算,也就是用户申请的大小。接着把vm_area_struct插入进程中管理vm_area_struct的avl树。并且把一些上下文信息保存到页表项。缺页中断的时候在shm_swap_in里使用。

        shm_sgn = shmd->vm_pte + ((shmd->vm_offset >> PAGE_SHIFT) << SHM_IDX_SHIFT);
	for (tmp = shmd->vm_start; tmp < shmd->vm_end; tmp += PAGE_SIZE,
	     shm_sgn += (1 << SHM_IDX_SHIFT)) {
		page_dir = pgd_offset(shmd->vm_task,tmp);
		page_middle = pmd_alloc(page_dir,tmp);
		if (!page_middle)
			return -ENOMEM;
		page_table = pte_alloc(page_middle,tmp);
		if (!page_table)
			return -ENOMEM;
		pte_val(*page_table) = shm_sgn;
	}

3 进程访问共享内存范围中的地址时,触发缺页中断。

void do_no_page(struct vm_area_struct * vma, unsigned long address,
	int write_access)
{
	pte_t * page_table;
	pte_t entry;
	unsigned long page;
	// 在进程页表里获取address对应的页表项地址
	page_table = get_empty_pgtable(vma->vm_task,address);
	// 分配失败则返回
	if (!page_table)
		return;
	entry = *page_table;
	// 已经建立了虚拟地址到物理地址的映射,返回
	if (pte_present(entry))
		return;
	// 还没有建立映射
	if (!pte_none(entry)) {
		do_swap_page(vma, address, page_table, entry, write_access);
		return;
	}
        ......
}

在缺页中断中调用do_swap_page。

static inline void do_swap_page(struct vm_area_struct * vma, unsigned long address,
	pte_t * page_table, pte_t entry, int write_access)
{
	pte_t page;

	if (!vma->vm_ops || !vma->vm_ops->swapin) {
		swap_in(vma, page_table, pte_val(entry), write_access);
		return;
	}
	page = vma->vm_ops->swapin(vma, address - vma->vm_start + vma->vm_offset, pte_val(entry));
	if (pte_val(*page_table) != pte_val(entry)) {
		free_page(pte_page(page));
		return;
	}
	if (mem_map[MAP_NR(pte_page(page))] > 1 && !(vma->vm_flags & VM_SHARED))
		page = pte_wrprotect(page);
	++vma->vm_task->mm->rss;
	++vma->vm_task->mm->maj_flt;
        // 写入物理地址
	*page_table = page;
	return;
}

其中vma->vm_ops->swapin对应shm.c的shm_swap_in

        pte_val(pte) = shp->shm_pages[idx];
	// 还没有分配物理内存
	if (!pte_present(pte)) {
		// 分配物理内存
		unsigned long page = get_free_page(GFP_KERNEL);
                ...
                // 记录物理地址
                shp->shm_pages[idx] = pte_val(pte);
        }
        mem_map[MAP_NR(pte_page(pte))]++;
	return pte_modify(pte, shmd->vm_page_prot);

如果还没分配物理地址则分配,否则直接范围已经分配的地址。do_swap_page函数的最后一句会把物理地址写入进程的页表项。下次就不会缺页中断了。

同理,其他进程共享该块内存的时候,如果访问范围内的地址,处理过程是类似的。进程访问某一个地址,发生缺页中断,然后进入do_swap_page函数处理,再到shm_swap_in。发现这时候共享内存已经映射了物理地址。最后改写自己的页表项。因为各个进程都对应同一块内存,所以操作的时候会互相感知,实现通信。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-11-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程杂技 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档