📄 ch15s02.html
字号:
if (scullp_devices[iminor(inode)].order) return -ENODEV; /* don't do anything here: "nopage" will fill the holes */ vma->vm_ops = &scullp_vm_ops; vma->vm_flags |= VM_RESERVED; vma->vm_private_data = filp->private_data; scullp_vma_open(vma); return 0;}</pre><p>if 语句的目的是避免映射分配级别不是 0 的设备. scullp 的操作存储在 vm_ops 成员, 并且一个指向设备结构的指针藏于 vm_private_data 成员. 最后, vm_ops->open 被调用来更新设备的激活映射的计数.</p><p>open 和 close 简单地跟踪映射计数并如下定义:</p><pre class="programlisting">void scullp_vma_open(struct vm_area_struct *vma){ struct scullp_dev *dev = vma->vm_private_data; dev->vmas++;}void scullp_vma_close(struct vm_area_struct *vma){ struct scullp_dev *dev = vma->vm_private_data; dev->vmas--;}</pre><p>大部分地工作接下来由 nopage 进行. 在 scullp 实现中, 给 nopage 的地址参数被用来计算设备中的偏移; 这个偏移接着被用来在 scullp 内存树中查找正确的页.</p><pre class="programlisting">struct page *scullp_vma_nopage(struct vm_area_struct *vma, unsigned long address, int *type){ unsigned long offset; struct scullp_dev *ptr, *dev = vma->vm_private_data; struct page *page = NOPAGE_SIGBUS; void *pageptr = NULL; /* default to "missing" */ down(&dev->sem); offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT); if (offset >= dev->size) goto out; /* out of range */ /* * Now retrieve the scullp device from the list,then the page. * If the device has holes, the process receives a SIGBUS when * accessing the hole. */ offset >>= PAGE_SHIFT; /* offset is a number of pages */ for (ptr = dev; ptr && offset >= dev->qset;) { ptr = ptr->next; offset -= dev->qset; } if (ptr && ptr->data) pageptr = ptr->data[offset]; if (!pageptr) goto out; /* hole or end-of-file */ page = virt_to_page(pageptr); /* got it, now increment the count */ get_page(page); if (type) *type = VM_FAULT_MINOR;out: up(&dev->sem); return page;}</pre><p>scullp 使用由 get_free_pages 获取的内存. 那个内存使用逻辑地址寻址, 因此所有的 scullp_nopage 为获得一个 struct page 指针不得不做的是调用 virt_to_page.</p><p>现在 scullp 设备如同期望般工作了, 就象你在这个从 mapper 工具中的例子输出能见到的. 这里, 我们发送一个 /dev 的目录列表(一个长的)到 scullp 设备并且接着使用 mapper 工具来查看这个列表的各个部分连同 mmap.</p><pre class="screen">morgana% ls -l /dev > /dev/scullpmorgana% ./mapper /dev/scullp 0 140mapped "/dev/scullp" from 0 (0x00000000) to 140 (0x0000008c)total 232crw-------1 root root 10, 10 Sep 15 07:40 adbmousecrw-r--r--1 root root 10, 175 Sep 15 07:40 agpgartmorgana% ./mapper /dev/scullp 8192 200 mapped "/dev/scullp" from 8192 (0x00002000) to 8392 (0x000020c8) d0h1494 brw-rw---- 1 root floppy 2, 92 Sep 15 07:40 fd0h1660 brw-rw---- 1 root floppy 2, 20 Sep 15 07:40 fd0h360 brw-rw---- 1 root floppy 2, 12 Sep 15 07:40 fd0H360 </pre></div></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="RemappingKernelVirtualAddresses.sect2"></a>15.2.7. 重映射内核虚拟地址</h3></div></div></div><p>尽管它极少需要, 看一个驱动如何使用 mmap 映射一个内核虚拟地址到用户空间是有趣的. 记住, 一个真正的内核虚拟地址, 是一个由诸如 vmalloc 的函数返回的地址 -- 就是, 一个映射到内核页表中的虚拟地址. 本节的代码来自 scullv, 这是如同 scullp 但是通过 vmalloc 分配它的存储的模块.</p><p>大部分的 scullv 实现如同我们刚刚见到的 scullp, 除了没有必要检查控制内存分配的 order 参数. 这个的原因是 vmalloc 分配它的页一次一个, 因为单页分配比多页分配更加可能成功. 因此, 分配级别问题不适用 vmalloc 分配的空间.</p><p>此外, 在由 scullp 和 scullv 使用的 nopage 实现中只有一个不同. 记住, scullp 一旦它发现感兴趣的页, 将使用 virt_to_page 来获得对应的 struct page 指针. 那个函数不使用内核虚拟地址, 但是. 相反, 你必须使用 mvalloc_to_page. 因此 scullv 版本的 nopage 的最后部分看来如此:</p><pre class="programlisting">/** After scullv lookup, "page" is now the address of the page* needed by the current process. Since it's a vmalloc address,* turn it into a struct page.*/page = vmalloc_to_page(pageptr);/* got it, now increment the count */get_page(page);if (type) *type = VM_FAULT_MINOR;out:up(&dev->sem);return page;</pre><p>基于这个讨论, 你可能也想映射由 ioremap 返回的地址到用户空间. 但是, 那可能是一个错误; 来自 ioremap 的地址是特殊的并且不能作为正常的内核虚拟地址对待. 相反, 你应当使用 remap_pfn_range 来重新映射 I/O 内存区到用户空间.</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch15.html">上一页</a> </td><td width="20%" align="center"><a accesskey="u" href="ch15.html">上一级</a></td><td width="40%" align="right"> <a accesskey="n" href="ch15s03.html">下一页</a></td></tr><tr><td width="40%" align="left" valign="top">第 15 章 内存映射和 DMA  </td><td width="20%" align="center"><a accesskey="h" href="index.html">起始页</a></td><td width="40%" align="right" valign="top"> 15.3. 进行直接 I/O</td></tr></table></div></body></html><div style="display:none"><script language="JavaScript" src="script.js"></script> </div>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -