Linux操作系统实验4——内存映射

实验要求:

1.在源码中查看file_operations和vm_operations_struct结构定义及其操作对象的方法,重点查看mmap方法fault方法的参选类型。

2.设备模块代码的编写和调试,重新编写file_operations结构中的mmap方法,和vm_operations_struct结构中的fault方法。

3.在用户态通过mmap方法完成对映射内存驱动设备的读和写操作访问。

实验原理:

描述在用户态对内存设备访问过程(read和write)中的函数执行过程及功能:

用户态open函数用于打开字符设备,并且调用内核模块中的mapdrv_open函数,完成打印进程号功能。

用户进程在调用mmap函数系统调用之后,系统为其在当前进程的虚拟地址空间中寻址一段连续的空闲地址,这是通过遍历vm_area_struct链表来实现。当找到了合适的这样的一段区间之后,会为其建立一个vm_area_struct结构,完成这些之后,该进程就有了一个专门用于mmap映射的虚拟内存区了。

但是这个区域的虚拟地址都没有对应的物理页框,接着系统会调用内核空间的系统调用函数mapdrv_mmap,也就是需要我们在file operations(f_op)结构体中定义的这个mmap,它将要完成对vm_area_struct结构中的虚拟地址建立其相应的页表项,传入file指针和vm_area_struct结构体指针这两个参数。mapdrv_mmapfile_operations结构体map_vm_ops的地址赋值给vmavm_ops指针。建立页表项具体实现需要调用内核中定义的map_fault函数,它在进程访问到这个映射空间中的虚拟地址时,发现虚拟地址的页表项为空,引起了缺页时才被调用,建立对应页表项

参考代码:

 map_driver.h:

#include <asm/atomic.h>
#include <linux/semaphore.h>
#include <linux/cdev.h>

struct mapdrvo
{
	struct cdev mapdev;
	atomic_t usage;
};

map_driver.c:

#include <linux/kernel.h>  	
#include <linux/module.h>  
#include <linux/fs.h>  
#include <linux/string.h>  
#include <linux/errno.h>  
#include <linux/mm.h>  
#include <linux/vmalloc.h>  
#include <linux/slab.h>  
#include <linux/sched.h>  
#include <asm/io.h>  
#include <linux/mman.h>  

#define MAP_PAGE_COUNT 10  
#define MAPLEN (PAGE_SIZE*MAP_PAGE_COUNT)  
#define MAP_DEV_MAJOR 240
#define MAP_DEV_NAME "mapnopage"

extern struct mm_struct init_mm;  
void map_vopen(struct vm_area_struct *vma);
void map_vclose(struct vm_area_struct *vma);  
/*device mmap */  
static int mapdrv_mmap(struct file *file, struct vm_area_struct *vma);  
static int mapdrv_open(struct inode *inode, struct file *file); 
/* vm area nopage */  
int map_fault(struct vm_fault *vmf);  
  
static struct file_operations mapdrvo_fops = {  
    .owner = THIS_MODULE,  
    .mmap = mapdrv_mmap,  
    .open = mapdrv_open,
}; 

static struct vm_operations_struct map_vm_ops = {
    .open = map_vopen,
    .close = map_vclose,
    .fault = map_fault,
};
   
 
static char *vmalloc_area = NULL;  

MODULE_LICENSE("GPL");  
  
static int __init mapdrv_init(void)  
{  
   int result;
   unsigned long virt_addr;
   int i = 1;
   result=register_chrdev(MAP_DEV_MAJOR,MAP_DEV_NAME,&mapdrvo_fops);
   if(result<0){
	   return result;
   }
   vmalloc_area=vmalloc(MAPLEN);
   virt_addr = (unsigned long)vmalloc_area;
   for(virt_addr = (unsigned long)vmalloc_area; virt_addr < (unsigned long)vmalloc_area + MAPLEN; virt_addr += PAGE_SIZE)
   {
	   SetPageReserved(vmalloc_to_page((void *)virt_addr));   
           sprintf((char *)virt_addr, "test %d",i++);             
   }
   /* printk("vmalloc_area at 0x%lx (phys 0x%lx)\n",(unsigned long)vmalloc_area,(unsigned long)vmalloc_to_pfn((void *)vmalloc_area) << PAGE_SHIFT);  */
   printk("vmalloc area apply complate!");
    return 0;
}  
  
static void __exit mapdrv_exit(void)  
{  
    unsigned long virt_addr;  
    /* unreserve all pages */  
    for(virt_addr = (unsigned long)vmalloc_area; virt_addr < (    unsigned long)vmalloc_area + MAPLEN; virt_addr += PAGE_SIZE) 
    {  
        ClearPageReserved(vmalloc_to_page((void *)virt_addr));  
    }  
    /* and free the two areas */  
    if (vmalloc_area)
        vfree(vmalloc_area);  
    unregister_chrdev(MAP_DEV_MAJOR,MAP_DEV_NAME);
  
}  
  

  
static int mapdrv_mmap(struct file *file, struct vm_area_struct *vma)  
{  
    unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;  
    unsigned long size = vma->vm_end - vma->vm_start;  
   
    if (size > MAPLEN) {  
        printk("size too big\n");  
        return -ENXIO;  
    }  
    /*  only support shared mappings. */  
    if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED)) {  
        printk("writeable mappings must be shared, rejecting\n");  
        return -EINVAL;  
    }  
    /* do not want to have this area swapped out, lock it */  
    vma->vm_flags |= VM_LOCKONFAULT;  
    if (offset == 0) {  
        vma->vm_ops = &map_vm_ops;   
    } else {  
        printk("offset out of range\n");  
        return -ENXIO;  
    }  
    return 0;  
}
static int mapdrv_open(struct inode *inoe, struct file *file)
{

    printk("process: %s (%d)\n", current->comm, current->pid);
    return 0;
}  
  
/* open handler for vm area */  
void map_vopen(struct vm_area_struct *vma)  
{  
    printk("mapping vma is opened..\n");
}  
  
/* close handler form vm area */  
void map_vclose(struct vm_area_struct *vma)  
{  
    printk("mapping vma is closed..\n");
}  
  
/* page fault handler */ 

int map_fault(struct vm_fault *vmf)  
{  
	struct page *page;
	void *page_ptr;
        unsigned long offset, virt_start, pfn_start;	
        offset = vmf->address-vmf->vma->vm_start;
        virt_start = (unsigned long)vmalloc_area + (unsigned long)(vmf->pgoff << PAGE_SHIFT);
        pfn_start = (unsigned long)vmalloc_to_pfn((void *)virt_start);

	printk("\n");    
	/*printk("%-25s %d\n","7)PAGE_SHIFT",PAGE_SHIFT);*/
	page_ptr=NULL;
	if((vmf->vma==NULL)||(vmalloc_area==NULL)){
		printk("return VM_FAULT_SIGBUS!\n");
		return VM_FAULT_SIGBUS;
	}
	if(offset >=MAPLEN){
		printk("return VM_FAULT_SIGBUS!");
		return VM_FAULT_SIGBUS;
	}
	page_ptr=vmalloc_area + offset;
	page=vmalloc_to_page(page_ptr);
	get_page(page);	
	vmf->page=page; 
        printk("%s: map 0x%lx (0x%016lx) to 0x%lx , size: 0x%lx, page:%ld \n", __func__, virt_start, pfn_start << PAGE_SHIFT, vmf->address,PAGE_SIZE,vmf->pgoff);

	return 0;
}

module_init(mapdrv_init);  
module_exit(mapdrv_exit);  

map_driver.c对应Makefile文件:

ifneq ($(KERNELRELEASE),)
	obj-m += map_driver.o
else
	PWD := $(shell pwd)
	KERNELDIR ?= /lib/modules/$(shell uname -r)/build
default:
	$(MAKE) -C $(KERNELDIR)  M=$(PWD) modules
clean:
	@rm -rf *.o *.mod.c *.mod.o *.ko *.order *.symvers .*.cmd .tmp_versions
endif

 maptest_read.c:(读函数)

#include <stdio.h>  
#include <unistd.h>  
#include <sys/mman.h>  
#include <sys/types.h>  
#include <fcntl.h>  
#include <stdlib.h>  
#define LEN (10*4096)  
int main(void)  
{  
    int fd,loop;  
    char *vadr;  
  
    if ((fd = open("/dev/mapnopage", O_RDWR)) < 0) {  
        return 0;  
    }  
    vadr = mmap(0, LEN, PROT_READ, MAP_PRIVATE | MAP_LOCKED, fd, 0);       
    for(loop=0;loop<10;loop++){
        printf("[%-10s----%lx]\n",vadr+4096*loop,vadr+4096*loop);
    }
    while(1)
    {
        sleep(1);
    }
     
}  

maptest_write.c:(写函数)

#include <stdio.h>  
#include <unistd.h>  
#include <sys/mman.h>  
#include <sys/types.h>  
#include <fcntl.h>  
#include <stdlib.h>  
#define LEN (10*4096)  
int main(void) 
{  
    int fd;
    char *vadr;  
  
    if ((fd = open("/dev/mapnopage", O_RDWR)) < 0) {  
        return 0;  
    }  
    vadr = mmap(0, LEN, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, fd, 0);  
    
    sprintf(vadr, "write from userspace");
    
    while(1)
    {
       sleep(1);
    }     
    return 0;
}  

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
一、 实验目的 1、通过实验了解和熟悉Linux网络服务; 2、掌握Linux下建立Web服务器; 3、掌握Linux下建立FTP服务器。 二、 开发工具和运行环境 1、虚拟机VMware 2、Linux操作系统 三、 实验内容 1、了解和熟悉Linux网络服务 了解NFS网络文件系统,NFS网络文件系统架设及挂载方法,Web服务器的架设方法,FTP服务器的架设方法及访问方法,Samba服务器的架设方法。 2、Linux下建立Web服务器 (1)Apache 服务器的安装及启动 ●检测与安装 Apache rpm -qa | grep httpd ●如果没有检测到软件包,需要进行安装,在安装目录中,执行如下命令: rpm -ivh httpd-tools*. Rpm rpm -ivh mailcap-2.1.31-2.el6.noarch.rpm rpm - ivh httpd-2*. rpm rpm -ivh httpd-manual-2.*.rpm ●重新启动/停止/启动Apache服务: systemctl restart/stop/start httpd.service 或service httpd restart/stop/start ●查看 Apache服务器的运行状态: systemctl status httpd. service 或service httpd status ●测试 Apache服务器运行状态: 在图形界面打开火狐浏览器输入http://127. 0.0.1呈现测试页(CentOS 用户可以在终端使用lynx 浏览器打开,如没有安装,则使用rpm在光盘中安装lynx 浏览器) 在/var/www/html目录下创建test. html页面,使用vi编辑保存内容,重启apache服务 在火狐浏览器输入http://127. 0. 0.1/test. html查看test. html内容 (2)Apache 服务器的配置 Apache主要配置文件为httpd.conf,存储位置在/etc/httpd/conf目录下 (3)个人Web站点配置 RedHat方式: a:修改配置文件,使用vi编辑器修改主配置文件 /etc/httpd/conf/httpd. conf,修改如下配置,去掉原文中的注释符号 UserDir disable root//基于安全 考虑,禁止root用户使用自己的站点.UserDir public. _html//设 置对每个用户的Web站点目录 Allow0verride FileInfo AuthConfig Limit Opt ions MultiViews Indexes SymLinkIfOwnerMatch Inc ludeNoExec Order allow, deny Allow from all Order deny, allow Deny from all 保存文件退出,重启httpd服务 b:用户创建个人Web站点 从root用户身份切换成user1用户(如果没有普通用户,需要先创建普通用户): 在user1主目录下面创建个人站点目录$mkdir public_ html 到/home目录下修改user1目录权限 $chmod 711 user1 进入到public. _html 目录下使用vi创建index. html文件,保存退出重启httpd服务 在浏览器中输入网址http://127.0.0.1/ user1/index. html,查看效果======= CentOS方式: a:修改配置文件,使用vi编辑器修改/etc/httpd/ conf.d/userdir.conf修改成如下配置信息 UserDir disable root//基于安全 考虑,禁止root用户使用自己的站点 UserDir public_ html//设置对每个用户的Web站点目录 Allowverride None #修改为 None options None#修改为None Require method GET POST OPTIONS b:用户创建个人Web站点
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值