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

📄 核心源码的物理布局.txt

📁 献给ARM初学者
💻 TXT
📖 第 1 页 / 共 3 页
字号:
      ”表示Sparc。引导Sparc比Alpha要简单一些;它的固件可以访问设备,silo只需要访问
      Linux文件系统,并与用户交互。出于这个目的,silo被链接到libext2,这是支持对未

      安装分区上的文件进行处理的一个库。
       
      若不使用silo,也可以从软盘或网络上引导计算机。固件可以用RARP(反向ARP)和tftp
      协议从以太网上装载一个核心。事实上,我从未用软盘引导过我自己的工作站,因为Lin
      ux的Sparc发布允许通过网络引导来完成系统安装。
       
      对Sparc来说,的确没有什么特别的要求。没有实模式,也没有需要复制的内存。一旦核
      心被加载到RAM,它便开始执行。
       
       
       
      Init进程
       
      由start_kernel生成的线程派生出bdflush(源码见fs/buffer.c)和kswapd(在mm/vmsc
      an.c中定义),它们因此被赋予进程号2和3。init进程(pid 1)接着进行进一步的初始
      化,这在之前不可能完成;也就是,它运行与SMP相关的函数,如果需要,还有initrd引
      导技巧,以另一个核心线程的形式。在initrd结束后,init线程激活UMSDOS文件系统的
      “伪根(pseudo-root)”。
       
      在完成初始化后,init的实际作用是进入用户空间并执行一个程序(因此变成一个进程
      )。这样三个stdio通道被连到第一个虚拟控制台,核心试图从/etc执行init。如果失败
      ,它将查看/bin和/sbin(在所有最近的发布中,init一定居于此)。如果init从这三个

      目录的执行都失败了,进程将会执行/etc/rc,如果这个也失败了,它就循环,执行/bin
      /sh。在大多数情况下,函数能运行init成功;其它选项的目的是为了在init不能执行时
      允许系统恢复。
       
      如果核心命令行指定了一条要执行的命令,使用init=some_program导语,进程1就执行
      指定的命令,而不是调用init。
       
      不管系统是怎么设置的,init最终在用户空间执行,以后的核心操作都是对来自用户程
      序的系统调用的响应。
       
       
       
      kernel目录
       
      大多数关键的核心功能都是在这个目录实现的。这里最重要的源文件是sched.c,它值得
      特别对待。
       
       
       
      sched.c
       
      正如源文件自己表明的,这是“主核心文件”。它由调度程序和相关操作组成,例如让

      进程睡眠和唤醒它们,以及核心计时器的管理(见第六章“时间流”中“核心计时器”
      一节),间隔计时器(它与计帐和性能刻划有关),以及预定义任务队列(见第六章“
      预定义任务队列”)。
       
      如果你对Linux调度程序的实时策略感兴趣,你可以在schedule函数及其相关者中找到低
      级的信息。其中一个相关者是goodness,它给进程赋优先值,并帮助调度程序选择下一
      个要运行的进程。
       
      与调度程序控制相关的函数(及系统调用)也在这个文件中定义。这包括设置和取得调
      度策略及优先级。在除Alpha外的其它体系结构上,系统调用nice也在这个源文件中。
       
      另外,取得和设置用户及组id也在sched.c中定义(除了Alpha),同时还有alarm调用*
       
      在sched.c中还能找到的其它好东西包括show_tasks和show_state函数,它们实现了在第
      四章“调试技巧”中“系统挂起”一节所描述的“魔幻”键中的两个。
       
       
       
      进程控制
       
      目录的其它主要部分都是管理进程的。fork和exit系统调用在两个同名源文件中实现,

      信号控制在signal.c中实现。大多数信号处理的调用在Alpha中实现的方法是不同的,以
      保证Alpha的移植与Digital Unix二进制兼容。
       
      fork的实现包括clone系统调用的代码,fork.c显示了clone的标志是如何使用的。应该
      注意sys_fork并不在fork.c中定义,因为Sparc的实现与其它的版本稍有不同;不过,多
      数sys_fork的实现只是调用do_fork,它在fork.c中定义。提供一个缺省实现(通常叫做
      do_fnct),而真正的系统调用(sys_fnct)则在各个移植中声明,这是Linux常用的一
      个技巧,随着新的移植的出现,这个技巧很可能扩展到其它的系统调用。
       
      exit.c实现sys_exit和不同的wait函数,以及信号的实际发送。(signal.c专用于信号
      处理,而不是发送。)
       
       
       
      模块化
       
      文件module.c和ksyms.c包含了在第二章“构造和运行模块”中描述的机制。module.c含
      有被insmod及相关程序使用的系统调用,ksyms.c声明不属于特定子系统的核心中的公共
      符号。其它的公共符号由特定核心子系统的初始化函数使用register_symtab声明。例如
      ,fs/proc/procfs_syms.c为注册新文件声明/proc接口。
       
       

       
      其它操作
       
      这个目录中的其余源文件为一些低级操作提供软件接口。time.c从用户程序读写核心时
      间值,resource.c为I/O端口实现请求和释放机制,dma.c为DMA通道完成同样的工作。so
      ftirq.c处理下半部(见第九章“中断处理”中“下半部”一节),itimer.c定义系统调
      用来设置和取得间隔计时器值。
       
      想知道核心的消息处理是如何工作的,你可以看printk.c,它显示了在第四章介绍的几
      个概念的一些细节(也就是说,它包含了printk和sys_syslog的代码)。
       
      exec_domain.c包含了获得与其它风格的Unix兼容性所需要的代码,info.c定义了sys_in
      fo。panic.c做的工作正如它的名字所示;它还支持在系统不稳定后自动重启动。重新引
      导发生在由/proc/sys/kernel/panic设置的延迟之后。这个延迟通过对udelay(1000)的
      重复调用实现,因为在系统崩溃后,调度程序不再运行,udelay可以用于不长于1毫秒的
      延迟(见第六章“长延迟”一节)。
       
      sys.c实现几个系统配置和权限处理函数,如uname,setsid及类似的调用。sysctl.c包
      含sysctl调用的实现和在sysctl表(系统控制入口点列表)注册及取消注册的入口点。
      这个文件也提供了按照注册的表访问/proc/sys文件的能力。
       
       

       
      mm目录
       
      在mm目录中的文件为Linux核心实现内存管理中体系结构无关的部分。这个目录包含换页
      及内存的分配和释放的函数,还有允许用户进程将内存区间映射到它们地址空间的各种
      技术。
       
       
       
      换页和对换
       
      令人惊奇的是,swap.c并未实现对换算法。相反,它处理核心的命令行选项swap=和buff
      =。这些选项也可以通过sysctl系统调用或写文件/proc/sys/vm来设置。
       
      sawp_state.c负责维护对换高速缓存,是这个目录中最难的文件;我不想讨论它的细节
      ,因为很难理解它的设计,除非以前对相关的数据结构和策略已经有了很好的了解。
       
      swapfile.c实现对换文件和设备的管理。swapon和swapoff系统调用在这里定义,后者代
      码非常困难。作为比较,有几个Unix系统没有实现swapoff,这样如果不重启动就无法停
      止向一个设备或文件的对换。swapfile.c还声明了get_swap_page,它从对换池中取得一
      个空闲页。
       

      vmscan.c实现换页策略。kswapd守护进程在这个文件定义,还有扫描内存,运行进程寻
      找可换出的页的函数。
       
      最后,page_io.c实现了与对换空间之间进行低级数据传送的功能。这个文件管理保证系
      统一致性的锁机制,提供同步和异步的I/O。它还处理与不同设备使用不同块大小相关的
      问题。(在Linux的早期版本,不可能对换到一个FAT分区上,因为不支持512字节的块。
       
       
       
      分配和释放
       
      再第七章介绍的内存分配技巧都在mm目录实现。让我们再一次从最常用的函数开始:kma
      lloc。
       
      kmalloc.c实现内存区域的分配和释放。kmalloc的内存池由一些“桶”组成,每个桶是
      同样大小的内存区域的列表。kmalloc.c的主要功能是管理每个桶的链表。
       
      当需要新页或有页面被释放时,这个文件利用在page_alloc.c中定义的函数。页面用__g
      et_free_pages从空闲内存中取得,这是一个从空闲页列表中取得页面的短函数。如果空
      闲列表中没有内存可用,就调用try_to_free_pages(vmscan.c)。
       

      vmalloc.c实现了vmalloc,vremap,vfree函数。vmalloc返回核心虚拟空间中的连续内
      存,vremap给出特定物理地址的新的虚地址;它主要用来访问高端内存的PCI缓冲。vfre
      e释放内存,如它的名字所示。
       
       
       
      其它接口
       
      Linux内存管理最重要的函数是memory.c文件的一部分。这些函数一般不能通过系统调用
      访问,因为它们处理硬件的换页机制。
       
      另一方面,模块的作者的确使用这些函数。verify_area和remap_page_range在memory.c
      中定义。其它有趣的函数是do_up_page和do_no_page,它们实现核心对次和主页面错的
      反应。文件中的其余函数处理页表,都非常低级。
       
      内存映射是mm目录中文件处理的另一个大任务。filemap.c的代码很复杂。它实现常规文
      件的内存映射,提供支持共享映射的能力。被映射文件通过被映射页的特殊结构vm_oper
      ations支持,如在十三章“Mmap和DMA”中“虚拟内存区域”一节中所描述的。这个源文
      件页处理异步提前读;注释解释了结构file中四个提前读域的含义。这个文件中出现的
      唯一的系统调用是sys_msym。到内存映射的顶级接口(即do_mmap)出现在mmap.c。
       
      这个文件有定义brk系统调用开始,它被一个进程用来请求其最高允许的虚地址被增加或

      减小。sys_brk的代码提供了很多信息,即使你不是内存管理的大师。mmap.c的其余部分
      集中在do_mmap和do_munmap。如你所期望的,内存映射通过filp->f_op完成,尽管filp
      对do_mmap可能为NULL。这是brk 如何分配新的虚拟空间的。它还是依赖于内存映射零页
      ,而不需要特殊代码。
       
      mremap.c包括sys_mremap。如果你已经搞清楚了mmap.c,这个文件就很容易了。
       
      与内存锁定和解锁相关的四个系统调用在mlock.c中定义,它是相当简单发源文件。类似
      第,mprotect.c负责执行sys_mprotect。这些文件在定义上很相似,因为它们都修改了
      与进程页相关的系统标志。
       
       
       
      fs目录
       
      在我的观点中,这个目录是整个源码树中最有趣的一部分。文件处理是任何Unix系统的
      一个基本活动,所有与文件相关的操作都在这个目录中实现。我不想在这儿描述fs子目
      录,因为每种文件系统类型仅仅是把VFS层映射到特别的信息布局上。但讲一下fs目录中
      多数文件的作用还是很重要的,因为它们携带了大量的信息。
       
       
       

      Exec和二进制格式
       
      Unix中最重要的系统调用是exec。用户程序可以exec六种不同的形式,但在只有一个在
      核心中实现----其它都是隐射到全特征实现execve上的库函数。
       
      exec函数用一个已注册二进制格式的表以查找用来加载和执行一个磁盘文件的正确的加
      载程序。源文件的第一部分定义了register_binfmt接口。有兴趣的是注意到脚本文件的
      #!魔幻键被作为二进制格式处理,如ELF和另外一些格式(尽管在1.2版本中它是exec.c
      的一种特殊情况)。kerneld也需要了解一个新的二进制格式是如何按照要求从读取源文
      件被加载的。
       
      每个二进制格式由一个定义了三种操作(加载一个二进制文件,加载一个库,倾倒内核
      (core dump))的数据结构描述。这个结构在<linux/binfmts.h>中定义。决大多数格
      式都只支持第一个操作(加载一个文件),不过这个接口已经足够一般化,能够支持任
      何可预见的新格式的需要。
       
       
       
      devices.ch和block_dev.c
       
      我们已经用了 devices.c中的代码,因为它负责设备注册和取消注册。它同时也负责缺
      省的设备open方法,以及对块设备的release方法。这些调用为被打开或关闭的设备取得

      正确的文件操作并将执行分派到正确的方法。对模块自动加载的支持在这个文件中实现
      。除了打开和关闭设备之外的所有东西都出现在drivers/*目录,如filp->f_op所指示的
       
      block_dev.c包含读写块设备的缺省方法。如你可能记得的,一个块驱动程序并不声明它
      自己的I/O方法,只是它的请求例程。block_dev.c的缺省读写实现了解缓冲高速缓存,
      除了实际的数据传送它提你做了所有的事情。
       
       
       

⌨️ 快捷键说明

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