OpenHarmony——分区切换之reboot源码解析

一年好景君须记,最是橙黄橘绿时。这篇文章主要讲述OpenHarmony——分区切换之reboot源码解析相关的知识,希望能为你提供帮助。
作者:曹芝展
简介系统在执行升级操作时,执行指令reboot updater,对misc分区写入相关信息,然后重启系统进行分区切换操作,加载updater分区并执行OTA升级,本文仅介绍reboot的操作原理,以下内容主要基于v3.0-Release版进行分析。
代码路径

base\\startup\\init_lite\\services\\cmds\\reboot\\init_cmd_reboot.c base\\startup\\init_lite\\interfaces\\innerkits\\reboot\\init_reboot.c base\\startup\\init_lite\\services\\src\\init_reboot.c

目录结构
base/startup/init_lite/# init组件 ├── LICENSE └── services ├── include# init组件头文件目录 ├── src# init组件源文件目录 └── test# init组件测试用例源文件目录 └── unittest vendor └──huawei └──camera └──init_configs# init配置文件目录(json格式,镜像烧写后部署于/etc/init.cfg)

init_cmd_reboot.c文件位于init组件中,init组件负责处理从内核加载第一个用户态进程开始,到第一个应用程序启动之间的系统服务进程启动过程。启动恢复子系统除负责加载各系统关键进程之外,还需在启动的同时设置其对应权限,并在子进程启动后对指定进程实行保活(若进程意外退出要重新启动),对于特殊进程意外退出时,启动恢复子系统还要执行系统复位操作。
流程图
OpenHarmony——分区切换之reboot源码解析

文章图片

源码解读 1.init_cmd_reboot.c文件的main函数
在命令行输入reboot updater指令,会调用到下面的函数中:
int main(int argc, char* argv[])if (argc > REBOOT_CMD_NUMBER)//判断输入参数是否合法 printf("usage: reboot shutdown\\nreboot updater\\nreboot updater[:options]\\nreboot\\n"); return 0; if (argc == REBOOT_CMD_NUMBER & & strcmp(argv[1], "shutdown") != 0 & & strcmp(argv[1], "updater") != 0 & & strncmp(argv[1], "updater:", strlen("updater:")) != 0) printf("usage: reboot shutdown\\nreboot updater\\nreboot updater[:options]\\nreboot\\n"); return 0; int ret; if (argc == REBOOT_CMD_NUMBER) ret = DoReboot(argv[1]); else ret = DoReboot(NULL); if (ret != 0) printf("[reboot command] DoReboot Api return error\\n"); else printf("[reboot command] DoReboot Api return ok\\n"); while (1) pause(); return 0;

main函数中主要作用是识别reboot指令,执行DoReboot操作。
2.DoReboot函数中的libuv库
int DoReboot(const char *cmdContent)uid_t uid1 = getuid(); uid_t uid2 = geteuid(); //鉴权操作,reboot需要在root权限下执行 if (uid1 != 0 || uid2 != 0) INIT_LOGE("uid1=%d, uid2=%d, user MUST be root, error!", uid1, uid2); return -1; //省略部分不重要代码 /... .../ if (snprintf_s(value, MAX_REBOOT_NAME_SIZE, MAX_REBOOT_NAME_SIZE - 1, "%s%s", "reboot,", cmdContent) < 0) INIT_LOGE("DoReboot api error, MAX_REBOOT_NAME_SIZE is not enough"); return -1; if (SystemSetParameter("sys.powerctrl", value) != 0) INIT_LOGE("DoReboot Api SystemSetParameter error"); return -1; return 0;

将SystemSetParameter函数展开,其最终调用到了StartRequest,我们来看下StartRequest:
static int StartRequest(int cmd, RequestNode *request)PARAM_CHECK(request != NULL, return -1, "Invalid request"); request-> result = -1; request-> msg.type = cmd; request-> loop = uv_loop_new(); PARAM_CHECK(request-> loop != NULL, return -1, "StartRequest uv_loop_new failed"); uv_pipe_init(request-> loop, & request-> handle, 1); uv_pipe_connect(& request-> connect, & request-> handle, PIPE_NAME, OnConnection); uv_run(request-> loop, UV_RUN_DEFAULT); uv_loop_delete(request-> loop); int result = request-> result; free(request); return result;

【OpenHarmony——分区切换之reboot源码解析】实际上是通过StartRequest函数是通过libuv库的pipe管道拉起了sys.powerctrl服务之后,再通过该服务调用到具体的do reboot操作。
备注:libuv是一个高性能事件驱动库,屏蔽了各种操作系统的差异从而提供了统一的API。libuv严格使用异步、事件驱动的编程风格。其核心工作是提供事件循环及基于 I/O或其他活动事件的回调机制。libuv库包含了诸如计时器、非阻塞网络支持、异步文件系统访问、线程创建、子进程等核心工具,关于libuv库的详细内容此次不做详解。
3.真正的DoReboot函数
void DoReboot(const char *value)if (value =https://www.songbingjia.com/android/= NULL) INIT_LOGE("DoReboot value = https://www.songbingjia.com/android/NULL"); return; INIT_LOGI("DoReboot value = https://www.songbingjia.com/android/%s", value); if (strlen(value) > MAX_VALUE_LENGTH || strlen(value) < strlen("reboot") || strlen(value) == strlen("reboot,")) INIT_LOGE("DoReboot reboot value error, value = https://www.songbingjia.com/android/%s.", value); return; const char *valueData = https://www.songbingjia.com/android/NULL; if (strncmp(value,"reboot,", strlen("reboot,")) == 0) valueData = https://www.songbingjia.com/android/value + strlen("reboot,"); else if (strlen(value) < strlen("reboot,") & & strncmp(value, "reboot", strlen("reboot")) == 0) valueData = https://www.songbingjia.com/android/NULL; else INIT_LOGE("DoReboot reboot value = https://www.songbingjia.com/android/%s, must started with reboot ,error.", value); return; if (valueData != NULL & & strncmp(valueData, "shutdown", strlen("shutdown")) != 0 & & strncmp(valueData, "updater:", strlen("updater:")) != 0 & & strncmp(valueData, "updater", strlen("updater")) != 0) INIT_LOGE("DoReboot value = https://www.songbingjia.com/android/%s, parameters error.", value); return; //在执行reboot updater之前需停止所有的服务 StopAllServicesBeforeReboot(); //挂载了vendor和data分区,则需先卸载这两个分区 if (GetMountStatusForMountPoint("/vendor") != 0) if (umount("/vendor") != 0) INIT_LOGE("DoReboot umount vendor failed! errno = %d.", errno); if (GetMountStatusForMountPoint("/data") != 0) if (umount("/data") != 0) INIT_LOGE("DoReboot umount data failed! errno = %d.", errno); int ret = DoRebootCore(valueData); if (ret != 0) INIT_LOGE("DoReboot value = https://www.songbingjia.com/android/%s, error.", value); return;

4.DoRebootCore函数中区分shutdown/upadter
static int DoRebootCore(const char *valueData)if (valueData =https://www.songbingjia.com/android/= NULL) reboot(RB_AUTOBOOT); return 0; else if (strncmp(valueData,"shutdown", strlen("shutdown")) == 0) reboot(RB_POWER_OFF); return 0; else if (strncmp(valueData, "updater", strlen("updater")) == 0) int ret = UpdateUpdaterStatus(valueData); if (ret == 0) reboot(RB_AUTOBOOT); return 0; else return -1; return 0;

若是shutdown指令则直接关机,updater指令则需更新信息到misc分区中,然后再重启系统。
5.UpdateUpdaterStatus函数
static int UpdateUpdaterStatus(const char *valueData)const char *miscFile = "/dev/block/platform/soc/10100000.himci.eMMC/by-name/misc"; struct RBMiscUpdateMessage msg; bool ret = RBMiscReadUpdaterMessage(miscFile, & msg); if (!ret) INIT_LOGE("RBMiscReadUpdaterMessage error."); return -1; if (snprintf_s(msg.command, MAX_COMMAND_SIZE, MAX_COMMAND_SIZE - 1, "%s", "boot_updater") == -1) INIT_LOGE("RBMiscWriteUpdaterMessage error"); return -1; if (strlen(valueData) > strlen("updater:") & & strncmp(valueData, "updater:", strlen("updater:")) == 0) if (snprintf_s(msg.update, MAX_UPDATE_SIZE, MAX_UPDATE_SIZE - 1, "%s", valueData + strlen("updater:")) == -1) INIT_LOGE("RBMiscWriteUpdaterMessage error"); return -1; ret = RBMiscWriteUpdaterMessage(miscFile, & msg); if (ret != true) INIT_LOGE("RBMiscWriteUpdaterMessage error"); return -1; else if (strlen(valueData) == strlen("updater") & & strncmp(valueData, "updater", strlen("updater")) == 0) ret = RBMiscWriteUpdaterMessage(miscFile, & msg); if (ret != true) INIT_LOGE("RBMiscWriteUpdaterMessage error"); return -1; else return -1; return 0;

(1) miscFile的文件路径需根据实际的文件路径进行修改;
(2) RBMiscReadUpdaterMessage根据miscFile的路径去获取misc文件的信息,一般情况下会是空的,因为并没有写入什么信息进去;
(3) 拼接boot_updater字符串,放到msg的command字段;
(4) RBMiscWriteUpdaterMessage函数将上文拼接的boot_updater字符串写入到misc文件中去;
至此我们的misc文件中保存着boot_updater的信息,在系统启动时切换分区会用到该信息。
执行效果在命令行中输入指令reboot updater,相关日志如下:
[121.829824] c0 [pid=1][init_reboot.c:216][Init][INFO] DoReboot value = https://www.songbingjia.com/android/reboot,updater [121.861187] c0 !! lit:we gonna plugin cpu1 !! [121.867579] c0 [pid=1][param_service.c:263][Init][INFO] SystemWriteParam name init.svc.ueventd value: stopping [121.878943] c0 [pid=1][trigger_processor.c:165][Init][INFO] PostParamTrigger init.svc.ueventd success [121.888205] c0 [pid=1][init_service.c:253][Init][INFO] stop service ueventd, pid 216. [121.898739] c0 [pid=1][param_service.c:263][Init][INFO] SystemWriteParam name init.svc.modem_control value: stopping [121.909471] c0 [pid=1][trigger_processor.c:165][Init][INFO] PostParamTrigger init.svc.modem_control success [121.920048] c1 CPU1: update max cpu_capacity 1024 [121.921607] c0 [pid=1][init_service.c:253][Init][INFO] stop service modem_control, pid 312. [121.935159] c0 [pid=1][param_service.c:263][Init][INFO] SystemWriteParam name init.svc.slogmodem value: stopping ... [122.764609] c1 [pid=1][init_service.c:253][Init][INFO] stop service installs, pid 260. [122.765543] c1 [pid=1][param_service.c:263][Init][INFO] SystemWriteParam name init.svc.wifi_hal_service value: stopping [122.765613] c1 [pid=1][trigger_processor.c:165][Init][INFO] PostParamTrigger init.svc.wifi_hal_service success [122.765622] c1 [pid=1][init_service.c:253][Init][INFO] stop service wifi_hal_service, pid 279. [123.382642] c2 sprd-powerdebug power-debug: ###--PMU submodule power states--### [123.389967] c2 sprd-powerdebug power-debug:##--reg offset:0x00bc value:0x00000000: [123.397928] c3 [pid=1][init_reboot.c:248][Init][ERROR] DoReboot umount data failed! errno = 16. [123.405106] c2 sprd-powerdebug power-debug:#--PD_CA53_TOP STATE:0x0 [123.405111] c2 sprd-powerdebug power-debug:#--PD_CA53_C0 STATE:0x0 [123.405117] c2 sprd-powerdebug power-debug:#--PD_CA53_C1 STATE:0x0 [123.405121] c2 sprd-powerdebug power-debug:#--PD_CA53_C2 STATE:0x0 [123.405126] c2 sprd-powerdebug power-debug:#--PD_CA53_C3 STATE:0x0 [123.405130] c2 sprd-powerdebug power-debug:#--PD_AP_SYS STATE:0x0 [123.405140] c2 sprd-powerdebug power-debug:##--reg offset:0x00c0 value:0x0e001ce7: [123.405145] c2 sprd-powerdebug power-debug:#--PD_WTLCP_HU3GE_A STATE:0x7 [123.405149] c2 sprd-powerdebug power-debug:#--PD_WTLCP_TGDSP STATE:0x7 [123.405154] c2 sprd-powerdebug power-debug:#--PD_WTLCP_LDSP STATE:0x7 [123.405159] c2 sprd-powerdebug power-debug:#--PD_WFI_WRAP STATE:0x0 [123.405163] c2 sprd-powerdebug power-debug:#--PD_WTLCP_LTE_P2 STATE:0x0 [123.405168] c2 sprd-powerdebug power-debug:#--PD_WTLCP_LTE_P1 STATE:0x7 [123.405174] c2 sprd-powerdebug power-debug:##--reg offset:0x00c4 value:0x0e0000e7: [123.405179] c2 sprd-powerdebug power-debug:#--PD_WTLCP_SYS STATE:0x7 [123.405184] c2 sprd-powerdebug power-debug:#--PD_PUBCP_SYS STATE:0x7 [123.405190] c2 sprd-powerdebug power-debug:#--PD_WTLCP_LTE_P3 STATE:0x0 [123.405194] c2 sprd-powerdebug power-debug:#--PD_WTLCP_LTE_P4 STATE:0x0 [123.405199] c2 sprd-powerdebug power-debug:#--PD_PUB_SYS STATE:0x0 [123.405204] c2 sprd-powerdebug power-debug:#--PD_GNSS_WRAP STATE:0x7 [123.405218] c2 sprd-powerdebug power-debug: PM: has wakeup events in progress: [123.655758] c3 mmc0: clock 0Hz busmode 2 powermode 0 cs 0 Vdd 0 width 1 timing 0 [123.671430] c3 sprd-sdhcr10 20600000.sdio: there is no signal voltage! [123.687965] c3 No poweroff alarm found [123.702537] c3 vbc-rxpx-codec-sc27xx sound@0: ASoC: DAPM unknown pin inter Spk1 PA [123.716967] c3 WCN BASE: enter [123.731343] c3 WCN BASE: ctrl_shutdown_reg[0] = 0x57c, val=0x80 [123.731362] c3 WCN BASE: ctrl_reg[0] = 0x57c, val=0x0 [123.748959] c3 WCN BASE: ctrl_shutdown_reg[1] = 0xb0, val=0x800 [123.768481] c3 WCN BASE: ctrl_reg[1] = 0xb0, val=0xc00 [123.785221] c3 WCN BASE: enable = 0, ret = 0 [123.820360] c3 WCN BASE: enable=0,en_count=0,ret=0,btwf=1,gnss=0 [123.820366] c3 WCN BASE: finish! [123.820369] c3 WCN BASE: dev name wcn_btwf [123.820818] c3 [drm] sprd_dsi_encoder_disable() [123.834277] c3 [drm][dpu_stop] dpu stop [123.891709] c3 [drm] sprd_panel_disable() [124.070280] c3 [drm] dphy ulps enter [124.080117] c3 [drm] sprd_panel_unprepare() [124.119843] c3 [drm] dphy suspend OK [124.130839] c3 [drm] dsi suspend OK [124.145576] c3 [drm] sprd_crtc_atomic_disable() [124.156786] c3 [drm] dpu has already powered off [124.239661] c3 FLASH_DRV: 1: 252 ctrl: led_index 3 status 0x0 [124.251967] c3 FLASH_DRV: 1: 252 ctrl: led_index 3 status 0x0 [124.252379] c0 Disabling non-boot CPUs ... [124.390047] c0 CPU0: update max cpu_capacity 1024 [125.128590] c0 CPU1 killed. [125.140088] c0 !! lit:we gonna plugin cpu1 !! [125.150086] c0 dev-> offline error, ret=1 [125.191109] c3 CPU3: update max cpu_capacity 1024 [125.233114] c0 CPU2 killed. [125.380724] c0 CPU3 killed. [125.384470] c0 reboot: Restarting system

总结本文仅介绍了在升级时执行reboot updater指令关机之前的一些流程和源码,除了需要向misc分区写入关键信息之外还需要关闭服务和卸载分区,另外需注意根据板子中misc文件的实际位置去修改msicfile变量的路径,重启之后的详细流程将放到下一篇讲解。
更多原创内容请关注:深开鸿技术团队入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。
想了解更多关于开源的内容,请访问:
51CTO 开源基础软件社区
https://ost.51cto.com/#bkwz

    推荐阅读