Android log_ki 초고속 로그 인쇄(MMAP)

최근에 mmap 작성 파일에 대한 글을 읽고 있는데, 학습 결과를 공고히 하기 위해 인터넷에 sharepreference 대신 mmap을 사용하는 오픈 소스 라이브러리가 많이 있지만 mmap을 사용하여 로그 인쇄를 구현하는 오픈 소스 라이브러리는 없습니다. , 그래서 안드로이드 시스템을 구현했습니다.mmap을 사용하여 로그를 인쇄하는 도구 log_kid.

소스 코드 공유: https://gitee.com/gggl/log_kid

MMAP의 원리:

인터넷에서 MMAP의 원리에 대한 많은 설명이 있지만 실제로 기억해야 할 세 가지 주요 사항이 있습니다.

  • 전통적인 IO는 쓸 때 사용자 공간에서 커널 공간으로 두 번 복사해야 합니다.

  • 페이지 폴트가 발생할 때 디스크에서 사용자 공간으로 직접 읽기 위한 파일 읽기 및 쓰기를 위한 MMAP용 메모리 복사본은 하나뿐입니다.

  • MMAP을 사용하여 파일을 쓰는 것은 메모리 작업으로 상상할 수 있습니다.

참조할 수 있습니다: https://bbs.huaweicloud.com/blogs/291892

웹의 이 부분에는 많은 것들이 있습니다.

log_kid 구현 원칙:

log_kid의 구현은 mmkv의 구현을 의미합니다.

1. log_kid는 로그 파일에 기록되며 파일의 처음 4바이트는 파일의 내용 크기입니다. mmap은 매번 페이지의 정수배의 메모리를 매핑해야 하기 때문에(시스템은 mmap의 읽기 및 쓰기가 페이지 폴트 인터럽트에 의해 실현된다고 규정합니다: https://juejin.cn/post/6956031662916534279 ), 그래서 거기에 있습니다. 파일 끝에 0이 있을 것입니다. 데이터를 채우고 파일 헤더에 4바이트를 작성하여 파일의 특정 쓰기 위치를 제어합니다.

2. write_log(태그, 메시지)를 통해 로그를 작성합니다.

각 로그의 처음 4바이트는 태그의 크기이고 그 다음에는 태그의 내용, 4바이트의 메시지 크기 및 메시지 내용이 옵니다.

파일 구조:

소스 코드 프로세스:

MemoryFile은 하드 디스크의 로그 파일에 해당합니다.

  1. 초기화 파일

  • 파일을 만들기 위해 열기

  • fstat는 파일 크기를 가져옵니다. 파일 크기가 페이지의 정수 배수가 아닌 경우 페이지의 정수 배수로 확장됩니다.

if (fileSize < pageSize || fileSize % pageSize != 0)

  • truncate函数对文件进行扩容,但是truncate扩容的文件属于稀疏文件,当系统出现内存或者硬盘空间不足的时候可能出现崩溃的问题,所以扩容后用0填充扩容的内容。

  • mmap映射文件,mmap具体参数含义网络上的讲解也比较多。

void * mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset)
  • 获取文件大小,读取ptr的前4个字节,如果是新文件则为一个页的大小。

memcpy(&actualSize , ptr , 4) ;

void MemoryFile::reloadFromFile() {
    int lastSlash = logFilePath.find_last_of('/') + 1 ;
    std::string  path = logFilePath.substr(0 , lastSlash) ;
    mkPath(path) ;
    fd = open(logFilePath.c_str() , O_RDWR | O_CREAT | O_CLOEXEC, S_IRWXU) ;
    if (fd < 0) {
        LOGD("faild to open %s" , logFilePath.c_str()) ;
    } else {
        struct stat st = {} ;
        if (fstat(fd , &st) != -1) {
            fileSize = (int)st.st_size ;
        }
        bool newFile = fileSize == 0 ;
        if (fileSize < pageSize || fileSize % pageSize != 0) {
            int roundSize = ((fileSize / pageSize) + 1 ) * pageSize ;
            if (truncate(roundSize)) {
                fileSize = roundSize ;
            }
        }

        smmap() ;

        if (newFile) {
            actualSize = 0 ;
            memcpy(ptr , &actualSize , 4) ;
        } else {
            memcpy(&actualSize , ptr , 4) ;
        }
        actualSize = actualSize + 4 ;
    }
}
  1. 日志写入:

计算本次写入的数据总大小:logSize,如果总大小加上actualSize大于文件大小,则文件进行扩容,grow,每次扩容为上次文件的一倍大小。

writeInt和writeStr为真正写入数据的方法。


bool MemoryFile::writeLog(const char *tag, const char *msg) {
    int logSize = 0 ;
    int tagSize = strlen(tag) ;
    int msgSize = strlen(msg) ;
    int intSize = 4 ;
    logSize = tagSize + msgSize + intSize * 2 ;
    if (actualSize + logSize > fileSize) {
        grow() ;
    }

    writeInt(tagSize) ;
    writeStr(tag , tagSize) ;
    writeInt(msgSize) ;
    writeStr(msg , msgSize) ;

    memcpy(ptr , &actualSize , 4) ;
    return true ;
}


void MemoryFile::writeInt(int &value) {
    memcpy(ptr + actualSize , &value , 4) ;
    actualSize += 4 ;
}

void MemoryFile::writeStr(const char *value , int &size) {
    memcpy(ptr + actualSize , value , size) ;
    actualSize += size ;
}

总结:

整个项目实现简单轻便,易于理解,易于上手,可能还达不到商业级组件的高度,还需慢慢打磨,也希望有兴趣的同学可以一起来维护这套项目。

实测表现:

使用mmap写入日志速度大概快于传统IO的4-5倍以上,目前项目并没有进一步优化,优化后的速度应该还可以进一步提升。

源码分享:https://gitee.com/gggl/log_kid

Supongo que te gusta

Origin blog.csdn.net/mldxs/article/details/129629928
Recomendado
Clasificación