linux .so mmap,linux中mmap系统调用原理分析与实现

1、mmap系统调用(功能)

void* mmap ( void * addr , size_t len , int prot , int flags ,int

fd , off_t offset )

内存映射函数mmap, 负责把文件内容映射到进程的虚拟内存空间,

通过对这段内存的读取和修改,来实现对文件的读取和修改,而不需要再调用read,write等操作。

a4c26d1e5885305701be709a3d33442f.png

2、mmap系统调用(参数)

1)addr: 指定映射的起始地址, 通常设为NULL, 由系统指定。

2)length: 映射到内存的文件长度。

3) prot: 映射区的保护方式, 可以是:

PROT_EXEC: 映射区可被执行

PROT_READ: 映射区可被读取

PROT_WRITE: 映射区可被写入

4)flags: 映射区的特性, 可以是:

MAP_SHARED:写入映射区的数据会复制回文件,

且允许其他映射该文件的进程共享。

MAP_PRIVATE:对映射区的写入操作会产生一个映射区的复制(copy-on-write),

对此区域所做的修改不会写回原文件。

5)fd: 由open返回的文件描述符, 代表要映射的文件。

6)offset: 以文件开始处的偏移量, 必须是分页大小的整数倍, 通常为0, 表示从文件头开始映射。

3、解除映射

int

munmap(void *start,size_t length)

功能:取消参数start所指向的映射内存,参数length表示欲取消的内存大小。

返回值:解除成功返回0,否则返回-1,错误原因存于errno中。

实例分析

mmap系统调用

4、虚拟内存区域

虚拟内存区域是进程的虚拟地址空间中的一个同质区间,即具有同样特性的连续地址范围。一个进程的内存映象由下面几部分组成:程序代码、数据、BSS

和栈区域,以及内存映射的区域。

一个进程的内存区域可以通过查看:/proc/pid/maps

08048000-0804f000 r-xp 00000000 08:01 573748 /sbin/rpc.statd

#text

0804f000-08050000 rw-p 00007000 08:01 573748 /sbin/rpc.statd

#data

08050000-08055000 rwxp 00000000 00:00 0 #bss

040000000-40015000 r-xp 00000000 08:01 933965 /lib/ld2.3.2.so

#text

40015000-40016000 rw-p 00014000 08:01 933965 /lib/ld-2.3.2.so

#data

每一行的域为:start_end perm offset major:minor inode

1) Start: 该区域起始虚拟地址

2) End: 该区域结束虚拟地址

3) Perm:

读、写和执行权限;表示对这个区域,允许进程做什么。这个域的最后一个字符要么是p表示私有的,要么是s表示共享的。

4) Offset: 被映射部分在文件中的起始地址

5) Major、minor:主次设备号

6) Inode:索引结点

5、vm_area_struct

Linux内核使用结构vm_area_struct()来描述虚拟内存区域,其中几个主要成员如下:

1)unsigned long vm_start 虚拟内存区域起始地址

2)unsigned long

vm_end 虚拟内存区域结束地址

3)unsigned long vm_flags 该区域的标记。如:VM_IO和VM_RESERVED。VM_IO将该VMA标记为内存映射的IO区域,VM_IO会阻止系统将该区域包含在进程的存放转

存(core dump )中,VM_RESERVED标志内存区域不能被换出。

6、mmap设备操作

映射一个设备是指把用户空间的一段地址关联到设备内存上。当程序读写这段用户空间的地址时,它实际上是在访问设备。

mmap设备方法需要完成什么功能?

mmap方法是file_oprations结构的成员,在mmap系统调用发出时被调用。在此之前,内核已经完成了很多工作。mmap设备方法所需要做的就是建立

虚拟地址到物理地址的页表。

int (*mmap) (struct file *, struct vm_area_struct *)

mmap如何完成页表的建立?

方法有二:

1)使用remap_pfn_range一次建立所有页表;

2)使用nopage VMA方法每次建立一个页表。

构造页表的工作可由remap_pfn_range函数完成,原型如下:

int remap_pfn_range(struct vm_area_struct *vma, unsigned long

addr,unsigned long pfn, unsigned long size, pgprot_t

prot)

vma: 虚拟内存区域指针

virt_addr: 虚拟地址的起始值

pfn: 要映射的物理地址所在的物理页帧号,可将物理地址>>PAGE_SHIFT得到。

size: 要映射的区域的大小。

prot: VMA的保护属性。

int memdev_mmap(struct file*filp,

struct vm_area_struct *vma)

{

Vma->vm_flags |= VM_IO;

Vma->vm_flags |= VM_RESERVED;

if (remap_pfn_range(vma, vma->vm_start,

virt_to_phys(dev- >data)>> PAGE_SHIFT,

size,

vma->vm_page_prot))

return -EAGAIN;

return 0;

}

7、mmap设备方法实例

1)memdev.源码

#ifndef _MEMDEV_H_

#define _MEMDEV_H_

#ifndef MEMDEV_MAJOR

#define MEMDEV_MAJOR 0 #endif

#ifndef MEMDEV_NR_DEVS

#define MEMDEV_NR_DEVS

2 #endif

#ifndef MEMDEV_SIZE

#define MEMDEV_SIZE 4096

#endif

struct

mem_dev { char

*data; unsigned long

size; };

#endif

2)memdev.c源码

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include "memdev.h"

static int mem_major =

MEMDEV_MAJOR;

module_param(mem_major, int,

S_IRUGO);

struct mem_dev

*mem_devp;

struct cdev cdev;

int mem_open(struct inode *inode, struct file *filp)

{

struct

mem_dev *dev;

int num =

MINOR(inode->i_rdev);

if (num >= MEMDEV_NR_DEVS) return -ENODEV;

dev =

&mem_devp[num];

filp->private_data = dev;

return

0; }

int mem_release(struct inode *inode, struct file *filp)

{

return 0;

}

static int memdev_mmap(struct file*filp, struct vm_area_struct

*vma)

{

struct mem_dev *dev = filp->private_data;

vma->vm_flags |= VM_IO;

vma->vm_flags |= VM_RESERVED;

if

(remap_pfn_range(vma,vma->vm_start,virt_to_phys(dev->data)>>PAGE_SHIFT,

vma->vm_end - vma->vm_start, vma->vm_page_prot))

return -EAGAIN;

return 0;

}

static const struct file_operations mem_fops =

{

.owner = THIS_MODULE,

.open = mem_open,

.release = mem_release,

.mmap = memdev_mmap,

};

static int memdev_init(void)

{

int result;

int i;

dev_t devno =

MKDEV(mem_major, 0);

if (mem_major)

result =

register_chrdev_region(devno, 2, "memdev");

else {

result =

alloc_chrdev_region(&devno, 0, 2, "memdev");

mem_major =

MAJOR(devno);

} if (result < 0)

return

result;

cdev_init(&cdev, &mem_fops);

cdev.owner = THIS_MODULE;

cdev.ops = &mem_fops;

cdev_add(&cdev, MKDEV(mem_major, 0),

MEMDEV_NR_DEVS);

mem_devp = kmalloc(MEMDEV_NR_DEVS *

sizeof(struct mem_dev), GFP_KERNEL);

if

(!mem_devp) {

result

= - ENOMEM;

goto

fail_malloc;

}

memset(mem_devp, 0, sizeof(struct

mem_dev));

for (i=0; i < MEMDEV_NR_DEVS;

i++) {

mem_devp[i].size = MEMDEV_SIZE;

mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);

memset(mem_devp[i].data, 0, MEMDEV_SIZE);

}

return 0;

fail_malloc: unregister_chrdev_region(devno, 1);

return result;

}

static void memdev_exit(void)

{

cdev_del(&cdev); kfree(mem_devp); unregister_chrdev_region(MKDEV(mem_major, 0),

2);

}

MODULE_AUTHOR("yinjiabin);

MODULE_LICENSE("GPL");

module_init(memdev_init);

module_exit(memdev_exit);

3)测试程序源码

#include

#include

#include

#include

#include

#include

int main()

{

int fd;

char *start;

//char buf[100];

char *buf;

fd = open("/dev/memdev0",O_RDWR);

buf = (char *)malloc(100);

memset(buf, 0, 100);

start=mmap(NULL,100,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);

strcpy(buf,start);

sleep (1);

printf("buf 1 =

%s\n",buf);

strcpy(start,"Buf Is Not Null!");

memset(buf, 0, 100);

strcpy(buf,start);

sleep (1);

printf("buf 2 = %s\n",buf);

munmap(start,100);

free(buf);

close(fd); return 0; }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值