Golang 异步(bsd/linux)io
在日常开发中,读写文件的底层调用函数是syscall.Read/Write。一切都是围绕这两个函数展开的,不过有时候需要或者就是单纯想异步执行。liburing是linux上一个很好的原生异步io库,这里需要适配bsd派系的系统,所以选择的是另一款libaio,这款异步io库在bsd派系(macos,freebsd,openbsd…)和linux上都是存在的。
golang 类unix系统下 os.OpenFile函数的底层(windows就不要来找茬了:-(
写入文件的底层:syscall.Write
是系统写入函数,ignoringEINTRIO
函数会回调传入的syscall.Write
函数
package main
//由于golang系统调用中没有定义aio需要用到的关键结构体aiocb,所以我们需要借助cgo来使其生成一份golang的aiocb结构体
/*
#include <aio.h>
*/
import "C"
import (
"fmt"
"log"
"syscall"
"time"
"unsafe"
)
type File struct {
fd int
}
type AIOCB C.struct_aiocb
func OpenFile(pathname string, mode, perm int) (*File, error) {
fd, err := syscall.Open(pathname, mode, uint32(perm))
if err != nil {
return nil, err
}
//bsd派系系统走此函数设定绕过内核和用户层缓冲区,linux系统直接上方mode加入O_DIRECT即可
syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_NOCACHE, 0)
var f = File{fd: fd}
return &f, nil
}
func (f *File) Write(b []byte) *AIOCB {
var aiocb AIOCB
aiocb.aio_buf = unsafe.Pointer(&b[0])
blen := len(b)
aiocb.aio_nbytes = C.size_t(blen)
aiocb.aio_fildes = C.int(f.fd)
syscall.Syscall(syscall.SYS_AIO_WRITE, uintptr(unsafe.Pointer(&aiocb)), 0, 0)
return &aiocb
}
func (f *File) Close() error {
return syscall.Close(f.fd)
}
func (f *File) Read(b []byte) *AIOCB {
var aiocbp AIOCB
aiocbp.aio_buf = unsafe.Pointer(&b[0])
aiocbp.aio_nbytes = C.size_t(len(b))
aiocbp.aio_fildes = C.int(f.fd)
syscall.Syscall(syscall.SYS_AIO_READ, uintptr(unsafe.Pointer(&aiocbp)), 0, 0)
return &aiocbp
}
//由于异步操作会立即返回,所以刚刚操作返回的aiocb结构体的指针非常关键,千万不要丢了,如果需要知道写入或读出操作有没有完成,需要通过aio_return这个函数来感知,如果操作未完成会返回-1
func Verify(a *AIOCB) int {
ans, _, _ := syscall.Syscall(syscall.SYS_AIO_RETURN, uintptr(unsafe.Pointer(a)), 0, 0)
return int(int32(ans))
}
由于异步操作会立即返回,所以刚刚操作返回的aiocb结构体的指针非常关键,千万不要丢了,如果需要知道写入或读出操作有没有完成,需要通过aio_return这个函数来感知,如果操作未完成会返回-1
测试代码
func main() {
f, err := OpenFile("test.txt", syscall.O_CREAT|syscall.O_RDONLY, 0644)
if err != nil {
log.Fatalln(err)
}
defer f.Close()
var b []byte = make([]byte, 128)
status := f.Read(b)
fmt.Println(Verify(status))
time.Sleep(time.Second)
fmt.Println(Verify(status))
}