OpenHarmony 启动流程优化

目前rk3568的开机时间有21s,统计的是关机后从按下 power 按键到显示锁屏的时间,当对openharmony的系统进行了裁剪子系统,系统app,禁用部分服务后发现开机时间仅仅提高到了20.94s 优化微乎其微。在对init进程的log进行分析并解决其中的时间断层后 开机时长优化到了16.5s左右,可以说是一个非常大的进步了,下面详细讲一下优化的过程。

一、定位 log

​ openharmony支持dmesg打印kernel log和hilog 打印openharony自己的log,所以需要在开机时抓取这两种log来分析开机流程。openharony的开机流程可以参考

由于本人对于kernel了解比较少,所以对于kernel部分的优化无法介绍。从上面的OpenHarmony init进程的启动流程中我们可以知道openharmony的启动和Android比较类似都是init进程去解析各种cfg文件启动服务,那么当kernel 内核初始化完后,init进程也就是pid=1的这个进程就是开机过程的主线程。由于openharmny的dmesg log中都携带了pid和函数名称,所以我们在开机时抓的dmesg log中搜索pid=1可以看到开机时主线程的打印log,先寻找是否有时间断层的情况,比如我的设备中dmesg有一处时间断层如下:

1515: [    4.396049] [pid=1][Init][INFO][init_cmds.c:291]Mount partitions from fstab file " /vendor/etc/fstab.rk3568 "1517: [    4.396782] [pid=1][BEGET][INFO][fstab.c:429]StoreFscryptPolicy:store fscrypt policy, 2:aes-256-cts:aes-256-xts
	行 1522: [    4.897721] [pid=1][BEGET][INFO][fstab_mount.c:78]Execute /system/bin/resize.f2fs begin
	行 1524: [    4.947110] [pid=1][BEGET][ERROR][fstab_mount.c:91]Command /system/bin/resize.f2fs failed with status 2551525: [    4.947141] [pid=1][BEGET][INFO][fstab_mount.c:93]Execute /system/bin/resize.f2fs end
	行 1526: [    4.947218] [pid=1][BEGET][ERROR][fstab_mount.c:394]Failed to resize.f2fs dir /dev/block/platform/fe310000.sdhci/by-name/userdata , ret = 2551527: [    4.947309] [pid=1][BEGET][INFO][fstab_mount.c:78]Execute /system/bin/fsck.f2fs begin
	行 1531: [    7.196164] [pid=1][BEGET][ERROR][fstab_mount.c:91]Command /system/bin/fsck.f2fs failed with status 11532: [    7.196179] [pid=1][BEGET][INFO][fstab_mount.c:93]Execute /system/bin/fsck.f2fs end
	行 1533: [    7.196225] [pid=1][BEGET][ERROR][fstab_mount.c:399]Failed to fsck.f2fs dir /dev/block/platform/fe310000.sdhci/by-name/userdata , ret = 11538: [    7.397003] [pid=1][BEGET][INFO][fstab_mount.c:421]Mount /dev/block/platform/fe310000.sdhci/by-name/userdata to /data successful
	行 1540: [    7.398757] [pid=1][BEGET][INFO][fstab_mount.c:421]Mount /dev/block/platform/fe310000.sdhci/by-name/chip-prod to /chip_prod successful
	行 1542: [    7.400545] [pid=1][BEGET][INFO][fstab_mount.c:421]Mount /dev/block/platform/fe310000.sdhci/by-name/sys-prod to /sys_prod successful
	行 1543: [    7.400598] [pid=1][Init][INFO][init_cmds.c:293]Mount partitions from fstab file " /vendor/etc/fstab.rk3568 " finish ret 0

可以看到上面在4.947309 到7.196164出现了断层,这段时间主线程什么都没有输出,并且查看log发现init主线程是在执行一个命令/system/bin/fsck.f2fs,并且在执行了2.2s后还失败了,但是设备是可以正常开机的。网上搜索发现fsck(file system check)用来检查和维护不一致的文件系统。若系统掉电或磁盘发生问题,可利用fsck命令对文件系统进行检查。由于这里是执行失败并且没有什么影响,所以我这里将这个命令改成了异步执行。这样主线程就可以继续往下执行了。

在将fsck命令的执行修改成异步执行后发现这里的时间断层消失了但是又发现了新的时间断层如下:

// fsck命令时间已经恢复
[    4.894672] [pid=1][BEGET][INFO][fstab_mount.c:102]Execute /system/bin/resize.f2fs begin
[    4.896229] [pid=1][BEGET][INFO][fstab_mount.c:118]Execute /system/bin/resize.f2fs end
[    4.896445] [pid=1][BEGET][INFO][fstab_mount.c:102]Execute /system/bin/fsck.f2fs begin
[    4.898137] [pid=1][BEGET][INFO][fstab_mount.c:118]Execute /system/bin/fsck.f2fs end
//新出现的时间断层
	行 1943: [    5.236372] [pid=1][BEGET][INFO][fstab.c:434]LoadFscryptPolicy start
	行 1944: [    5.236398] [pid=1][BEGET][INFO][fstab.c:449]LoadFscryptPolicy success
	行 1945: [    5.236533] [pid=1][Init][INFO][init_cmds.c:99]Sync exec: /system/bin/sdc
	行 1992: [    8.223549] [pid=1][Init][INFO][init_cmds.c:112]Sync exec: /system/bin/sdc result 0 11993: [    8.232997] [pid=1][Init][INFO][fscrypt_control.c:217]Fscrypt policy init success
	行 1994: [    8.233204] [pid=1][Init][INFO][key_control.c:204]version 2 loaded
	行 1995: [    8.233234] [pid=1][Init][INFO][fscrypt_control.c:234]key path /data/service/el0/storage_daemon/sd, name /key_id
	行 1996: [    8.233313] [pid=1][Init][INFO][key_control.c:110]enter
	行 1997: [    8.233447] [pid=1][Init][INFO][key_control.c:78]success
	行 1998: [    8.251213] [pid=1][Init][INFO][fscrypt_control.c:186]Have been init
	行 1999: [    8.251311] [pid=1][Init][INFO][key_control.c:204]version 2 loaded
	行 2000: [    8.251328] [pid=1][Init][INFO][fscrypt_control.c:234]key path /data/service/el0/storage_daemon/sd, name /key_id
	行 2001: [    8.251377] [pid=1][Init][INFO][key_control.c:110]enter
	行 2002: [    8.251453] [pid=1][Init][INFO][key_control.c:78]success
	行 2004: [    8.261900] [pid=1][Init][INFO][fscrypt_control.c:186]Have been init
	行 2005: [    8.261969] [pid=1][Init][INFO][key_control.c:204]version 2 loaded
	行 2006: [    8.262016] [pid=1][Init][INFO][fscrypt_control.c:234]key path /data/service/el0/storage_daemon/sd, name /key_id
	行 2007: [    8.262068] [pid=1][Init][INFO][key_control.c:110]enter
	行 2008: [    8.262149] [pid=1][Init][INFO][key_control.c:78]success
	行 2009: [    8.264017] [pid=1][Init][INFO][init_cmds.c:99]Sync exec: /system/bin/sdc
	行 2011: [    8.338298] [pid=1][Init][INFO][init_cmds.c:112]Sync exec: /system/bin/sdc result 0 1

从上面可以知道是系统执行sdc命令导致耗时了将近3s那么这个命令又是做什么的为什么会耗费3s,这个我们后面再去分析。

到这里init阶段的耗时基本就分析完了,同样的我们需要接着去分析hilog,我们知道hilog是需要hilogd启动才能打印,所以dmesg的log中hilogd启动时间和hilog的打印时间应该不会错的太开。接着我们知道init服务启动了很多,那么哪些服务是启动launcher的呢?答案是foundation服务。foundation服务会启动AbilityManagerService,在AbilityManagerService中当AccountManagerService发出切换user 100的请求时就会启动launcher。我在看dmesg和hilog的过程中发现了foundation和account的启动时间如下所示

[    9.420796] [pid=1][Init][INFO][init_service_manager.c:1084]Start service foundation
[    9.457414] [pid=1][Init][INFO][init_common_service.c:387]Service foundation(pid 536) started
[    9.570249] [pid=1][Init][INFO][init_service_manager.c:1084]Start service accountmgr
[    9.571380] [pid=1][Init][INFO][init_common_service.c:387]Service accountmgr(pid 553) started

可以看到foundation的启动时间在accountmagr之前,但是在hilog中我看到了如下log

行 24030: 07-04 11:03:24.761   553   893 I C01b00/AccountMgrService: [SendToAMSAccountStart:53]:start
行 52650: 07-04 11:03:28.073   553   893 I C01b00/AccountMgrService: [SendToAMSAccountStart:63]:end, succeed!

Account向AMS发送消息竟然耗时长达3.3s,原因是由于foundation内部包含的ability比较多,当foundation服务启动时,会逐个启动ability,openharmony的原始设定是只开了4个线程去启动ability所以只有等上个ability启动完成后才能启动下个ability,这样会延迟开机时间,这里可以将线程池的线程扩为2倍这样就可以加速ability的启动。

在abilitymanagerserice启动launcher时发现会一直等待bootevent.bootanimation.started属性被置为ture后才会启动launcher,这里也存在一定的耗时,后面我们在代码分析中继续深入了解。

二、代码分析

2.1 fcsk的异步执行

​ 首先我们先看一下fcsk如何修改成异步执行,首先我们先看一下这个命令是在哪里执行的,通过前后的相关log我们可以确定他是在挂载data分区时执行的,代码时序图如下:

init进程在启动时在pre_init阶段会去挂载系统的分区表,这里在挂载data分区时会调用到DoFsckF2fs去执行文件检查如下:

/base/startup/init/interfaces/innerkits/fs_manager/fstab_mount.c
static int DoFsckF2fs(const char* device)
{
    char *file = "/system/bin/fsck.f2fs";
    if (access(file, F_OK) != 0) {
        BEGET_LOGE("fsck.f2fs is not exists.");
        return -1;
    }

    char *cmd[] = {
        file, "-a", (char *)device, NULL
    };
    int argc = ARRAY_LENGTH(cmd);
    char **argv = (char **)cmd;
    return ExecCommand(argc, argv);
}
static int ExecCommand(int argc, char **argv)
{
    if (argc == 0 || argv == NULL || argv[0] == NULL) {
        return -1;
    }
    BEGET_LOGI("Execute %s begin", argv[0]);
    pid_t pid = fork();
    if (pid < 0) {
        BEGET_LOGE("Fork new process to format failed: %d", errno);
        return -1;
    }
    if (pid == 0) {
        execv(argv[0], argv);
        exit(-1);
    }
    int status;
    //这里的waitpid 参数设置为0就会挂起调用进程(这里是init进程)知道子进程终止
    waitpid(pid, &status, 0);
    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
        BEGET_LOGE("Command %s failed with status %d", argv[0], WEXITSTATUS(status));
    }
    BEGET_LOGI("Execute %s end", argv[0]);
    return WEXITSTATUS(status);
}

想要DoFsckF2fs异步执行,加入下面的修改即可

+//add by yuw@guideir.com for exec async commond start
+static int AsyncExecCommand(int argc, char **argv)
+{
+    if (argc == 0 || argv == NULL || argv[0] == NULL) {
+        return -1;
+    }
+    BEGET_LOGI("AsyncExecute %s begin", argv[0]);
+    pid_t pid = fork();
+    if (pid < 0) {
+        BEGET_LOGE("Fork new process to format failed: %d", errno);
+        return -1;
+    }
+    if (pid == 0) {
+        execv(argv[0], argv);
+        exit(-1);
+    }
+    int status;
+    waitpid(pid, &status, WNOHANG);
+    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+        BEGET_LOGE("Command %s failed with status %d", argv[0], WEXITSTATUS(status));
+    }
+    BEGET_LOGI("AsyncExecute %s end", argv[0]);
+    return WEXITSTATUS(status);
+}
+//add by yuw@guideir.com for exec async commond end
 
 int DoFormat(const char *devPath, const char *fsType)
 {
@@ -192,7 +217,9 @@ static int DoResizeF2fs(const char* device, const unsigned long long size)
         };
         int argc = ARRAY_LENGTH(cmd);
         char **argv = (char **)cmd;
-        ret = ExecCommand(argc, argv);
+        //modify by yuw@guideir.com for optimize boot startup time start
+        ret = AsyncExecCommand(argc, argv);
+        //modify by yuw@guideir.com for optimize boot startup time end
     } else {
         unsigned long long realSize = size *
             ((unsigned long long)RESIZE_BUFFER_SIZE * RESIZE_BUFFER_SIZE / FS_MANAGER_BUFFER_SIZE);
@@ -206,7 +233,9 @@ static int DoResizeF2fs(const char* device, const unsigned long long size)
         };
         int argc = ARRAY_LENGTH(cmd);
         char **argv = (char **)cmd;
-        ret = ExecCommand(argc, argv);
+        //modify by yuw@guideir.com for optimize boot startup time start
+        ret = AsyncExecCommand(argc, argv);
+        //modify by yuw@guideir.com for optimize boot startup time end
     }
     return ret;
 }
@@ -224,7 +253,9 @@ static int DoFsckF2fs(const char* device)
     };
     int argc = ARRAY_LENGTH(cmd);
     char **argv = (char **)cmd;
-    return ExecCommand(argc, argv);
+    //modify by yuw@guideir.com for optimize boot startup time start
+    return AsyncExecCommand(argc, argv);
+    //modify by yuw@guideir.com for optimize boot startup time end
 }

其实很简单就是将ExecCommand拷贝一份修改成AsyncExecCommand后再将其中的waitpid的参数设置为WNOHANG,这样子进程没有返回的时候父进程也可以继续执行其他工作了,关于waitpid可以参考

2.2 sdc命令的耗时缩短

sdc命令是openharmony执行init_global_key命令时调用过来的,流程如下:

梳理流程后发现是init_global_key会调用到openssl的RAND_bytes去生成随机数,经过调查发现linux在开机时由于系统开机时间太短可能会导致系统的随机熵不够当程序从dev/random获取随机数时,系统会阻塞直到随机熵增长到一定程度才会返回。这里也是这个原因导致系统阻塞。并且由于这个流程是为了生成分区解密的随机key,无法s使用类似于上面的异步方式去执行命令(可能会导致分区无法解密导致某些不可预置的异常),所以只能寻找其他的办法去解决。经过一番查找资料后发现这类随机熵不够的问题一般都是使用工具比如haveged或者rng-tools在开机时迅速增大随机熵来解决的。由于我的设备是aarch64版本的,rng-tools无法编译aarch64版本的,所以这里我选择了haveged将他预置到系统中后可以正常运行。预置完后再pre_init阶段执行“haveged -F”这条命令即可。预置完后烧录重启可以看到sdc命令从上面的3s左右降低到了0.5s左右,也是很大的优化。haveged的编译可以参考下面的链接:

获取到haveged的可执行文件后,我们就可以考虑预置到系统中了

首先在系统源代码的base/startup/init/目录下新建haveged文件夹并在其创建一个BUILD.gn,内容如下:

import("//build/ohos.gni")
HAVEGED_DIR = "//base/startup/init/haveged"
print("prebuilt haveged")
ohos_prebuilt_executable("haveged") {
  source = "$HAVEGED_DIR/bin/haveged"
  install_enable = true
  install_images = [ "system" ]
  part_name = "init"
  subsystem_name = "startup"
}

然后将上面我们编译的可执行文件放到base/startup/init/haveged的bin目录下

最后还要在base/startup/init/bundle.json中添加我们的服务:

                "service_group": [
                    "//base/startup/init/watchdog:watchdog",
++                    "//base/startup/init/haveged:haveged",
                    "//base/startup/init/services/etc:watchdog.cfg",
                    "//base/startup/init/ueventd:startup_ueventd",
                    "//base/startup/init/services/etc:ueventd.cfg"

这样haveged命令就预置到系统中了,剩下的就是要在哪里执行了,我这里是把他放到了pre_init阶段,修改如下:

+++ a/base/startup/init/services/etc/init.cfg

    "jobs" : [{
            "name" : "pre-init",
            "cmds" : [
++                "exec /system/bin/haveged -F",
                "write /proc/sys/kernel/sysrq 0",
                "start ueventd",
                "start watchdog_service",
                "mkdir /data",


最后要想让这条命令执行成功还需要关闭selinux的权限检查,openharmony的selinux关闭修改如下:

/base/security/selinux/selinux.gni
-- selinux_enforce = true
++ selinux_enforce = false

2.3 AbilityManagerService的启动

AbilityManagerService是作为foundation服务中的一个ability启动并且由于foundation的ability较多,AbilityManagerService的启动较慢。要解决启动慢的问题可以通过增加ability启动的线程池的大小,如下:

diff --git a/safwk/services/safwk/src/local_ability_manager.cpp b/safwk/services/safwk/src/local_ability_manager.cpp
index 11f7a06..3f6fdf0 100644
--- a/safwk/services/safwk/src/local_ability_manager.cpp
+++ b/safwk/services/safwk/src/local_ability_manager.cpp
@@ -680,7 +680,7 @@ bool LocalAbilityManager::Run(int32_t saId)
     HILOGD(TAG, "success to add process name:%{public}s", Str16ToStr8(procName_).c_str());
     uint32_t concurrentThreads = std::thread::hardware_concurrency();
     HILOGI(TAG, "concurrentThreads is %{public}d", concurrentThreads);
-    initPool_->Start(concurrentThreads);
+    initPool_->Start(2*concurrentThreads);
     initPool_->SetMaxTaskNum(MAX_TASK_NUMBER);
 
     FindAndStartPhaseTasks(

2.4 launcher启动的耗时缩短

launcher的启动流程如下:

简单说一下上面的流程就是account manager启动后会创建一个account启动这个account时会向abilityManagerService发送一条startUser的消息,abilityManagerService在接收到消息后会启动launcher,最终会调用到abiltiy_manager_service的StartHighestPriorityAbility去启动launcher。

/foundation/ability/ability_runtime/services/abilitymgr/src/ability_manager_service.cpp
void AbilityManagerService::StartHighestPriorityAbility(int32_t userId, bool isBoot)
{

....
#ifdef SUPPORT_GRAPHICS
    abilityWant.SetParam(NEED_STARTINGWINDOW, false);
    // wait BOOT_ANIMATION_STARTED to start LAUNCHER
    //这边会等待bootevent.bootanimation.started属性被置为true后才能继续往下走
    WaitParameter(BOOTEVENT_BOOT_ANIMATION_STARTED.c_str(), "true", amsConfigResolver_->GetBootAnimationTimeoutTime());
#endif

    /* note: OOBE APP need disable itself, otherwise, it will be started when restart system everytime */
    (void)StartAbility(abilityWant, userId, DEFAULT_INVAL_VALUE);
}

接下来看一下bootevent.bootanimation.started是在哪里设置为true的,搜索代码后发现是在下面的函数中置为ture的,

/foundation/graphic/graphic_2d/frameworks/bootanimation/src/boot_animation.cpp
void BootAnimation::CheckExitAnimation()
{
    LOGI("CheckExitAnimation enter");
    if (!setBootEvent_) {
        LOGI("CheckExitAnimation set bootevent parameter");
        system::SetParameter("bootevent.bootanimation.started", "true");
        setBootEvent_ = true;
    }
    std::string windowInit = system::GetParameter("bootevent.boot.completed", "false");
    if (windowInit == "true") {
        PostTask(std::bind(&AppExecFwk::EventRunner::Stop, runner_));
        LOGI("CheckExitAnimation read windowInit is true");
        return;
    }
}

接着看一下CheckExitAnimation在哪里调用的

/foundation/graphic/graphic_2d/frameworks/bootanimation/src/boot_animation.cpp
void BootAnimation::Draw()
{
    if (picCurNo_ < (imgVecSize_ - 1)) {
        picCurNo_ = picCurNo_ + 1;
    } else {
        CheckExitAnimation();
        return;
    }
    ROSEN_TRACE_BEGIN(HITRACE_TAG_GRAPHIC_AGP, "BootAnimation::Draw RequestFrame");
    auto frame = rsSurface_->RequestFrame(windowWidth_, windowHeight_);
    if (frame == nullptr) {
        LOGE("Draw frame is nullptr");
        return;
    }
    ROSEN_TRACE_END(HITRACE_TAG_GRAPHIC_AGP);
    framePtr_ = std::move(frame);
    auto canvas = framePtr_->GetCanvas();
    OnDraw(canvas, picCurNo_);
    ROSEN_TRACE_BEGIN(HITRACE_TAG_GRAPHIC_AGP, "BootAnimation::Draw FlushFrame");
    rsSurface_->FlushFrame(framePtr_);
    ROSEN_TRACE_END(HITRACE_TAG_GRAPHIC_AGP);
}

从上面的代码可以看出来是当开机动画播放完成后才能被置为true。这里的意思就是只有当开机动画全部播放完成后才能启动launcher,rk3568的开机动画有150张,帧率被设置为了30hZ这样开机动画就需要5s才能播放完毕,这样显然是不合理的,开机画应该是用来给用户一个提示,用来进行系统的初始化的工作的。所以我这边改成每播放一帧开机动画都去检查一下是否能够退出动画。这样也可以加速launcher的启动,修改如下:

diff --git a/graphic_2d/frameworks/bootanimation/src/boot_animation.cpp b/graphic_2d/frameworks/bootanimation/src/boot_animation.cpp
index 158469f..33e8bb3 100644
--- a/graphic_2d/frameworks/bootanimation/src/boot_animation.cpp
+++ b/graphic_2d/frameworks/bootanimation/src/boot_animation.cpp
@@ -52,9 +52,6 @@ void BootAnimation::Draw()
 {
     if (picCurNo_ < (imgVecSize_ - 1)) {
         picCurNo_ = picCurNo_ + 1;
-    } else {
-        CheckExitAnimation();
-        return;
     }
     ROSEN_TRACE_BEGIN(HITRACE_TAG_GRAPHIC_AGP, "BootAnimation::Draw RequestFrame");
     auto frame = rsSurface_->RequestFrame(windowWidth_, windowHeight_);
@@ -69,6 +66,9 @@ void BootAnimation::Draw()
     ROSEN_TRACE_BEGIN(HITRACE_TAG_GRAPHIC_AGP, "BootAnimation::Draw FlushFrame");
     rsSurface_->FlushFrame(framePtr_);
     ROSEN_TRACE_END(HITRACE_TAG_GRAPHIC_AGP);
+    CheckExitAnimation();
 }

同时也需要修改开机动画让开机动画不要结束的太过突兀。至此rk3568的设备就从原来的21s优化到了现在的16.5s左右,当然系统内部还有其他可以优化的部分。这里仍然需要持续优化。

三、错误修复

在打入上述修改后发现系统的data分区发生了改变,查找原因发现是上面的fcsk的异步执行导致的,resize.f2fs在系统第一次启动时会扩充data分区,当data分区扩充后后面再重启时resize.f2fs的命令就会报错。所以解决方案如下:

diff --git a/startup/init/interfaces/innerkits/fs_manager/fstab_mount.c b/startup/init/interfaces/innerkits/fs_manager/fstab_mount.c
index da4bf43c..fce50d64 100755
--- a/startup/init/interfaces/innerkits/fs_manager/fstab_mount.c
+++ b/startup/init/interfaces/innerkits/fs_manager/fstab_mount.c
@@ -43,6 +43,11 @@ extern "C" {
const off_t MISC_PARTITION_ACTIVE_SLOT_OFFSET = 4096;
const off_t MISC_PARTITION_ACTIVE_SLOT_SIZE = 4;

+//add by yuw@guideir.com for fix data partition size error start
+const char *DATA_RESIZE_KEY = "persist.sys.resize_data";
+int data_resize = 0;
+//add by yuw@guideir.com for fix data partition size error end
+
#ifdef SUPPORT_HVB
__attribute__((weak)) int UeventdSocketInit(void)
{
@@ -210,6 +215,11 @@ static int DoResizeF2fs(const char* device, const unsigned long long size)
        return -1;
    }

+    char values[2] = {0};
+    uint32_t len = sizeof(values);
+    int paramGetRet = SystemGetParameter(DATA_RESIZE_KEY,values,&len);
+    int isResize = atoi(values);
+
    int ret = 0;
    if (size == 0) {
        char *cmd[] = {
@@ -218,7 +228,16 @@ static int DoResizeF2fs(const char* device, const unsigned long long size)
        int argc = ARRAY_LENGTH(cmd);
        char **argv = (char **)cmd;
        //modify by yuw@guideir.com for optimize boot startup time start
-        ret = AsyncExecCommand(argc, argv);
+        if(paramGetRet != 0 || isResize == 0){
+            ret = ExecCommand(argc, argv);
+            if(ret != 0 ){
+                data_resize = 0;
+            } else {
+                data_resize = 1;
+            }
+        } else {
+            ret = AsyncExecCommand(argc, argv);
+        }
        //modify by yuw@guideir.com for optimize boot startup time end
    } else {
        unsigned long long realSize = size *
@@ -234,7 +253,16 @@ static int DoResizeF2fs(const char* device, const unsigned long long size)
        int argc = ARRAY_LENGTH(cmd);
        char **argv = (char **)cmd;
        //modify by yuw@guideir.com for optimize boot startup time start
-        ret = AsyncExecCommand(argc, argv);
+         if(paramGetRet != 0 || isResize == 0){
+            ret = ExecCommand(argc, argv);
+            if(ret != 0 ){
+                data_resize = 0;
+            } else {
+                data_resize = 1;
+            }
+        } else {
+            ret = AsyncExecCommand(argc, argv);
+        }
        //modify by yuw@guideir.com for optimize boot startup time end
    }
    return ret;
diff --git a/startup/init/interfaces/innerkits/include/fs_manager/fs_manager.h b/startup/init/interfaces/innerkits/include/fs_manager/fs_manager.h
index 726ad3ed..73186707 100644
--- a/startup/init/interfaces/innerkits/include/fs_manager/fs_manager.h
+++ b/startup/init/interfaces/innerkits/include/fs_manager/fs_manager.h
@@ -42,6 +42,8 @@ extern "C" {
#define FM_MANAGER_REQUIRED_ENABLED(fsMgrFlags) FS_MANAGER_FLAGS_ENABLED((fsMgrFlags), REQUIRED)
#define FM_MANAGER_NOFAIL_ENABLED(fsMgrFlags) FS_MANAGER_FLAGS_ENABLED((fsMgrFlags), NOFAIL)

+extern int data_resize;
+extern const char *DATA_RESIZE_KEY;
typedef enum MountStatus {
    MOUNT_ERROR = -1,
    MOUNT_UMOUNTED = 0,
diff --git a/startup/init/services/init/standard/init_cmds.c b/startup/init/services/init/standard/init_cmds.c
index 5d61fbda..c56033dc 100755
--- a/startup/init/services/init/standard/init_cmds.c
+++ b/startup/init/services/init/standard/init_cmds.c
@@ -290,6 +290,9 @@ static void DoMountFstabFile(const struct CmdArgs *ctx)
{
    INIT_LOGI("Mount partitions from fstab file \" %s \"", ctx->argv[0]);
    int ret = MountAllWithFstabFile(ctx->argv[0], 0);
+    if(data_resize == 1){^M
+        SystemWriteParam(DATA_RESIZE_KEY, "1");^M
+    }^M
    INIT_LOGI("Mount partitions from fstab file \" %s \" finish ret %d", ctx->argv[0], ret);
}

原理时使用一个属性来表示第一次启动,如果是第一次启动那么resize.f2fs将会同步执行,否则使用异步执行。

为了能让大家更好的学习鸿蒙 (Harmony OS) 开发技术,这边特意整理了《鸿蒙 (Harmony OS)开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙 (Harmony OS)开发学习手册》

入门必看:https://qr21.cn/FV7h05

  1. 应用开发导读(ArkTS)
  2. 应用开发导读(Java)

HarmonyOS 概念:https://qr21.cn/FV7h05

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全

如何快速入门:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. 构建第一个JS应用
  4. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/259322.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

三相异步电机动态数学模型推导及矢量控制仿真

文章目录 **原文链接&#xff0c;点击跳转**三相异步电机动态数学模型及矢量控制仿真1、异步电机三相方程2、坐标变换3、磁链3/2变换推导4、两相静止坐标系下的方程5、两相旋转坐标系下的方程6、以 ω-is-Ψr 为状态变量的状态方程7、矢量控制及 matlab 仿真 原文链接&#xff…

吴恩达深度学习intuition

这里是看吴恩达课程的一些记录和联想&#xff08;因为以前听过&#xff0c;因此不会很细致&#xff0c;只做个人记录&#xff09; 课程链接 首先提到training set, validation set (dev set)&#xff0c;test set的分割问题。老师提到&#xff0c;最常用的划分方法传统方法是…

Python 正则表达式入门:轻松掌握字符串匹配的艺术

Python 正则表达式入门&#xff1a;轻松掌握字符串匹配的艺术 引言&#xff1a;什么是正则表达式&#xff1f;基础知识&#xff1a;正则表达式的语法和规则Python中的正则表达式&#xff1a;re模块的使用实战应用&#xff1a;常见的正则表达式案例最佳实践与常见错误结语&#…

听GPT 讲Rust源代码--src/tools(16)

File: rust/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs rust-analyzer是一个基于Rust语言的IntelliSense引擎&#xff0c;用于提供IDE自动补全、代码导航和其他代码编辑功能。在rust-analyzer的源代码中&#xff0c;rust/src/tools/rust-analyzer…

hive企业级调优策略之分组聚合优化

测试用表准备 hive企业级调优策略测试数据 (阿里网盘下载链接)&#xff1a;https://www.alipan.com/s/xsqK6971Mrs 订单表(2000w条数据) 表结构 建表语句 drop table if exists order_detail; create table order_detail(id string comment 订单id,user_id …

Axure中如何使用交互样式交互事件交互动作情形

&#x1f3ac; 艳艳耶✌️&#xff1a;个人主页 &#x1f525; 个人专栏 &#xff1a;《产品经理如何画泳道图&流程图》 ⛺️ 越努力 &#xff0c;越幸运 目录 一、Axure中交互样式 1、什么是交互样式&#xff1f; 2、交互样式的作用&#xff1f; 3、Axure中如何…

自动化测试工具选择指南

随着软件开发周期的不断缩短和需求的增加&#xff0c;自动化测试变得愈发重要。然而&#xff0c;选择适合项目的自动化测试工具并非易事。以下是一些指导原则&#xff0c;帮助你在众多自动化测试工具中做出明智的选择。 1. 项目需求分析 在选择自动化测试工具之前&#xff0c;首…

JRT打印元素绘制协议整合PDF

打印不光要能打印内部的单据&#xff0c;对于检验的打印还有外送回传的PDF报告也需要能够打印&#xff0c;所以需要把打印PDF文件整合进来&#xff0c;为此给打印元素绘制协议增加PDF类型的元素。 定义如下&#xff0c;由绘制协议按地址下载文件后和其他打印元素整合&#xff…

Java中Integer和int的区别

文章目录 一、介绍二、不同点三、相同点四、使用equals()和的区别五、解惑 一、介绍 各位小伙伴们无论在工作还是学习中&#xff0c;与Integer都有着过硬的交情&#xff0c;我说的没错吧&#xff0c;大家都知道他可以表示一个整数&#xff0c;而且也知道可以表示整数的还有int…

网工内推 | 上市公司中级网工,思科、华为认证优先,有带薪年假

01 新晨科技 招聘岗位&#xff1a;中级网络工程师 职责描述&#xff1a; 1. 负责公司网络系统的规划、设计、实施、维护和优化&#xff1b; 2. 负责网络设备的选型、采购、安装、配置和调试&#xff1b; 3. 负责网络安全策略的制定和实施&#xff0c;保障公司网络安全&#xf…

C# 将 Word 转化分享为电子期刊

目录 需求 方案分析 相关库引入 关键代码 Word 转 Pdf Pdf 转批量 Jpeg Jpeg 转为电子书 实现效果演示 小结 需求 曾经的一个项目&#xff0c;要求实现制作电子期刊定期发送给企业进行阅读&#xff0c;基本的需求如下&#xff1a; 1、由编辑人员使用 Microsoft Word…

Lucene

目录 1. Lucene概述 1.1 什么是Lucene 1.2 Lucene的原理 2. Lucene的使用 2.1 准备 2.2 生成索引 2.3 全文检索 2.4 多Field检索 2.5 中文分词器 2.6 停用词 2.7 是否索引,是否储存 1. Lucene概述 1.1 什么是Lucene Lucene是一个全文搜索框架&#xff0c;而不是应用…

标准库中的string类(上)——“C++”

各位CSDN的uu们好呀&#xff0c;好久没有更新小雅兰的C专栏的知识啦&#xff0c;接下来一段时间&#xff0c;小雅兰就又会开始更新C这方面的知识点啦&#xff0c;以及期末复习的一些知识点&#xff0c;下面&#xff0c;让我们进入西嘎嘎string的世界吧&#xff01;&#xff01;…

智能优化算法应用:基于混沌博弈算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于混沌博弈算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于混沌博弈算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.混沌博弈算法4.实验参数设定5.算法结果6.…

HamronyOS 自动化测试框架使用指南

概述 为支撑 HarmonyOS 操作系统的自动化测试活动开展&#xff0c;我们提供了支持 JS/TS 语言的单元及 UI 测试框架&#xff0c;支持开发者针对应用接口进行单元测试&#xff0c;并且可基于 UI 操作进行 UI 自动化脚本的编写。 本指南重点介绍自动化测试框架的主要功能&#x…

跟着我学Python进阶篇:01.试用Python完成一些简单问题

往期文章 跟着我学Python基础篇&#xff1a;01.初露端倪 跟着我学Python基础篇&#xff1a;02.数字与字符串编程 跟着我学Python基础篇&#xff1a;03.选择结构 跟着我学Python基础篇&#xff1a;04.循环 跟着我学Python基础篇&#xff1a;05.函数 跟着我学Python基础篇&#…

c语言力扣题目:消失的数字(有关时间复杂度O(N²)O(N))以及对异或操作符的更深入的理解(如何用人脑的十进制去考量二进制)

目录 Way One :暴力求解,时间复杂度为 O(N) 代码1 Way Two : 时间复杂度限制到 O(N) 代码及其详解 如题 Way One :暴力求解,时间复杂度为 O(N) 大体思路:比如这里我们需要处理的整型数组是"3,0,1",我们可以用冒泡排序或者 qsort函数将他从大到小进行排序成"…

纳米流体传热CFD模拟仿真

纳米流体传热CFD模拟仿真 一、引言 纳米流体传热是当前研究热点之一,由于其独特的传热特性和应用前景,受到了广泛关注。计算流体动力学(CFD)模拟作为一种有效的研究手段,在纳米流体传热领域发挥着重要作用。本文将介绍纳米流体传热CFD模拟的基本原理、方法、应用及未来发…

100GPTS计划-AI编码CodeWizard

地址 https://chat.openai.com/g/g-vX7yfHNcC-code-wizard https://poe.com/CodeWizardGPT 测试 sql 优化 select a.id,a.name,count(b.id),count(c.id) from product a LEFT JOIN secretkey b on a.id b.productId group by a.id LEFT JOIN secretkey c on a.id c.pr…

SLAM算法与工程实践——RTKLIB编译

SLAM算法与工程实践系列文章 下面是SLAM算法与工程实践系列文章的总链接&#xff0c;本人发表这个系列的文章链接均收录于此 SLAM算法与工程实践系列文章链接 下面是专栏地址&#xff1a; SLAM算法与工程实践系列专栏 文章目录 SLAM算法与工程实践系列文章SLAM算法与工程实践…