高通BoostFramework io-prefectch方案

845 阅读4分钟

背景介绍

应用启动性能是Android设备上一个比较重要的性能指标。该方案通过预读取文件的方式,实现了提升应用启动性能的目的。该方案后面简称IO-prefectch。

方案流程图

触发一次IO-prefectch流程的调用流程如下:
vzLUeS.png iop_server工作线程处理Msg流程如下:
vzL7y6.png 预读取流程如下:
vzLLwD.png

源码分析

frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java

    void findTaskToMoveToFront(TaskRecord task, int flags, ActivityOptions options, String reason,
            boolean forceNonResizeable) {
        //top_activity = task.stack.topRunningActivityLocked();
        /* App is launching from recent apps and it's a new process */
        if(top_activity != null && top_activity.getState() == ActivityState.DESTROYED) {
            acquireAppLaunchPerfLock(top_activity);
        }
    }

    ActivityRecord findTaskLocked(ActivityRecord r, int displayId) {
        mTmpFindTaskResult.r = null;
        mTmpFindTaskResult.matchedByRootAffinity = false;
        ActivityRecord affinityMatch = null;
        if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
            final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
                final ActivityStack stack = display.getChildAt(stackNdx);
                if (!r.hasCompatibleActivityType(stack)) {
                    if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping stack: (mismatch activity/stack) "
                            + stack);
                    continue;
                }
                stack.findTaskLocked(r, mTmpFindTaskResult);
                // It is possible to have tasks in multiple stacks with the same root affinity, so
                // we should keep looking after finding an affinity match to see if there is a
                // better match in another stack. Also, task affinity isn't a good enough reason
                // to target a display which isn't the source of the intent, so skip any affinity
                // matches not on the specified display.
                if (mTmpFindTaskResult.r != null) {
                    if (!mTmpFindTaskResult.matchedByRootAffinity) {
                        if(mTmpFindTaskResult.r.getState() == ActivityState.DESTROYED ) {
                            /*It's a new app launch */
                            acquireAppLaunchPerfLock(r);
                        }
                        if(mTmpFindTaskResult.r.getState() == ActivityState.STOPPED) {
                            /*Warm launch */
                            acquireUxPerfLock(BoostFramework.UXE_EVENT_SUB_LAUNCH, r.packageName);
                        }
                        return mTmpFindTaskResult.r;
                    } else if (mTmpFindTaskResult.r.getDisplayId() == displayId) {
                        // Note: since the traversing through the stacks is top down, the floating
                        // tasks should always have lower priority than any affinity-matching tasks
                        // in the fullscreen stacks
                        affinityMatch = mTmpFindTaskResult.r;
                    } else if (DEBUG_TASKS && mTmpFindTaskResult.matchedByRootAffinity) {
                        Slog.d(TAG_TASKS, "Skipping match on different display "
                                + mTmpFindTaskResult.r.getDisplayId() + " " + displayId);
                    }
                }
            }
        }

        /* Acquire perf lock *only* during new app launch */
        if (mTmpFindTaskResult.r == null || mTmpFindTaskResult.r.getState() == ActivityState.DESTROYED) {
            acquireAppLaunchPerfLock(r);
        }

        if (DEBUG_TASKS && affinityMatch == null) Slog.d(TAG_TASKS, "No task found");
        return affinityMatch;
    }

    void acquireAppLaunchPerfLock(ActivityRecord r) {
       /* Acquire perf lock during new app launch */
       if (mPerfBoost == null) {
           mPerfBoost = new BoostFramework();
       }
       if (mPerfBoost != null) {
           mPerfBoost.perfHint(BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, r.packageName, -1, BoostFramework.Launch.BOOST_V1);
           mPerfSendTapHint = true;
           mPerfBoost.perfHint(BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, r.packageName, -1, BoostFramework.Launch.BOOST_V2);
           if(mPerfBoost.perfGetFeedback(BoostFramework.VENDOR_FEEDBACK_WORKLOAD_TYPE, r.packageName) == BoostFramework.WorkloadType.GAME)
           {
               mPerfHandle = mPerfBoost.perfHint(BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, r.packageName, -1,
                                                                                               BoostFramework.Launch.BOOST_GAME);
           } else {
               mPerfHandle = mPerfBoost.perfHint(BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, r.packageName, -1,
                                                                                                 BoostFramework.Launch.BOOST_V3);
           }
           if (mPerfHandle > 0)
               mIsPerfBoostAcquired = true;
           // Start IOP
           Slog.d(TAG, "perfIOPrefetchStart");
           mPerfBoost.perfIOPrefetchStart(-1,r.packageName,
                   r.appInfo.sourceDir.substring(0, r.appInfo.sourceDir.lastIndexOf('/')));
       }
    }

frameworks/base/core/java/android/util/BoostFramework.java

    /** @hide */
    public int perfIOPrefetchStart(int pid, String pkgName, String codePath) {
        int ret = -1;
        try {
            Object retVal = sIOPStart.invoke(mPerf, pid, pkgName, codePath);
            ret = (int) retVal;
            Log.d(TAG, "ret1 = " + ret);
        } catch (Exception e) {
            Log.e(TAG, "Exception " + e);
        }
        try {
            Object retVal = sUxIOPStart.invoke(mUxPerf, pid, pkgName, codePath);
            ret = (int) retVal;
            Log.d(TAG, "ret2 = " + ret);
        } catch (Exception e) {
            Log.e(TAG, "Ux Perf Exception " + e);
        }

        return ret;
    }

vendor/qcom/proprietary/commonsys/android-perf/UxPerfomance/src/com/qualcomm/qti/UxPerformance.java

    public int perfIOPrefetchStart(int PId, String PkgName, String CodePath)
    {
        if(EnablePrefetch)
        {
           QLogI("DexPrefetchThread Feature is Enable ");
           PreDexThread = new DexPrefetchThread(CodePath);
            if(PreDexThread != null)
                (new Thread(PreDexThread)).start();
        }
        else
        {
            QLogI("DexPrefetchThread Feature is disabled ");
            return REQUEST_FAILED;
        }
        return REQUEST_SUCCEEDED;
    }

    private class DexPrefetchThread implements Runnable {
        public String CodePath;
        public DexPrefetchThread(String CodePath) {
            this.CodePath = CodePath;
        }

        public void LoadFiles(String path,String FileName)
        {
            String[] Files = {".art", ".odex", ".vdex"};
            QLogD(" LoadFiles() path is " + path);
            for ( int i = 0; i< Files.length; i++ )
            {
                File MyFile = new File(path + FileName + Files[i]);
                if(MyFile.exists())
                {
                    try {
                        FileChannel fileChannel = new RandomAccessFile(MyFile, "r").getChannel();
                        MappedByteBuffer buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());
                        if(EnableDebug)
                            QLogD( "Before Is Buffer Loaded " + buffer.isLoaded());
                        QLogD( "File is " + path + FileName + Files[i]);
                        buffer.load();
                        if(EnableDebug)
                            QLogD( "After Is Buffer Loaded " + buffer.isLoaded());
                    } catch (FileNotFoundException e) {
                        QLogE( "DexPrefetchThread Can not find file " + FileName + Files[i] + "at " + path);
                    } catch (IOException e) {
                        QLogE( "DexPrefetchThread IO Error file " + FileName + Files[i] + "at " + path);
                    }
                }
            }
        }
        
        public void run() {
            //Fix me : This loop is added just to comply with thread run functions.
            while(true) {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "DexPrefetchThread");
                if ( CodePath.startsWith("/data") )
                {
                    //Check for 32/64 bit apps
                    QLogD( "Data pkg ");
                    if((new File(CodePath + "/oat/arm64/")).exists())
                    {
                        LoadFiles(CodePath + "/oat/arm64/", "base");
                    }
                    else if((new File(CodePath + "/oat/arm/")).exists())
                    {
                        LoadFiles(CodePath + "/oat/arm/", "base");
                    }
                }
                else
                {
                    // Get the name of package
                    QLogD( "system/vendor pkg ");
                    String[] SplitPath = CodePath.split("/");
                    String PkgName = SplitPath[SplitPath.length-1];
                    QLogD( "PKgNAme : " + PkgName);

                    //Check for 32/64 bit apps
                    if((new File(CodePath + "/oat/arm64/")).exists())
                    {
                        LoadFiles(CodePath + "/oat/arm64/", PkgName);
                    }
                    else if((new File(CodePath + "/oat/arm/")).exists())
                    {
                        LoadFiles(CodePath + "/oat/arm/", PkgName);
                    }
                }
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                return;

            }
        }
    }

vendor/qcom/proprietary/commonsys/android-perf/QPerformance/src/com/qualcomm/qti/Performance.java

    public int perfIOPrefetchStart(int PId, String Pkg_name, String Code_path)
    {
        return native_perf_io_prefetch_start(PId,Pkg_name, Code_path);
    }

vendor/qcom/proprietary/android-perf/iop-hal/Iop.cpp

Return<int32_t> Iop::iopStart(int32_t pid, const hidl_string& pkg_name, const hidl_string& code_path) {
    iop_msg_t pMsg;
    uint32_t retVal = -1;
    memset(&pMsg, 0, sizeof(iop_msg_t));

    pMsg.cmd = IOP_CMD_PERFLOCK_IOPREFETCH_START;
    pMsg.pid = pid;
    strlcpy(pMsg.pkg_name, pkg_name.c_str(), PKG_LEN);
    strlcpy(pMsg.code_path, code_path.c_str(), PKG_LEN);
    if (mHandle.dlhandle != NULL)
        retVal = (*(mHandle.iop_server_submit_request))(&pMsg);
    return retVal;
}

vendor/qcom/proprietary/android-perf/io-p/io-p.cpp

static void *iop_server(void *data)
{
    for (;;) {
            switch (cmd) {
                case IOP_CMD_PERFLOCK_IOPREFETCH_START:
            {
                static bool is_in_recent_list = false;
                char property[PROPERTY_VALUE_MAX];
                int enable_prefetcher = 0;
                int enable_prefetcher_ofr = 0;

                property_get("vendor.enable_prefetch", property, "0");
                enable_prefetcher = strtod(property, NULL);

                property_get("vendor.iop.enable_prefetch_ofr", property, "0");
                enable_prefetcher_ofr = strtod(property, NULL);

                // if PID < 0 consider it as playback operation
                if(msg->pid < 0)
                {
                    int ittr = 0;
                    char *week_day = NULL;
                    week_day = (char *) malloc(6*sizeof(char));
                    // Insert package into the table
                    if (week_day == NULL) {
                       //Malloc failed. Most-probably low on memory.
                       break;
                    }
                    strlcpy(pkg_info.pkg_name,msg->pkg_name,PKG_NAME_LEN);
                    strlcpy(tmp_pkg_name,pkg_info.pkg_name,PKG_NAME_LEN);
                    bindApp_dur = 0;
                    disAct_dur = 0;
                    launching = true;
                    time(&pkg_info.last_time_launched);
                    compute_time_day_slot(week_day, &time_slot);
                    QLOGI("UXEngine Updating Details: pkg_name: %s, week_day: %s, time_slot: %d %s\n", pkg_info.pkg_name, week_day, time_slot, tmp_pkg_name);
                    update_ux_pkg_details(pkg_info, week_day, time_slot, 0);
                    update_palm_table(msg->pkg_name, 0, 1);

                    QLOGI("UXEngine finished ux_pkg_details update \n");
                    free(week_day);
                    is_in_recent_list = false;
                    if(!enable_prefetcher)
                    {
                        QLOGE("io prefetch is disabled");
                        break;
                    }

                    //Check app is in recent list
                    for(ittr = 0; ittr < IOP_NO_RECENT_APP; ittr++)
                    {
                        if(0 == strcmp(msg->pkg_name,recent_list[ittr]))
                        {
                            is_in_recent_list = true;
                            QLOGE("is_in_recent_list is TRUE");
                            break;
                        }
                    }
                    // IF Application is in recent list, return
                    if(true == is_in_recent_list)
                    {
                        QLOGE("io prefetch is deactivate");
                        break;
                    }

                    if(recent_list_cnt == IOP_NO_RECENT_APP)
                        recent_list_cnt = 0;

                    //Copy the package name to recent list
                    strlcpy(recent_list[recent_list_cnt],msg->pkg_name,PKG_LEN);
                    recent_list_cnt++;

                    stop_capture();
                    stop_playback();
                    start_playback(msg->pkg_name);
                }
                // if PID > 0 then consider as capture operation
                if(msg->pid > 0)
                {
                    if(!enable_prefetcher)
                    {
                        QLOGE("io prefetch is disabled");
                        break;
                    }
                    if(true == is_in_recent_list)
                    {
                        QLOGE("io prefetch Capture is deactivated ");
                        break;
                    }
                    stop_capture();
                    start_capture(msg->pid,msg->pkg_name,msg->code_path,enable_prefetcher_ofr);
                }

                break;
            }
            }
    }
}

vendor/qcom/proprietary/android-perf/io-p/io-prefetch/list_capture.cpp

/*
 * start capture function, this function will start new thead to get list of the files
 */
int start_capture(pid_t pid, char *pkg_name, char *code_path, bool ofr) {
    char property[PROPERTY_VALUE_MAX];
    capture_thread_arg *arg_bundle = NULL;
    int len = 0;
    int len_code_path = 0;

    if(NULL == pkg_name)
        return -1;

    len = strlen(pkg_name);
    if ( len <= 0)
    {
        QLOGW("incorrect length for pkg_name");
        return -1;
    }

    arg_bundle = (capture_thread_arg *)malloc(sizeof(capture_thread_arg));
    if (NULL == arg_bundle)
        return -1;

    total_files = 0;
    QLOGE(" start_capture ENTER ");

    arg_bundle->pid = pid;
    arg_bundle->pkg_name = (char *) malloc(len+1);
    arg_bundle->ofr = ofr;
    len_code_path = strlen(code_path);
    if (NULL == arg_bundle->pkg_name) {
        free(arg_bundle);
        return -1;
    }

    QLOGI("%d %s %zd %d\n", pid, arg_bundle->pkg_name,strlen(pkg_name),PKG_NAME_LEN);
    strlcpy(arg_bundle->pkg_name, pkg_name, len+1);
    strlcpy(list_code_path, code_path, len_code_path+1);
    QLOGI("%d %s %zd %s %zd\n", pid, arg_bundle->pkg_name,strlen(pkg_name),
            list_code_path, strlen(code_path));

    halt_thread = 0;
    start_capture_time = (long) now_ms();

    time(&start_capture_time_s);

    property_get("vendor.iop.read_fd_interval_ms", property, "50");
    read_fd_interval_ms = atoi(property);
    if(read_fd_interval_ms <= 0) {
        QLOGI("read_fd_interval_ms is set to a non-positive value %d. Resetting to default value 50\n", read_fd_interval_ms);
        property_set("read_fd_interval_ms", "50");
        read_fd_interval_ms = 50;
    }
    QLOGI("property = %d\n", read_fd_interval_ms);
    //TBD must remove comments
    if(pthread_create(&capture_pthread_id, NULL, capture_thread, arg_bundle)) {
        return -1;
    }
   QLOGI("start_capture EXIT");
    return 0;
}

void * capture_thread(void * arg) {
    int i = 0;
    int pkg_len = 0;
    int duration_counter = 0;
    pkg_details pkg_info;
    int index = 0;
    int halt_counter = CAPTURE_MAX_DURATION/read_fd_interval_ms;
    int polling_enable = 0;
    char property[PROPERTY_VALUE_MAX];

    property_get("vendor.polling_enable", property, "0");
    polling_enable = atoi(property);
    ATRACE_BEGIN("capture_thread");
    capture_thread_arg * arg_bundle = (capture_thread_arg *)arg;

    //Copy the package to same string to remove ':'
    pkg_len = strlen(arg_bundle->pkg_name);
    i = 0;
    while(i < pkg_len && arg_bundle->pkg_name[i] != ':')
    {
        i++;
    }
    arg_bundle->pkg_name[i] = '\0';
    strlcpy(list_pkg_name,arg_bundle->pkg_name,PKG_NAME_LEN);

    QLOGI("pkg_name  = %s",arg_bundle->pkg_name);

    if(polling_enable)
    {
        while (duration_counter < halt_counter) {
            if (halt_thread) goto cleanup;
            QLOGI("Getting snapshot %d\n", arg_bundle->pid);
            get_snapshot(list_pkg_name,arg_bundle->pid);
            duration_counter++;
            usleep(read_fd_interval_ms * 1000);
        }
    }
    get_priv_code_files(arg_bundle->pkg_name);
    get_priv_files(arg_bundle);
    QLOGI("pkg_name = %s total_files = %d ",arg_bundle->pkg_name,total_files);

    // Insert package into the table
    strlcpy(pkg_info.pkg_name,arg_bundle->pkg_name,PKG_NAME_LEN);
    time(&pkg_info.last_time_launched);
    // Update Mincore data
    if(arg_bundle->ofr)
    {
        for(index = 0; index < total_files;index++)
        {
           if(update_mincore_data(file_list[index]) != 0)
           {
               file_list[index]->mincore_array = NULL;
           }
        }
    }
    update_pkg_details(pkg_info);
    update_file_details(arg_bundle->pkg_name, file_list, total_files);
    delete_mark_files();
cleanup:
    //log a report about how many files need insert or update this time
    QLOGE("# Final entry : pkg_name file_name file_time_stamp filesize file_iop_size");
    for(i = 0; i < total_files; i++) {
        QLOGE("%d. Final entry : %s %s %d %d %d\n",i
                ,arg_bundle->pkg_name,file_list[i]->file_name
                , file_list[i]->file_time_stamp, file_list[i]->filesize, file_list[i]->file_iop_size);
        if (file_list[i]->mincore_array) {
            free(file_list[i]->mincore_array);
            file_list[i]->mincore_array = NULL;
        }
        free(file_list[i]);
    }
    ATRACE_END();

    free(arg_bundle->pkg_name);
    free(arg_bundle);
    QLOGI("Exit capture_thread");
    return NULL;
}

int get_snapshot(char *pkg_name, pid_t pid)
{
    struct stat sb_link, sb;
    char dirname[PATH_MAX + 1];
    char pathname[PATH_MAX + 1];
    char linkname[PATH_MAX + 1];
    ssize_t r;
    int need_to_filter = 0;

    DIR *dp;
    struct dirent *ep;
    long file_capture_time;
    char trace_buf[1024];
    snprintf(trace_buf, sizeof(trace_buf), "GetSnapSthot");
    ATRACE_BEGIN(trace_buf);


    snprintf(dirname,PATH_MAX,"/proc/%d/fd", pid);
    QLOGI("Dirname = %s MAX_PATH = %d\n", dirname, PATH_MAX);
    dp = opendir(dirname);
    QLOGI("get_snapshot Enter dp = %p",dp);
    QLOGI("dp = %p",dp);
    if (dp != NULL) {
        int i = 0;
        while ((ep = readdir(dp)))
        {
            snprintf(pathname, PATH_MAX , "%s/%s", dirname, ep->d_name);
            int ret = lstat(pathname, &sb_link);
            if (ret == -1) {
                continue;
            }
            file_capture_time = (long) now_ms() - start_capture_time;
            r = readlink(pathname, linkname, PATH_MAX + 1);
            if (r < 0) {
                continue;
            }
            linkname[r] = '\0';
            need_to_filter = file_need_to_filter(linkname);

            if(need_to_filter)
            {
                need_to_filter = 0;
                QLOGI("file is in filter list %s",linkname);
                continue; // goto  next file
            }

            if (stat(linkname, &sb) == -1) {
                continue;
            }

            if(memcmp("/storage/emulated/0/",linkname,20) == 0) {
                for(i = r; i >= 19; i--) {
                    linkname[i+5] = linkname[i];
                }
                memcpy((void *)&linkname[18],(void *)"legacy", 6);
            }
            if (stat(linkname, &sb) == -1) {
                continue;
            }

            if (S_ISREG(sb.st_mode)) {
                find_insert_into_list(pkg_name, linkname, sb.st_size, file_capture_time);
            }
        }
        QLOGI("get_snapshot Exit dp = %p",dp);
        closedir(dp);
    }
    ATRACE_END();
    return 0;
}

vendor/qcom/proprietary/android-perf/io-p/io-prefetch/file_playback.cpp

void start_playback(char *pkg_name_arg) {
    char *pkg_name = NULL;
    int len = 0;
    QLOGE(" start_playback-1 ");

    if(NULL == pkg_name_arg)
        return;

    len = strlen(pkg_name_arg);
    if(len <= 0)
    {
        QLOGW("Incorrect length for pkg_name_arg");
        return;
    }

    pkg_name = (char *) malloc(len + 1);
    if (NULL == pkg_name)
        return;

    strlcpy(pkg_name, pkg_name_arg,len+1);

    QLOGI(" %s %zd\n", pkg_name,strlen(pkg_name));

    if(pthread_create(&start_playback_pthread_id, NULL, start_playback_thread, pkg_name)) {
         return ;
    }
    QLOGI("Exit playback");
    return ;
}

static void* start_playback_thread(void *pkg_name_arg)
{
    file_details *file_detail_ptr;
    int num_file = 0;
    int num_of_files = 0;
    char *pkg_name = (char *)pkg_name_arg;
    int i = 0;
    total_data = 0;
    struct stat file_stat;
    int pagesize = sysconf(_SC_PAGESIZE);
    ATRACE_BEGIN("start_playback_thread: Enter");

    QLOGI("pkg_name = %s",pkg_name);
    num_file = get_total_file(pkg_name);

    if( num_file <= 0)
    {
        QLOGI("no file to read get_total_file %d", num_file);
        remove_pkg(pkg_name);
        free(pkg_name);
        ATRACE_END();
        return NULL;
    }

    file_detail_ptr = (file_details *) malloc(sizeof(file_details) * num_file);
    if (NULL == file_detail_ptr)
    {
        QLOGI("Fail to allocate memory");
        remove_pkg(pkg_name);
        free(pkg_name);
        ATRACE_END();
        return NULL;
    }

    num_of_files = get_file_list(pkg_name,file_detail_ptr,num_file);
    QLOGI("num_of_files = %d",num_of_files);

    if (num_of_files <= 0)
    {
        QLOGI("no file to read get_file_list");
        remove_pkg(pkg_name);
        free(file_detail_ptr);
        free(pkg_name);
        ATRACE_END();
        return NULL;
    }

    if( num_file > MAX_NUM_FILE)
    {
        //free mincore array that exceed MAX_NUM_FILE
        for (i=MAX_NUM_FILE; i<num_of_files; i++) {
            if (file_detail_ptr[i].mincore_array)
            {
                free(file_detail_ptr[i].mincore_array);
                file_detail_ptr[i].mincore_array=NULL;
            }
        }
        num_file = MAX_NUM_FILE;
    }

    for(i = 0; i < num_file;i++)
    {
        int fd = -1;
        char trace_buf[4096];
        if (file_detail_ptr[i].disabled) {
            QLOGE("skip preload disabled file %d %s", i, file_detail_ptr[i].file_name);
            continue;
        }
        if (file_detail_ptr[i].study_finish && file_detail_ptr[i].file_iop_size < pagesize) {
            file_detail_ptr[i].cache_dropped = NEED_MARK_CACHE_NOT_DROPPED;
            QLOGE("skip preload ZERO needed file %d %s", i, file_detail_ptr[i].file_name);
            continue;
        }
        fd = open(file_detail_ptr[i].file_name, O_RDONLY);
        if(fd == -1)
        {
            mark_for_delete(pkg_name,file_detail_ptr[i].file_name);
            continue;
        }
        if ( fstat( fd, &file_stat ) < 0 )
        {
            QLOGI("fail to get file stat");
            mark_for_delete(pkg_name,file_detail_ptr[i].file_name);
            close(fd);
            continue;// goto  next file
        }
        if ( file_stat.st_size == 0 )
        {
            // File zie is zero
            mark_for_delete(pkg_name,file_detail_ptr[i].file_name);
            close(fd);
            continue;// goto  next file
        }
        snprintf(trace_buf,4096,"Opening file %s size = %d",file_detail_ptr[i].file_name
                               , (int)file_stat.st_size);
        ATRACE_BEGIN(trace_buf);
        if (file_stat.st_mtime != file_detail_ptr[i].file_modify_time) {
            if (strstr(file_detail_ptr[i].file_name,".apk")!=NULL
                    && (strstr(file_detail_ptr[i].file_name,"/data/app/")!=NULL ||
                    strstr(file_detail_ptr[i].file_name, "/system/app/")!=NULL ||
                    strstr(file_detail_ptr[i].file_name, "/system/priv-app/")!=NULL))
                {
                    //readonly /data/app/base.apk
                    QLOGI("apk file %s modify time changed %d/%ld, \
                              remove files belong to pkg, and restudy",
                              file_detail_ptr[i].file_name,
                              file_detail_ptr[i].file_modify_time, file_stat.st_mtime);
                    snprintf(trace_buf, sizeof(trace_buf), "apk file %s upgrade, remove pkg"
                                                         , file_detail_ptr[i].file_name);
                    ATRACE_BEGIN(trace_buf);
                    file_detail_ptr[i].mark_for_delete = NEED_MARK_FOR_DELETE_PKG;
                    close(fd);
                    ATRACE_END();

                    ATRACE_END(); // end for opening file
                    ATRACE_END(); //end for start_playback_thread enter
                    return NULL;
                } else {
                    QLOGI("file %s modify time changed update db", file_detail_ptr[i].file_name);
                    //mark study_finish=0 and cache_dropped=1
                    snprintf(trace_buf, sizeof(trace_buf), "file %s modifie mark update"
                                                         , file_detail_ptr[i].file_name);
                    ATRACE_BEGIN(trace_buf);
                    //restudy if file modify time changed for .odex and .dex file
                    if (strstr(file_detail_ptr[i].file_name, ".odex") != NULL
                        || strstr(file_detail_ptr[i].file_name, ".dex") != NULL
                        || strstr(file_detail_ptr[i].file_name, ".vdex") != NULL)
                    {
                        file_detail_ptr[i].mark_for_delete = NEED_MARK_FOR_UPDATE;
                        file_detail_ptr[i].file_modify_time = file_stat.st_mtime;
                        file_detail_ptr[i].filesize = file_stat.st_size;
                        drop_file_cache(fd, &(file_detail_ptr[i]));
                    } else {//disable unstable file
                        file_detail_ptr[i].mark_for_delete = NEED_MARK_FOR_DISABLE;
                        file_detail_ptr[i].disabled = 1;
                    }
                ATRACE_END();
            }
            close(fd);
            continue;
        }
        //try if need drop cache or playback
        if (file_detail_ptr[i].study_finish == 0) {
            QLOGI("Drop cache of file %s", file_detail_ptr[i].file_name);
            snprintf(trace_buf, sizeof(trace_buf), "Drop cache of file %s",
                    file_detail_ptr[i].file_name);
            ATRACE_BEGIN(trace_buf);
            drop_file_cache(fd, &(file_detail_ptr[i]));
            file_detail_ptr[i].cache_dropped = NEED_MARK_CACHE_DROPPED;
            ATRACE_END();
        } else {
            QLOGI("io preload of file %s", file_detail_ptr[i].file_name);
            snprintf(trace_buf, sizeof(trace_buf), "add file %s %.2f / %.2f MB to task",
                    file_detail_ptr[i].file_name,
                    file_detail_ptr[i].file_iop_size/1024.0/1024,
                    file_detail_ptr[i].filesize/1024.0/1024);
            ATRACE_BEGIN(trace_buf);
            iop_fileplayback_run(file_detail_ptr[i],file_stat,fd);
            ATRACE_END();
            if (file_detail_ptr[i].cache_dropped) {
                file_detail_ptr[i].cache_dropped = NEED_MARK_CACHE_NOT_DROPPED;
            }
        }
        close(fd);
        if(total_data > MAX_TOTAL_DATA)
        {
            QLOGI("Max total size limit reached Total = %d",total_data);
            //STOP reading and max total reached
            break;
        }
        ATRACE_END(); // end for opening file
    }
    commit_filelist_info(file_detail_ptr, num_file);
    delete_mark_files();

    ATRACE_END(); //end for start_playback_thread enter
    QLOGI(" Total Data = %.2f MB", total_data/1024.0/1024);
    for (i=0; i<num_of_files; i++) {
        if (file_detail_ptr[i].mincore_array) {
            free(file_detail_ptr[i].mincore_array);
            file_detail_ptr[i].mincore_array=NULL;
        }
    }
    free(pkg_name);
    free(file_detail_ptr);
    return NULL;
}

/* Run playback operation */
static void *iop_fileplayback_run(file_details info, struct stat file_stat, int fd)
{
    int page_itrr;
    int total_mincore_pages = (info.file_iop_size+PAGE_SIZE-1)/PAGE_SIZE;
    int total_pages = (file_stat.st_size+PAGE_SIZE-1)/PAGE_SIZE;
    char *mincore_array = info.mincore_array;
    char trace_buf[1024];
    if (total_mincore_pages > 0) {
        snprintf(trace_buf, sizeof(trace_buf), "%s read size:%.4fMB",
                                             info.file_name,
                                             total_mincore_pages*4/1024.0);
    }
    else
    {
        snprintf(trace_buf, sizeof(trace_buf), "%s read size:%.4fMB",
                    info.file_name,file_stat.st_size/1024.0/1024);
    }
    ATRACE_BEGIN(trace_buf);
    int count_pages=0;
    int read_data = 0;
    int ret = 0;
    QLOGI("MINCORE, PLAYBACK %s START, mincore_array: %p",
                info.file_name, mincore_array);
    //load each page by posix_fadvise here to avoid read_ahead
    //and decrease CPU usage of copy io content result to local viriable
    if (mincore_array == NULL) {
        //if all file need read to cache
        int len_size = file_stat.st_size;
        ret = posix_fadvise(fd, 0, len_size, POSIX_FADV_WILLNEED);
        read_data += len_size;
        if (ret !=0) {
            QLOGE("posix_fadvise failed: %s", strerror(errno));
        }
    } else {//load file content based on mincore result
        count_pages=0;
        for(page_itrr = 0; page_itrr < total_pages; page_itrr++) {
            if (mincore_array[page_itrr] & 0x1) {
                if (count_pages>=total_mincore_pages)
                    break;
                ret=posix_fadvise(fd, page_itrr*PAGE_SIZE, PAGE_SIZE, POSIX_FADV_WILLNEED);
                if (ret !=0) {
                    QLOGE("posix_fadvise failed: %s", strerror(errno));
                }
                read_data += PAGE_SIZE;
                count_pages++;
            }
        }
    }
    //after finish preload, mark file cache advise as normal
    ret = posix_fadvise(fd, 0, file_stat.st_size, POSIX_FADV_NORMAL);
    if (ret !=0) {
        QLOGE("posix_fadvise failed: %s", strerror(errno));
    }
    close(fd);
    total_data += read_data;
    QLOGI("MINCORE, PLAYBACK %s END real read file size = %.4fMB",
                                      info.file_name, read_data/1024.0/1024);
    ATRACE_END();

    return NULL;
}

参考文档

将RandomAccessFile、FileChannel、MappedByteBuffer分别讲解了一遍:blog.csdn.net/xiaoduanayu…
posix_fadvise的man手册解释:man7.org/linux/man-p…