Golang 异步(bsd/linux)io

发布于:2024-03-21 ⋅ 阅读:(97) ⋅ 点赞:(0)

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))
}
本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到