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

📄 ch16.html

📁 介绍Linux内核驱动编程的一本书 最主要的是有源代码,都是可用的 学习操作系统很好
💻 HTML
📖 第 1 页 / 共 2 页
字号:
<dt><span class="term"><span>struct block_device_operations *fops;</span></span></dt><dd><p>来自前一节的设备操作集合.</p></dd><dt><span class="term"><span>struct request_queue *queue;</span></span></dt><dd><p>被内核用来管理这个设备的 I/O 请求的结构; 我们在"请求处理"一节中检查它.</p></dd><dt><span class="term"><span>int flags;</span></span></dt><dd><p>一套标志(很少使用), 描述驱动器的状态. 如果你的设备有可移出的介质, 你应当设置 GENHD_FL_REMOVABLE. CD-ROM 驱动器可设置 GENHD_FL_CD. 如果, 由于某些原因, 你不需要分区信息出现在 /proc/partitions, 设置 GENHD_FL_SUPPRESS_PARTITIONS_INFO.</p></dd><dt><span class="term"><span>sector_t capacity;</span></span></dt><dd><p>这个驱动器的容量, 以512-字节扇区来计. sector_t 类型可以是 64 位宽. 驱动不应当直接设置这个成员; 相反, 传递扇区数目给 set_capacity.</p></dd><dt><span class="term"><span>void *private_data;</span></span></dt><dd><p>块驱动可使用这个成员作为一个指向它们自己内部数据的指针.</p></dd></dl></div><p>内核提供了一小部分函数来使用 gendisk 结构. 我们在这里介绍它们, 接着看 sbull 如何使用它们来使系统可使用它的磁盘驱动器.</p><p>struct gendisk 是一个动态分配的结构, 它需要特别的内核操作来初始化; 驱动不能自己分配这个结构. 相反, 你必须调用:</p><pre class="programlisting">struct gendisk *alloc_disk(int minors); </pre><p>minors 参数应当是这个磁盘使用的次编号数目; 注意你不能在之后改变 minors 成员并且期望事情可以正确工作. 当不再需要一个磁盘时, 它应当被释放, 使用:</p><pre class="programlisting">void del_gendisk(struct gendisk *gd);</pre><p>一个 gendisk 是一个被引用计数的结构(它含有一个 kobject). 有 get_disk 和 put_disk 函数用来操作引用计数, 但是驱动应当从不需要做这个. 正常地, 对 del_gendisk 的调用去掉了最一个 gendisk 的最终的引用, 但是不保证这样. 因此, 这个结构可能继续存在(并且你的方法可能被调用)在调用 del_gendisk 之后. 但是, 如果你删除这个结构当没有用户时(即, 在最后的释放之后, 或者在你的模块清理函数), 你可确信你不会再收到它的信息.</p><p>分配一个 gendisk 结构不能使系统可使用这个磁盘. 要做到这点, 你必须初始化这个结构并且调用 add_disk:</p><pre class="programlisting">void add_disk(struct gendisk *gd); </pre><p>这里记住一件重要的事情:一旦你调用add_disk, 这个磁盘是"活的"并且它的方法可被在任何时间被调用. 实际上, 这样的第一个调用将可能发生, 即便在 add_disk 返回之前; 内核将读前几个字节以试图找到一个分区表. 因此你不应当调用 add_disk 直到你的驱动被完全初始化并且准备好响应对那个磁盘的请求.</p></div></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="Initializationinsbull.sect2"></a>16.1.3.&#160;在 sbull 中的初始化</h3></div></div></div><p>是时间进入一些例子了. sbull 驱动(从 O'Reilly 的 FTP 网站, 以及其他例子源码)实现一套内存中的虚拟磁盘驱动器. 对每个驱动器, sbull 分配(使用 vmalloc, 为了简单)一个内存数组; 它接着使这个数组可通过块操作来使用. 这个 sbull 驱动可通过分区这个驱动器, 在上面建立文件系统, 以及加载到系统层级中来测试. </p><p>象我们其他的例子驱动一样, sbull 允许一个主编号在编译或者模块加载时被指定. 如果没有指定, 动态分配一个. 因为对 register_blkdev 的调用被用来动态分配, sbull 应当这样做:</p><pre class="programlisting">sbull_major = register_blkdev(sbull_major, "sbull");if (sbull_major &lt;= 0){        printk(KERN_WARNING "sbull: unable to get major number\n");        return -EBUSY;}</pre><p>同样, 象我们在本书已展现的其他虚拟设备, sbull 设备由一个内部结构描述:</p><pre class="programlisting">struct sbull_dev { int size;  /* Device size in sectors */  u8 *data;  /* The data array */  short users;  /* How many users */  short media_change;  /* Flag a media change? */  spinlock_t lock;  /* For mutual exclusion */  struct request_queue *queue;  /* The device request queue */  struct gendisk *gd;  /* The gendisk structure */  struct timer_list timer;  /* For simulated media changes */  };  </pre><p>需要几个步骤来初始化这个结构, 并且使系统可用关联的设备. 我们从基本的初始化开始, 并且分配底层的内存:</p><pre class="programlisting">memset (dev, 0, sizeof (struct sbull_dev));dev-&gt;size = nsectors*hardsect_size;dev-&gt;data = vmalloc(dev-&gt;size);if (dev-&gt;data == NULL){        printk (KERN_NOTICE "vmalloc failure.\n");        return;}spin_lock_init(&amp;dev-&gt;lock);</pre><p>重要的是在下一步之前分配和初始化一个自旋锁, 下一步是分配请求队列. 我们在进入请求处理时详细看这个过程; 现在, 只需说必要的调用是:</p><pre class="programlisting">dev-&gt;queue = blk_init_queue(sbull_request, &amp;dev-&gt;lock); </pre><p>这里, sbull_request 是我们的请求函数 -- 实际进行块读和写请求的函数. 当我们分配一个请求队列时, 我们必须提供一个自旋锁来控制对那个队列的存取. 这个锁由驱动提供而不是内核通常的部分, 因为, 常常, 请求队列和其他的驱动数据结构在相同的临界区; 它们可能被同时存取. 如同任何分配内存的函数, blk_init_queue 可能失败, 因此你必须在继续之前检查返回值.</p><p>一旦我们有我们的设备内存和请求队列, 我们可分配, 初始化, 并且安装对应的 gendisk 结构. 做这个工作的代码是:</p><pre class="programlisting">dev-&gt;gd = alloc_disk(SBULL_MINORS);if (! dev-&gt;gd){        printk (KERN_NOTICE "alloc_disk failure\n");        goto out_vfree;}dev-&gt;gd-&gt;major = sbull_major;dev-&gt;gd-&gt;first_minor = which*SBULL_MINORS;dev-&gt;gd-&gt;fops = &amp;sbull_ops;dev-&gt;gd-&gt;queue = dev-&gt;queue;dev-&gt;gd-&gt;private_data = dev;snprintf (dev-&gt;gd-&gt;disk_name, 32, "sbull%c", which + 'a');set_capacity(dev-&gt;gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));add_disk(dev-&gt;gd);</pre><p>这里, SBULL_MINORS 是每个 sbull 设备所支持的次编号的数目. 当我们设置第一个次编号给每个设备, 我们必须考虑被之前的设备所用的全部编号. 磁盘的名子被设置, 这样第一个是 sbulla, 第二个是 sbullb, 等等. 用户空间可接着添加分区号以便它们在第 2 个设备上的分区可能是 /dev/sbull3. </p><p>一旦所有的都被设置, 我们以对 add_disk 的调用来结束. 我们的几个方法将在 add_disk 返回时被调用, 因此我们负责做这个调用, 这是初始化我们的设备的最后一步.</p></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="ANoteonSectorSizes.sect2"></a>16.1.4.&#160;注意扇区大小</h3></div></div></div><p>如同我们之前提到的, 内核对待每个磁盘如同一个 512-字节扇区的数组. 不是所有的硬件都使用那个扇区大小, 但是. 使一个有不同扇区大小的设备工作不是一件很难的事; 只要小心处理几个细节. sbull 设备输出一个 hardsect_size 参数, 可被用来改变设备的"硬件"扇区大小. 通过看它的实现, 你可见到如何添加这个支持到你自己的驱动.</p><p>这些细节中的第一个是通知内核你的设备支持的扇区大小. 硬件扇区大小是一个在请求队列的参数, 而不是在 gendisk 结构. 这个大小通过调用 blk_queue_hardsect_size 设置的, 在分配队列后马上进行:</p><pre class="programlisting">blk_queue_hardsect_size(dev-&gt;queue, hardsect_size); </pre><p>一旦完成那个, 内核坚持你的设备的硬件扇区大小. 所有的 I/O 请求被正确对齐到一个硬件扇区的起始, 并且每个请求的长度是一个整数的扇区数. 你必须记住, 但是, 内核一直以 512-字节扇区表述自己; 因此, 有必要相应地转换所有的扇区号. 因此, 例如, 当 sbull 在它的 gendisk 结构中设置设备的容量时, 这个调用看来象:</p><pre class="programlisting">set_capacity(dev-&gt;gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));</pre><p>KERNEL_SECTOR_SIZE 是一个本地定义的常量, 我们用来调整内核的 512-字节和任何我们已被告知要使用的大小. 在我们查看 sbull 请求处理逻辑中会不时看到这类计算出来.</p></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch15s05.html">上一页</a>&#160;</td><td width="20%" align="center">&#160;</td><td width="40%" align="right">&#160;<a accesskey="n" href="ch16s02.html">下一页</a></td></tr><tr><td width="40%" align="left" valign="top">15.5.&#160;快速参考&#160;</td><td width="20%" align="center"><a accesskey="h" href="index.html">起始页</a></td><td width="40%" align="right" valign="top">&#160;16.2.&#160;块设备操作</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 + -