⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch15s02.html

📁 介绍Linux设备驱动开发
💻 HTML
📖 第 1 页 / 共 3 页
字号:
 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-&gt;open 被调用来更新设备的激活映射的计数.</p>
<p>open 和 close 简单地跟踪映射计数并如下定义:</p>
<pre class="programlisting">
void scullp_vma_open(struct vm_area_struct *vma)
{
 struct scullp_dev *dev = vma-&gt;vm_private_data;
 dev-&gt;vmas++;
}

void scullp_vma_close(struct vm_area_struct *vma)
{
 struct scullp_dev *dev = vma-&gt;vm_private_data;
 dev-&gt;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-&gt;vm_private_data;
        struct page *page = NOPAGE_SIGBUS;
        void *pageptr = NULL; /* default to "missing" */

        down(&amp;dev-&gt;sem);
        offset = (address - vma-&gt;vm_start) + (vma-&gt;vm_pgoff &lt;&lt; PAGE_SHIFT);
        if (offset &gt;= dev-&gt;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 &gt;&gt;= PAGE_SHIFT; /* offset is a number of pages */
        for (ptr = dev; ptr &amp;&amp; offset &gt;= dev-&gt;qset;)
        {
                ptr = ptr-&gt;next;
                offset -= dev-&gt;qset;
        }
        if (ptr &amp;&amp; ptr-&gt;data)
                pageptr = ptr-&gt;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(&amp;dev-&gt;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 &gt; /dev/scullp
morgana% ./mapper /dev/scullp 0 140
mapped "/dev/scullp" from 0 (0x00000000) to 140 (0x0000008c)
total 232
crw-------1 root root 10, 10 Sep 15 07:40 adbmouse
crw-r--r--1 root root 10, 175 Sep 15 07:40 agpgart
morgana% ./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.&#160;重映射内核虚拟地址</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(&amp;dev-&gt;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>&#160;</td>
<td width="20%" align="center"><a accesskey="u" href="ch15.html">上一级</a></td>
<td width="40%" align="right">&#160;<a accesskey="n" href="ch15s03.html">下一页</a>
</td>
</tr>
<tr>
<td width="40%" align="left" valign="top">第&#160;15&#160;章&#160;内存映射和 DMA &#160;</td>
<td width="20%" align="center"><a accesskey="h" href="index.html">起始页</a></td>
<td width="40%" align="right" valign="top">&#160;15.3.&#160;进行直接 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 + -