操纵系统实践7-实现共享内存系统调用

一、 目标

Linux在2.2版本后实现了共享内存的系统调用(shm.h),但是在Linux-0.11是没有共享内存的系统调用的。本次实践的目标如下:
1、实现2个系统调用

int create_shm(int key,unsigned long size,int flag);
void* get_shm(int shmid,int flag);

create_shm的功能如下:

  • 如果Key已经存在
    直接返回共享内存的系统标识shmid
  • 如果Key不存在
    打开一页物理内存作为共享内存,返回该页共享内存的shmid
  • flag参数可以忽略

get_shm的功能如下:

  • 将shmid指定的共享页面映射到当前进程的虚拟地址空间中,并返回一个逻辑地址p,调用进程可以读写逻辑地址p来读写这一页共享内存。
  • 两个进程都可以通过相同的shmid关联到同一页内存上
  • 如果shmid非法,返回-1,置error为EINVAL
  • flag可以忽略

在这里插入图片描述
注:添加系统调用的步骤
https://blog.csdn.net/Ecust_applied_math/article/details/102730987

二、伪代码分析

#define MAX_SHM 100
struct shm{
    int key;
    int shmid;
    void * page;
};
struct shm shm_table[MAX_SHM] = {0};
int next_shm_index = 0;

int sys_create_shm(int key,unsigned long size,int flag){
    #工作在内核态中,即这些代码是加载在内核段中的
	
	if(key == 0 || nex_shm_index == MAX_SHM)
		return -1;
	#key不能为0,或者没有地方再存储了
	
	for(i = 0;i < MAX_SHM;i++){
	    if(shm_table[i].key == key)
	        return shm_table[i].shmid;
	}
	#这里可以优化为二叉、或者红黑,库太少,懒得改了
	
	new_page_addr = get_free_page()
	#获取一个页,这个页对应一个物理页框
	
	shm_table[next_shm_index].page = new_page_addr;
	shm_table[next_shm_index].key = key;
	new_shmid = shm_table[next_shm_index].shmid = key * 3;
	next_shm_index++;#下个位置
	return new_shmid;
	#这里的shmid应该置为一个随机数的,这里为了方便,直接做了个变换
}

void * get_shm(int shmid){
    for(i = 0;i < MAX_SHM;i++){
		if(shm_table[i].shmid == shmid){
			shm_index = i;
		    break;
		}
	}
	#这里必须用顺序查找了
	
	if(i == MAX_SHM)
		return NULL;
	#没找到,返回一个空指针

	struct shm * pShm = &shm_table[i];
	#设置一个指针指向共享内存描述符

	#下面会用到put_page(linear_addr,addr)宏,
	#put_page是Linux-0.11的一个宏,把虚拟内存中linear_addr所在页的页框设置为addr所在页的页框

	user_linear_addr = get_base(current->ldt[1]) + current->brk;
	#get_base(current->ldt[1]) + current->brk;指向的是当前进程的段中的brk,Linux-0.11未使用这一块地址,随意用

	put_page(user_linear_addr,pShm->page);
	return user_linear_addr;
	#luser_linear_addr是用户段中的一个地址,pShm->page再内核段中
	#注:在Linux-0.11中,所有的进程共享一个虚拟内存,每个按次序占用64MB,所以这个程序蛮简单的。
}

三、 步骤

3.1 编写sharemem.c

在Kernel下创建sharemem.c

//linux-0.11/kernel/sharemem.c
#include <errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#define MAX_SHM 100
struct shm{
	int key;
	int shmid;
	void * page;
};
struct shm shm_table[MAX_SHM] = {{0},};
int next_shm_index = 0;

int sys_create_shm(int key,unsigned long size){
	int i = 0,new_shmid = 0;
	
	if(key == 0 || next_shm_index == MAX_SHM || size > 4096)
		errno = ENOMEM;
	
	for(i = 0;i < MAX_SHM;i++){
		if(shm_table[i].key == key)
			return shm_table[i].shmid;
	}
	
	void * new_page_addr = get_free_page();
	shm_table[next_shm_index].page = new_page_addr;
	shm_table[next_shm_index].key = key;
	new_shmid = key * 3 + 47;
	shm_table[next_shm_index].shmid = new_shmid;
	next_shm_index++;
	return new_shmid;
}

void * sys_get_shm(int key){
	int i = 0;
	for(i = 0;i < MAX_SHM;i++){
		if(shm_table[i].shmid == shmid)
			break;
	}
	if(i == MAX_SHM)
		return NULL;
	struct shm * pShm = &shm_table[i];
	extern struct task_struct * current;
	unsigned long user_linear_addr = (unsigned long)get_base(current->ldt[1]) + current->brk;
	put_page(pShm->page,user_linear_addr);
	return current->brk;
}

3.2 注册系统调用

  1. 在include/linux/sys.h中注册所添加的系统调用。添加两行,新增两项。
extern int sys_create_shm();
extern int sys_get_shm();

fn_ptr sys_call_table[] = { .....,sys_create_shm,sys_get_shm };
  1. 修改kernel/system_call.s中的系统调用数量
nr_system_calls = 74

3.3 编译

make clean
make

四、 测试结果

在Bochs中编写程序

//p.c
#define __LIBRARY__
#define __NR_create_shm 72
#define __NR_get_shm 73
#include <unistd.h>
#include <stdio.h>
_syscall2(int,create_shm,int,key,unsigned long,size)
_syscall1(void*,get_shm,int,shmid)
int main(void){
    int t = create_shm(3,1024);
    int * p = (int*)get_shm(t);
?  printf("shmid = %d\n",t);
    printf("Logical address is %p\n",p);
    p[0] = 4;
    while(p[0]);
    return 0;
}
//c.c
#define __LIBRARY__
#define __NR_create_shm 72
#define __NR_get_shm 73
#include <unistd.h>
#include <stdio.h>
_syscall2(int,create_shm,int,key,unsigned long,size)
_syscall1(void*,get_shm,int,shmid)
int main(void){
    int t = create_shm(3,1024);
    int * p = (int*)get_shm(t);
    printf("Shared  Memory = %d\n",p[0]);
    return 0;
}

在这里插入图片描述
可以看出,进程C顺利的读取到了进程A的地址空间的值。

五、 总结

  1. 共享内存的核心是使得进程A的内存空间中的一页和进程B的内存空间中的一页映射到同一个物理页框上。
  2. get_free_page()返回的是一个逻辑地址,不过是在内核段中。
  3. 项目地址
    https://github.com/ecustlmc/linux-0.11-lab/tree/share_mem
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值