5分钟搞懂kexec工作原理【转】
转自:https://zhuanlan.zhihu.com/p/105284305
什么是kexec?
可从当前正在运行的内核直接跳转到新内核
为什么需要kexec?
跳过boot阶段,减少重启时间
kexec整体思路如下
1)新的kernel镜像和initrd镜像连续存储在内存中,initrd的位置记录在boot_params中
2)切换到新内核就是跳转到新的kernel镜像所在内存位置,CPU执行其entry的代码即可,新的内核通过boot_params记录的initrd位置完成根文件系统内容的加载
原理不复杂,但受到一些实际情况的限制,所以在实现上会略复杂一些。
首先,kernel镜像有指定的入口地址,kernel镜像要加载到入口地址位置才能正常启动,而这块内存正在被当前内核使用,所以kernel镜像需要临时存放的内存,在跳转前要将内容搬移到入口地址。
其次,initrd的体积可能较大,找不到用于存储initrd的连续大段物理内存,因此需要分散存储在能申请到的内存页面中,在kernel跳转前搬移拼接到目的地址。
简单讲,镜像先临时存放,切换时再搬移到目标地址。
以kexec -s uImage --ramdisk=./ramdisk.bin为例进行说明。
uImage和ramdisk.bin都是要使用的内容,抽象为segment对象进行管理,那么这里存在2个segment,分别对应uImage和ramdisk.bin
内核使用alloc_pages来申请页面,如果碎片化严重,很难申请到高阶物理内存,所以kexec的做法是循环申请,每次申请一个页面。这会带来一个问题,属于同一个镜像的页面彼此不连续,需要对这些临时承载镜像内容的页面进行管理。
为此,kexec引入了entry的概念,其实就是一个unsigned long对象,记录申请到的page的物理地址,并利用低位的4bit来对entry进行分类,共计有如下4类entry。
1)IND_DESTINATION,用于记录segment要搬移到的目标地址
2)IND_SOURCE,用于记录segment内容所在的物理地址
3)IND_INDIRECTION
4)IND_DONE
entry本身也需要内存来存储,为此,kexec首先申请一个page用于存储entry,如果page存满entry时,需要再新分配一个page,并使用之前page内最后一个entry记录新分配page的物理地址,这个entry就是3)IND_INDIRECTION类型entry。
那么uImage作为第一个segment,会对应一个IND_DESTINATION类型entry和若干IND_SOURCE类型entry,ramdisk.bin作为第二个segment同样会对应一个IND_DESTINATION类型entry和若干IND_SOURCE类型entry,当全部segment存储完成,kexec会加入4)IND_DONE类型entry,表示全部segment的内容到此记录完成。
结合下图会有较直观的认识,IND_DESTINATION类型entry表示一个segment的开始,直到下一个IND_DESTINATION类型entry或IND_DONE类型entry之前的IND_SOURCE类型entry都记录了该segment内容存储的物理地址。
接下来的事情相对简单,在用户执行kexec -e后,会进入跳转流程,将segment的内容依次搬移到IND_DESTINATION类型entry记录的物理地址对应位置,结合下图可有较直观的认识。
全部segment内容搬移完成,跳转到入口地址,就切换到了新的kernel。