内核更新方法

 

一方案,妄想

1.jtag 烧写。

jtag主要用于bootloader的烧写。但是同样也可以用于kernel rootfs的烧写。可是由于jtag是完全串行的数据输入,所以对于uboot 200k的空间大小,还算勉强可以接受。而对kernel 2M左右,rootfs几十M甚至几百M,变得不可接受。此时,jtag烧写工具对硬件完全是未知的,需要提供配置文档。

2.uboot bootloader

当使用jtaguboot烧写到flash中以后就可以利用uboot进行烧写。目前,uboot支持的所有数据来源有:串口线,usb线,usb storage,网线。其中usb线被模拟成网络接口。

Uboot支持向nand flashnor flash 烧写。

3.kernel

利用rootfs中提供的mtdtool,向底层的提供的接口中写回数据。这种方法同时也就使用了rootfs,所以要使用这种方法,必须制作rootfs

 

二需求

对于一款产品来说,对内核的更新,对用户应该是透明的,也就是不需要用户进行过多,过于复杂的操作,同时最好提供有有好用户界面的烧写程序。

基于此原因,jtag的慢速,以及uboot的需要手动配置,都不是最佳的方案。那么利用内核进行烧写,成了产品级更新内核的最佳方案。

 

嵌入式系统不同于pc机之一是,其文件系统和资料多数存储于flash之上。flash上通常包含的数据有ubootkernelrootfsinitramfs ,以及其他一些分区。而更新内核,则目的是利用现有的内核,下载并烧写新的内核。同理,更新rootfs的原理跟更新内核相似。

 

----局限:由于在内核最终mount rootfs之后,会启动一些程序,这些程序会hold住一些flash资源,所以在烧写的时候就必须停止这些程序并释放flash资源。这样在烧写flash的时候就不会引起意外。

 

我们知道,内核在加载过程中,是完全载入内存的,也就是在内核运行过程中,内核并不hold flash资源。所以如果在内核中烧写flash,就不会干扰到flash。但是,在内核态下,并不提供策略层的烧写,只是提供了对于不同类型flash的共同的操作接口。因此最终的指定烧写文件,烧写类型,都是放在用户态的程序中完成的。

 

用户态的程序必然依赖于rootfs,而通常的rootfs又是基于flash的。所以要制作基于非flashrootfs----linux内核支持的ramfs

要使用ramfsinitramfs),则在编译kernel时,需要添加两项支持:

INITRD,BLOCK_RAM。这样在uboot启动内核时,才能有效加载ramfsRamfs,从他的名称就可以得知,他是基于内存的rootfs,既可以作为最终的rootfs,可以作为mount 最后rootfs的桥梁。

 

三方法

现在读flash操作的局限解除了,还需要考虑的是更新的策略。

以下是我所想到的(有些可能是妄想,也或许有实现方法和实现价值)。

  1.  
    1.  
      1.  
        1. 利用 ramfs 做单内核的自我更新。Flash 依次存储的是ubootkernelramfsrootfsuboot启动后,向kernel的启动参数中添加一项 burn=kernel,或者burn=rootfs。当kernel启动并mount ramfs后,ramfs根据传入的burn参数,从usb storage 或者网络寻找指定名称的文件,并烧写。要实现系统自动的选择参数,必须修改uboot,让其可以知道硬件状态的改变。

        2. 利用ramfs做双内核的更新。flash依次存储的是ubootkernel_burn,kernel,ramfs,rootfsuboot启动后,根据硬件选择指定的kernel启动。如果选择kernel_burn,那么跟随启动的还有ramfs。在ramfs中,对kernel进行跟新。烧写的实现方法跟方法1的相同。

          跟方法1比较,由于kernel_burn会永远受保护不会损坏,这种方法可以有效的解决内核被损坏,而无法自身修复的问题。但同时由于有额外的kernel_burn,所以会占用额外的空间。

        3. (需要调查,但可行)利用ramfs做完全的在线热更新。

          既然我们可以通过ramfs mount rootfs,那么是否可以通过rootfs mount ramfs呢?这样rootfs 中的所有进程停止,最后转到ramfs,也就释放了flash 资源了。问题是在ramfs mount rootfs的时候,使用的是busybox提供的switch_rootrun_init),而这个程序必须是在进程号为0的进程中调用,意思是说,必须在init中调用。

           

          而实际在rootfs运行中的init,在进入rootfs之后就不会继续派生进程了。这貌似无法进行下去了,但是是否可以利用命令telinit来更改init的启动级别,使其进入其他的级别中,在其他级别的initmount rootfsramfs呢?如果可行的话,需要修改的地方有两处:

a. init

b. 其他级别的启动脚本(或者需要把这部分放入init

这样,在其他级别运行后,就进入了ramfs。在ramfs中烧写后,重启系统,就进入了更新后的系统。

 

同前两个方法比较,这个方法并没有修改uboot,完全的软件层的操作。而uboot的修改又依赖于硬件支持,所以在这个层面上来看,这个方法有一定的通用性(不需要修改硬件和uboot)。另一个方面,由于修改了init,和运行级别,会对其他开发人员造成一定的障碍,这也会降低通用性。但优点也是明显的,他可以实现开机状态的内核更新,不需要关机重启,对用户体验来说,是极大的提升。

 

这种方法,还有许多细节地方要考虑,但是大致的方向,觉得是可行的。

 

四共同点

ramfs中,影响更新内核时间的因素主要存在于两个方面。

1.内核的来源。针对不同的硬件,可以分为

a。串口

bUsb host

cUsb client

dNet

显然从串口传入数据,将会变成整个过程的瓶颈,当然如果只支持串口,就没有办法了。

usbhostusbhost可以插入usbstorage。在storage中,存放着要烧写的image。这样在ramfsmount 这个usb storage之后,就可以获得数据了。

usbclientUsbclient 意味着可以将嵌入式设备当做从设备链接到pc上。在linux 2.6的内核中,有关于linux usb client的驱动支持----gadget。这样在连接主机时,我们既可以把设备当做storage,亦可以当做网卡,还可以当做串口。如果当做storage,那么就可以从pc机把数据拷贝到设备。如果当做网卡,就跟下面所说的net一样了。

netpc机跟设备之间,通过网络传输数据,上面的协议既可以是自定义的数据格式,也可以是通用的数据协议。底层可以简单的使用socket

这样数据来源问题就解决了。相比而言,后面三种可以提供满意的速度,而基于网络的传输,又可以达到灵活的可控性。

 

结论:任何支持这四种接口之一的设备都可以进行内核更新。应该优先选择基于网络的方法。

2。数据写入时间。这主要依赖于设备提供的不同的存储介质。不同的flash类型,对应着不同的擦出时间,写入时间。所以对于经常更新的地方,应该尽量使用nand

 

五细节

1.flash烧写。Mtd-tools linux最常用的flash烧写程序。浏览其源码。选择可用部分到自己的程序中。这样的编辑编译可以有效的减小最终程序的大小。在代码中发现linux内核,对用户层的flash操作已经提供了统一的操作接口。爽!

2.ramfs格式。目录linux 内核支持两种格式的initramfsgz压缩格式和cpio格式。当前cpio格式的是主流。使用cpio工具可以方便的创建ramfs

3.内核裁剪。kernel_burn在配置时,只需提供非常小的功能,这样最后的image就会非常的小。

4.uboot的内核参数。如果在kernel启动时,配置了initramfs,那么所有的后续工作都是由ramfs 处理,包括内核参数处理和rootfs mount。因此ramfs就有机会进行自己的其他工作。

5.ramfs制作。参考另一篇文章。

6.usbnet配置。参考另一篇文章。

7.busybox。参考另一篇文章。

你可能感兴趣的:(linux,网络,Flash,嵌入式,存储,linux内核)