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

📄 (ldd) ch12-加载块设备驱动程序(转载).htm

📁 LINUX驱动编程
💻 HTM
📖 第 1 页 / 共 4 页
字号:
      color=#ffffff size=3>
      <P>当前缓冲区头是CURRENT-&gt;bh,而数据块是CURRENT-&gt;bh-&gt;b_data。后一个指针为了象sbu<BR>ll一类忽略集簇的驱动程序缓冲在CURRENT-&gt;buffer中。<BR>&nbsp;<BR>请求集簇在drivers/block/ll_rw_block.c的函数make_request中实现。不过,如上所说<BR>,集簇只对几个驱动程序有效(软驱,IDE,和SCSI),以其主设备号为准。我曾通过以<BR>major=34装载sbull看到过集簇是如何工作的,因为34是IDE3_MAJOR,而我的系统中没有<BR>第三个IDE控制器。<BR>&nbsp;<BR>下面列表总结了当扫描一个集簇请求时应做的事项。bh是被处理的缓冲区头——列表的<BR>第一项。对列表中的每个缓冲区头,驱动程序要完成下面一系列操作:<BR>&nbsp;<BR>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;传送位于地址bh-&gt;b_data,大小为bh-&gt;b_size字节数据块。数据传送的方向通<BR>常由CURRENT-&gt;cmd指出。<BR>&nbsp;<BR>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;从列表中找出下一个缓冲区头:bh-&gt;b_request。接着通过将b_request置为0<BR>,把刚传送过的缓冲区从列表中摘下。b_reqnext指向你刚找出的新缓冲区。<BR>&nbsp;<BR>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;通过调用mark_buffer_uptodate(bh,1),unlock_buffer(bh),告诉核心你已<BR>完成对上个缓冲区的操作。这些调用保证缓冲高速缓存保持正确,不致有错误指向的指<BR>针。mark_buffer_uptodate中参数“1”表示传送成功,若传送失败,则换为0。<BR>&nbsp;<BR>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;循环回到开始,传送下一个相邻块。<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>当你做完了集簇请求,CURRENT-&gt;bh必须被更新以指向“已经被处理但未被解锁”的第一<BR>个缓冲区。如果列表中所有的缓冲区都已被处理和解锁,CURRENT-&gt;bh可被置为NULL。<BR>&nbsp;<BR>此时,驱动程序可以调用end_request。如果CURRENT-&gt;bh是有效的,那么这个函数在转<BR>到下一个缓冲之前对其进行解锁——这是非集簇操作所发生的情况,此时由end_request<BR>照管所有的事情。如果指针为空,这个函数直接转到下一个请求。<BR>&nbsp;<BR>全功能的集簇实现出现在driver/block/floppy.c,而要求的所有操作出现在blk.h的end<BR>_request中。floppy.c和blk.h都不容易理解,不过建议先从后者开始。<BR>&nbsp;<BR>安装(Mounting)是如何工作的<BR>&nbsp;<BR>块设备与字符设备及一般文件的不同在于它们可以被安装到计算机的文件系统上。这与<BR>一般的访问方式不同。一般的访问方式通过结构file进行,这个结构与特定的进程相关<BR>联,并且只在open到close之间存在。当一个文件系统被安装后,没有进程拥有一个filp<BR>。<BR>&nbsp;<BR>当核心把一个设备安装到文件系统上,它调用一般的open方法来访问驱动程序。然而,<BR>这种情况下open的参数filp是个虚的变量,几乎只是为了占个地方,它唯一有意义的域<BR>是f_mode。其它域含任意值并不使用。f_mode的值是告诉驱动程序设备是以只读(f_mod<BR>e==FMODE_READ)还是读写(f_mode==(FMODE_READ|FMOD_WRITE))方式被安装。使用<BR>一个虚变量而不是file结构的原因是因为实际的结构file在进程结束时将被释放,而被<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>一个虚变量而不是file结构的原因是因为实际的结构file在进程结束时将被释放,而被<BR>安装的文件系统在mount命令完成后仍然存在。<BR>&nbsp;<BR>在安装时,驱动程序唯一调用的是open方法。当磁盘被安装后,核心调用设备中的read<BR>和write&nbsp;方法(被映射到request_fn)来管理文件系统中的文件。驱动程序并不知道req<BR>uest_fn服务的是一个进程(象fsck)还是核心中的文件系统层。<BR>&nbsp;<BR>至于umount,它只是刷新缓冲高速缓存并调用驱动程序的release(close)方法。由于<BR>没有有意义的filp可以传递给fop-&gt;realse,核心使用NULL。<BR>&nbsp;<BR>因此,当你实现release时,你应将驱动程序设为能处理为NULL的filp指针。不然,如果<BR>你用了filp,你可能运行mkfs和fsck,它们都使用filp来访问设备,你也可能mount这个<BR>设备,但umount将无法运行,原因就是NULL指针。<BR>&nbsp;<BR>由于一个块设备驱动程序的release实现不能用filp-&gt;private_data来访问设备信息,它<BR>采用inode-&gt;i_rdev来区分设备。这里是release的sbull实现:<BR>&nbsp;<BR>(代码249)<BR>&nbsp;<BR>其它的驱动程序函数并不关心filp问题,因为它们与安装的文件系统无关。例如,一个<BR>显示地open这个设备的进程只发出ioctl。<BR>&nbsp;<BR>ioctl方法<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>ioctl方法<BR>&nbsp;<BR>如字符设备一样,块设备也可以通过ioctl系统调用进行操作。两者之间相对不一样的地<BR>方在于块设备驱动程序有大量驱动程序都要支持的ioctl命令。<BR>&nbsp;<BR>块设备驱动程序经常要处理的命令如下所示,它们在&lt;linux/fs.h&gt;中被声明。<BR>&nbsp;<BR>BLKGETSIZE<BR>&nbsp;<BR>获取当前设备的大小,以扇区数表示。由系统调用传递的&nbsp;数值arg是一个指向long数值<BR>的指针,用来将大小拷贝到一个用户空间的变量中。这个ioctl命令可以被mkfs用来获知<BR>产生的文件系统的大小。<BR>&nbsp;<BR>BLKFLSBUF<BR>&nbsp;<BR>字面上的意思是“刷新缓冲区”。这个命令的实现对每个设备都是一样的,我们将在后<BR>面整个ioctl方法的示例代码中给出来。<BR>&nbsp;<BR>BLKRAGET<BR>&nbsp;<BR>用来为设备取得当前提前读的值。当前数值应该用在参数arg中传递给ioctl的指针写进<BR>一个long类型的用户空间变量。<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>BLKRASET<BR>&nbsp;<BR>设置提前读的值。用户进程在arg中传递这个新值。<BR>&nbsp;<BR>BLKRRPART<BR>&nbsp;<BR>重读分区表。这个命令只对可分区设备有意义,将在后面“可分区设备”中介绍。<BR>&nbsp;<BR>BLKROSET<BR>&nbsp;<BR>BLKROGET<BR>&nbsp;<BR>这些命令用来改变和检查设备的只读标志。因为代码是设备无关的,它们由宏RO_IOCTLS<BR>(kdev_tdev,unsigned&nbsp;long&nbsp;where)来实现。这个宏在blk.h中定义。<BR>&nbsp;<BR>HDIO_GETGEO<BR>&nbsp;<BR>在&lt;linux/hdreg.h&gt;中定义,用来获得磁盘的几何参数。这个参数应被写入用户空间的结<BR>构hd_geometry中,它也在hdreg.h中定义。sbull显示了这个命令的一般实现。<BR>&nbsp;<BR>HDIO_GETGEO是&lt;linux/hdreg.h&gt;中定义的一系列HDIO命令中最常用的一个。感兴趣的读<BR>者可以查看ide.c和hd.c以获得这些命令的更多信息。<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>者可以查看ide.c和hd.c以获得这些命令的更多信息。<BR>&nbsp;<BR>这里列出的这些命令的一个主要缺点是它们是以“老”方法定义的(是第五章“增强的<BR>字符设备驱动程序操作”中“选择ioctl命令”一节),因此无法使用位域的宏来减化代<BR>码——每个命令要实现它自己的verify_area。不过,如果一个驱动程序需要定义它自己<BR>的命令来利用设备的一些特殊特点,你可以自由地使用“新”方法来定义命令。<BR>&nbsp;<BR>sbull设备只支持上面的通用命令,因为实现设备特定的命令与实现字符设备驱动程序的<BR>命令没有什么不同。sbull的ioctl实现如下所示,它将有助于你理解上面列出的命令。<BR>&nbsp;<BR>(代码250)<BR>&nbsp;<BR>(代码251)<BR>&nbsp;<BR>函数开始的PDEBUG语句被留出,这样当你编译这个模块时,你可以打开调试(debugging<BR>)来看看设备上调用了哪个ioctl命令。<BR>&nbsp;<BR>例如,对于显示的ioctl命令,你可以在sbull上使用fdisk。下面是在我自己系统上的一<BR>个示例执行过程:<BR>&nbsp;<BR>(代码252&nbsp;1#)<BR>&nbsp;<BR>在会话过程中下面的消息出现在我的系统日志中:<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>在会话过程中下面的消息出现在我的系统日志中:<BR>&nbsp;<BR>(代码252&nbsp;2#)<BR>&nbsp;<BR>第一个ioctl是HDIO_GETGEO,它在fdisk启动时被调用;第二个是BLKRRPART。对后一个<BR>命令的sbull实现仅仅是调用一下revalidate函数,它则在打印输出中打印最后的消息(<BR>见本章后面的“revalidate”)。<BR>&nbsp;<BR>可拆卸的设备<BR>&nbsp;<BR>在我们讨论字符设备驱动程序时,我们忽略了fops结构中的最后两个文件操作,因为它<BR>们只是为可拆卸块设备而设的。现在是看看它们的时候了。sbull并不真是可拆卸的,但<BR>它假装是,因此它实现了这些方法。<BR>&nbsp;<BR>我所说的操作是check_media-_change和revalidate。前者用来发现设备自上次访问以来<BR>是否改变过,后者则在磁盘变动之后重新初始化驱动程序的状态。<BR>&nbsp;<BR>至于sbull,与设备相联的数据区在使用计数下降为零后半分钟要释放。待这个设备处于<BR>未安装状态(或关闭状态)足够长的时间以模拟一次磁盘的改变,下一次对设备的访问<BR>分配一个新的内存区域。<BR>&nbsp;<BR>这一类的“时间到期”通过一个核心计数器来实现。<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>check_media_change<BR>这个检查函数接收到kev_t做为一个确定设备的参数。如果介质被改变了返回值为1,否<BR>则为0。如果一块设备驱动程序不支持可拆卸设备,可以通过置fops-&gt;check_media_chan<BR>ge为NULL来避免这个声明函数。<BR>&nbsp;<BR>有趣的是要注意,当一个设备是可拆卸的,但却无法判断它是否改变了,这时,返回1是<BR>个安全选择。事实上,IDE驱动程序在处理可&nbsp;鹦洞排淌&nbsp;就是这么做的。<BR>&nbsp;<BR>sbull的实现是这样的,当由于计数器超时,设备已经从内存中删除时就返回1,如果数<BR>据仍然有效则返回0。如果设置了调试,它同时向系统日志打印一条消息,这样用户就可<BR>以检查核心什么时候调用了这个方法。<BR>&nbsp;<BR>(代码253&nbsp;1#)<BR>&nbsp;<BR>revalidate<BR>&nbsp;<BR>这个有效化函数是在检测到一个磁盘的改变时被调用。它也被在核心的2.1版中实现的各<BR>种stat系统调用。返回值目前不做使用;为安全起见,返回0表示成功,出错时返回一个<BR>负的错误代码。<BR>&nbsp;<BR>revalidate执行的动作是设备特定的,但revalidate通常更新一些内部状态信息以反映<BR>新的设备。<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>新的设备。<BR>&nbsp;<BR>在sbull中,revalidate方法在没有一个有效区域的情况下试图分配一块新的数据区域。<BR>&nbsp;<BR>&nbsp;<BR>(代码253&nbsp;&nbsp;2#)<BR>&nbsp;<BR>(代码254&nbsp;&nbsp;1#)<BR>&nbsp;<BR>特别注意<BR>&nbsp;<BR>当可拆卸设备已经打开时,驱动程序也应该检查是否有磁盘的改变;在mount时核心自动<BR>调用它的check-_disk_change函数,但在open时,并不这样做。<BR>&nbsp;<BR>不过,有些程序直接访问磁盘数据而不安装这个设备,fsck,mcopy和fdisk都是这类程<BR>序的例子。如果驱动程序在内存中保存可拆卸设备的状态信息,它应在设备第一次打开<BR>时调用check_disk_change函数。这个核心函数还要依赖驱动程序方法(check_media_ch<BR>ange和revalidate),因此在open里不须实现任何特别的东西。<BR>&nbsp;<BR>这里是open的sbull实现,它关注了发生磁盘改变的情况:<BR>&nbsp;<BR>(代码254&nbsp;&nbsp;2#)<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>在驱动程序中不需对磁盘的改变做任何别的。如果一个磁盘被改变了,而它的打开计数<BR>大于零,那么数据会被破坏。防止这种情况发生的唯一方法是让利用在物理上支持的设<BR>备使用计数控制门锁。open和close可以在合适的时候关闭或打开锁。<BR>&nbsp;<BR>可分区设备<BR>&nbsp;<BR>如果你想用fdisk生成分区,你会发现它们有一些问题。fdisk程序称这些分区为/dev/sb<BR>ull01,/dev/sbull02以&nbsp;此类推,但文件系统上并不存在这些名字。的确,基本的sbull<BR>设备是一个字节阵列,不存在提供访问数据区域的子区域的入口点,因此想对sbull进行<BR>分区是行不通的。<BR>&nbsp;<BR>为了能对设备分区,我们必须给每个物理设备分配几个次设备号。一个数字用来访问整<BR>个设备(如/dev/hda),其它的用来访问不同的分区(如/dev/hda1)。由于fdisk产生<BR>分区名的办法是在全盘设备名后加一个数字后缀,我们将在后面的块设备驱动程序中遵<BR>循同样的命名规则。<BR>&nbsp;<BR>在本节中我将要介绍的设备叫spull,因此它是一个“简单的可分区工具(Simple<BR>Partitionable&nbsp;Utility)”。这个设备位于spull目录,完全与sbull无关,尽管它们共<BR>享很多代码。<BR>&nbsp;<BR>在字符设备驱动程序scull中,不同的次设备号可以实现不同的行为,因此一个驱动程序<BR>可以显示几种不同的实现。而按照次设备号区分块设备是不可行的,这就是为什么sbull<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>可以显示几种不同的实现。而按照次设备号区分块设备是不可行的,这就是为什么sbull<BR>和spull被分离开。这种无能为力是块设备驱动程序的一个基本特征,因为几个数据结构<BR>和宏只是作为主设备号的函数定义的。<BR>&nbsp;<BR>关于移植,需要注意的是可分区模块不能被加载到核心的1.2版,因为符号resetup_one_<BR>dev(在本节后面介绍)没有被引出到模块。在对SCSI盘的支持模块化之前,没有人会考<BR>虑可分区的模块。<BR>&nbsp;<BR>我要介绍的设备结点被称做pd,表示“可分区磁盘(partitionable&nbsp;disk)”。四个完<BR>整的设备(又称“单元”)被称做/dev/pda直到/dev/pdd;每个设备最多支持15个分区<BR>。次设备号有下面的含义:低四位表示分区号(0为完整的设备),高四位表示单元号。<BR>这个规则在源文件中由下面的宏表达:<BR>&nbsp;<BR>(代码255)<BR>&nbsp;<BR>普通硬盘<BR>&nbsp;<BR>每个可分区设备需要知道它是如何分区的。这个信息可以从分区表中得到。初始化进程<BR>的一部分包括解码分区表,并更新内部数据结构以反映分区信息。<BR>&nbsp;<BR>这个解码并不容易。不过幸运的是,核心提供可被所有块设备驱动程序使用的“普通硬<BR>盘”支持,它显著地减少了处理分区驱动程序的代码。这个普通支持的另一个好处是驱<BR>动程序的作者不必理解分区是如何完成的,而不需要修改驱动程序的代码就可以在核心<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>动程序的作者不必理解分区是如何完成的,而不需要修改驱动程序的代码就可以在核心<BR>中支持新的分区方式。<BR>&nbsp;<BR>想要支持分区的块设备驱动程序要包含&lt;linux/genhd.h&gt;,并声明结构gendisk。所有这<BR>样的结构被组织在一个链表中,它的头是全局指针gendisk_head。<BR>&nbsp;<BR>在我们进行下一步之前,让我们先看看结构gendisk的域。你为了利用普通设备支持就需<BR>要理解它们。<BR>&nbsp;<BR>int&nbsp;major<BR>&nbsp;<BR>确定这个结构所指的设备驱动程序的主设备号。<BR>&nbsp;<BR>const&nbsp;char*major_name<BR>&nbsp;<BR>属于这个主设备号的设备的基本名。每个设备名是通过在这个名字后为每个单元加一个<BR>字母并为每个分区加一个数字得到。例如,“hd”是用来构成/dev/hda1和/dev/hda3的<BR>基本名。基本名最多5个字符长,因为add_partition在一个8字节的缓冲区中构造全名,<BR>它要附加上一个确定单元的字母,分区号和一个终止符‘\0’。spull所用的名字是pd(<BR>“可分区磁盘(partitionable&nbsp;disk)”)。<BR>&nbsp;<BR>int&nbsp;minor_shift<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>从设备的次设备号中获取驱动器号要进行移位的次数。在spull中这个数是4。这个域中<BR>的值应与宏DEVICE_NR(device)中的定义一致(见本章前面的“头文件blk.h”)。spull<BR>中的宏扩展为device&gt;&gt;4。<BR>&nbsp;<BR>int&nbsp;max_p<BR>&nbsp;<BR>分区的最大数目。在我们的例子中,max_p1是16,或更一般地,是&lt;&lt;minor_shift。<BR>&nbsp;<BR>int&nbsp;max-_nr<BR>&nbsp;<BR>单元的最大数目。在spull中,这个数字是4。单元最大数目在移位minor_shift次后的结<BR>果应匹配次设备号的可能的范围,目前是0-255。IDE驱动程序可以同时支持很多驱动器<BR>和每一个驱动器很多分区,因为它注册了几个主设备号,从而绕过了次设备号范围小的<BR>问题。<BR>&nbsp;<BR>void(*init)(struct&nbsp;gendisk*)<BR>&nbsp;<BR>驱动程序的初始化函数,它在初始化设备后和分区检查执行前被调用。我将在下面介绍<BR>这个函数更多的细节。<BR>&nbsp;<BR>struct&nbsp;hd_struct&nbsp;*part<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>设备的解码后的分区表。驱动程序用这一项确定通过每个次设备号哪些范围的磁盘扇区<BR>是可以访问的。大多数驱动程序实现max_nr&lt;&lt;minor_shift个结构的静态数值,并负责数<BR>组的分配和释放。在核心解码分区表之前驱动程序应将数组初始化为零。<BR>&nbsp;<BR>int&nbsp;*sizes<BR>&nbsp;<BR>这个域指向一个整数数组。这个数组保持着与blk_size同样的信息。驱动程序负责分配<BR>和释放该数据区域。注意设备的分区检查把这个指针拷贝到blk_size,因此处理可分区<BR>设备的驱动程序不必分配这后一个数组。<BR>&nbsp;<BR>int&nbsp;nr_real<BR>&nbsp;<BR>存在的真实设备(单元)的个数。这个数字必须小于等于max_nr。<BR>&nbsp;<BR>void&nbsp;*real_devices<BR>&nbsp;<BR>这个指针被那些需要保存一些额外私有信息的驱动程序内部使用(这与filp-&gt;private_d<BR>ata类似)。<BR>&nbsp;<BR>void&nbsp;struct&nbsp;gendisk&nbsp;*next<BR>&nbsp;<BR>在普通硬盘列表中的一根链。<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>在普通硬盘列表中的一根链。<BR>&nbsp;<BR>分区检查的设计最适合那些直接链入核心映象的驱动程序,因此我将从介绍核心代码的<BR>基本结构开始。以后我将介绍spull模块处理它的分区的方法。<BR>&nbsp;<BR>核心中的分区检测<BR>&nbsp;<BR>在引导时,init/main.c调用了各种各样的初始化函数。其中一个是start_kernel,它通<BR>过调用device_setup来初始化所有的驱动程序。这个函数又调用blk_dev_init,接着检<BR>查所有注册的普通硬盘的分区信息。任何一个块设备驱动程序,如果它找到至少一个它<BR>的设备,就将这个驱动程序的genhd结构注册到核心列表中,这样它的分区便可以被正确<BR>地检测出来。<BR>&nbsp;<BR>因此,一个可分区的驱动程序应该声明它自己的结构genhd。这个结构看起来如下:<BR>&nbsp;<BR>(代码258)<BR>&nbsp;<BR>于是,在这个驱动程序的初始化函数中,这个结构被排队在可分区设备的主列表中。<BR>&nbsp;<BR>被链入核心的驱动程序的初始化函数与init_module等价,即使它被调用的方式不同。这<BR>个函数一定包含如下两行,它们用来将结构排队:<BR>&nbsp;<BR>my_gendisk.next=gendisk_head;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>my_gendisk.next=gendisk_head;<BR>&nbsp;<BR>gendisk_head=my_gendisk;<BR>&nbsp;<BR>通过将结构插入链表,这简单的两行便是驱动程序入口点为所有的分区正确地识别和配<BR>置所需要的所有内容。<BR>&nbsp;<BR>额外的设置通过my_geninit完成。在上面的例子中,这个函数填充“单元数”域来反映<BR>计算机系统的实际硬件设置。在my_geninit结束后,gendisk.c为所有的盘(单元)执行<BR>实际的分区检测。你可以看到系统启动时被检测的分区,因为gendisk.c在系统控制台上<BR>打印分区检查Partition&nbsp;check:,后面跟随它在可得的普通硬盘上找到的所有分区。<BR>&nbsp;<BR>你可以修改前面的代码,推迟my_sizes和my_partitions的分配直到my_geninit函数。这<BR>可以节省少量的核心内存,因为这些数组可以小到nr_real&lt;&lt;minor_shift,而竟态数组<BR>则必须为max_nr&lt;&lt;minor_shift字节长。不过,典型的数值是每个物理单元节省几百个字<BR>节。<BR>&nbsp;<BR>模块中的分区检测<BR>&nbsp;<BR>一个模块化的驱动程序和链接到核心的驱动程序的区别在于它无法受益于集体中的初始<BR>化。相反,它需要处理它自己的设置。由于没有为模块的两步初始化,所以spull的gend<BR>isk结构在它的init函数指针中有一个NULL指针:<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>(代码259&nbsp;1#)<BR>&nbsp;<BR>同时也不必在普通硬盘的全局链表里注册gendisk结构。<BR>&nbsp;<BR>通过引出函数resetup_one_dev,文件gendisk.c被准备用来处理象模块需要一类“晚的<BR>”初始化。resetup_one_dev为单个物理设备扫描分区。其原型是:<BR>&nbsp;<BR>boid&nbsp;resetup_one_dev(struct&nbsp;gendisk&nbsp;*dev,int&nbsp;drive);<BR>&nbsp;<BR>从这个函数名字你可以看出来它是要改变一个设备的设置信息。这个函数被设计为由ioc<BR>tl里BLKRRPART实现调用,但他也可以被用来完成一个模块的初始设置。<BR>&nbsp;<BR>当一个模块被初始化后,它应该为每个它将要访问的物理设备调用resetup_one_dev,从<BR>而将分区信息贮存my_gendisk-&gt;part中。分区信息会被设备的request_fn函数使用。<BR>&nbsp;<BR>在spull中,init_module函数除了通常的指令外还包含了下面的代码。它分配分区检测<BR>所需的数组并初始化数组中完整磁盘的项目。<BR>&nbsp;<BR>(代码259&nbsp;2#)<BR>&nbsp;<BR>(代码260&nbsp;1#)<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>有趣的是注意到resetup_one_dev通过重复调用下面函数打印分区信息:<BR>&nbsp;<BR>printk(“%s:”,disk_name(hd,minor,buf));<BR>&nbsp;<BR>这就是为什么spull要打印一个引导串。它意味着要为塞进系统日志的信息增加一些上下<BR>文。<BR>&nbsp;<BR>当一个可分区的模块被卸载时,驱动程序应该通过为每个支持的主/次对调用fsync_dev<BR>来安排所有的分区刷新。而且,如果结构gendisk被插在全局链表中,它应该被删除——<BR>注意spull并未自己插入它,原因上面提到过。<BR>&nbsp;<BR>spull的清除函数是:<BR>&nbsp;<BR>(代码260&nbsp;2#)<BR>&nbsp;<BR>(代码261)<BR>&nbsp;<BR>使用Initrd进行分区检测<BR>&nbsp;<BR>如果你想从一个设备上安装你的根文件系统,而这个设备的驱动程序只有模块化的形式<BR>,你就必须使用由现代Linux核心提供的Initrd工具。我不想在这里介绍Initrd,这一小<BR>节是针对那些了解Initrd并想知道它是如何影响块设备驱动程序的读者的。<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>使用Initrd进行分区检测<BR>&nbsp;<BR>如果你想从一个设备上安装你的根文件系统,而这个设备的驱动程序只有模块化的形式<BR>,你就必须使用由现代Linux核心提供的Initrd工具。我不想在这里介绍Initrd,这一小<BR>节是针对那些了解Initrd并想知道它是如何影响块设备驱动程序的读者的。<BR>&nbsp;<BR>当你用Initrd引导一个核心时,它会在安装真正的根文件系统之前建立一个暂时的运行<BR>环境。模块通常是从被用作临时根文件系统的ramdisk中装载。<BR>&nbsp;<BR>&nbsp;<BR>--<BR><FONT 
      color=#00ff00>※&nbsp;来源:.华南网木棉站&nbsp;bbs.gznet.edu.cn.[FROM:&nbsp;202.38.196.234]</FONT><BR>--<BR><FONT 
      color=#00ffff>※&nbsp;转寄:.华南网木棉站&nbsp;bbs.gznet.edu.cn.[FROM:&nbsp;211.80.41.106]</FONT><BR>--<BR><FONT 
      color=#0000ff>※&nbsp;转寄:.华南网木棉站&nbsp;bbs.gznet.edu.cn.[FROM:&nbsp;211.80.41.106]</FONT><BR>--<BR><FONT 
      color=#ffff00>※&nbsp;转载:.南京大学小百合站&nbsp;bbs.nju.edu.cn.[FROM:&nbsp;211.80.41.106]</FONT><BR>--<BR><FONT 
      color=#ff0000>※&nbsp;转载:·饮水思源&nbsp;bbs.sjtu.edu.cn·[FROM:&nbsp;211.80.41.106]</FONT><BR></P></FONT>
      <P align=center><A href="http://joyfire.net/lsdp/index.htm"><FONT 
      color=#ffffff size=2>目录页</FONT></A> | <A 
      href="http://joyfire.net/lsdp/14.htm"><FONT color=#ffffff 
      size=2>上一页</FONT></A> | <A href="http://joyfire.net/lsdp/16.htm"><FONT 
      color=#ffffff size=2>下一页</FONT></A></P></SPAN></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="90%" align=center border=0>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -