鴻蒙輕核心M核原始碼分析:LibC實現之Musl LibC

華為雲開發者社群 發表於 2022-01-24
摘要:本文學習了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

 

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

相關文章