4.4.3 系统调用详解

4.4.3 系统调用详解 #

系统调用是用户程序与操作系统内核交互的接口。Go 语言通过 syscall 包提供了对系统调用的直接访问能力。本节将深入探讨系统调用的原理、使用方法以及性能优化技巧。

系统调用基础 #

系统调用的概念 #

系统调用是操作系统提供给应用程序的编程接口,允许程序请求内核服务。主要包括:

  1. 进程控制:创建、终止、等待进程
  2. 文件操作:打开、读取、写入、关闭文件
  3. 设备管理:请求设备、释放设备、读写设备
  4. 信息维护:获取系统信息、设置时间
  5. 通信:创建连接、发送消息

系统调用的工作原理 #

系统调用的执行过程:

  1. 用户态调用:应用程序调用系统调用接口
  2. 模式切换:从用户态切换到内核态
  3. 内核处理:内核执行相应的系统服务
  4. 返回结果:将结果返回给用户程序
  5. 模式恢复:从内核态切换回用户态

Go 中的系统调用 #

syscall 包基础 #

Go 的 syscall 包提供了对系统调用的底层访问:

package main

import (
    "fmt"
    "syscall"
    "unsafe"
    "log"
)

func basicSyscallExample() {
    // 使用 syscall.Syscall 进行系统调用
    // 获取进程 ID
    pid, _, _ := syscall.Syscall(syscall.SYS_GETPID, 0, 0, 0)
    fmt.Printf("进程 ID: %d\n", pid)

    // 获取用户 ID
    uid, _, _ := syscall.Syscall(syscall.SYS_GETUID, 0, 0, 0)
    fmt.Printf("用户 ID: %d\n", uid)

    // 获取组 ID
    gid, _, _ := syscall.Syscall(syscall.SYS_GETGID, 0, 0, 0)
    fmt.Printf("组 ID: %d\n", gid)
}

func fileOperationSyscall() {
    filename := "/tmp/syscall_test.txt"
    content := "Hello, System Call!"

    // 创建文件 (open 系统调用)
    fd, _, errno := syscall.Syscall(syscall.SYS_OPEN,
        uintptr(unsafe.Pointer(syscall.StringBytePtr(filename))),
        uintptr(syscall.O_CREAT|syscall.O_WRONLY|syscall.O_TRUNC),
        uintptr(0644))

    if errno != 0 {
        log.Fatalf("创建文件失败: %v", errno)
    }

    fmt.Printf("文件描述符: %d\n", fd)

    // 写入文件 (write 系统调用)
    data := []byte(content)
    n, _, errno := syscall.Syscall(syscall.SYS_WRITE,
        fd,
        uintptr(unsafe.Pointer(&data[0])),
        uintptr(len(data)))

    if errno != 0 {
        log.Fatalf("写入文件失败: %v", errno)
    }

    fmt.Printf("写入字节数: %d\n", n)

    // 关闭文件 (close 系统调用)
    _, _, errno = syscall.Syscall(syscall.SYS_CLOSE, fd, 0, 0)
    if errno != 0 {
        log.Fatalf("关闭文件失败: %v", errno)
    }

    fmt.Println("文件操作完成")
}

func main() {
    fmt.Println("=== 基础系统调用示例 ===")
    basicSyscallExample()

    fmt.Println("\n=== 文件操作系统调用示例 ===")
    fileOperationSyscall()
}

高级系统调用封装 #

为了更方便地使用系统调用,我们可以创建封装函数:

package main

import (
    "fmt"
    "syscall"
    "unsafe"
    "log"
    "time"
)

// 系统信息结构
type SystemInfo struct {
    Uptime    int64
    LoadAvg   [3]uint64
    TotalRAM  uint64
    FreeRAM   uint64
    SharedRAM uint64
    BufferRAM uint64
    TotalSwap uint64
    FreeSwap  uint64
    Procs     uint16
}

// 获取系统信息
func getSysInfo() (*SystemInfo, error) {
    var info SystemInfo

    _, _, errno := syscall.Syscall(syscall.SYS_SYSINFO,
        uintptr(unsafe.Pointer(&info)), 0, 0)

    if errno != 0 {
        return nil, fmt.Errorf("获取系统信息失败: %v", errno)
    }

    return &info, nil
}

// 内存映射文件
func mmapFile(filename string) ([]byte, error) {
    // 打开文件
    fd, err := syscall.Open(filename, syscall.O_RDONLY, 0)
    if err != nil {
        return nil, err
    }
    defer syscall.Close(fd)

    // 获取文件大小
    var stat syscall.Stat_t
    err = syscall.Fstat(fd, &stat)
    if err != nil {
        return nil, err
    }

    size := int(stat.Size)

    // 内存映射
    data, err := syscall.Mmap(fd, 0, size, syscall.PROT_READ, syscall.MAP_SHARED)
    if err != nil {
        return nil, err
    }

    return data, nil
}

// 解除内存映射
func munmapFile(data []byte) error {
    return syscall.Munmap(data)
}

// 创建目录
func createDirectory(path string, mode uint32) error {
    _, _, errno := syscall.Syscall(syscall.SYS_MKDIR,
        uintptr(unsafe.Pointer(syscall.StringBytePtr(path))),
        uintptr(mode), 0)

    if errno != 0 {
        return fmt.Errorf("创建目录失败: %v", errno)
    }

    return nil
}

// 删除目录
func removeDirectory(path string) error {
    _, _, errno := syscall.Syscall(syscall.SYS_RMDIR,
        uintptr(unsafe.Pointer(syscall.StringBytePtr(path))), 0, 0)

    if errno != 0 {
        return fmt.Errorf("删除目录失败: %v", errno)
    }

    return nil
}

func systemInfoExample() {
    info, err := getSysInfo()
    if err != nil {
        log.Printf("获取系统信息失败: %v", err)
        return
    }

    fmt.Printf("系统运行时间: %d 秒\n", info.Uptime)
    fmt.Printf("总内存: %d MB\n", info.TotalRAM/1024/1024)
    fmt.Printf("可用内存: %d MB\n", info.FreeRAM/1024/1024)
    fmt.Printf("进程数: %d\n", info.Procs)
}

func mmapExample() {
    // 创建测试文件
    filename := "/tmp/mmap_test.txt"
    content := "This is a test file for memory mapping example."

    err := syscall.WriteFile(filename, []byte(content), 0644)
    if err != nil {
        log.Printf("创建测试文件失败: %v", err)
        return
    }
    defer syscall.Unlink(filename)

    // 内存映射文件
    data, err := mmapFile(filename)
    if err != nil {
        log.Printf("内存映射失败: %v", err)
        return
    }
    defer munmapFile(data)

    fmt.Printf("映射的文件内容: %s\n", string(data))
}

func directoryExample() {
    dirPath := "/tmp/syscall_test_dir"

    // 创建目录
    err := createDirectory(dirPath, 0755)
    if err != nil {
        log.Printf("创建目录失败: %v", err)
        return
    }

    fmt.Printf("目录创建成功: %s\n", dirPath)

    // 等待一下
    time.Sleep(1 * time.Second)

    // 删除目录
    err = removeDirectory(dirPath)
    if err != nil {
        log.Printf("删除目录失败: %v", err)
        return
    }

    fmt.Printf("目录删除成功: %s\n", dirPath)
}

func main() {
    fmt.Println("=== 系统信息示例 ===")
    systemInfoExample()

    fmt.Println("\n=== 内存映射示例 ===")
    mmapExample()

    fmt.Println("\n=== 目录操作示例 ===")
    directoryExample()
}

网络系统调用 #

套接字操作 #

直接使用系统调用进行网络编程:

package main

import (
    "fmt"
    "syscall"
    "unsafe"
    "log"
    "net"
)

// 创建 TCP 套接字
func createTCPSocket() (int, error) {
    fd, _, errno := syscall.Syscall(syscall.SYS_SOCKET,
        syscall.AF_INET, syscall.SOCK_STREAM, 0)

    if errno != 0 {
        return -1, fmt.Errorf("创建套接字失败: %v", errno)
    }

    return int(fd), nil
}

// 绑定地址
func bindSocket(fd int, addr *syscall.SockaddrInet4) error {
    _, _, errno := syscall.Syscall(syscall.SYS_BIND,
        uintptr(fd),
        uintptr(unsafe.Pointer(addr)),
        unsafe.Sizeof(*addr))

    if errno != 0 {
        return fmt.Errorf("绑定地址失败: %v", errno)
    }

    return nil
}

// 监听连接
func listenSocket(fd int, backlog int) error {
    _, _, errno := syscall.Syscall(syscall.SYS_LISTEN,
        uintptr(fd), uintptr(backlog), 0)

    if errno != 0 {
        return fmt.Errorf("监听失败: %v", errno)
    }

    return nil
}

// 接受连接
func acceptConnection(fd int) (int, *syscall.SockaddrInet4, error) {
    var addr syscall.SockaddrInet4
    addrLen := uint32(unsafe.Sizeof(addr))

    clientFd, _, errno := syscall.Syscall(syscall.SYS_ACCEPT,
        uintptr(fd),
        uintptr(unsafe.Pointer(&addr)),
        uintptr(unsafe.Pointer(&addrLen)))

    if errno != 0 {
        return -1, nil, fmt.Errorf("接受连接失败: %v", errno)
    }

    return int(clientFd), &addr, nil
}

// 简单的 TCP 服务器
func tcpServerExample() {
    // 创建套接字
    serverFd, err := createTCPSocket()
    if err != nil {
        log.Fatal(err)
    }
    defer syscall.Close(serverFd)

    // 设置地址重用
    err = syscall.SetsockoptInt(serverFd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
    if err != nil {
        log.Fatal(err)
    }

    // 绑定地址
    addr := &syscall.SockaddrInet4{
        Port: 8080,
        Addr: [4]byte{127, 0, 0, 1},
    }

    err = bindSocket(serverFd, addr)
    if err != nil {
        log.Fatal(err)
    }

    // 监听
    err = listenSocket(serverFd, 10)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("TCP 服务器启动,监听端口 8080")

    // 接受连接
    for i := 0; i < 3; i++ { // 只处理 3 个连接作为示例
        clientFd, clientAddr, err := acceptConnection(serverFd)
        if err != nil {
            log.Printf("接受连接失败: %v", err)
            continue
        }

        fmt.Printf("接受连接: %v\n", clientAddr)

        // 处理客户端
        go func(fd int) {
            defer syscall.Close(fd)

            // 读取数据
            buffer := make([]byte, 1024)
            n, err := syscall.Read(fd, buffer)
            if err != nil {
                log.Printf("读取数据失败: %v", err)
                return
            }

            fmt.Printf("接收到数据: %s\n", string(buffer[:n]))

            // 发送响应
            response := "HTTP/1.1 200 OK\r\nContent-Length: 13\r\n\r\nHello, World!"
            _, err = syscall.Write(fd, []byte(response))
            if err != nil {
                log.Printf("发送响应失败: %v", err)
            }
        }(clientFd)
    }
}

func main() {
    tcpServerExample()
}

UDP 套接字操作 #

package main

import (
    "fmt"
    "syscall"
    "unsafe"
    "log"
    "time"
)

// 创建 UDP 套接字
func createUDPSocket() (int, error) {
    fd, _, errno := syscall.Syscall(syscall.SYS_SOCKET,
        syscall.AF_INET, syscall.SOCK_DGRAM, 0)

    if errno != 0 {
        return -1, fmt.Errorf("创建 UDP 套接字失败: %v", errno)
    }

    return int(fd), nil
}

// 发送 UDP 数据
func sendUDPData(fd int, data []byte, addr *syscall.SockaddrInet4) error {
    _, _, errno := syscall.Syscall6(syscall.SYS_SENDTO,
        uintptr(fd),
        uintptr(unsafe.Pointer(&data[0])),
        uintptr(len(data)),
        0,
        uintptr(unsafe.Pointer(addr)),
        unsafe.Sizeof(*addr))

    if errno != 0 {
        return fmt.Errorf("发送 UDP 数据失败: %v", errno)
    }

    return nil
}

// 接收 UDP 数据
func receiveUDPData(fd int, buffer []byte) (int, *syscall.SockaddrInet4, error) {
    var addr syscall.SockaddrInet4
    addrLen := uint32(unsafe.Sizeof(addr))

    n, _, errno := syscall.Syscall6(syscall.SYS_RECVFROM,
        uintptr(fd),
        uintptr(unsafe.Pointer(&buffer[0])),
        uintptr(len(buffer)),
        0,
        uintptr(unsafe.Pointer(&addr)),
        uintptr(unsafe.Pointer(&addrLen)))

    if errno != 0 {
        return 0, nil, fmt.Errorf("接收 UDP 数据失败: %v", errno)
    }

    return int(n), &addr, nil
}

// UDP 服务器
func udpServer() {
    fd, err := createUDPSocket()
    if err != nil {
        log.Fatal(err)
    }
    defer syscall.Close(fd)

    // 绑定地址
    serverAddr := &syscall.SockaddrInet4{
        Port: 8081,
        Addr: [4]byte{127, 0, 0, 1},
    }

    err = bindSocket(fd, serverAddr)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("UDP 服务器启动,监听端口 8081")

    buffer := make([]byte, 1024)
    for i := 0; i < 3; i++ { // 处理 3 个消息作为示例
        n, clientAddr, err := receiveUDPData(fd, buffer)
        if err != nil {
            log.Printf("接收数据失败: %v", err)
            continue
        }

        fmt.Printf("接收到来自 %v 的数据: %s\n", clientAddr, string(buffer[:n]))

        // 发送响应
        response := fmt.Sprintf("Echo: %s", string(buffer[:n]))
        err = sendUDPData(fd, []byte(response), clientAddr)
        if err != nil {
            log.Printf("发送响应失败: %v", err)
        }
    }
}

// UDP 客户端
func udpClient() {
    fd, err := createUDPSocket()
    if err != nil {
        log.Fatal(err)
    }
    defer syscall.Close(fd)

    serverAddr := &syscall.SockaddrInet4{
        Port: 8081,
        Addr: [4]byte{127, 0, 0, 1},
    }

    messages := []string{"Hello", "World", "UDP"}

    for _, msg := range messages {
        // 发送消息
        err = sendUDPData(fd, []byte(msg), serverAddr)
        if err != nil {
            log.Printf("发送消息失败: %v", err)
            continue
        }

        fmt.Printf("发送消息: %s\n", msg)

        // 接收响应
        buffer := make([]byte, 1024)
        n, _, err := receiveUDPData(fd, buffer)
        if err != nil {
            log.Printf("接收响应失败: %v", err)
            continue
        }

        fmt.Printf("接收响应: %s\n", string(buffer[:n]))
        time.Sleep(1 * time.Second)
    }
}

func main() {
    // 启动服务器
    go udpServer()

    // 等待服务器启动
    time.Sleep(1 * time.Second)

    // 启动客户端
    udpClient()

    // 等待完成
    time.Sleep(1 * time.Second)
}

系统调用性能优化 #

批量系统调用 #

减少系统调用次数可以提高性能:

package main

import (
    "fmt"
    "syscall"
    "time"
    "log"
)

// 单次写入测试
func singleWriteTest(filename string, data []byte, iterations int) time.Duration {
    fd, err := syscall.Open(filename, syscall.O_CREAT|syscall.O_WRONLY|syscall.O_TRUNC, 0644)
    if err != nil {
        log.Fatal(err)
    }
    defer syscall.Close(fd)

    start := time.Now()

    for i := 0; i < iterations; i++ {
        _, err := syscall.Write(fd, data)
        if err != nil {
            log.Fatal(err)
        }
    }

    return time.Since(start)
}

// 批量写入测试
func batchWriteTest(filename string, data []byte, iterations int) time.Duration {
    fd, err := syscall.Open(filename, syscall.O_CREAT|syscall.O_WRONLY|syscall.O_TRUNC, 0644)
    if err != nil {
        log.Fatal(err)
    }
    defer syscall.Close(fd)

    // 创建批量数据
    batchData := make([]byte, len(data)*iterations)
    for i := 0; i < iterations; i++ {
        copy(batchData[i*len(data):], data)
    }

    start := time.Now()

    _, err = syscall.Write(fd, batchData)
    if err != nil {
        log.Fatal(err)
    }

    return time.Since(start)
}

// 使用 writev 进行向量化写入
func vectorWriteTest(filename string, data []byte, iterations int) time.Duration {
    fd, err := syscall.Open(filename, syscall.O_CREAT|syscall.O_WRONLY|syscall.O_TRUNC, 0644)
    if err != nil {
        log.Fatal(err)
    }
    defer syscall.Close(fd)

    // 创建 iovec 结构
    iovecs := make([]syscall.Iovec, iterations)
    for i := 0; i < iterations; i++ {
        iovecs[i] = syscall.Iovec{
            Base: &data[0],
            Len:  uint64(len(data)),
        }
    }

    start := time.Now()

    _, err = syscall.Writev(fd, iovecs)
    if err != nil {
        log.Fatal(err)
    }

    return time.Since(start)
}

func performanceTest() {
    data := []byte("Hello, World!\n")
    iterations := 10000

    // 单次写入测试
    singleTime := singleWriteTest("/tmp/single_write.txt", data, iterations)
    fmt.Printf("单次写入 %d 次耗时: %v\n", iterations, singleTime)

    // 批量写入测试
    batchTime := batchWriteTest("/tmp/batch_write.txt", data, iterations)
    fmt.Printf("批量写入耗时: %v\n", batchTime)

    // 向量化写入测试
    vectorTime := vectorWriteTest("/tmp/vector_write.txt", data, iterations)
    fmt.Printf("向量化写入耗时: %v\n", vectorTime)

    fmt.Printf("批量写入性能提升: %.2fx\n", float64(singleTime)/float64(batchTime))
    fmt.Printf("向量化写入性能提升: %.2fx\n", float64(singleTime)/float64(vectorTime))

    // 清理文件
    syscall.Unlink("/tmp/single_write.txt")
    syscall.Unlink("/tmp/batch_write.txt")
    syscall.Unlink("/tmp/vector_write.txt")
}

func main() {
    fmt.Println("=== 系统调用性能测试 ===")
    performanceTest()
}

异步 I/O 模拟 #

虽然 Go 标准库没有直接支持 Linux 的 io_uring,但我们可以模拟异步 I/O:

package main

import (
    "fmt"
    "syscall"
    "time"
    "sync"
    "log"
)

// 异步 I/O 请求
type AsyncIORequest struct {
    Operation string
    Filename  string
    Data      []byte
    Offset    int64
    Callback  func([]byte, error)
}

// 异步 I/O 管理器
type AsyncIOManager struct {
    requests chan AsyncIORequest
    workers  int
    wg       sync.WaitGroup
}

// 创建异步 I/O 管理器
func NewAsyncIOManager(workers int) *AsyncIOManager {
    return &AsyncIOManager{
        requests: make(chan AsyncIORequest, 100),
        workers:  workers,
    }
}

// 启动工作协程
func (aio *AsyncIOManager) Start() {
    for i := 0; i < aio.workers; i++ {
        aio.wg.Add(1)
        go aio.worker(i)
    }
}

// 工作协程
func (aio *AsyncIOManager) worker(id int) {
    defer aio.wg.Done()

    fmt.Printf("异步 I/O 工作者 %d 启动\n", id)

    for req := range aio.requests {
        switch req.Operation {
        case "read":
            aio.handleRead(req)
        case "write":
            aio.handleWrite(req)
        default:
            req.Callback(nil, fmt.Errorf("不支持的操作: %s", req.Operation))
        }
    }

    fmt.Printf("异步 I/O 工作者 %d 退出\n", id)
}

// 处理读取请求
func (aio *AsyncIOManager) handleRead(req AsyncIORequest) {
    fd, err := syscall.Open(req.Filename, syscall.O_RDONLY, 0)
    if err != nil {
        req.Callback(nil, err)
        return
    }
    defer syscall.Close(fd)

    // 定位到指定偏移
    if req.Offset > 0 {
        _, err = syscall.Seek(fd, req.Offset, 0)
        if err != nil {
            req.Callback(nil, err)
            return
        }
    }

    // 读取数据
    buffer := make([]byte, 1024)
    n, err := syscall.Read(fd, buffer)
    if err != nil {
        req.Callback(nil, err)
        return
    }

    req.Callback(buffer[:n], nil)
}

// 处理写入请求
func (aio *AsyncIOManager) handleWrite(req AsyncIORequest) {
    fd, err := syscall.Open(req.Filename, syscall.O_CREAT|syscall.O_WRONLY, 0644)
    if err != nil {
        req.Callback(nil, err)
        return
    }
    defer syscall.Close(fd)

    // 定位到指定偏移
    if req.Offset > 0 {
        _, err = syscall.Seek(fd, req.Offset, 0)
        if err != nil {
            req.Callback(nil, err)
            return
        }
    }

    // 写入数据
    _, err = syscall.Write(fd, req.Data)
    req.Callback(nil, err)
}

// 提交异步读取请求
func (aio *AsyncIOManager) AsyncRead(filename string, offset int64, callback func([]byte, error)) {
    req := AsyncIORequest{
        Operation: "read",
        Filename:  filename,
        Offset:    offset,
        Callback:  callback,
    }

    aio.requests <- req
}

// 提交异步写入请求
func (aio *AsyncIOManager) AsyncWrite(filename string, data []byte, offset int64, callback func([]byte, error)) {
    req := AsyncIORequest{
        Operation: "write",
        Filename:  filename,
        Data:      data,
        Offset:    offset,
        Callback:  callback,
    }

    aio.requests <- req
}

// 关闭管理器
func (aio *AsyncIOManager) Close() {
    close(aio.requests)
    aio.wg.Wait()
}

func asyncIOExample() {
    // 创建异步 I/O 管理器
    aio := NewAsyncIOManager(3)
    aio.Start()
    defer aio.Close()

    filename := "/tmp/async_io_test.txt"

    // 异步写入
    var writeWg sync.WaitGroup
    writeWg.Add(3)

    for i := 0; i < 3; i++ {
        data := fmt.Sprintf("异步写入数据 %d\n", i)
        aio.AsyncWrite(filename, []byte(data), int64(i*20), func(result []byte, err error) {
            defer writeWg.Done()
            if err != nil {
                log.Printf("异步写入失败: %v", err)
            } else {
                fmt.Printf("异步写入完成\n")
            }
        })
    }

    writeWg.Wait()
    fmt.Println("所有写入操作完成")

    // 异步读取
    var readWg sync.WaitGroup
    readWg.Add(1)

    aio.AsyncRead(filename, 0, func(data []byte, err error) {
        defer readWg.Done()
        if err != nil {
            log.Printf("异步读取失败: %v", err)
        } else {
            fmt.Printf("异步读取结果: %s", string(data))
        }
    })

    readWg.Wait()

    // 清理文件
    syscall.Unlink(filename)
}

func main() {
    fmt.Println("=== 异步 I/O 示例 ===")
    asyncIOExample()
}

实践练习 #

练习 1:系统监控工具 #

使用系统调用创建一个系统监控工具:

package main

import (
    "fmt"
    "syscall"
    "unsafe"
    "time"
    "log"
    "strings"
    "strconv"
)

// 系统统计信息
type SystemStats struct {
    CPUUsage    float64
    MemoryUsage float64
    DiskUsage   float64
    NetworkIO   NetworkStats
}

// 网络统计信息
type NetworkStats struct {
    BytesReceived uint64
    BytesSent     uint64
}

// 系统监控器
type SystemMonitor struct {
    interval time.Duration
}

// 创建系统监控器
func NewSystemMonitor(interval time.Duration) *SystemMonitor {
    return &SystemMonitor{
        interval: interval,
    }
}

// 获取 CPU 使用率
func (sm *SystemMonitor) getCPUUsage() (float64, error) {
    // 读取 /proc/stat 文件
    fd, err := syscall.Open("/proc/stat", syscall.O_RDONLY, 0)
    if err != nil {
        return 0, err
    }
    defer syscall.Close(fd)

    buffer := make([]byte, 1024)
    n, err := syscall.Read(fd, buffer)
    if err != nil {
        return 0, err
    }

    content := string(buffer[:n])
    lines := strings.Split(content, "\n")
    if len(lines) == 0 {
        return 0, fmt.Errorf("无法解析 CPU 信息")
    }

    // 解析第一行 CPU 总体信息
    fields := strings.Fields(lines[0])
    if len(fields) < 8 {
        return 0, fmt.Errorf("CPU 信息格式错误")
    }

    var total, idle uint64
    for i := 1; i < len(fields); i++ {
        val, _ := strconv.ParseUint(fields[i], 10, 64)
        total += val
        if i == 4 { // idle time
            idle = val
        }
    }

    if total == 0 {
        return 0, nil
    }

    return float64(total-idle) / float64(total) * 100, nil
}

// 获取内存使用率
func (sm *SystemMonitor) getMemoryUsage() (float64, error) {
    fd, err := syscall.Open("/proc/meminfo", syscall.O_RDONLY, 0)
    if err != nil {
        return 0, err
    }
    defer syscall.Close(fd)

    buffer := make([]byte, 2048)
    n, err := syscall.Read(fd, buffer)
    if err != nil {
        return 0, err
    }

    content := string(buffer[:n])
    lines := strings.Split(content, "\n")

    var memTotal, memFree, memAvailable uint64

    for _, line := range lines {
        fields := strings.Fields(line)
        if len(fields) < 2 {
            continue
        }

        switch fields[0] {
        case "MemTotal:":
            memTotal, _ = strconv.ParseUint(fields[1], 10, 64)
        case "MemFree:":
            memFree, _ = strconv.ParseUint(fields[1], 10, 64)
        case "MemAvailable:":
            memAvailable, _ = strconv.ParseUint(fields[1], 10, 64)
        }
    }

    if memTotal == 0 {
        return 0, fmt.Errorf("无法获取内存总量")
    }

    used := memTotal - memAvailable
    return float64(used) / float64(memTotal) * 100, nil
}

// 获取磁盘使用率
func (sm *SystemMonitor) getDiskUsage(path string) (float64, error) {
    var stat syscall.Statfs_t
    err := syscall.Statfs(path, &stat)
    if err != nil {
        return 0, err
    }

    total := stat.Blocks * uint64(stat.Bsize)
    free := stat.Bavail * uint64(stat.Bsize)
    used := total - free

    if total == 0 {
        return 0, nil
    }

    return float64(used) / float64(total) * 100, nil
}

// 获取网络统计信息
func (sm *SystemMonitor) getNetworkStats() (NetworkStats, error) {
    fd, err := syscall.Open("/proc/net/dev", syscall.O_RDONLY, 0)
    if err != nil {
        return NetworkStats{}, err
    }
    defer syscall.Close(fd)

    buffer := make([]byte, 4096)
    n, err := syscall.Read(fd, buffer)
    if err != nil {
        return NetworkStats{}, err
    }

    content := string(buffer[:n])
    lines := strings.Split(content, "\n")

    var totalRx, totalTx uint64

    for _, line := range lines[2:] { // 跳过头部两行
        if strings.TrimSpace(line) == "" {
            continue
        }

        fields := strings.Fields(line)
        if len(fields) < 10 {
            continue
        }

        // 跳过 lo 接口
        if strings.HasPrefix(fields[0], "lo:") {
            continue
        }

        rx, _ := strconv.ParseUint(fields[1], 10, 64)
        tx, _ := strconv.ParseUint(fields[9], 10, 64)

        totalRx += rx
        totalTx += tx
    }

    return NetworkStats{
        BytesReceived: totalRx,
        BytesSent:     totalTx,
    }, nil
}

// 获取系统统计信息
func (sm *SystemMonitor) GetStats() (*SystemStats, error) {
    stats := &SystemStats{}

    // 获取 CPU 使用率
    cpu, err := sm.getCPUUsage()
    if err != nil {
        log.Printf("获取 CPU 使用率失败: %v", err)
    } else {
        stats.CPUUsage = cpu
    }

    // 获取内存使用率
    mem, err := sm.getMemoryUsage()
    if err != nil {
        log.Printf("获取内存使用率失败: %v", err)
    } else {
        stats.MemoryUsage = mem
    }

    // 获取磁盘使用率
    disk, err := sm.getDiskUsage("/")
    if err != nil {
        log.Printf("获取磁盘使用率失败: %v", err)
    } else {
        stats.DiskUsage = disk
    }

    // 获取网络统计信息
    network, err := sm.getNetworkStats()
    if err != nil {
        log.Printf("获取网络统计信息失败: %v", err)
    } else {
        stats.NetworkIO = network
    }

    return stats, nil
}

// 启动监控
func (sm *SystemMonitor) Start() {
    fmt.Println("系统监控器启动")

    for {
        stats, err := sm.GetStats()
        if err != nil {
            log.Printf("获取系统统计信息失败: %v", err)
            continue
        }

        fmt.Printf("=== 系统状态 ===\n")
        fmt.Printf("CPU 使用率: %.2f%%\n", stats.CPUUsage)
        fmt.Printf("内存使用率: %.2f%%\n", stats.MemoryUsage)
        fmt.Printf("磁盘使用率: %.2f%%\n", stats.DiskUsage)
        fmt.Printf("网络接收: %d 字节\n", stats.NetworkIO.BytesReceived)
        fmt.Printf("网络发送: %d 字节\n", stats.NetworkIO.BytesSent)
        fmt.Println()

        time.Sleep(sm.interval)
    }
}

func main() {
    monitor := NewSystemMonitor(3 * time.Second)
    monitor.Start()
}

总结 #

本节深入介绍了 Go 语言中系统调用的使用方法:

  1. 系统调用基础:了解了系统调用的概念和工作原理
  2. syscall 包使用:学会了使用 Go 的 syscall 包进行底层系统调用
  3. 网络系统调用:掌握了使用系统调用进行网络编程
  4. 性能优化:学习了批量操作和异步 I/O 的优化技巧
  5. 实践应用:构建了完整的系统监控工具

这些知识为深入理解操作系统和进行系统级编程提供了坚实的基础。在下一节中,我们将学习共享内存和信号量的使用方法。