[Android]高性能MMKVデータインタラクティブ分析-MKV初期化

20772 ワード

皆さん、こんにちは、私は蒼王です.以下は私のこのシリーズの関連文章で、興味があれば参考にして、私の文章が好きか注目してもいいです.
[Android]どのようにして崩壊率が千分の3以下のアプリケーションapp-章のリストを作成します
組立体化群1はすでに満員で、入ってきたのは2 763094035を加えることができます
MMKVフレーム初期化
    MMKV.initialize(this);

    public static String initialize(Context context) {
        //      
        rootDir = context.getFilesDir().getAbsolutePath() + "/mmkv";
        initialize(rootDir);
        return rootDir;
    }

    private static native void initialize(String var0);


その後はjniの操作を呼び出しました
extern "C" JNIEXPORT JNICALL void
Java_com_tencent_mmkv_MMKV_initialize(JNIEnv *env, jobject obj, jstring rootDir) {
    //c++   0        
    if (!rootDir) {
        return;
    }

    //string  char*
    const char *kstr = env->GetStringUTFChars(rootDir, nullptr);
    if (kstr) {
        MMKV::initializeMMKV(kstr);
        //       kstr
        env->ReleaseStringUTFChars(rootDir, kstr);
    }
}


MMKV初期化、pthread_once()関数の詳細
マルチスレッド環境では、一度だけ実行する必要があることがあります.通常、アプリケーションを初期化する場合、main関数に簡単に配置できます.しかし、ライブラリを書くとmainで初期化できません.静的で初期化できますが、一度の初期化(pthread_once)を使うと簡単です.
int pthread_once(pthread_once_t *once_control, void (*init_routine) (void));
機能:この関数はPTHREAD_の初期値を使用します.ONCE_INITのonce_コントロール変数保証init_routine()関数は、このプロセス実行シーケンスで1回のみ実行されます.
マルチスレッドプログラミング環境ではpthread_once()呼び出しは複数のスレッドに表示され、init_routine()関数は1回のみ実行され,どのスレッドで実行されるかは不定であり,カーネルスケジューリングによって決定される.ではinitializeメソッドは1回しか実行されません.
void MMKV::initializeMMKV(const std::string &rootDir) {
    //    
    static pthread_once_t once_control = PTHREAD_ONCE_INIT;
    //        initialize
    pthread_once(&once_control, initialize);
    //      
    g_rootDir = rootDir;
    //strdup     
    //c_str()          C      ,     string   .     c    , c     string  ,     string        c_str() string      c       。  :     strcpy()         c_str()     
    //       
    char *path = strdup(g_rootDir.c_str());
    //  MmapedFile
    mkPath(path);
    //      
    free(path);

    MMKVInfo("root dir: %s", g_rootDir.c_str());
}

void initialize() { 
    //     
   //   unordered_map            ,            ,              
    g_instanceDic = new unordered_map<:string mmkv="">;
    //      
    g_instanceLock = ThreadLock();

    //testAESCrypt();

    MMKVInfo("page size:%d", DEFAULT_MMAP_SIZE);
}
//  getpagesize          
//             ,       ,               
 //      :           ,       ,      
 //                    。
const int DEFAULT_MMAP_SIZE = getpagesize();


保存されたフォルダを作成し、アドレス名を巡回してから、繰り返し作成します.
bool mkPath(char *path) {
    //  ( )     
    struct stat sb = {};
    bool done = false;
    char *slash = path;

    while (!done) {
        //      
        slash += strspn(slash, "/");
        slash += strcspn(slash, "/");
        //      
        done = (*slash == '\0');
        *slash = '\0';
        //        
        if (stat(path, &sb) != 0) {
            //     
            if (errno != ENOENT || mkdir(path, 0777) != 0) {
                MMKVWarning("%s", path);
                return false;
            }
        } else if (!S_ISDIR(sb.st_mode)) {  //       ,   
            MMKVWarning("%s: %s", path, strerror(ENOTDIR));
            return false;
        }

        *slash = '/';
    }

    return true;
}


ファイル初期化の保存
    //    mmapId,  (   ,   ),    
    MMKV.mmkvWithID(mmapID, MMKV.SINGLE_PROCESS_MODE, cryptKey);

    public static MMKV mmkvWithID(String mmapID, int mode, String cryptKey) {
        if(rootDir == null) {  //             
            throw new IllegalStateException("You should Call MMKV.initialize() first.");
        } else {
            //id     46   
            verifyMMID(mmapID); 
               jni    ID
            long handle = getMMKVWithID(mmapID, mode, cryptKey);
            return new MMKV(handle);
        }
    }
    //mmkv id long  
    private static native long getMMKVWithID(String var0, int var1, String var2);

extern "C" JNIEXPORT JNICALL jlong Java_com_tencent_mmkv_MMKV_getMMKVWithID(
    JNIEnv *env, jobject obj, jstring mmapID, jint mode, jstring cryptKey) {
    MMKV *kv = nullptr;  //       
    if (!mmapID) {  //    ,       
        return (jlong) kv;  
    }
    string str = jstring2string(env, mmapID);  //   mmapId

    if (cryptKey != nullptr) {
        string crypt = jstring2string(env, cryptKey);  //     
        if (crypt.length() > 0) { 
            //    ,     ,     
            kv = MMKV::mmkvWithID(str, DEFAULT_MMAP_SIZE, (MMKVMode) mode, &crypt);
        }
    }
    if (!kv) {  //      ,            
        kv = MMKV::mmkvWithID(str, DEFAULT_MMAP_SIZE, (MMKVMode) mode, nullptr);
    }

    return (jlong) kv;
}

MMKV *MMKV::mmkvWithID(const std::string &mmapID, int size, MMKVMode mode, string *cryptKey) {
    //  mmapId       
    if (mmapID.empty()) {
        return nullptr;
    }
    //     
    SCOPEDLOCK(g_instanceLock);
    //      map  mampID,   mapFile  
    auto itr = g_instanceDic->find(mmapID);
    //          
    //      
    if (itr != g_instanceDic->end()) {
        //      mmkv
        MMKV *kv = itr->second;
        return kv;
    }
    //    mmkv
    auto kv = new MMKV(mmapID, size, mode, cryptKey);
     //   map 
    (*g_instanceDic)[mmapID] = kv;
    return kv;
}


MMKV初期化の作成
MMKV::MMKV(const std::string &mmapID, int size, MMKVMode mode, string *cryptKey)
    : m_mmapID(mmapID)
    , m_path(mappedKVPathWithID(m_mmapID, mode)) //  mmkv    
    , m_crcPath(crcPathWithID(m_mmapID, mode))  //crc  
    , m_metaFile(m_crcPath, DEFAULT_MMAP_SIZE, (mode & MMKV_ASHMEM) ? MMAP_ASHMEM : MMAP_FILE) //   MmapedFile
    , m_crypter(nullptr)  //   
    , m_fileLock(m_metaFile.getFd())  //   
    , m_sharedProcessLock(&m_fileLock, SharedLockType)   //   
    , m_exclusiveProcessLock(&m_fileLock, ExclusiveLockType)  //     
    , m_isInterProcess((mode & MMKV_MULTI_PROCESS) != 0)  //     
    , m_isAshmem((mode & MMKV_ASHMEM) != 0) {       //        
    m_fd = -1;
    m_ptr = nullptr;
    m_size = 0;
    m_actualSize = 0;
    m_output = nullptr;

    if (m_isAshmem) { //        ,             MmapedFile
        m_ashmemFile = new MmapedFile(m_mmapID, static_cast(size), MMAP_ASHMEM);
        //         fd  
        m_fd = m_ashmemFile->getFd();
    } else {
        m_ashmemFile = nullptr;
    }

    if (cryptKey && cryptKey->length() > 0) { //        ,     AES   
        m_crypter = new AESCrypt((const unsigned char *) cryptKey->data(), cryptKey->length());
    }
    //         
    m_needLoadFromFile = true;

    m_crcDigest = 0;
    //       
    m_sharedProcessLock.m_enable = m_isInterProcess;
    //         
    m_exclusiveProcessLock.m_enable = m_isInterProcess;

    // sensitive zone
    {
         //   
        SCOPEDLOCK(m_sharedProcessLock);
        loadFromFile();
    }
}


ファイルの読み込み
void MMKV::loadFromFile() {
    if (m_isAshmem) {  //        ,         
        loadFromAshmem();
        return;
    }

    m_metaInfo.read(m_metaFile.getMemory());
   //    ,m_fd     
    m_fd = open(m_path.c_str(), O_RDWR | O_CREAT, S_IRWXU);  
    if (m_fd < 0) {  //     
        MMKVError("fail to open:%s, %s", m_path.c_str(), strerror(errno));
    } else {
        m_size = 0;
        struct stat st = {0};
        if (fstat(m_fd, &st) != -1) { //        
            m_size = static_cast(st.st_size);
        }
        // round up to (n * pagesize)
        if (m_size < DEFAULT_MMAP_SIZE || (m_size % DEFAULT_MMAP_SIZE != 0)) { //       ,   1 
            size_t oldSize = m_size;
            m_size = ((m_size / DEFAULT_MMAP_SIZE) + 1) * DEFAULT_MMAP_SIZE;
            if (ftruncate(m_fd, m_size) != 0) {  //            
                MMKVError("fail to truncate [%s] to size %zu, %s", m_mmapID.c_str(), m_size,
                          strerror(errno));
                m_size = static_cast(st.st_size);
            }
            zeroFillFile(m_fd, oldSize, m_size - oldSize); //    
        }
         //        
        m_ptr = (char *) mmap(nullptr, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0);
        //              
        if (m_ptr == MAP_FAILED) {
            MMKVError("fail to mmap [%s], %s", m_mmapID.c_str(), strerror(errno));
        } else {
            memcpy(&m_actualSize, m_ptr, Fixed32Size);
            MMKVInfo("loading [%s] with %zu size in total, file size is %zu", m_mmapID.c_str(),
                     m_actualSize, m_size);
            bool loaded = false;
            if (m_actualSize > 0) {
                if (m_actualSize < m_size && m_actualSize + Fixed32Size <= m_size) {
                    if (checkFileCRCValid()) {
                        MMKVInfo("loading [%s] with crc %u sequence %u", m_mmapID.c_str(),
                                 m_metaInfo.m_crcDigest, m_metaInfo.m_sequence);
                        MMBuffer inputBuffer(m_ptr + Fixed32Size, m_actualSize, MMBufferNoCopy);
                        if (m_crypter) {
                            decryptBuffer(*m_crypter, inputBuffer);
                        }
                        //   Photobuf    orderedMap
                        m_dic = MiniPBCoder::decodeMap(inputBuffer);
                        m_output = new CodedOutputData(m_ptr + Fixed32Size + m_actualSize,
                                                       m_size - Fixed32Size - m_actualSize);
                        loaded = true;
                    }
                }
            }
            if (!loaded) {
                SCOPEDLOCK(m_exclusiveProcessLock);

                if (m_actualSize > 0) {
                    writeAcutalSize(0);
                }
                m_output = new CodedOutputData(m_ptr + Fixed32Size, m_size - Fixed32Size);
                recaculateCRCDigest();
            }
            MMKVInfo("loaded [%s] with %zu values", m_mmapID.c_str(), m_dic.size());
        }
    }

    if (!isFileValid()) {
        MMKVWarning("[%s] file not valid", m_mmapID.c_str());
    }

    m_needLoadFromFile = false;
}


匿名メモリの読み込み
void MMKV::loadFromAshmem() {
    //  MmapedFile    
    m_metaInfo.read(m_metaFile.getMemory());
    //                 
    if (m_fd < 0 || !m_ashmemFile) {
        MMKVError("ashmem file invalid %s, fd:%d", m_path.c_str(), m_fd);
    } else {
        //          
        m_size = m_ashmemFile->getFileSize();
        //          
        m_ptr = (char *) m_ashmemFile->getMemory();
        if (m_ptr != MAP_FAILED) {  //      
            //        
            memcpy(&m_actualSize, m_ptr, Fixed32Size);
            MMKVInfo("loading [%s] with %zu size in total, file size is %zu", m_mmapID.c_str(),
                     m_actualSize, m_size);
            bool loaded = false;
            if (m_actualSize > 0) {
                if (m_actualSize < m_size && m_actualSize + Fixed32Size <= m_size) {
                    if (checkFileCRCValid()) {//  m_ptr m_size    
                        MMKVInfo("loading [%s] with crc %u sequence %u", m_mmapID.c_str(),
                                 m_metaInfo.m_crcDigest, m_metaInfo.m_sequence);
                        //  MMBuffer                  
                        MMBuffer inputBuffer(m_ptr + Fixed32Size, m_actualSize, MMBufferNoCopy);
                        if (m_crypter) { //    ,  MMBuffer  
                            decryptBuffer(*m_crypter, inputBuffer);
                        }
                        //               
                        m_dic = MiniPBCoder::decodeMap(inputBuffer);
                        m_output = new CodedOutputData(m_ptr + Fixed32Size + m_actualSize,
                                                       m_size - Fixed32Size - m_actualSize);
                       //        
                        loaded = true;
                    }
                }
            }
            if (!loaded) {
                 //     
                SCOPEDLOCK(m_exclusiveProcessLock);

                if (m_actualSize > 0) {
                    writeAcutalSize(0);
                }
                //   
                m_output = new CodedOutputData(m_ptr + Fixed32Size, m_size - Fixed32Size);
                //    crc  
                recaculateCRCDigest();
            }
            MMKVInfo("loaded [%s] with %zu values", m_mmapID.c_str(), m_dic.size());
        }
    }

    if (!isFileValid()) {
        MMKVWarning("[%s] ashmem not valid", m_mmapID.c_str());
    }

    m_needLoadFromFile = false;
}


ストレージファイルの作成について説明します(ファイルと共有メモリに分かれます)
MmapedFile::MmapedFile(const std::string &path, size_t size, bool fileType)
    : m_name(path), m_fd(-1), m_segmentPtr(nullptr), m_segmentSize(0), m_fileType(fileType) {
    if (m_fileType == MMAP_FILE) {  //    
        //      
        m_fd = open(m_name.c_str(), O_RDWR | O_CREAT, S_IRWXU);
        if (m_fd < 0) {
            MMKVError("fail to open:%s, %s", m_name.c_str(), strerror(errno));
        } else {
            //       
            struct stat st = {};
            if (fstat(m_fd, &st) != -1) {
                m_segmentSize = static_cast(st.st_size);
            }
            if (m_segmentSize < DEFAULT_MMAP_SIZE) {
                m_segmentSize = static_cast(DEFAULT_MMAP_SIZE);
                if (ftruncate(m_fd, m_segmentSize) != 0 || !zeroFillFile(m_fd, 0, m_segmentSize)) {
                    MMKVError("fail to truncate [%s] to size %zu, %s", m_name.c_str(),
                              m_segmentSize, strerror(errno));
                    close(m_fd);
                    m_fd = -1;
                    removeFile(m_name);
                    return;
                }
            }
            m_segmentPtr =
                (char *) mmap(nullptr, m_segmentSize, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0);
            if (m_segmentPtr == MAP_FAILED) {
                MMKVError("fail to mmap [%s], %s", m_name.c_str(), strerror(errno));
                close(m_fd);
                m_fd = -1;
                m_segmentPtr = nullptr;
            }
        }
    } else {
        //       #define ASHMEM_NAME_DEF "/dev/ashmem"
        m_fd = open(ASHMEM_NAME_DEF, O_RDWR); 
        if (m_fd < 0) { //    
            MMKVError("fail to open ashmem:%s, %s", m_name.c_str(), strerror(errno));
        } else {
            if (ioctl(m_fd, ASHMEM_SET_NAME, m_name.c_str()) != 0) {  //    io  
                MMKVError("fail to set ashmem name:%s, %s", m_name.c_str(), strerror(errno));
            } else if (ioctl(m_fd, ASHMEM_SET_SIZE, size) != 0) {
                MMKVError("fail to set ashmem:%s, size %d, %s", m_name.c_str(), size,
                          strerror(errno));
            } else { //    
                 //     
                m_segmentSize = static_cast(size);
                //     
                m_segmentPtr = (char *) mmap(nullptr, m_segmentSize, PROT_READ | PROT_WRITE,
                                             MAP_SHARED, m_fd, 0);
                if (m_segmentPtr == MAP_FAILED) {
                    MMKVError("fail to mmap [%s], %s", m_name.c_str(), strerror(errno));
                    m_segmentPtr = nullptr;
                } else {
                    return;
                }
            }
            close(m_fd);
            m_fd = -1;
        }
    }
}


本節は第1編、第2~4編の分析ですが、すでに小欄に掲載されていますので、ご覧くださいhttps://xiaozhuanlan.com/cangwang_processより詳細な分析を表示します.