文件I/O:通用的 I/O 模型 — Go 封裝

WhitneyWert 8年前發布 | 11K 次閱讀 Linux Google Go/Golang開發 Go

本文介紹 Unix I/O 模型中的4個通用系統調用:open()、read()、write()和close() 的 Go 語言封裝。

1、Linux 中 open 系統調用的定義

#include <sys/stat.h>
#include <fcntl.h>

int open(const char* pathname, int flags, … /* mode_t mode */);
                   Returns file descriptor on success, or -1 on error

2、Go 中 open 系統調用的封裝

一般的,我們使用 os 標準庫的 Open/Create 方法來間接調用 open 系統調用,跟蹤代碼,找到 Go 中 open 系統調用的封裝:

func Open(path string, mode int, perm uint32) (fd int, err error) {
    return openat(_AT_FDCWD, path, mode|O_LARGEFILE, perm)
}

2.1 openat 又是什么呢?

從 2.6.16 (Go 支持的 Linux 版本是 2.6.23)開始,Linux 內核提供了一系列新的系統調用(以at結尾),它們在執行與傳統系統調用相似任務的同時,還提供了一些附加功能,對某些程序非常有用,這些系統調用使用目錄文件描述符來解釋相對路徑。

#define _XOPEN_SOURCE 700 /* Or define _POSIX_C_SOURCE >= 200809 */
#include <fcntl.h>

int openat(int dirfd, const char* pathname, int flags, … /* mode_t mode */);
                   Returns file descriptor on success, or -1 on error

可見,openat 系統調用和 open 類似,只是添加了一個 dirfd 參數,其作用如下:

1)如果 pathname 中為一相對路徑名,那么對其解釋則以打開文件描述符 dirfd 所指向的目錄為參照點,而非進程的當前工作目錄;

2)如果 pathname 中為一相對路徑,且 dirfd 中所含為特殊值 AT_FDCWD(其值為-100),那么對 pathname 的解釋則相對于進程當前工作目錄,這時 openat 和 open 行為一致;

3)如果 pathname 中為絕對路徑,那么將忽略 dirfd 參數;

在 Go 中,只要存在相應的 at 系統調用,都會使用它。

2.2 解讀 Go 中的 openat

有上可知,Go 中的 Open 并非執行 open 系統調用,而是 openat 系統調用,行為和 open 一致。

func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) {
    var _p0 *byte
    // 根據要求,path 必須是 C 語言中的字符串,即以 NULL 結尾
    // BytePtrFromString 的作用就是返回一個指向 NULL 結尾的字節數組指針
    _p0, err = BytePtrFromString(path)
    if err != nil {
        return
    }
    // SYS_OPENAT openat 系統調用編號
    r0, _, e1 := Syscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(flags), uintptr(mode), 0, 0)
    // 空操作,用于保證 _p0 存活
    use(unsafe.Pointer(_p0))
    fd = int(r0)
    if e1 != 0 {
        err = errnoErr(e1)
    }
    return
}

2.3 反過來解讀 os.OpenFile 函數

OpenFile 函數有一個點需要注意,創建或打開文件時,自動加上了 O_CLOEXEC 標志,也就是執行 Exec 系統調用時該文件描述符不會被繼承;

os.OpenFile 返回 os.File 類型的指針,通過 File.Fd() 可以獲取到文件描述符;

3、read、write 和 close 系統調用

通過 open 系統調用的分析可以很容易的自己分析 read、write和close 系統調用。

說明一點:close 系統調用,企圖關閉一個未打開的文件描述符或兩次關閉同一個文件描述符,會返回錯誤。一般都不需要關心錯誤。

4、lseek 系統調用

Go 中的 os 包的 File.Seek 對應的系統調用是 lseek

5、其他文件 I/O 相關的系統調用

os 包中,File 類型中 ReadAt/WriteAt 對應的系統調用是 pread/pwrite

Truncate 對應 truncate 等;

幾乎所有 Linux 文件相關系統調用,Go 都有封裝;另外,通過上面 Open 的介紹,即使沒有封裝,自己封裝也不是難事。

 

來自: http://blog.studygolang.com/2016/06/go-wrapper-io/

 

 本文由用戶 WhitneyWert 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!