卡塔尔世界杯_中国进过几次世界杯 - 210rc.com
首页世界杯波兰正文

手机双系统安装攻略

2025-07-14 20:30:17

展开

1. 前言:

刷机,似乎是安卓手机用户的一项专利,但是,会刷机的用户一般都是喜新厌旧的角色,

一个系统用久了,就想换到另一个系统,或者觉得没有原来的好,或者又觉得要换回去,这样又要重刷。

但是刷来刷去都麻烦啊,并且每次刷机也不是没有风险的,一不小心就可能造成重要数据的丢失。

没有解决办法吗?有!双系统!甚至三系统,四系统!!

本文就是解决这个问题的,并且用本文中的方法,完全可以实现一键安装,一键卸载系统的功能,把系统的安装和卸载变成apk的安装和卸载一样简单。

(说明下,下面的方法以三星i93xx系列的手机为例的)

2. 先来简单介绍下安卓系统的启动过程:

在手机上电时,最先执行的集成到CPU芯片上的一段rom里的程序

这段程序负责加载nand flash或者sd卡上的引导程序,引导程序一般来讲都是uboot

uboot会完成一些设备的初始化,这里很重要的部分就是nand flash,以便将linux内核加载读到内存里并运行。

加载的内核根据情况,有可能是boot分区里的内核,也有可能是recovery分区里的内核。

内核跑起来之后,首先会挂载ramdisk到"/"根目录,然后执行/init创建第一个进程

init读取/init.rc,进行进一步的初始化,完成如创建目录,设置权限,挂载data,system,cache分区,启动一系列的service,包括重要的zygote进程

zygote进程又会创建system_server进程以完成进一步的初始化工作,并加载一系列的apk进程。

3. 接下来看下,一个安卓系统所需的分区:

一个uboot分区,负责引导内核

一个内核分区

一个system分区,用于存放安卓的系统程序和文件

一个data分区,用于存放系统的数据,apk程序,以及apk程序的数据等等。

一个cache分区,一般用于升级之用,用于保存ota升级包,升级日志等等。

4. 再按下来看下双系统的实现方案:

4.1 内核的引导问题

这是一个比较头痛的问题,上面讲到,内核是由uboot通过boot分区或者recovery分区加载进来的

如果还要加载其它分区的内核,就要考虑修改uboot的配置参数或者代码了

uboot的代码我们肯定是没有 的,

虽然一般来讲uboot的配置参数往往也是保存在某个分区里的,但一般都是加密的,所以也改不了。

所以我们只能考虑利用己有的分区了。

最简单的方法就是覆盖boot分区,将第二个安卓的boot.img写到boot分区,

然后写一个apk,当要启动哪个系统时,就把哪个系统的boot.img写到boot分区。

这种方式的缺点是切换麻烦,每次切换都要先启动其中的一个系统,然后运行apk进行切换。

然后,我们把贪婪的目光瞄向了recovery分区。

大家知道,recovery分区一般在系统升级或者恢复出厂设置的时候才会用到,所以我们考虑对recovery分区进行下手。

最简单的方法是把第二个安卓系统boot.img放到recovery分区里,这样可以实现触发进recovery来引导第二个安卓系统了。

当然,也可以在recovery分区里再放一个定制的uboot,从而实现更加灵活的加载方式,如可以显示引导菜单等等,再如从SD卡里加载内核等等。

但是这还是要有uboot的源码才行。

当然,在使用recovery分区之前,要对recovery分区作下备份。

使用recovery分区作为第二个系统的linux引导分区还有个好处就是一般手机都有开机进recovery的快捷键,

如三星的手机一般是在开机时同时按下:音量加,HOME,POWER三个按键就可以进recovery.

从而实现方便的系统切换。

4.2 system,data分区的创建问题:

解决了引导的问题,再来看下system和data分区的创建问题。

因为cache分区只在升级的时候会用到,所以两个系统可以共用,不用再创建了。

1)重新分区法:

也就是为每个系统建立不同的分区,这种方法需要对存储空间进行重新划分,显然比较麻烦,风险也比较高,可行性比较低。

2)使用虚拟磁盘的方案

大家一定对ubuntu能够在windows下直接安装的方式印象十分深刻

实际上ubuntu能够在不重新分区的情况下实现安装真是利用的虚拟磁盘实现的。

相对于第一种方案,避免了重新分区的麻烦。

虚拟磁盘是linux下很早内核就已经支持了,是很成熟的技术了。

所以这里虚拟磁盘是最好的选择,并且借助于虚拟磁盘,我们不仅可以实现双系统,还可以实现三系统,四系统,这完全取决于存储空间。

5. 理论讲清楚了,接下来,看下具体如何干吧。

首先,我们要创建一个system虚拟磁盘,这里有两种方法:

一种是从img直接生成虚拟磁盘,另一种方法是要将一个ota升级包中的system分区写到虚拟磁盘中。

第一种方法比较简单,只要执行一条命令即可:

simg2img system.img system.disk

simg2img可以在编译完的out/host/linux-x86/bin/目录下找到

6.5 如果你拿到的是一个zip格式的ota升级包,内容类似图中所示:

那么制作system虚拟磁盘就略微有些麻烦了,看下如何制作:

需要修改压缩包的META-INF\com\google\android目录下的update-script脚本

1)去除format和mount /system分区的脚本

format("ext4", "EMMC", "/dev/block/mmcblk0p9", "0", "/system");

mount("ext4", "EMMC", "/dev/block/mmcblk0p9", "/system");

2)去除写boot.img和data分区的脚本

package_extract_file("boot.img", "/dev/block/mmcblk0p5");

这里要千万小心,一定要把boot.img的写操作去掉

总之,去除一却和system分区无关的操作,去除/system分区的格式化操作和挂载操作

这里一定要小心+细心。

3)接下来将解压出来的ota升级包重新打包成zip文件

4)然后进入recovery进行进一步的操作(这里recovery最好是第三方的recovery,如cm的recovery,命令比较丰富,比较好操作)

5)用usb连接手机,进入recovery后,adb会自动连接到recovery,然后依次执行如下命令:

在执行下面的脚本之前请确认你的手机可以在shell中获取系统权限

将升级脚本执行程序传到手机的/data/local/tmp目录,update-binary在升级包里的META-INF\com\google\android目录,和updater-script同一个目录。

adb push update-binary /data/local/tmp

将修改过update-script的ota升级包传到手机的相应目录

adb push ota.zip /data/local/tmp

切换到root权限

adb shell

su

创建一个800M大小的虚拟磁盘

cd /data/local/tmp

dd if=/dev/zero of=system.disk bs=1024 count=819200

loop虚拟磁盘system.disk

busybox losetup /dev/block/loop7 system.disk

对虚拟磁盘进行格式化

busybox mkfs.ext2 /dev/block/loop7

挂载虚拟磁盘到/system目录

busybox mount -o loop -t ext4 /dev/block/loop7 /system

修改update-binary为可执行

chmod 777 update-binary

开始执行update-script脚本,把ota升级包安装到/system目录

./update-binary 2 0 ota.zip

卸载system虚拟磁盘

umount /system

busybox losetup -d /dev/block/loop7

退出系统权限

exit

退出adb shell

exit

从手机上将system.disk传到PC:

adb pull /data/local/tmp/system.disk

这样,一个烧饼--system虚拟磁盘就做好了:)。

7. 接下来看下如何去创建一个ext4格式的data虚拟磁盘

dd if=/dev/zero of=data.disk bs=1024 count=30720

busybox losetup /dev/loop0 data.disk

mkfs.ext4 -m 1 -v /dev/block/loop7

8. 接下来看下如何去挂载虚拟磁盘

1)先要对boot.img动手术,先要对其解压, 网络上应该有解压的工具,但是我一般都喜欢自己写一些小工具来完成一些简单的任务,一来加深认识,二来也方便功能的扩展。

所以,这里就用python写了个解压的程序:

unpack.py

"""

unsigned char magic[BOOT_MAGIC_SIZE];

unsigned kernel_size; /* size in bytes */

unsigned kernel_addr; /* physical load addr */

unsigned ramdisk_size; /* size in bytes */

unsigned ramdisk_addr; /* physical load addr */

unsigned second_size; /* size in bytes */

unsigned second_addr; /* physical load addr */

unsigned tags_addr; /* physical addr for kernel tags */

unsigned page_size; /* flash page size we assume */

unsigned unused[2]; /* future expansion: should be 0 */

unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */

unsigned char cmdline[BOOT_ARGS_SIZE];

unsigned id[8]; /* timestamp / checksum / sha1 / etc */

"""

import sys

import struct

import subprocess

BOOT_MAGIC_SIZE = 8

BOOT_NAME_SIZE = 16

BOOT_ARGS_SIZE = 512

boot_img = None

kernel_img = None

ramdisk_img = None

ramdisk_cpio = None

ramdisk_out = None

if len(sys.argv) >= 2:

boot_img = sys.argv[1]

else:

boot_img = 'boot.img'

if len(sys.argv) >= 3:

kernel_img = sys.argv[2]

else:

kernel_img = 'kernel.img'

if len(sys.argv) >= 4:

ramdisk_img = sys.argv[3]

else:

ramdisk_img = 'ramdisk.img'

if len(sys.argv) >= 5:

ramdisk_cpio = sys.argv[4]

else:

ramdisk_cpio = 'ramdisk-m.cpio'

if len(sys.argv) >= 6:

ramdisk_out = sys.argv[5]

else:

ramdisk_out = 'ramdisk'

f = open(boot_img,'rb')

magic = f.read(BOOT_MAGIC_SIZE)

kernel_size = f.read(4)

kernel_size = struct.unpack('I',kernel_size)[0]

print kernel_size

kernel_addr = f.read(4)

kernel_addr = struct.unpack('I',kernel_addr)[0]

print "0x%x"%(kernel_addr,)

ramdisk_size = f.read(4)

ramdisk_size = struct.unpack('I',ramdisk_size)[0]

print ramdisk_size

ramdisk_addr = f.read(4)

ramdisk_addr = struct.unpack('I',ramdisk_addr)[0]

print "0x%x"%(ramdisk_addr,)

second_size = f.read(4)

second_size = struct.unpack('I',second_size)[0]

print second_size

second_addr = f.read(4)

second_addr = struct.unpack('I',second_addr)[0]

print "0x%x"%(second_addr,)

"""

unsigned tags_addr; /* physical addr for kernel tags */

unsigned page_size; /* flash page size we assume */

unsigned unused[2]; /* future expansion: should be 0 */

"""

f.seek(BOOT_MAGIC_SIZE + 3*2*4 + 4+4+4*2)

"""

unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */

unsigned char cmdline[BOOT_ARGS_SIZE];

"""

name = f.read(BOOT_NAME_SIZE)

print name

cmdline = f.read(BOOT_ARGS_SIZE)

print cmdline

page_size = 2048

f.seek(2048)

data = f.read(kernel_size)

with open(kernel_img,'wb') as ff:

ff.write(data)

ramdisk_offset = 2048 + (kernel_size+page_size-1)/page_size*page_size

print 'ramdisk_offset:',ramdisk_offset

f.seek(ramdisk_offset)

data = f.read(ramdisk_size)

with open(ramdisk_img,'wb') as ff:

ff.write(data)

cmd = 'gzip -d -r < %s >%s'%(ramdisk_img,ramdisk_cpio)

subprocess.check_output(cmd,shell=True)

cmd = 'mkdir %(ramdisk)s;cd %(ramdisk)s;cpio -i < ../ramdisk-m.cpio'%{'ramdisk':ramdisk_out}

subprocess.check_output(cmd,shell=True)

这个程序会从boot.img中提取出kernel.img和ramdisk.img,并且默认将zip格式的ramdisk.img解压到ramdisk目录

2)修改fstab.smdk4x12

原内容:

# Android fstab file.

#

# The filesystem that contains the filesystem checker binary (typically /system) cannot

# specify MF_CHECK, and must come before any filesystems that do specify MF_CHECK

/dev/block/mmcblk0p9 /system ext4 ro,errors=panic wait

/dev/block/mmcblk0p3 /efs ext4 nosuid,nodev,noatime,noauto_da_alloc,discard,journal_async_commit,errors=panic wait,check

/dev/block/mmcblk0p8 /cache ext4 nosuid,nodev,noatime,noauto_da_alloc,discard,journal_async_commit,errors=panic wait,check

# data partition must be located at the bottom for supporting device encryption

/dev/block/mmcblk0p12 /data ext4 nosuid,nodev,noatime,noauto_da_alloc,discard,journal_async_commit,errors=panic wait,check,encryptable=footer

# VOLD

/devices/platform/s3c-sdhci.2/mmc_host/mmc1/ /storage/extSdCard vfat default voldmanaged=sdcard:auto

/devices/platform/s5p-ehci/usb1 /storage/UsbDriveA vfat default voldmanaged=sda:auto

/devices/platform/s5p-ehci/usb1 /storage/UsbDriveB vfat default voldmanaged=sdb:auto

/devices/platform/s5p-ehci/usb1 /storage/UsbDriveC vfat default voldmanaged=sdc:auto

/devices/platform/s5p-ehci/usb1 /storage/UsbDriveD vfat default voldmanaged=sdd:auto

/devices/platform/s5p-ehci/usb1 /storage/UsbDriveE vfat default voldmanaged=sde:auto

/devices/platform/s5p-ehci/usb1 /storage/UsbDriveF vfat default voldmanaged=sdf:auto

修改后的内容:

# Android fstab file.

#

# The filesystem that contains the filesystem checker binary (typically /system) cannot

# specify MF_CHECK, and must come before any filesystems that do specify MF_CHECK

/dev/block/mmcblk0p3 /efs ext4 nosuid,nodev,noatime,noauto_da_alloc,discard,journal_async_commit,errors=panic wait,check

/dev/block/mmcblk0p8 /cache ext4 nosuid,nodev,noatime,noauto_da_alloc,discard,journal_async_commit,errors=panic wait,check

# data partition must be located at the bottom for supporting device encryption

/dev/block/mmcblk0p12 /dat ext4 nosuid,nodev,noatime,noauto_da_alloc,discard,journal_async_commit,errors=panic wait,check,encryptable=footer

# VOLD

/devices/platform/s3c-sdhci.2/mmc_host/mmc1/ /storage/extSdCard vfat default voldmanaged=sdcard:auto

/devices/platform/s5p-ehci/usb1 /storage/UsbDriveA vfat default voldmanaged=sda:auto

/devices/platform/s5p-ehci/usb1 /storage/UsbDriveB vfat default voldmanaged=sdb:auto

/devices/platform/s5p-ehci/usb1 /storage/UsbDriveC vfat default voldmanaged=sdc:auto

/devices/platform/s5p-ehci/usb1 /storage/UsbDriveD vfat default voldmanaged=sdd:auto

/devices/platform/s5p-ehci/usb1 /storage/UsbDriveE vfat default voldmanaged=sde:auto

/devices/platform/s5p-ehci/usb1 /storage/UsbDriveF vfat default voldmanaged=sdf:auto

这里主要是删除了system分区的挂载,并且把data分区由原来的挂载到dat改为挂载到/dat目录

因为这里我们实际上要挂载的是虚拟磁盘

3)init.rc中:

修改on init:

mkdir /system

后面加上:

mkdir /dat

4)init.smdk4x12.rc中,修改on fs

mount_all /fstab.smdk4x12

后面加上:

mount ext4 loop@/dat/system.disk /system rw wait noatime mount ext4 loop@/dat/data.disk /data wait nosuid nodev noatime

总结:

1)这里,先把应该mount到/data目录的分区mount到/dat目录

2)接下来的两行脚本就把虚拟磁盘给分别挂载到/system和/data

3) 是的,so easy!,但是当功能还未实现时,那种从酝酿到尝试,再到成功的过程还有有一翻体会的,虽然关键的虚拟分区的挂载是在三心二意的情况下完成的(一边看着CCTV的记录片,哈哈,千万不要学)。

9. 重新把kernel和ramdisk打包成boot.img

修改好的ramdisk,接下来就要对其进行重新打包成boot.img了,这里会用到三条命令:mkbootfs,minigzip和mkbootimg。mkbootfs把ramdisk里的所有文件打包成cpio格式的文件

minigzip再对其进行压缩,mkbootimg从kernel和ramdisk.img生成boot.img,使用方法如下:

mkbootfs ramdisk | minigzip > ramdisk.img

mkbootimg --kernel kernel.img --ramdisk ramdisk.img --output boot.img

10. 接下来,到了最后一步了,如何安装第二个Android操作系统?

安装的工作其实真的很简单,把boot.img写到recovery分区,把system.disk,data.disk拷贝到/data分区。

当然,也可以把system.disk和data.disk放到外部SD卡中,这要涉及到修改ramdisk

11.最后,总结下全文吧:

在手机上实现双系统已经不是什么新技术了,在用虚拟磁盘的方式实现双系统之后,后来百度了下,看到11年写的一篇文章, 是在SD卡上进行分区来实现双系统的,操作比较麻烦,

切换还要启动到其中一个系统通过刷boot分区来实现系统的切换,还不如本文直接刷recovery分区的方式来的方便。

本文的双系统实现其实还是比较简单的:

1)将system.img通过simg2img转换成可以直接通过loop挂载的格式system.disk

2)创建data分区虚拟磁盘data.disk

3)修改boot.img中的启动相关的脚本,实现挂载虚拟磁盘,并重新打包

4)将system.disk,data.disk放到原data分区

5)将重新打包的boot.img刷到recovery分区。

12. Is this the end or just the beginning?

如果是网络机顶盒,上面的已经足够了,但是我们是在手机上创建双系统,所以仍然有一段路要走,以下是需要思考的问题:

1)如何保持双系统中的通信录的同步问题

2)如何同步双系统中常用工具的数据,如微信聊天记录等等。

3)如何处理不同版本的android系统基带的不兼容性问题。

4) 如何处理efs分区在不同版本的android系统不相互兼容问题。

5) 如何实现Android+WP双系统

6) 如何实现三系统,四系统?

7) ...

(完)

版权声明:本文为CSDN博主「I2Cbus」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/I2Cbus/article/details/38985255

土豆播放器官方下载 打游戏多久休息合适一点 如何科学地安排游戏时间和休息时间
相关内容