安卓双系统的分析与实现
mnsd

注:本文为资深刷机用户推荐,小白请勿轻易尝试,可作为分析思路浏览,以下操作如未说明则都需解锁Bootloader锁(如果有的话),并已获得root权限。

引言

首先你应对电脑多系统有大致了解,BIOS如何引导操作系统,Bootloader是干了什么事,怎样通过磁盘分区、多重引导等实现多系统。相对于手机这些嵌入式系统,由于牵涉到各家硬件驱动是否开源等等一些问题,通过类似电脑端grub,systemd-boot,refind等达到多重引导是较难实现的。但是通过转换思路,既然操作系统它是通过挂载对应磁盘分区进行之后的一系列操作,是否又可以通过改变分区表(或者说特定分区指向)从而达到敌不动我动的效果,从而欺骗了bootloader。

安卓双系统底层实现原理

磁盘分区工具的选择

方向既然已经清楚了,那接下来就是具体思路。由于安卓系统本就基于linux内核,通过一些C/C++库支撑,每个apk应用都是运行在ART(Dalvik)虚拟机之上。则可以尝试通过一些磁盘分区工具尝试对分区进行删改,在arm架构处理器上处理gpt分区表用的较多的GNU工具有sgdisk、parted等,可自行选择。由于大多数手机系统都已经内置sgdisk工具,本文以sgdisk举例,如系统未内置则需要自行解决,解决方法参考linux。

安卓手机磁盘分区

在对磁盘进行重分区之前,有必要先对手机内存(如未说明则后文内存指手机内部存储,即等价于电脑硬盘,非电脑内存条)驱动器有一定了解,目前市面上手机内存主要为ufs闪存,有少部分为emmc闪存,在磁盘分区处理上有稍微差异,本文主要以ufs闪存为例。ufs闪存的手机通常把逻辑驱动器划分为sda,sdb,sdc,sdd,sde,sdf等,设备分别映射文件到/dev/block/sda,/dev/block/sdb…等,emmc则映射到/dev/block/mmcblk0…,磁盘分区表记录了该逻辑驱动器上的各分区始末位置、分区大小等信息,位于该逻辑驱动器的前n个块。

mix2s分区表信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
$ sgdisk --print /dev/block/sda
Disk /dev/block/sda: 14419968 sectors, 55.0 GiB
Logical sector size: 4096 bytes
Disk identifier (GUID): E9F67F91-775B-75A5-2D93-3CDF9DEB39EF
Partition table holds up to 32 entries
First usable sector is 6, last usable sector is 14419962
Partitions will be aligned on 2-sector boundaries
Total free space is 0 sectors (0 bytes)

Number Start (sector) End (sector) Size Code Name
1 6 9 16.0 KiB FFFF switch
2 10 17 32.0 KiB FFFF ssd
...... ...... ......
18 98304 311295 832.0 MiB FFFF cust
19 311296 327679 64.0 MiB FFFF recovery
20 327680 393215 256.0 MiB FFFF cache
21 393216 14419962 53.5 GiB FFFF userdata

$ sgdisk --print /dev/block/sde
Disk /dev/block/sde: 1179648 sectors, 4.5 GiB
Logical sector size: 4096 bytes
Disk identifier (GUID): 09A16BAE-F899-2669-BA14-602F3A220D2F
Partition table holds up to 64 entries
First usable sector is 6, last usable sector is 1179642
Partitions will be aligned on 2-sector boundaries
Total free space is 16379 sectors (64.0 MiB)

Number Start (sector) End (sector) Size Code Name
1 6 13 32.0 KiB FFFF sec
2 14 21 32.0 KiB FFFF limits
...... ...... ......
42 24576 32767 32.0 MiB FFFF splash
43 32768 40959 32.0 MiB FFFF logo
44 40960 49151 32.0 MiB FFFF dsp
45 49152 65535 64.0 MiB FFFF boot
46 65536 114687 192.0 MiB 0700 modem
47 114688 376831 1024.0 MiB FFFF vendor
48 376832 1163263 3.0 GiB FFFF system
49 1163264 1179642 64.0 MiB FFFF last_parti

sgdisk使用方法

sgdisk工具需要在su下使用,详细使用方法可在终端输入sgdisk按回车查看
以下列出几个需要用到的几个命令

1
2
3
4
5
6
$ sgdisk --print /dev/block/sda #打印驱动器sda分区表
$ sgdisk --new=26:12345:34567 #新建分区号为26的分区,从第12345块开始,到34567块结束
$ sgdisk --new=27:0:+512M /dev/block/sda #新建分区号为27的分区,从第一个可以容下512M的空间的起始位置开始,建立一个512M的分区
$ sgdisk --new=0:0:0 /dev/block/sda #从第一个可用分区号开始,从第一个可用分区块位置开始,到结束
$ sgdisk --change-name=18:boot /dev/block/sda #将分区号为18的分区改名为boot
$ sgdisk --delete=21 /dev/block/sda #删除分区号为21的分区

注:以上命令均需指定驱动器位置,不指定则命令无任何作用。电脑上驱动器一般为/dev/sda,手机上稍微有不同之处,通常为/dev/block/sda,且执行命令后重启生效,所以在未完成全部操作之前切勿重启手机,重启即黑砖,需9008复活。后悔药:在结束之前可随时刷入最初备份的原分区表进行回退

双系统共存方法

结构概览

image

image

双分区的选择

接下来,要通过对不同系统差异的分区进行重分区,做双分区处理,相同的当然就没必要了。

分区名 作用 所在驱动器(基于mix2s示范)
system 系统分区 sde
vendor 驱动分区 sde
boot 内核分区 sde
userdata 用户数据分区 sda
dsp 可能和媒体声音有关 sde
modem 基带解调器 sde

由于开机时bootloader加载时挂载分区是根据分区名称进行挂载,只认名字,所以要实现双系统,就需要将这些分区可以进行单独分开(自由选择分开哪些分区,此处只做示范),分为system1,system2,vendor1,vendor2,boot1,boot2,userdata1,userdata2,dsp1,dsp2,modem1,modem2,再进一步做屏蔽处理。

分区表的备份

可以用sgdisk等工具自带的备份命令直接备份,也可直接用dd命令拷贝出驱动器前512个块(已完整包含该驱动器所有分区表信息)

1
$ dd if=/dev/block/sda of=/sdcard/sda_fqb bs=512 count=1024 #从起始位置开始拷贝sda驱动器上的块,每次512B,累计1024次,即512K

系统一分区的实例

实现双系统的切换,则我们需要两张分区表,一张保存系统一的磁盘分区情况如下,此时系统二对应分区被屏蔽掉,开机不加载

系统一 系统二(不加载)
system(sde) system2(sda)
vendor(sde) vendor2(sda)
boot(sde) boot2(sda)
userdata(sda) userdata2(sda)

代码如下:(基于miMIX2s的示范)

1
2
3
4
5
6
7
8
9
10
$ dd if=/dev/block/sda bs=512 count=1024 of=/sdcard/sda #备份单系统时的分区表,后悔药
$ dd if=/dev/block/sde bs=512 count=1024 of=/sdcard/sde
$ sgdisk --delete=21 /dev/block/sda #删掉原data,留下空闲空间
$ sgdisk --new=21:0:+64M /dev/block/sda --change-name=21:boot2 /dev/block/sda #新建另一个系统相关分区
$ sgdisk --new=22:0:+1024M /dev/block/sda --change-name=22:vendor2 /dev/block/sda
$ sgdisk --new=23:0:+3584M /dev/block/sda --change-name=23:system2 /dev/block/sda
$ sgdisk --new=24:0:+5632M /dev/block/sda --change-name=24:userdata2 /dev/block/sda
$ sgdisk --new=25:0:+40000M /dev/block/sda --change-name=25:userdata /dev/block/sda #将剩余空间还非配给这个系统
$ dd if=/dev/block/sda bs=512 count=1024 of=/sdcard/sda_fqb1
$ dd if=/dev/block/sde bs=512 count=1024 of=/sdcard/sde_fqb1

系统二分区的实例

另一张保存系统二的磁盘分区情况如下,此时系统一对应分区被屏蔽掉,开机不加载

系统一(不加载) 系统二
system1 system
vendor1 vendor
boot1 boot
userdata1 userdata
代码如下:(基于miMIX2s的示范)
1
2
3
4
5
6
7
8
9
10
$ sgdisk --change-name=25:userdata1 /dev/block/sda
$ sgdisk --change-name=21:boot /dev/block/sda
$ sgdisk --change-name=22:vendor /dev/block/sda
$ sgdisk --change-name=23:system /dev/block/sda
$ sgdisk --change-name=24:userdata /dev/block/sda
$ sgdisk --change-name=45:boot1 /dev/block/sde
$ sgdisk --change-name=47:vendor1 /dev/block/sde
$ sgdisk --change-name=48:system1 /dev/block/sde
$ dd if=/dev/block/sda bs=512 count=1024 of=/sdcard/sda_fqb2
$ dd if=/dev/block/sde bs=512 count=1024 of=/sdcard/sde_fqb2

双系统的切换

每次只要载入不同的分区表信息,就能实现系统的切换,注意一定要保管好分区表信息不可丢失

1
2
$ dd if=/sdcard/sda_fqb1 of=/dev/block/sda bs=512 count=1024 #将系统一sda分区表信息载入
$ dd if=/sdcard/sde_fqb1 of=/dev/block/sde bs=512 count=1024 #将系统一sde分区表信息载入

载入相关驱动器分区表信息即可切换到系统一。系统二同理

双系统方案的优化

将更多的分区独立开来

当多个系统之间安卓底层版本不同时极易出现各种bug,即部分分区被共用所产生的问题,例如modem导致基带问题、dsp导致媒体声音问题。解决方案为将更多的分区给分开,当熟练掌握原理后即可仿照上例实现,但部分核心分区不可随意动手,切勿贪心。

切换方式多样化

将分区表或打包有分区表的卡刷包放至例如cust等空闲分区,通过如下方式切换

  • 终端命令切换。即上方提到的方式
  • shell脚本切换。将命令打包添加至shell脚本,每次执行脚本即可
  • 卡刷包切换。将分区表打包进卡刷包到rec卡刷
  • recovery切换。将打包好的卡刷包打包进recovery.img,需自行搜索解包工具
  • app切换。

分区共享数据

将未挂载的另外一个系统的相应分区挂载到本系统。

1
2
$ mkdir /mnt/data_2
$ mount -t ext4 -o rw /dev/block/by-name/userdata2 /mnt/data_2
 评论
评论插件加载失败
正在加载评论插件