Linux内核态和用户态共享内存

您所在的位置:网站首页 内核和用户态 Linux内核态和用户态共享内存

Linux内核态和用户态共享内存

2024-05-27 00:59| 来源: 网络整理| 查看: 265

Linux内核态和用户态共享内存

1、mmap系统调用(功能)       void* mmap ( void * addr , size_t len , int prot , int flags ,int fd , off_t offset )       内存映射函数mmap, 负责把文件内容映射到进程的虚拟内存空间, 通过对这段内存的读取和修改,来实现对文件的读取和修改,而不需要再调用read,write等操作。

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; } dev- >data就是传给用户态的共享数据。

7、mmap设备方法实例        1)memdev.源码

#ifndef _MEMDEV_H_ #define _MEMDEV_H_ #ifndef MEMDEV_MAJOR #define MEMDEV_MAJOR 0   /*预设的mem的主设备号*/ #endif #ifndef MEMDEV_NR_DEVS #define MEMDEV_NR_DEVS 2    /*设备数*/ #endif #ifndef MEMDEV_SIZE #define MEMDEV_SIZE 4096 #endif /*mem设备描述结构体*/ struct mem_dev                                      {                                                           char *data;                         unsigned long size;        }; #endif /* _MEMDEV_H_ */        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结构*/   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;  }  int munmap( void * addr, size_t len ) 

        该调用在进程地址空间中解除一个映射关系,addr是调用mmap()时返回的地址,len是映射区的大小。当映射关系解除后,对原来映射地址的访问将导致段错误发生。

 int msync ( void * addr , size_t len, int flags) 

 一般说来,进程在映射空间的对共享内容的改变并不直接写回到磁盘文件中,往往在调用munmap()后才执行该操作。可以通过调用msync()实现磁盘上文件内容与共享内存区的内容一致。

有个地方查了很多资料,没有详细的解释,在mmap成功以后,关闭文件描述符close(fd). 共享内存区还是存在的,但是数据还是受保护的吗?  



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3