ITPub博客

首页 > 数据库 > 数据库开发技术 > 【GO入门到放弃】Golang标准库-syscall

【GO入门到放弃】Golang标准库-syscall

原创 数据库开发技术 作者:五分钟学SRE 时间:2023-02-21 21:16:03 0 删除 编辑

    

        syscall 是 Go 语言标准库中提供的一个用于调用底层操作系统 API 的包。它支持多种操作系统,包括 Linux、Windows、Darwin 等。我们可以使用 syscall 包来实现一些底层的系统功能,如进程管理、信号处理、文件操作等。

在开始介绍go sys call 库之前先介绍下Linux syscall的几个概念

Linux syscall(系统调用)

    Linux syscall(系统调用)是一种Linux内核提供的编程接口,允许应用程序直接请求操作系统核心的服务,例如读写文件、网络通信、进程管理等。。在Linux中,系统调用是应用程序与操作系统之间进行交互的主要方法之一,也是编写底层系统软件、优化性能和增强安全性的重要手段。

用户态与内核态

    在Linux操作系统中,内核是操作系统的核心部分,用于管理计算机硬件、进程调度、内存管理等。为了保证内核运行的稳定性和安全性,Linux采用了一种特殊的运行模式:用户态和内核态。

    用户态是指应用程序运行的环境,应用程序只能访问自己的内存空间和系统资源,不能直接访问操作系统内核,必须通过系统调用来请求内核执行操作。在用户态中,CPU访问内存的地址是虚拟地址,操作系统会将虚拟地址映射到物理地址上。

    内核态是指操作系统内核运行的环境,具有更高的权限和更广泛的访问权限。在内核态中,CPU访问内存的地址是物理地址,操作系统可以直接访问硬件资源。操作系统内核运行时处于内核态。

系统调用类型

    在Go语言中,syscall库支持的系统调用类型与Linux syscall(是一种Linux内核提供的编程接口,允许应用程序直接请求操作系统核心的服务)类似,该包中的每个函数都直接映射到相应的Linux系统调用。由于Go语言具有跨平台的优势,因此syscall包在各种平台上都可以使用。主要包括以下几类:

  1. 进程控制:创建、终止、等待进程、设置进程优先级等。

  2. 文件操作:打开、读取、写入文件等。

  3. 网络操作:建立、连接、断开网络连接等。

  4. 内存管理:申请、释放内存等。

  5. 时间管理:获取、设置系统时间等。

  6. 设备操作:读写设备、设置设备参数等。

我们写一些demo 来实际体验下这个包的应用

使用 syscall 包实现了以下功能:

syscall.Getpid():获取当前进程的进程 ID。syscall.Getuid():获取当前进程的用户 ID。syscall.Getgid():获取当前进程的组 ID。syscall.Getpagesize():获取系统的页大小。syscall.Open():打开一个文件。syscall.Close():关闭一个文件。syscall.Read():读取一个文件的内容。
syscall_demo.go

package main
import ( "fmt" "syscall")
func main() { // 获取进程 ID pid := syscall.Getpid() fmt.Println("Process ID:", pid)
// 获取进程用户 ID uid := syscall.Getuid() fmt.Println("User ID:", uid)
// 获取进程组 ID gid := syscall.Getgid() fmt.Println("Group ID:", gid)
// 获取系统页大小 pagesize := syscall.Getpagesize() fmt.Println("Page size:", pagesize)
// 打开一个文件 fd, err := syscall.Open("/etc/hosts", syscall.O_RDONLY, 0) if err != nil { fmt.Println("Failed to open file:", err) return } defer syscall.Close(fd)
// 读取文件内容 //声明并初始化一个 byte 类型的切片(slice),其长度为 1024 个字节。这个切片用于存储从文件中读取的内容。 buf := make([]byte, 1024) //使用 syscall 包中的 Read 函数从文件中读取数据,并将结果存储在 buf 变量中。Read 函数接受两个参数:文件描述符(fd)和一个 byte 类型的切片(buf),它返回读取的字节数(n)和一个可能出现的错误(err)。这里的 fd 是之前打开文件得到的文件描述符。 n, err := syscall.Read(fd, buf) if err != nil { fmt.Println("Failed to read file:", err) return } fmt.Printf("Read %d bytes from file:\n%s\n", n, buf[:n])}

执行结果

go run syscall_demo.gopid :  135442uid :  0gid :  0pagesize :  4096read 39 bytes from file :testtest1let us use syscallbingo!!!


在使用 syscall 包时,我们需要注意一些与底层操作系统相关的细节。例如,文件描述符(fd)在 Unix 系统中是一种重要的概念,我们需要使用 syscall.Open() 函数打开一个文件,并返回一个文件描述符。使用完文件描述符后,我们需要使用 syscall.Close() 函数将其关闭。在读取文件时,我们需要使用 syscall.Read() 函数,而不是标准库中的 io.Read() 函数。

同时,由于不同操作系统支持的功能不同,我们在使用 syscall 包时需要根据实际情况进行调用。例如,Windows 系统中可能不支持某些功能,或者实现方式与 Unix 系统中不同。


使用syscall 对系统调用的优化

使用syscall.Mmap()提高文件I/O性能

与Linux syscall类似,使用syscall.Mmap()可以将文件映射到进程的虚拟内存中,减少I/O操作次数,提高I/O性能

demo code

package main
import ("fmt""os""syscall")
func main() {// 打开文件file, err := os.OpenFile("syscall_mmap.txt", os.O_RDWR, 0666)if err != nil { panic(err)}defer file.Close()
// 将文件映射到进程的虚拟内存中//使用 syscall.Mmap() 函数将文件 "yscall_mmap.txt" 的前 1024 字节映射到进程的虚拟内存中,并设置映射的权限为 PROT_READ|PROT_WRITE,映射的方式为 MAP_SHARED,表示映射后的文件内容可以被多个进程共享。data, err := syscall.Mmap(int(file.Fd()), 0, 1024, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)if err != nil { panic(err)}//在函数结束前调用 syscall.Munmap() 函数释放映射后的内存。defer syscall.Munmap(data)
// 修改映射后的文件内容//修改映射后的文件内容,将 "hi go syscall mmap" 的字节序列复制到映射后的内存中。因为映射后的内存已经和文件关联,所以修改内存的内容也会修改文件的内容copy(data, []byte("hi go syscall mmap"))fmt.Println(string(data))}

运行

echo "hi  go syscall mmap" >syscall_mmap.txtecho "Learn SRE in five minutes , become an expert" >>syscall_mmap.txtgo run syscal_mmap.go

打印

hi go syscall mmappLearn SRE in five minutes , become an expert

常见的gin框架就使用到了syscall.Mmap()进行优化,Gin 框架使用了 x/net/trace 包来对 HTTP 请求进行跟踪,而在跟踪时需要将请求和响应的信息写入文件,因此 Gin 框架使用 syscall.Mmap() 将日志文件映射到进程的虚拟内存中,以提高日志文件的写入性能。

使用 syscall.Poll() 提高网络 I/O 性能

syscall.Poll() 函数可以用来等待文件描述符上的事件。在进行网络 I/O 时,我们可以使用 syscall.Poll() 函数来等待网络事件,从而避免了频繁的系统调用,提高了网络 I/O 的性能。

gnet 是一个基于 Reactor 模式的高性能网络库,它使用 syscall.Poll() 函数来等待网络事件,并使用 Goroutine 来处理网络事件。这种方式可以显著提高网络 I/O 的性能。

使用 syscall.Syscall() 调用系统调用

syscall.Syscall() 函数可以用来直接调用操作系统提供的系统调用。在进行一些底层操作时,我们可以使用 syscall.Syscall() 函数来直接调用系统调用,从而避免了一些高层的封装,提高了程序的性能。

containerd 是一个面向容器的运行时环境,它需要进行一些底层的操作,例如创建进程、挂载文件系统等。在进行这些操作时,containerd 使用 syscall.Syscall() 函数直接调用系统调用,从而避免了一些高层的封装,提高了程序的性能。

原文作者:五分钟学SRE;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2021-11-23

  • 博文量
    81
  • 访问量
    38726