天天看點

鴻蒙輕核心M核源碼分析:LibC實作之Musl LibC

摘要:本文學習了LiteOS-M核心Musl LibC的實作,特别是檔案系統和記憶體配置設定釋放部分。

本文分享自華為雲社群《鴻蒙輕核心M核源碼分析系列十九 Musl LibC》,作者:zhushy。

LiteOS-M核心LibC實作有2種,可以根據需求進行二選一,分别是musl libC和newlibc。本文先學習下Musl LibC的實作代碼。文中所涉及的源碼,均可以在開源站點https://gitee.com/openharmony/kernel_liteos_m 擷取。LiteOS-M核心提供了和核心相關的檔案系統、記憶體申請釋放接口,其他接口可以直接使用Musl提供的。我們分别來看下核心提供的接口部分。

1、Musl LibC檔案系統

在使用Musl LibC并且使能支援POSIX FS API時,可以使用檔案kal\libc\musl\fs.c中定義的檔案系統操作接口。這些是标準的POSIX接口,如果想了解其用法,可以參考Section 2: system calls。可以在網頁上搜尋,也可以直接把上述網址和函數名稱進行拼接,如對于mount()函數,可以直接通路https://linux.die.net/man/2/mount。opendir等部分函數需要在Section 3: library functions網頁上檢視。下文快速記錄下各個函數的使用方法。

1.1 函數mount

函數mount會挂載source參數(通常是裝置名稱,也可以是目錄)指定的檔案系統到target參數指定的目錄。檔案系統類型LiteOS-M核心支援"fat"和"littlefs"兩種類型。"littlefs"檔案系統不需要挂載選項參數mountflags。對于fat檔案類型,挂載選項參數定義在檔案third_party\musl\porting\liteos_m\kernel\include\sys\mount.h中,如MS_RDONLY、MS_NOSUID、MS_REMOUNT等等。參數data由檔案系統進行解析,fat檔案類型不需要該參數;"littlefs"檔案系統需要傳入的data參數應該為 (struct lfs_config*)指針類型。

該函數會調用components\fs\vfs\los_fs.c中的函數LOS_FsMount,後文會專門講解FS VFS。

int mount(const char *source, const char *target,
              const char *filesystemtype, unsigned long mountflags,
              const void *data)
{
    return LOS_FsMount(source, target, filesystemtype, mountflags, data);
}      

1.2 函數umount和umount2

函數umount, umount2用于unmount解除安裝檔案系統。參數target指定要解除安裝的檔案系統。函數umount2除了解除安裝,還可以指定flag參數來控制解除安裝行為。支援的參數定義在third_party\musl\porting\liteos_m\kernel\include\sys\mount.h,如MNT_FORCE、MNT_DETACH、MNT_EXPIRE和UMOUNT_NOFOLLOW。

int umount(const char *target)
{
    return LOS_FsUmount(target);
}

int umount2(const char *target, int flag)
{
    return LOS_FsUmount2(target, flag);
}      

1.3 函數open、close和unlink

函數open用于打開一個檔案或裝置,可能會先建立檔案或裝置。參數path指定檔案或裝置的路徑,參數oflag需要使用下面的通路模式O_RDONLY, O_WRONLY, O_RDWR中的一個,這幾個定義在檔案third_party\musl\porting\liteos_m\kernel\include\fcntl.h。third_party\musl\porting\liteos_m\kernel\include\bits\fcntl.h。另外,還有些其他檔案建立标簽或檔案狀态标簽可以通過邏輯與進行指定。檔案建立标簽有O_CLOEXEC, O_CREAT, O_DIRECTORY, O_EXCL, O_NOCTTY, O_NOFOLLOW, O_TRUNC和O_TTY_INIT。其餘的為檔案狀态标簽,這些标簽定義檔案中third_party\musl\porting\liteos_m\kernel\include\bits\fcntl.h中。可以通路https://linux.die.net/man/2/open了解這些标簽的詳細用法。

函數open傳回值為檔案描述符file descriptor,會被其他函數如read, write, lseek, fcntl等使用。函數close用于關閉一個檔案描述符,使fd不再引用任何檔案,可被再次重用。函數unlink用于删除path路徑指定的檔案。

int open(const char *path, int oflag, ...)
{
    va_list vaList;
    va_start(vaList, oflag);
    int ret;
    ret = LOS_Open(path, oflag, vaList);
    va_end(vaList);
    return ret;
}

int close(int fd)
{
    return LOS_Close(fd);
}

int unlink(const char *path)
{
    return LOS_Unlink(path);
}      

1.4 函數read和write

函數read嘗試從fd中讀取nbyte位元組的資料到buf開始的緩存裡,讀取成功時傳回讀取的位元組數目。函數write把buf處開始的nbyte位元組資料寫入fd引用的檔案裡,寫入成功時傳回實際寫入的位元組數目。

ssize_t read(int fd, void *buf, size_t nbyte)
{
    return LOS_Read(fd, buf, nbyte);
}

ssize_t write(int fd, const void *buf, size_t nbyte)
{
    return LOS_Write(fd, buf, nbyte);
}      

1.5 函數lseek

函數lseek用于重新定位檔案讀寫的偏移位置。參數whence取值為SEEK_SET、SEEK_CUR或SEEK_END,定義在檔案third_party\musl\porting\liteos_m\kernel\include\fcntl.h。

  • SEEK_SET

    偏移設定在offset位元組處。

  • SEEK_CUR

    偏移設定在目前位置加上offset位元組處。

  • SEEK_END

    偏移設定在檔案大小加上offset位元組處。

函數執行成功時,傳回值為從檔案開頭的偏移位元組數值。

off_t lseek(int fd, off_t offset, int whence)
{
    return LOS_Lseek(fd, offset, whence);
}}      

1.6 函數fstat、stat和statfs

函數fstat和stat用于擷取檔案的狀态state,參數參數分别是檔案描述符和檔案路徑。參數中的struct stat結構體定義在檔案third_party\musl\porting\liteos_m\kernel\include\bits\stat.h中。

函數statfs傳回檔案系統統計statistics資料,結構體struct statfs定義在檔案third_party\musl\porting\liteos_m\kernel\include\bits\statfs.h中。

int fstat(int fd, struct stat *buf)
{
    return LOS_Fstat(fd, buf);
}

int stat(const char *path, struct stat *buf)
{
    return LOS_Stat(path, buf);
}
int statfs(const char *path, struct statfs *buf)
{
    return LOS_Statfs(path, buf);
}      

1.7 函數mkdir、opendir、readir、closedir和rmdrir

函數mkdir用于建立一個目錄,目錄名稱由參數path指定。參數mode指定目錄權限。建立成功傳回0,否則傳回-1。

函數opendir用于打開一個目錄流a directory stream,目錄名稱由參數dirName指定,傳回一個執行目錄劉的指針。發生錯誤時,傳回NULL,并設定errno。傳回值類型DIR是struct __dirstream的别名,定義在檔案中third_party\musl\porting\liteos_m\kernel\include\dirent.h。可以通路https://linux.die.net/man/3/opendir了解更多關于該函數的資訊。

函數readdir用于讀取一個目錄,傳回一個struct dirent結構體指針,代表目錄流DIR *dir中的下一個目錄條目directory entry。到達目錄流尾部或錯誤時,傳回NULL。結構體定義在檔案third_party\musl\porting\liteos_m\kernel\include\bits\dirent.h中。 可以通路https://linux.die.net/man/3/readdir了解更多關于該函數的資訊。

函數closedir用于關閉一個目錄。函數rmdir用于删除一個目錄,隻有空目錄才會被删除。

int mkdir(const char *path, mode_t mode)
{
    return LOS_Mkdir(path, mode);
}

DIR *opendir(const char *dirName)
{
    return LOS_Opendir(dirName);
}

struct dirent *readdir(DIR *dir)
{
    return LOS_Readdir(dir);
}

int closedir(DIR *dir)
{
    return LOS_Closedir(dir);
}

int rmdir(const char *path)
{
    return LOS_Unlink(path);
}      

1.8 函數fsync

函數mkdir用于同步記憶體中所有已修改的檔案資料到儲存裝置。可以通路https://linux.die.net/man/3/fsync了解更多關于該函數的資訊。

int fsync(int fd)
{
    return LOS_Fsync(fd);
}      

1.9 函數rename

函數rename用于重命名一個檔案。可以通路https://linux.die.net/man/3/rename了解更多關于該函數的資訊。

int rename(const char *oldName, const char *newName)
{
    return LOS_Rename(oldName, newName);
}      

1.10 函數ftruncate

函數ftruncate用于截斷一個檔案到指定的長度。可以通路https://linux.die.net/man/3/ftruncate了解更多關于該函數的資訊。

int ftruncate(int fd, off_t length)
{
    return LOS_Ftruncate(fd, length);
}      

2、Musl LibC記憶體配置設定釋放

LiteOS-M核心提供了記憶體配置設定釋放函數。這些是标準的POSIX接口,如果想了解其用法,可以參考Section 3: library functions。可以在網頁上搜尋,也可以直接把上述網址和函數名稱進行拼接,如對于malloc()函數,可以直接通路https://linux.die.net/man/3/malloc。opendir等部分函數需要在網頁上檢視。下文快速記錄下各個函數的使用方法。

2.1 函數malloc、free和memalign

函數malloc和free分别調用核心記憶體子產品的接口來實作記憶體申請和釋放。函數memalign可以以指定的記憶體對齊大小來申請記憶體。

void free(void *ptr)
{
    if (ptr == NULL) {
        return;
    }

    LOS_MemFree(OS_SYS_MEM_ADDR, ptr);
}

void *malloc(size_t size)
{
    if (size == 0) {
        return NULL;
    }

    return LOS_MemAlloc(OS_SYS_MEM_ADDR, size);
}
void *memalign(size_t boundary, size_t size)
{
    if (size == 0) {
        return NULL;
    }

    return LOS_MemAllocAlign(OS_SYS_MEM_ADDR, size, boundary);
}      

2.2 函數malloc、free和memalign

函數calloc在記憶體的動态存儲區中配置設定nitems個長度為size的連續空間,函數傳回一個指向配置設定起始位址的指針;如果配置設定不成功,傳回NULL。

函數zalloc和malloc的差別是,申請成功後,對申請的記憶體區域置0。函數realloc用于重新申請一塊記憶體區域。

void *calloc(size_t nitems, size_t size)
{
    size_t real_size;
    void *ptr = NULL;

    if (nitems == 0 || size == 0) {
        return NULL;
    }

    real_size = (size_t)(nitems * size);
    ptr = LOS_MemAlloc(OS_SYS_MEM_ADDR, real_size);
    if (ptr != NULL) {
        (void)memset_s(ptr, real_size, 0, real_size);
    }
    return ptr;
}
void *zalloc(size_t size)
{
    void *ptr = NULL;

    if (size == 0) {
        return NULL;
    }

    ptr = LOS_MemAlloc(OS_SYS_MEM_ADDR, size);
    if (ptr != NULL) {
        (void)memset_s(ptr, size, 0, size);
    }
    return ptr;
}

void *realloc(void *ptr, size_t size)
{
    if (ptr == NULL) {
        return malloc(size);
    }

    if (size == 0) {
        free(ptr);
        return NULL;
    }

    return LOS_MemRealloc(OS_SYS_MEM_ADDR, ptr, size);
}      

小結

本文學習了LiteOS-M核心Musl LibC的實作,特别是檔案系統和記憶體配置設定釋放部分。時間倉促和能力關系,如有失誤,歡迎指正。感謝閱讀,如有任何問題、建議,都可以部落格下留言給我,謝謝。

參考資料

  • library functions - Linux man pages
  • system calls - Linux man pages

點選關注,第一時間了解華為雲新鮮技術~

繼續閱讀