dev/mem

之前有一个需求是uboot 将一段key写到memory中,然后要求kernel reserve 这段memory,最后user space 通过打开/dev/mem 来映射这段memory,这样就可以将kernel space的内容和user space共享.
所以我们来看看kernel中第/dev/mem的实现。
/dev/mem 是一个字符设备,因此源码放在char/mem.c 中.
843 static int __init chr_dev_init(void)
844 {
845         int minor;
846 
847         if (register_chrdev(MEM_MAJOR, "mem", &memory_fops))
848                 printk("unable to get major %d for memory devs\n", MEM_MAJOR);
849 
850         mem_class = class_create(THIS_MODULE, "mem");
851         if (IS_ERR(mem_class))
852                 return PTR_ERR(mem_class);
853 
854         mem_class->devnode = mem_devnode;
855         for (minor = 1; minor < ARRAY_SIZE(devlist); minor++) {
856                 if (!devlist[minor].name)
857                         continue;
858 
859                 /*
860                  * Create /dev/port?
861                  */
862                 if ((minor == DEVPORT_MINOR) && !arch_has_dev_port())
863                         continue;
864 
865                 device_create(mem_class, NULL, MKDEV(MEM_MAJOR, minor),
866                               NULL, devlist[minor].name);
867         }
868 
869         return tty_init();
870 }
871 
872 fs_initcall(chr_dev_init);
kernel 在启动的最后阶段会调用fs_initcall。因此/dev/mem 是由kernel 自己建立的.而且建立的字符设备不知一个。具体如devlist所示:
782 static const struct memdev {
783         const char *name;
784         umode_t mode;
785         const struct file_operations *fops;
786         fmode_t fmode;
787 } devlist[] = {
788 #ifdef CONFIG_DEVMEM
789          [1] = { "mem", 0, &mem_fops, FMODE_UNSIGNED_OFFSET },
790 #endif
791 #ifdef CONFIG_DEVKMEM
792          [2] = { "kmem", 0, &kmem_fops, FMODE_UNSIGNED_OFFSET },
793 #endif
794          [3] = { "null", 0666, &null_fops, 0 },
795 #ifdef CONFIG_DEVPORT
796          [4] = { "port", 0, &port_fops, 0 },
797 #endif
798          [5] = { "zero", 0666, &zero_fops, 0 },
799          [7] = { "full", 0666, &full_fops, 0 },
800          [8] = { "random", 0666, &random_fops, 0 },
801          [9] = { "urandom", 0666, &urandom_fops, 0 },
802 #ifdef CONFIG_PRINTK
803         [11] = { "kmsg", 0644, &kmsg_fops, 0 },
804 #endif
805 };
可见dev/mem,dev/kmem,dev/null,dev/port,dev/zero,dev/full,dev/random,/dev/urandom,dev/kmsg都是在这个文件中实现的且major number都是一样的。
我们来看看/dev/mem的fops
725 static const struct file_operations __maybe_unused mem_fops = {
726         .llseek         = memory_lseek,
727         .read           = read_mem,
728         .write          = write_mem,
729         .mmap           = mmap_mem,
730         .open           = open_mem,
731 #ifndef CONFIG_MMU
732         .get_unmapped_area = get_unmapped_area_mem,
733         .mmap_capabilities = memory_mmap_capabilities,
734 #endif
735 };
上层一般会先open 这个字符设备后,调用mmap来做映射。
fm = open("/dev/mem", O_RDWR|O_SYNC);  
mmap((void *)addr, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, fm, pfn< 其中open 就调用的是open_mem
2 #define open_mem        open_port
713 static int open_port(struct inode *inode, struct file *filp)
714 {
715         return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
716 }
仅仅是检查一下权限设定而已,
我们来看看mmap对应的实现。
321 static int mmap_mem(struct file *file, struct vm_area_struct *vma)
322 {
323         size_t size = vma->vm_end - vma->vm_start;
324 
325         if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size))
326                 return -EINVAL;
327 
328         if (!private_mapping_ok(vma))
329                 return -ENOSYS;
330 
331         if (!range_is_allowed(vma->vm_pgoff, size))
332                 return -EPERM;
333 
334         if (!phys_mem_access_prot_allowed(file, vma->vm_pgoff, size,
335                                                 &vma->vm_page_prot))
336                 return -EINVAL;
337 
338         vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff,
339                                                  size,
340                                                  vma->vm_page_prot);
341 
342         vma->vm_ops = &mmap_mem_ops;
343 
344         /* Remap-pfn-range will mark the range VM_IO */
345         if (remap_pfn_range(vma,
346                             vma->vm_start,
347                             vma->vm_pgoff,
348                             size,
349                             vma->vm_page_prot)) {
350                 return -EAGAIN;
351         }
352         return 0;
353 }
这个函数从321~345都是做一些检测或者赋值。最重要的是345行调用remap_pfn_range来做实际的映射,这样user space 就可以看到kernel space memory中的内容了.其中vma->vm_start 就对应mmap的第一个参数addr.

你可能感兴趣的:(Linux,源码分析)