




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Linux内核解析
IBootstrap
1汇编代码分析
2start_kernel函数
3准备进入用户态
3.1Initrd初始化
3.1.0准备知识
在讲述如何释放initrd到rootfs之前,有比较讲述一下什么是rootfs,rootfs的初始化相关的函数;以及rootfs
的初始化函数是如何被调用的。
这里所说的rootfs指的是VFS的根节点/,以及在内存中创建的根目录/下的文件和目录节点,这个文件系
统仅仅存在于内存之中,由内核初始化的时候负责创建,该文件系统不会存储到其它非易失性介质上。该
rootfs文件系统mntinit函数调用initrootfs和initmounttree两个函数来负责创建和初始化:
void_initmnt_init(void)
〃这个函数很简单,就是注册了rootfs的文件系统。
init_rootfs();
〃在这里,将rootfs文件系统挂载,它的挂载点默认为。
〃最后切换进程的根目录和当前目录为,这也就是根目录的由来。
〃不过这里只是初始化,等挂载完具体的文件系统之后,
〃一般都会将根目录切换到具体的文件系统,所以在系统启动之后,
〃用mount命令是看不到rootfs的挂载信息的。
initmounttree();
有了rootfs后,就可以将initrd的image释放到rootfs中了,至于哪个函数完成这项工作?在讲述该函数之
前,我们首先看看该函数是如何被调用的。首先看kernelinit函数中的dobasicsetup函数:
staticint_initkernel_init(void*unused)
do_basic_setup();
dobasicsetup()是•个很关键的函数,所有直接编译在kernel中的模块都是由它启动的。
*Ok,themachineisnowinitialized.Noneofthedevices
*havebeentouchedyet,buttheCPUsubsystemisupand
*running,andmemoryandprocessmanagementworks.
*
*Nowwecanfinallystartdoingsomerealwork..
*/
〃注意上面的关于该函数的注释:CPU和进程管理模块已经正常工作,但是外设还没初始化。
staticvoid_initdo_basic_setup(void)
{
cpuset_init_smp();
usermodehelper_init();
init_tmpfs();
driver_init();
init_irq_proc();
do_ctors();
〃启动所有在—initcallstart和—initcallend段的函数,
//而静态编译进内核的modules也会将其入口放置在这段区间里。
do_initcalls();
将initrd的image释放至rootfs中的工作是由populate_rootfs函数完成,该函数由rootfs_initcall()所引用。
注意到有以下初始化函数:
rootfsinitcall(populaterootfs);
如此:也就是说会在系统初始化的时候,也就是do_initcalls被调用的时候,会调用populate_rootfs进行初
始化。
3.1.1释放initrd
总的来说,rootfs分为两种:虚拟rootfs和真实rootfs。现在kernel的发展趋势是将更多的功能放到用户
空间完成,以保持内核的精简。虚拟rootfs也是各linux发行厂商普遍采用的一种方式,可以将一部份的
初始化工作放在虚拟的rootfs里完成,然后切换到真实的文件系统。在虚拟rootfs的发展过程中,又有以
下几个版本:
Initramfs:Initramfs是在kernel2.5中引入的技术,实际上它的含义就是:在内核镜像中附加一个cpio包,
这个cpio包中包含了一个小型的文件系统,当内核启动时,内核将这个cpio包解开,并且将其中包含的
文件系统释放到rootfs中,内核中的一部分初始化代码会放到这个文件系统中,作为用户层进程来执行。
这样带来的明显的好处是精简了内核的初始化代码,而且使得内核的初始化过程更容易定制。这种这种方
式的rootfs是包含在kernelimage之中的。
cpio-initrd:cpio格式的rootfs
image-initrd:传统格式的rootfs
将在下文具体讲述这两种initrd。
populaterootfs代码如下:
*处理Initramfs/cpio-initrd/image-initrd三个格式的image。
*主要功能实现了将Initramfs/cpio-initrd两种格式的image释放到rootfs;
*不同不出image-initrd这种老式的initrd格式,需要依赖其它手段,后续会讲。
*/
staticint_initpopulate_rootfs(void)
*第一种情况是跟kernel融为一体的initramfs,从内核2.5引入这种技术。在编译kernel的时候,
通过链接脚本
*将其存放在—initramfs_start至—initramfs_end的区域。这种情况下,
*直接调用unpack_to_rootfs将其释放到根目录/。
*
*如果不是属于initramfs这种形式的,也就是_initramfs_start和_initramfs_end的值相等,
*长度为零,不会做任何处理,退出;err此时也为NULL。
*/
char*err=unpack_to_rootfs(_initramfs_start,_initramfs_size);
if(err)
panic(err);/*FailedtodecompressINTERNALinitramfs*/
*紧接着处理initrd格式,而initrd又有两种格式:Image・initrd和CPIO-initrdo
*由initrd_start变量是否为0,来判断内核是否接受了参数:“initrd=",如果有参数“initrd=",
*由下面的代码对initrd进行处理,处理过程又根据initrd的两种格式而不尽相同:
*CPIO-initrd:会被直接释放到rootfs;
*Image-initrd:暂时不会被释放到rootfs,而是将initrdimage拷贝到/initrd.image文件中。
*/
if(initrd_start){
/*
*必须要配制CONFIG_BLK_DEV_RAM才会支持image・initrd。否则全当成cpio-initrd的形式
处理。
*/
#iftlefCONFIG_BLK_DEV_RAM
intfd;
printk(KERN_INFO"Tryingtounpackrootfsimageasinitramfs...\nu);
err=unpack_to_rootfs((char*)initrd_start,initrd_end-initrdstart);
//如果unpack_to_rootfs返回正常
if(!err){
free_initrd();
return0;
}else{
〃如果释放initrd到根目录发生错误,重新释放initramfso
clean_rootfs();
unpack_to_rootfs(_initramfs_start,_initramfs_size);
}
printk(KERN_INFO"rootfsimageisnotinitramfs(%s)n
lookslikeaninitrd\nH,err);
fd=sys_open((constchar_user_force*)"/initrd.image”,O_WRONLY|O_CREAT,0700);
/*
*将initrd写入/initrd.image文件
*看上去image-initrd和cpio-initrd两种格式的image都会被写入/initrd.image
*这看上去不太合理,因为unpack_to_rootfs能够正确处理cpio-initrd,就不需要
*拷贝到/initrd.image了;其实内核是根据是否配置CONFIG_BLK_DEV_RAM来
*判断是否支持image-initrd,如果不支持image-initrd,就不要配置CONFIG_BLK_DEV_RAM,
*内核就会编译下面的宏定义段内代码,就不会拷贝initrd到/initdimage了。
*/
if(fd>=0){
sys_write(f(i,(char*)initrd_start,initrd_end-initrd_start);
sys_close(fci);
/*
*对于是image・initrd的情况。将其释放到/initrd.image.
*最后将initrd内存区域归入伙伴系统。这段内存就可以由操作系统来做其它的用途了。
*/
free_initrd();
)
#else
printk(KERN_INFO"Unpackinginitramfs...\nu);
err=unpacktorootfs((char*)initrdstart,
initrdend-initrd_start);
if(err)
printk(KERN_EMERGnInitramfsunpackingfailed:%s\n”,err);
free_initrd();
#endif
}
return0;
}
rootfsinitcall(populaterootfs);
*正确,返回NULL;否则返回一个C语言格式字符串。
*
*顾名思义就是解压包,并将其释放至rootfs。
*在这个函数里,对应如下的三种虚拟根文件系统的情况:
*Initramfs是在kernel2.5中引入的技术,实际上它的含义就是:在内核镜像中附加一个cpio包,这个cpio
*包中包含了一个小型的文件系统,当内核启动时,内核将这个cpio包解开,并且将其中包含的文件系
*统释放到rootfs中,内核中的一部分初始化代码会放到这个文件系统中,作为用户层进程来执行。这样
*带来的明显的好处是精简了内核的初始化代码,而且使得内核的初始化过程更容易定制。这种这种方
*式的rootfs是包含在kernelimage之中的.
*cpio-initrd:cpio格式的rootfs
*image-initrd:传统格式的rootfs
*关于这两种虚拟文件系统,请参考后续章节。
*
*
*一种是跟kernel融为一体的initramfs,在编译kernel的时候,通过链接脚本
*将其存放在—initramfs_start至—initramfs_end的区域。这种情况下,
*直接调用unpack_to_rootfs将其释放到根目录。
*如果不是属于这种形式的,也就是_initramfs_start和__initramfs_end的值相等,
*长度为零。不会做任何处理,退出。
*对应后两种情况,image・initrd不会被释放到rootfs,该格式的处理细节后文继续讲解;
*也就是说unpack_to_rootfs不能正确处理image-initrd,需要依赖其它手段。
*对于是cpio・initrd的情况,直接将其释放到根目录。
*/
staticchar*_initunpack_to_rootfs(char*buf,unsignedlen)
(
intwritten,res;
decompress_fhdecompress;
constchar*compress_name;
static_initdatacharmsg_buf[64];
header_buf=kmalloc(110,GFP_KERNEL);
symlink_buf=kmalloc(PATH_MAX+N_ALIGN(PATH_MAX)+1,GFP_KERNEL);
name_buf=kmalloc(N_ALIGN(PATH_MAX),GFP_KERNEL);
if(!header_buf||Jsymlinkbuf||!name_buf)
panic(ncan,tallocatebuffers");
state=Start;
thisheader=0;
message=NULL;
while(!message&&len){
loff_tsaved_oflset=thisheader;
if(*buf==O&&!(this_header&3)){
state=Start;
written=write_buffer(buf,len);
buf+=written;
len-=written;
continue;
)
if(!*buf){
bufH-;
len—;
this_header++;
continue;
)
this_header=0;
decompress=decompressmethodCbui;len,&compress_name);
if(decompress){
res=decompress(buf,len,NULL,flushbuffer,NULL,
&my_inptr,error);
if(res)
error(ndecompressorfailed");
}elseif(compress_name){
if(!message){
snprintf{msg_buf,sizeofmsgbuf,
"compressionmethod%snotconfigured*1,
compressname);
message=msg_buf;
}
}else
error(njunkincompressedarchive1*);
if(state!=Reset)
error(njunkincompressedarchive");
this_header=saved_offset+my_inptr;
buf+=myinptr;
len-=my_inptr;
dirutime();
kfree(name_buf);
kfree(symlink_buf);
kfree(headerbuf);
returnmessage;
3.2initrd
到目前为止,initrd技术虽然是一种比较过时但还是一种比较常用的技术。LinuxKemel2.6的initrd的文件
系统镜像文件变成了叩i。格式;变化不仅仅体现在文件格式上,内核对这两个格式的initrd有着截然不同
的处理方法。本节首先介绍什么是initrd技术,然后介绍了3.0内核的initrd的处理流程。
Initrd的含义是bootloaderinitializedRAMDisk.就是由bootloader初始化的内存盘。在Linux内核启动之
前,bootloader会将存储介质中的initrd文件加载到内存,内核启动时会在加载rootfs之前首先访问内存中
的initrd文件系统。在bootloader配置了initrd的情况下,内核启动被分为两个阶段:第一阶段首先执行initrd
中的某个文件(/linuxrc或者/init,下文会详细讲),文成一些特定的任务,如加载RAID驱动等;第二阶
段才是真正执行rootfs中的/sbin/init,以启动init进程。
Initrd主要有以下四个优势:
1动态适应底层硬件平台:Linux发行版必须支持各种各样的底层平台,将所有的驱动都编译进内核是不
太现实的,而且内核会常驻内存,这也会造成内存资源的极大浪费。Linux发行版的内核中只会包含最基
本的一些驱动;而通过在安装过程中检测系统硬件,动态生成底层平台依赖的initrd无非是一种既可行又
灵活的解决方案。
2用于LiveCD:LiveCD可能面对复杂的硬件环境,所以也必须使用initrd。
3制作USB启动盘时必须使用initrd:USB设备是一个启动比较慢的设备,从驱动加载到设备真正可用大
概需要几秒的时间。如果将USB驱动编译进内核,内核通常不能正确访问USB中的文件系统,因为内核
访问USB时;USB设备还没有初始化完毕。所以通常的做法是:在initrd中加载USB驱动,然后休眠几
秒钟,等待USB设备初始化完毕后再去挂载USB中的文件系统。
4initrd(脚本)很灵活,而且是工作在用户态,可以在其中很方便的定制一些功能,如系统安全相关的设
fio
在深入介绍内核2.6及其以后版本使用的initrd机制之前,先简单介绍内核2.4中的initrd机制。内核2.4
中的initrd的文件格式是一个文件系统镜像文件,姑且称之为image-initrd,以区分内核2.6及其以后的CPIO
格式的initrdo内核2.4对initrd的处理流程如下:
1bootloader将内核和initrd两个image加载到内存,经过一些必要的初始化后,将控制权交给内核;
2内核经过一些进一步的初始化后,将initrd所在的内存区域(由bootloader告知内核)设置为/dev/initrd
设备;然后kernel透过内部的decompressor(gzip解压缩)解开该内容并复制到/dev/ramO装置设备上;
3内核以R/W(可读写)模式将/dev/ramO挂载为暂时性的rootfs;
4kernel准备执行/dev/ramO上的/linuxrc,该程序(脚本)是在用户态执行的;如果/dev/ramO被指定
为真正的根文件系统,那么内核跳至第7步正常启动;
5/linuxrc与相关程序处理特定的操作;linuxrc通常是一个脚本文件,负责加载内核访问根文件系统必须
的驱动,以及加载根文件系统;
6/linuxrc执行即将完毕,执行权转交给kernel;如果rootfs中存在/initrd目录,那么/dev/ramO将从/移
动至lj/initrdo否则如果/initrd目录不存在,/dev/ramO将被卸载。
7Linux挂载真正的rootfs并执行/sbin/init程式
8依据Linuxdistribution规范的流程,执行各式系统与应用程式;linux2.4内核的initrd的执行是作为内
核启动的一个中间阶段,也就是说initrd的/linuxrc执行以后,内核会继续执行初始化代码,我们后面会
看到这是linux2.4内核同2.6内核的initrd处理流程的一个显著区别。
内核2.6支持两种格式的initrd,一种是前面提到的传统格式个image-initrd,其核心文件是/linuxrc;另外
一种格式是CPIO,这种格式从内核2.5开始引入,使用CPIO工具生成,其核心文件是/init,姑且称这种
initrd为CPIO-initrdo尽管内核2.6对image-initrd和CPIO-initrd都有支持,但处理流程却有着显著的区别。
下面分别介绍内核2.6对这两种格式的initrd的处理流程:
CPIO-initrd的处理流程:
1Bootloader将内核initrd加载到内存特定位置;
2内核判断initrd是否是CPIO格式,如果是:
3将initrd的内容释放到rootfs中;
4执行initrd中的/init文件;
5执行完/init后,控制权再交给内核
Image-initrd的处理流程:
1Bootloader将内核initrd加载内存特定位置;
2内核判断initrd格式,如果不是CPIO格式,则作为Image-initrd格式处理;
3将initrd的内容保存在rootfsft\]/initrd.image文件中;
4将/initrd.image中的内容读入到/dev/ramO中,也就是读入了一个内存盘中;
5接着内核以读写的方式将/dev/ramO挂载原始的根文件系统;
6如果/dev/ramO被指定为rootfs,跳转到最后一步进行正常启动;
7执行/linuxrc文件,linuxrc通常是一个脚本,负责加载根文件需要的驱动,以及加载根文件系统;
8/linuxrc执行完毕,控制权交给内核,常规跟文件系统被挂载;
9如果常规根文件系统中存在/initrd目录,那么/dev/ramO将从/移动到/initrd;否则如果/initrd不存在,
/dev/ramO将被卸载;
10在rootfs上进行启动,执行/sbin/init。
通过上面的流程可知,内核2.6对image-initrd的处理流程相较内核2.4并无明显变化;CPIO-image相较
image・initrd有很大差别,流程非常简单。
CPIO-image相较image-initrd,有一些区别和优势:
1CPIO-initrd的制作比较简单,通过以下两个命令即可完成:
#假设当前目录位于准备好的initrd文件系统的根目录下
bash#find.|cpio-c-o>../initrd.img
bash#gzip../initrd.img
而image・initrd的制作相对繁琐:
#假设当前目录位于准备好的initrd文件系统的根目录下
bash#ddif=/dev/zeroof=../initrd.imgbs=512kcount=5
bash#mkfs.ext2-F-mO../initrd.img
bash#mount-text2-oloop../initrd.img/mnt
bash#cp-r*/mnt
bash#umount/mnt
bash#gzip-9../initrd.img
2CPIO-image的内核处理流程简单
通过上面的对CPIO-initrd处理流程的介绍,CPIO-initrd的处理流程也显得格外简单,CPIO-initrd主要在以
下两个方面做了简化:
aCPIO-image并没有使用额外的RamDisk,这样就省略了RamDisk的挂载和卸载工作;
bCPIOinitrd启动完/init进程,内核任务就结束了,剩下的工作都交给/init处理;而对于image・initrd,内
核在执行完/linuxrc之后,还需要等待其结束,再进行一些收尾工作,并且需要负责执行rootfs中的/sbin/init。
可以通过下面的简化的流程图更加清晰表示两者之间的区别:
内核对cpio-initrd和image-initrd处理流程示意图
image-mitrd的处理阶段cpio-initrd的处理阶段
CPIO-initrd的职能更加重要,不再像image-initrd那样是Linux启动过程的一个中间步骤,而是作为内核启
动的终极步骤,内核将控制权交给/init后就完了,所以CPIO-initrd可以做更多的工作,而不需要担心和内
核后续处理的衔接问题。
3.2.2内核2.6Initrd处理代码分析
下面对内核2.6中与initrd相关的代码进行详细分析。首先阐述几个相关的概念;
rootfs:一个驻留在内存中的文件系统,也是Linux初始化时构建的第一个文件系统,其实VFS。
initramfs:和本文的主题关系不是太大,但是代码涉及到了initramfs,为了更好地理解代码,对其做简单
介绍。Initramfs是内核2.5引入的技术,它的实际含义是,内核镜像中附加一个CPIO包,该CPIO包中包
含了一个小型的文件系统,当内核启动的时候,内核将包解开,并且将其中包含的文件系统释放到rootfs
中。内核中的一部分初始化代码会放到这个文件系统中,作为用户态程序来运行。这样明显的好处是简化
了内核的初始化代码,而且使得内核的初始化过程更加容易定制。Linux内核2.6.12中的initramfs还没有
什么实质性的内容,一个完整的initramfs的实现还需要一个漫长的过程。
realfs:用户最终使用文件系统。
内核初始化代码起始于start_kemeh和initrd相关的代码起始于kernelinit,下图列出了跟该initrd相关的
函数调用关系:
日•Akernel_execve(constcharconstchar*const*,constchar*const*):int
国•*___call_usermodehelper(void*):int
•{init_ksymtab_kernel_execve}0:constkernel_symbol
B•:dojinuxrc(void*):int
E)•:handleJnitrdO:void
B•AinitrdJoadO:int
Q・一prRpa「R,amRspacR()voidImage-initrd
pcernel_init(void*):int
B•:rest_initO:void
•start_kernelO:void
日run_initjxocess(constchar*):void
Binitjx)stOint(6matches)CPIO-initrd
日kernel_init(void*):int
臼•:restJnitO:void
•start_kemelO:void
首先从kernel)正函数开始分析,我们省略了该函数中跟initrd无关的代码。一
staticint_initkemel_init(void*unused)
{
〃省略跟initrd无关的部分
if(!ramdisk_execute_coininand)
ramdisk_execute_command="/init";
if(sys_access((constchar_user*)ramdisk_execute_command,0)!=0){
〃返回值!=0,表示不存在该文件;所以就会使用老式的方法去执行/linuxrc,也就是传统的initrd
格式
ramdiskexecutecommand=NULL;
preparenamespace();
*Ok,wehavecompletedtheinitialbootup,and
*we'reessentiallyupandrunning.Getridofthe
*initmemsegmentsandstarttheuser-modestuff..
*/
init_post();
return0;
从上面的代码可知,根据rootfs中是否存在ramdisk_execute_command文件,HP/init文件,处理流程分支
为Image-initrd和GPIO-initrdo
GPIO-initrd处理
我们首先看看GPIO-initrd启动的完整代码,有上面介绍的kemelinit函数可知,该过程从init_post函数开
始:
staticnoinlineintinit_post(void)
(
〃省略跟initrd无关的代码
〃假如变量ramdisk_execute_command被设置;
//ramdisk_execute_command一般在函数kemelinit被设置为/init
if(ramdisk_execute_command){
run_init_process(ramdisk_execute_command);
printk(KERN_WARNING"Failedtoexecute%s\nn,
ramdisk_execute_command);
*Wetryeachoftheseuntilonesucceeds.
*
*TheBourneshellcanbeusedinsteadofinitifweare
*tryingtorecoverareallybrokenmachine.
*/
〃由于一些历史原因,有一些不同的init程序文件存在,我们会依次测试每一种合理的
〃可能性;如果run_init_process执行某个文件成功,则不会返回了,也就从内核状态
〃调用用户态程序,然后返回到了用户态。关于细节请看runjnit_process函数的实现。
if(execute_command){
runinitprocess(executecommand);
printk(KERN_WARNING"Failedtoexecute%s.Attempting
ndefaults...\n",execute_command);
}
run_init_process(n/sbin/initu);
run_init_process(*7etc/initn);
run_init_process(*7bin/init");
run_init_process(n/bin/shu);
panic("Noinitfound.Trypassinginit=optiontokernel.n
"SeeLinuxDocumentation7init.txtforguidance.**);
runinitprocess函数定义如下,该函数只是一个对kemelexecve函数的简单wrap。
staticvoidrun_init_process(constchar*init_filename)
{
argv_init[O]=init_filename;
kernel_execve(init_filename,argv_init,envp_init);
kernelexecve函数的定义如下:
intkemel_execve(constchar*filename,
constchar*constargv[],
constchar*constenvp[])
{
structptregsregs;
intret;
memset(®s,0,sizeofifstructpt_regs));
//加载并执行一个新的可执行程序
ret=doexecve(fi1ename,
(constchar_user*const_user*)argv,
(constchar_user*const_user*)envp,®s);
if(ret<0)
gotoout;
/*
*Saveargctotheregisterstructurefbruserspace.
*/
〃将doexecve的返回值存入regs.ARMrO;
〃因为ARM规定,使用RO返回函数的执行结果。
regs.ARMrO=ret;
*Weweresuccessful.Wewon'tbereturningtoourcaller,but
*insteadtouserspacebymanipulatingthekernelstack.
*/
asm("addrO,%0,%l\n\t"
Hmovrl,%2\n\tH
Hmovr2,%3\n\t”
“blmemmove\n\t,7*copyregstotopofstack*/
"movr8,#0\n\t"/*notasyscall*/
Hmovr9,%0\n\tu/*threadstructure*/
"movsp,r0\n\t*7*repositionstackpointer*/
“bret_to_usern
:nr"(current_thread_infb()),
"Irn(THREAD_START_SP-sizeofifregs)),
nr"(®s),
nIrn(sizeof(regs))
:“r2“JipT'hV'memory");
out:
returnret;
}
EXPORTSYMBOL(kernelexecve);
Image-initrd处理
为处理Image-initrd,首先将ramdisk_execute_command设置为NULL,然后调用preparenamespace函数,
如下面的代码所示:
if(sys_access((constchar_user*)ramdisk_execute_command,0)!=0){
〃返回值!=0,表示不存在该文件;所以就会使用老式的方法去执行/linuxrc,也就是传统的initrd格式
ramdiskexecutecommand=NULL;
prepare_namespace();
*Preparethenamespace-decidewhat/wheretomount,loadramdisks,etc.
*
*该函数完成很多工作,几乎是Image-initrd流程的全部工作:
*0这个步骤是有bootloader完成的:bootloader把内核以及initrd文件加载到内存的特定位置
*1内核将initrd的内容保存在rootfs下的/initrd.image文件中
*2内核将/initrd.image的内容读入/dev/ramO设备中
*3接着内核以可读写的方式把/dev/ramO设备挂载为原始的根文件系统
*4执行initrd上的/linuxrc文件
*5等待/linuxrc(linuxrc是通过另外一个进程去执行的,所以内核使用wait系统调用来等待linuxrc进程)
执行完毕
*6将realrootdevicemount到/root目录,且当前目录就是/root
*7由该函数倒数第二行将/root挂载到/。
*
*但是该函数没有执行/sbin/init程序。该程序的执行实在ini匚post函数中,该函数实现比较巧妙:
*1首先看ramdisk_execute_command是否被设置,如果被设置,一般设置成/init;则/init作为第一个进程
开始执行,就是CPIO-initrd
*中执行的步骤;
*2在就是看”/sbin/init”这个可执行文件
*3还会查看其它类似文件,具体请看init_post函数的实现。
*
*由此可见,在内核3.0中,为支持Image-initrd,就会多执行image-initrd相关的操作,最后也会到init_post
函数;
*而CPIO-image会直接调用initpost函数;在init_post函数中根据ramdisk_execute_command参数是否
被设置来决定执行:
*/init(包含在CPIO-initrd)还是/sbin/init(包含在realrootdevice)o
*
*
*由此可见,CPIdinitrd在/init就交出了控制权,而Image・initrd在/linuxrc暂时交出控制权,然后又回收,
最后将控制权交给了/sbin/inito
*
*/
void_initprepare_namespace(void)
{
intisfloppy;
if(rootdelay){
printk(KERN_INFO"Waiting%dsecbeforemountingrootdevice..Anu,
root_delay);
ssleep(rootdelay);
*waitfbrtheknowndevicestocompletetheirprobing
*
*Note:thisisapotentialsourceoflongbootdelays.
*Forexample,itisnotatypicaltowait5secondshere
*fbrthetouchpadofalaptoptoinitialize.
*/
wait_fbr_device_probe();
〃软件磁盘阵列相关的设备:/dev/mdO
md_run_setup();
if(saved_root_name[0]){
root_device_name=savedrootname;
〃如果rootdevice是mtd或者ubi
if(!strncmp(root_device_name,nmtdn,3)||
nn
!stmcmp(root_device_name,ubi?3)){
mount_block_root(root_device_name,root_mountflags);
gotoout;
}
ROOTDEV=name_to_dev_t(root_device_name);
〃去掉传给kernel的root参数的前5个字符:/dev/
if(stmcmp(root_device_name,H/dev/n,5)==0)
root_device_name+=5;
*加initrd加载到内存,具体的工作分为如下几步:
*0这个步骤是有bootloader完成的:bootloader把内核以及initrd文件加载至lj内存的特定位置
*1内核将initrd的内容保存在rootfs下的/initrd.image文件中
*2内核将/initrd.image的内容读入/dev/ramO设备中
*3接着内核以可读写的方式把/dev/ramO设备挂载为原始的根文件系统
*4执行initrd上的/linuxrc文件
*5等待/linuxrc(linuxrc是通过另外一个进程去执行的,所以内核使用wait系统调用来等待linuxrc
进程)执行完毕
*6将realrootdevicemount到/root目录,且当前目录就是/root
*/
if(initrd_load())
gotoout;
/*waitfbranyasynchronousscanningtocomplete*/
if((ROOT_DEV=0)&&root_wait){
printk(KERN_INFOnWaitingfbrrootdevice%s...\nu,
saved_root_name);
while(driver_probe_done()!=0||
(ROOTDEV=name_to_dev_t(saved_root_name))==0)
msleep(100);
async_synchronize_full();
is_floppy=MAJOR(ROOT_DEV)==FLOPPY_MAJOR;
if(is_floppy&&rddoload&&rdloaddisk(O))
ROOTDEV=Root_RAM0;
mount_root();
out:
devtmpfs_mount(ndevn);
〃将realrootdevice挂载到/;原先挂载在/root下。
〃至此,我们已经完成image・initrd的挂载、执行其中的/linuxrc并等待结束、通过mount_root将
//realrootdevice挂载到/root目录、如果可能就将/dev/ram挂载/root/initrd等一系列工作。
〃其实到此我们已经完成image-initrd相关的所有工作;
〃下面一句就是将/root(realrootdevice)挂载到/目录,也就完成了image・initrd的中间步骤。
sys_mount(n.';f7n,NULL,MS_MOVE,NULL);
sys_chroot((constchar_user_force*)”.");
接着,我们来详细看看initrdjoad函数的细节:
int_initinitrdload(void)
(
if(mount_initrd){
〃创建/dev/ram这个设备节点
create_dev(,7dev/ramn,RootRAMO);
/*
*Loadtheinitrddatainto/dev/ramO.Executeitasinitrd
*unless/dev/ramOissupposedtobeouractualrootdevice,
*inthatcasetheramdiskisjustsetuphere,andgets
*mountedinthenormalpath.
*/
〃由上面的内核注释可以比较清楚地看出下面几句的含义,这样清晰的注释在内核并不多见
〃首先通过rdloadimage函数将initrdimage从/initrd.image加载到/dev/ramO
〃接着判断如果/dev/ramO不是真正的根文件系统,执行if中的两个函数,其实就是
//image-initrd挂载的其他步骤,在分析handleinitrd的时候给出细节。
if(rd_load_image('7initrd.image")&&ROOTDEV!=RootRAMO){
sys_unlink('7initrd.image");
handle_initrd();
return1;
}
}
sys_unlink(,7initrd.imageH);
return0;
省略一些无关紧要的代码。
/*
*正确返回1;
*/
int_initrd_load_image(char*from)
(
intres=0;
intinfd,outfd;
unsignedlongrd_blocks,devblocks;
intnblocks,i,disk;
char*buf=NULL;
unsignedshortrotate=0;
decompress_fndecompressor=NULL;
#if!defined(CONFIG_S390)&&!defined(CONFIG_PPC_ISERIES)
charrotator[4]={T,7,,,-,,,\V);
#endif
//[1]分别打开存放initrdimage的源文件和目的文件
outfd=sys_open((constchar_user_force*)n/dev/ram",ORDWR,0);
if(out_fti<0)
gotoout;
in_fd=sys_open(from,ORDONLY,0);
if(in_fd<0)
gotonoclose_input;
/*[2]
*该函数只是用来验证initrdimage,该image目前存储在/initrd.image文件中:
*1returnsthenumberofblockstoreadforanon-compressedimage
*2returns0iftheimageisacompressedimage,
*3returns-1ifanimagewiththerightmagicnumberscouldnotbefound.
*/
nblocks=identify_ramdisk_image(in_fcl,rd_image_start,&decompressor);
if(nblocks<0)
gotodone;
/*[3]
*/initrd.image中存储的是压缩格式的initrdimageo
*将in_fd(/initrd.image)中的内容解压到out_fii(/dev/ram)o
*/
if(nblocks=0){
/*
*如果initrdimage是压缩的,通过crdload函数将image解压到outfd
*/
if(crd_load(in_fd,outfd,decompressor)=0)
gotosuccessfulload;
gotodone;
}
//[4]如果initrd是非压缩的,将其从/initrd.image拷贝到/dev/ram中
/*
*NOTENOTE:nblocksisnotactuallyblocksbut
*thenumberofkibibytesofdatatoloadintoaramdisk.
*SoanyraindiskblocksizethatisamultipleofIKiBshould
*workwhentheappropriateramdisk_blocksizeisspecified
*onthecommandline.
*
*Thedefaultramdisk_blocksizeisIKiBanditisgenerally
*sillytouseanythingelse,somakesuretouseIKiB
*blocksizewhilegeneratingext2fsramdisk-images.
*
*该函数返回out_fd中的块的个数,即/dev/ram的块的个数。
*/
if(sys_ioctl(out_fti,BLKGETSIZE,(unsignedlong)&rd_blocks)<0)
rd_blocks=0;
else
rdblocks»=1;
/*
*如果将in_fd(/initrd.image)中的块的数目大于out_fd(/dev/ram)中的一半,出错。
*如果块的个数大于块的大小的一半时,出错。
*/
if(nblocks>rdblocks){
printk(nRAMDISK:imagetoobig!(%dKiB/%IdKiB)\nu,
nblocks,rdblocks);
gotodone;
*OK,timetocopyinthedata
*/
if(sys_ioctl(in_fa,BLKGETSIZE,(unsignedlong)&devblocks)<0)
devblocks=0;
else
devblocks»=1;
if(strcmp(from,n/initrd.imageu)==0)
devblocks=nblocks;
if(devblocks==0){
printk(KERN_ERR"RAMDISK:couldnotdeterminedevicesize\nn);
gotodone;
)
buf=kmalloc(BLOCK_SIZE,GFP_KERNEL);
if(!buf){
printk(KERN_ERR"RAMDISK:couldnotallocatebuffer\nM);
gotodone;
}
printk(KERN_NOTICE"RAMDISK:Loading%dKiB[%lddisk%s]intoramdisk...n,
nblocks,((nblocks-1)/devblocks)+1,nblocks>devblocks?:"”);
for(i=0,disk=1;i<nblocks;i++){
if(i&&(i%devblocks=0)){
printk(udonedisk#%d.\n",disk-H-);
rotate=0;
if(sys_close(in_fcl)){
printk(uErrorclosingthedisk.'n”);
gotonoclose_input;
)
changefloppy(Hdisk#%d",disk);
infd=sys_open(from,ORDONLY,0);
if(in_fd<0){
printk(HErroropeningdisk.\nn);
gotonoclose_input;
)
printk(HLoadingdisk#%d...",disk);
}
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025至2030年中国铝艺匙牌数据监测研究报告
- 2025至2030年升降机齿轮项目投资价值分析报告
- 2025至2030年中国软包装热压密封机数据监测研究报告
- 2025年轮轴拔卸器项目可行性研究报告
- 2025年节能灯灯管项目可行性研究报告
- 2025至2030年中国磁卡卡基材料数据监测研究报告
- 电脑分期合同范本
- 第10课 小型网络的搭建 教学设计 2024-2025学年 浙教版(2023)初中信息技术七年级上册
- 阿归养血颗粒胶囊行业行业发展趋势及投资战略研究分析报告
- 雇佣羊倌合同范本
- 食品检验员聘用合同样本
- 六年级信息技术下册教学计划
- 2025年九年级数学中考复习计划
- 2023年长沙自贸投资发展集团有限公司招聘笔试真题
- 《物料摆放规范》课件
- 2024年资助政策主题班会课件
- 《烟花效果及制作》课件
- 2024江苏太仓市城市建设投资集团限公司招聘易考易错模拟试题(共500题)试卷后附参考答案
- 2024年全新统计法培训课件:普法教育新方向
- 保育教育评估指南
- 现代康复治疗腕管综合症
评论
0/150
提交评论