您好,欢迎访问三七文档
第二章内核系统初始化2.1系统初始化流程简介2.2内核文件解读2.3中断及任务调度管理2.4虚拟文件系统2.5网络协议栈各部分初始化2.6linux设备管理编译源码首先进入linux源码目录,一般在/usr/src/linux目录下,如果没有的话可以从网上下载。下载地址为:下载到本地后复制到/usr/src/目录,文件名一般如下格式:linux-2.6.38-rc6.tar.bz2对下载的文件进行解压,命令如下:#tar–jxvflinux-2.6.38-rc6.tar.bz2#ln–slinux-2.6.38-rc6.tar.bz2linux进入/usr/src/linux目录,运行makemenuconfig命令,出现内核加载菜单,如图所示图中选项前的“*”表示模块被编译进内核,在系统启动的时候被主函数调用执行选项“M”表示被编译成一个“.ko”文件,放在某个目录下,系统启动的时候依靠脚本把这些目标文件装入内核2.1系统初始化流程简介Linux系统启动,主要与以下代码密切相关:4个汇编程序:bootsect.S、setup.S、head.S、entry.S。init目录下的main.c在系统启动中,要关注以下几个方面:中断系统及调度系统文件系统初始化设备管理系统的初始化网络协议的初始化2.4虚拟文件系统VFS在Linux及UNIX系统中是非常重要的概念与其他任何系统不同,Linux系统并不通过设备标识访问某个文件系统,而是将它们捆绑在一个树型结构中文件系统安装时(mount),linux将它挂到树的某个节点(目录),文件系统的所有文件就是该目录下的文件或子目录,直到文件系统卸载(umount)VFS只存在于内存中,在系统启动时被创建,系统关闭时注销。VFS的作用就是屏蔽各种文件系统的差异,给用户、应用程序,甚至linux其他管理模块提供统一的接口集合管理VFS数据结构的组成部分主要包括超级块和inodeVFS的目录树文件系统的注册这里的文件系统是指可能会被挂载到目录树中的各个实际文件系统,所谓实际文件系统,即是指VFS中的实际操作最终要通过它们来完成而已,并不意味着它们一定要存在于某种特定的存储设备上。比如在Linux机器下就注册有“rootfs”、“proc”、“ext2”、“ext3”、sockfs等十几种文件系统。数据结构在Linux源代码中,每种实际的文件系统用以下的数据结构表示:structfile_system_type{constchar*name;intfs_flags;structsuper_block*(*read_super)(structsuper_block*,void*,int);structmodule*owner;structfile_system_type*next;structlist_headfs_supers;};注册过程实际上将表示各实际文件系统的structfile_system_type数据结构的实例化,然后形成一个链表,内核中用一个名为file_systems的全局变量来指向该链表的表头。注册rootfs文件系统在众多的实际文件系统中,之所以单独介绍rootfs文件系统的注册过程,实在是因为该文件系统VFS的关系太过密切,如果说ext2/ext3是Linux的本土文件系统,那么rootfs文件系统则是VFS存在的基础。一般文件系统的注册都是通过module_init宏以及do_initcalls()函数来完成(读者可通过阅读module_init宏的声明及arch\i386\vmlinux.lds文件来理解这一过程),但是rootfs的注册却是通过init_rootfs()这一初始化函数来完成,这意味着rootfs的注册过程是Linux内核初始化阶段不可分割的一部分。init_rootfs()通过调用register_filesystem(&rootfs_fs_type)函数来完成rootfs文件系统注册的,其中rootfs_fs_type定义如下:structfile_system_typerootfs_fs_type={\name:rootfs,\read_super:ramfs_read_super,\fs_flags:FS_NOMOUNT|FS_LITTER,\owner:THIS_MODULE,\}注册之后的file_systems链表结构如下图VFS目录树的建立既然是树,所以根是其赖以存在的基础,本节阐述Linux在初始化阶段是如何建立根结点的,即/目录。这其中会包括挂载rootfs文件系统到根目录/的具体过程。构造根目录的代码是在init_mount_tree()函数(fs\namespace.c)中。首先,init_mount_tree()函数会调用do_kern_mount(rootfs,0,rootfs,NULL)来挂载前面已经注册了的rootfs文件系统。这看起来似乎有点奇怪,因为根据前面的说法,似乎是应该先有挂载目录,然后再在其上挂载相应的文件系统,然而此时VFS似乎并没有建立其根目录。没关系,这是因为这里我们调用的是do_kern_mount(),这个函数内部自然会创建我们最关心也是最关键的根目录(在Linux中,目录对应的数据结构是structdentry)。在这个场景里,do_kern_mount()做的工作主要是:1)调用alloc_vfsmnt()函数在内存里申请了一块该类型的内存空间(structvfsmount*mnt),并初始化其部分成员变量。2)调用get_sb_nodev()函数在内存中分配一个超级块结构(structsuper_block)sb,并初始化其部分成员变量,将成员s_instances插入到rootfs文件系统类型结构中的fs_supers指向的双向链表中。3)通过rootfs文件系统中的read_super函数指针调用ramfs_read_super()函数。还记得当初注册rootfs文件系统时,其成员read_super指针指向了ramfs_read_super()函数,参见图2.4)ramfs_read_super()函数调用ramfs_get_inode()在内存中分配了一个inode结构(structinode)inode,并初始化其部分成员变量,其中比较重要的有i_op、i_fop和i_sb:5)ramfs_read_super()函数在分配和初始化了inode结构之后,会调用d_alloc_root()函数来为VFS的目录树建立起关键的根目录(structdentry)dentry,并将dentry中的d_sb指针指向sb,d_inode指针指向inode。6)将mnt中的mnt_sb指针指向sb,mnt_root和mnt_mountpoint指针指向dentry,而mnt_parent指针则指向自身。Linux系统下的文件普通文件目录文件块设备字符设备链接命名管道套接字2.5网络实现概述内核中的联网代码组织成三层,如图所示(1)插口层是一个到下面协议相关层的协议无关接口。所有系统调用从协议无关的插口层开始。例如:在插口层中的bind系统调用的协议无关代码包含几十行代码,它们验证的第一个参数是一个有效的插口描述符,并且第二个参数是一个进程中的有效指针。然后调用下层的协议相关代码,协议相关代码可能包含几百行代码。(2)协议层包括我们前面提到的四种协议族(TCP/IP,XNS,OSI和UNIX域)的实现。每个协议族可能包含自己的内部结构,在图中我们没有显示出来。例如,在Internet协议族中,IP(网络层)是最低层,TCP和UDP两运输层在IP的上面。(3)接口层包括同网络设备通信的设备驱动程序。网络缓存mbuf在伯克利联网代码设计中的一个基本概念就是存储器缓存,称作一个mbuf,在整个联网代码中用于存储各种信息。包含插口地址结构的mbuf包含数据的mbuf添加IP和UDP首部IP输出IP输出例程要填写IP首部中剩余的字段,包括IP检验和;确定数据报应发到哪个输出接口(这是IP路由功能);必要时,对IP报文分片;以及调用接口输出函数。假设输出接口是一个以太网接口,再次把此mbuf链表的指针作为一个参数,调用一个通用的以太网输出函数。以太网输出以太网输出函数的第一个功能就是把32位IP地址转换成相应的48位以太网地址。在使用ARP(地址解析协议)时会使用这个功能,并且会在以太网上发送一个ARP请求并等待一个ARP应答。此时,要输出的mbuf链表已得到,并等待应答。然后以太网输出例程把一个14字节的以太网首部添加到链表的第一个mbuf中,紧接在IP首部的前面。以太网首部包括6字节以太网目标地址、6字节以太网源地址和2字节以太网帧类型。之后此mbuf链表被加到此接口的输出队列队尾。如果接口不忙,接口的“开始输出”例程立即被调用。若接口忙,在它处理完输出队列中的其他缓存后,它的输出例程会处理队列中的这个新mbuf。当接口处理它输出队列中的一个mbuf时,它把数据复制到它的传输缓存中,并且开始输出。在我们的例子中,192字节被复制到传输缓存中:14字节以太网首部、20字节IP首部、8字节UDP首部及150字节用户数据。这是内核第三次遍历这些数据。一旦数据从mbuf链表被复制到设备传输缓存,mbuf链表就被以太网设备驱动程序释放。这三个mbuf被放回到内核的自由缓存池中。输入处理输入处理与刚讲过的输出处理不同,因为输入是异步的。就是说,它是通过一个接收完成中断驱动以太网设备驱动程序来接收一个输入分组,而不是通过进程的系统调用。内核处理这个设备中断,并调度设备驱动程序进入运行状态。以太网输入以太网设备驱动程序处理这个中断,假定它表示一个正常的接收已完成,数据从设备读到一个mbuf链表中。在我们的例子中,接收了54字节的数据并复制到一个mbuf中:20字节IP首部、8字节UDP首部及26字节数据(服务器的时间与日期)。下图的是这个mbuf的格式。设备驱动程序把mbuf传给一个通用以太网输入例程,它通过以太网帧中的类型字段来确定哪个协议层来接收此分组。在这个例子中,类型字段标识一个IP数据报,从而mbuf被加入到IP输入队列中。另外,会产生一个软中断来执行IP输入例程。这样,这个设备中断处理就完成了。IP输入IP输入是异步的,并且通过一个软中断来执行。当接口层在系统的一个接口上收到一个IP数据报时,它就设置这个软中断。当IP输入例程执行它时,循环处理在它的输入队列中的每一个IP数据报,并在整个队列被处理完后返回。IP输入例程处理每个接收到的IP数据报。它验证IP首部检验和,处理IP选项,验证数据报被传递到正确的主机(通过比较数据报的目标IP地址与主机IP地址),并当系统被配置为一个路由器,且数据报被表注为其他的IP地址时,转发此数据报。如果IP数据报到达它的最终目标,调用IP首部中标识的协议的输入例程:ICMP,IGMP,TCP或UDP。在我们的例子中,调用UDP输入例程去处理UDP数据报。UDP输入UDP输入例程验证UDP首部中的各字段(长度与可选的检验和),然后确定是否一个进程应该接收此数据报。一个进程可以接收到一指定UDP端口的所有数据报,或让内核根据源与目标IP地址及源与目标端口号来限制数据报的接收。因为这个UDP数据报要传递给我们的进程,发送方的IP地址和UDP端口号被放置到一个mbuf中,这个mbuf和数据(在我们的例子中是26字节)被追加到此插口的接收队列中。下图所示的是被追加到这个插口的接收队列中的这两个mbuf。在链表的第一个mbuf中包括一个16字节internet插口地址结构,它带有发送方IP地址和UDP端口号。它的类型是MT_SONAME。这个mbuf是插口层创建的,将这些信息返回给通过调用系
本文标题:网络协议第二章
链接地址:https://www.777doc.com/doc-2142119 .html