一年好景君须记,最是橙黄橘绿时。这篇文章主要讲述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组件负责处理从内核加载第一个用户态进程开始,到第一个应用程序启动之间的系统服务进程启动过程。启动恢复子系统除负责加载各系统关键进程之外,还需在启动的同时设置其对应权限,并在子进程启动后对指定进程实行保活(若进程意外退出要重新启动),对于特殊进程意外退出时,启动恢复子系统还要执行系统复位操作。
流程图
文章图片
源码解读 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
推荐阅读
- 如何在 Flask 项目中使用 MQTT
- client-go gin的简单整合四-list-watch初探
- 思科与华为ACL使用区别
- netty系列之:在netty中实现线程和CPU绑定
- 安装 aconda 后Linux的终端界面前部出现(base)字样
- Linux目录和文件权限
- 视频推荐(Linux 的make自动化编译和通用makefile)
- Java中的线程到底有哪些安全策略
- Linux中scanf类型匹配错误,特指scanf(