本文共 3509 字,大约阅读时间需要 11 分钟。
之前有一个需求是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<<PAGE_SHIFT); 其中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.转载地址:http://xvcmi.baihongyu.com/