OpenHarmony RK3568 启动流程优化

news2024/10/6 20:36:48

目前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 255
    行 1525: [    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 = 255
    行 1527: [    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 1
    行 1532: [    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 = 1
    行 1538: [    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 1
    行 1993: [    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将会同步执行,否则使用异步执行。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习:多项式回归(Python)

多元线性回归闭式解&#xff1a; closed_form_sol.py import numpy as np import matplotlib.pyplot as pltclass LRClosedFormSol:def __init__(self, fit_interceptTrue, normalizeTrue):""":param fit_intercept: 是否训练bias:param normalize: 是否标准化…

重写Sylar基于协程的服务器(1、日志模块的架构)

重写Sylar基于协程的服务器&#xff08;1、日志模块的架构&#xff09; 重写Sylar基于协程的服务器系列&#xff1a; 重写Sylar基于协程的服务器&#xff08;0、搭建开发环境以及项目框架 || 下载编译简化版Sylar&#xff09; 重写Sylar基于协程的服务器&#xff08;1、日志模…

2.室内设计学习 - CAD 2021 调整经典界面教程及基本设置

设置经典界面 1.在第二行的空白处右击&#xff0c;弹出对话框&#xff0c;并点击【关闭】&#xff0c;关闭掉。 2.菜单栏没有显示的情况下&#xff0c;在最上面的一排&#xff0c;点击向下的箭头展开下拉框&#xff0c;勾选 【显示菜单栏】 3.点击菜单【工具】-【工具栏】-【a…

AES 加解密python实现

1. 要求 编程实现AES-128的加解密算法&#xff0c;满足给定明文和密钥加密得到密文&#xff0c;给定密文和密钥解密得到明文&#xff0c;最终用界面化的形式呈现。 2. 算法流程 程序主要分为加密与解密两个大模块。在加密模块中包括四个小模块&#xff0c;分别为轮密钥加、字…

C语言KR圣经笔记 6.4结构体指针 6.5自引用结构体

6.4 结构体指针 为了说明结构体指针和数组的某些注意事项&#xff0c;我们把上一节的关键字计数程序再写一次&#xff0c;不过这回使用指针而不是数组下标。 keytab 的外部声明不需要动&#xff0c;但 main 和 binsearch 确实需要修改。 #include <stdio.h> #include …

3、css设置样式总结、节点、节点之间关系、创建元素的方式、BOM

一、css设置样式的方式总结&#xff1a; 对象.style.css属性 对象.className ‘’ 会覆盖原来的类 对象.setAttribut(‘style’,‘css样式’) 对象.setAttribute(‘class’,‘类名’) 对象.style.setProperty(css属性名,css属性值) 对象.style.cssText “css样式表” …

开发工具之GIT协同开发流程和微服务部署实践与总结

GIT协同开发流程和微服务部署的实践&#xff0c;并总结经验和教训。通过合理的GIT协同开发流程和良好的微服务部署策略&#xff0c;团队可以更高效地开发和部署软件。 ## 引言 在当今快节奏的软件开发环境中&#xff0c;采用合适的工具和流程对于实现高效协同开发和可靠部署至…

1.25时间序列分析,FB先知模型、简要傅里叶变化解决周期性变化,实例步骤

目录 FB概念 ​编辑 GEOGEBRA可视化傅里叶​编辑 先知模型步骤 财务数据要考虑到可解释性 FB模型概念 可以用傅里叶级数来描述周期性变化的因素 GEOGEBRA可视化傅里叶 先知模型步骤

vue+ElementPlus实现中国省市区三级级联动封装

安装插件获取中国省份的所有数据 npm install element-china-area-data -S 借助ElementPlus 级联选择器 Cascader实现 <template><div><el-cascadersize"large":options"options"v-model"selectedOptions"change"handleCh…

C# 一个快速读取写入操作execl的方法封装

这里封装了3个实用类ExcelDataReaderExtensions&#xff0c;ExcelDataSetConfiguration&#xff0c;ExcelDataTableConfiguration和一个实用代码参考&#xff1a; using ExcelDataReader; using System; using System.Collections.Generic; using System.Linq; using System.T…

2024.1.29 关于 Redis 缓存详解

目录 缓存基本概念 二八定律 Redis 作为缓存 缓存更新策略 定期生成 实时生成 内存淘汰策略 缓存使用的注意事项 关于缓存预热 关于缓存穿透 关于缓存雪崩 关于缓存击穿&#xff08;瘫痪&#xff09; 缓存基本概念 所谓缓存&#xff0c;其实就是将一部分常用数据放…

向日葵企业“云策略”升级 支持Android 被控策略设置

此前&#xff0c;贝锐向日葵推出了适配PC企业客户端的云策略功能&#xff0c;这一功能支持管理平台统一修改设备设置&#xff0c;上万设备实时下发实时生效&#xff0c;很好的解决了当远程控制方案部署后&#xff0c;想要灵活调整配置需要逐台手工操作的痛点&#xff0c;大幅提…

计算机网络-数据交换方式(电路交换 报文交换 分组交换及其两种方式 )

文章目录 为什么要数据交换&#xff1f;总览电路交换电路交换的各个阶段建立连接数据传输释放连接 电路交换的特点电路交换的优缺点 报文交换报文交换流程报文交换的优缺点 分组交换分组交换流程分组交换的优缺点 数据交换方式的选择分组交换的两种方式数据报方式数据报方式的特…

正则表达式(RE)

什么是正则表达式 正则表达式&#xff0c;又称规则表达式&#xff08;Regular Expression&#xff09;。正则表达式通常被用来检索、替换那些符合某个规则的文本 正则表达式的作用 验证数据的有效性替换文本内容从字符串中提取子字符串 匹配单个字符 字符功能.匹配任意1个…

(一)Spring 核心之控制反转(IoC)—— 配置及使用

目录 一. 前言 二. IoC 基础 2.1. IoC 是什么 2.2. IoC 能做什么 2.3. IoC 和 DI 是什么关系 三. IoC 配置的三种方式 3.1. XML 配置 3.2. Java 配置 3.3. 注解配置 四. 依赖注入的三种方式 4.1. 属性注入&#xff08;setter 注入&#xff09; 4.2. 构造方法注入&a…

ES Serverless让日志检索更加便捷

前言 在项目中,或者开发过程中,出现bug或者其他线上问题,开发人员可以通过查看日志记录来定位问题。通过日志定位 bug 是一种常见的软件开发和运维技巧,只有观察日志才能追踪到具体代码。在软件开发过程中,开发人员会在代码中添加日志记录,以记录程序的运行情况和异常信…

【蓝桥杯日记】复盘篇二:分支结构

前言 本篇笔记主要进行复盘的内容是分支结构&#xff0c;通过学习分支结构从而更好巩固之前所学的内容。 目录 前言 目录 &#x1f34a;1.数的性质 分析&#xff1a; 知识点&#xff1a; &#x1f345;2.闰年判断 说明/提示 分析&#xff1a; 知识点&#xff1a; &am…

【Linux操作系统】:Linux开发工具编辑器vim

目录 Linux 软件包管理器 yum 什么是软件包 注意事项 查看软件包 如何安装软件 如何卸载软件 Linux 开发工具 Linux编辑器-vim使用 vim的基本概念 vim的基本操作 vim正常模式命令集 插入模式 插入模式切换为命令模式 移动光标 删除文字 复制 替换 撤销 跳至指…

C++——list的使用及其模拟实现

list 文章目录 list1. 基本使用1.1 list对象的定义1.2 增&#xff08;插入数据&#xff09;1.3 删&#xff08;删除数据&#xff09;1.4 遍历访问 2. 模拟实现2.1 节点类ListNode2.2 封装ListNode类&#xff0c;实现list基本功能2.3 实现迭代器iterator2.3.1 实现const迭代器co…

使用Hutool工具包解析、生成XML文件

说明&#xff1a;当我们在工作中需要将数据转为XML文件、或者读取解析XML文件时&#xff0c;使用Hutool工具包中的XMLUtil相关方法是最容易上手的方法&#xff0c;本文介绍如何使用Hutool工具包来解析、生成XML文件。 开始之前&#xff0c;需要导入Hutool工具包的依赖 <de…