文章

操作系统交互

操作系统交互

包总览

核心功能用途
os文件/环境变量/进程OS 基础(详见 01-IO与文件系统
os/execCmd, Run, Start, Output执行外部命令
os/signalNotify, Stop信号处理
syscall底层系统调用系统级操作

os/exec 包

执行外部命令

import "os/exec"

// 方式1:Run(等待完成)
cmd := exec.Command("ls", "-la", "/data")
err := cmd.Run()

// 方式2:Start + Wait(异步)
cmd := exec.Command("sleep", "5")
cmd.Start()
// 做其他事...
err := cmd.Wait()

// 方式3:Output(获取 stdout)
out, err := exec.Command("date").Output()
fmt.Println(string(out))  // "Tue Aug 4 20:34:00 CST 2026"

// 方式4:CombinedOutput(stdout + stderr 合并)
out, err := exec.Command("ls", "/nonexistent").CombinedOutput()
// out 包含 stderr 的错误信息

捕获输出和错误

// 分别捕获 stdout 和 stderr
cmd := exec.Command("go", "build", "./...")
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr

err := cmd.Run()
fmt.Println("stdout:", stdout.String())
fmt.Println("stderr:", stderr.String())
fmt.Println("error:", err)

// 管道:将上一个命令的输出作为下一个的输入
cmd1 := exec.Command("cat", "file.txt")
cmd2 := exec.Command("grep", "error")

cmd2.Stdin, _ = cmd1.StdoutPipe()
cmd2.Stdout = os.Stdout

cmd2.Start()
cmd1.Run()
cmd2.Wait()

// 或用管道简写
cmd := exec.Command("sh", "-c", "cat file.txt | grep error")
out, _ := cmd.Output()

传递输入

// 从 stdin 读取
cmd := exec.Command("wc", "-l")
cmd.Stdin = strings.NewReader("line1\nline2\nline3\n")
out, _ := cmd.Output()
fmt.Println(string(out))  // "3"

// 交互式命令
cmd := exec.Command("mysql", "-u", "root", "-p")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Run()

设置环境变量和工作目录

cmd := exec.Command("go", "test", "./...")
cmd.Dir = "/path/to/project"      // 工作目录
cmd.Env = append(os.Environ(),    // 继承父进程环境变量
    "GOOS=linux",
    "GOARCH=amd64",
    "CGO_ENABLED=0",
)
out, err := cmd.Output()

进程信息

cmd := exec.Command("sleep", "10")
cmd.Start()

fmt.Println("PID:", cmd.Process.Pid)     // 进程 PID

// 发送信号
cmd.Process.Signal(syscall.SIGTERM)

// 杀死进程
cmd.Process.Kill()

// 等待退出
state, _ := cmd.Process.Wait()
fmt.Println("ExitCode:", state.ExitCode())
fmt.Println("SysTime:", state.SystemTime())
fmt.Println("UserTime:", state.UserTime())

进程组与超时

// 带超时的命令执行
func RunWithTimeout(cmd *exec.Cmd, timeout time.Duration) error {
    if err := cmd.Start(); err != nil {
        return err
    }

    done := make(chan error, 1)
    go func() {
        done <- cmd.Wait()
    }()

    select {
    case err := <-done:
        return err
    case <-time.After(timeout):
        cmd.Process.Kill()
        return fmt.Errorf("timeout after %v", timeout)
    }
}

// 使用 context 控制超时
func RunWithContext(ctx context.Context, name string, args ...string) ([]byte, error) {
    cmd := exec.CommandContext(ctx, name, args...)
    return cmd.Output()
}

// 当 ctx 被取消时,cmd 会被自动 Kill

os/signal 包

信号处理

import "os/signal"
import "syscall"

// 监听信号
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh,
    syscall.SIGINT,   // Ctrl+C
    syscall.SIGTERM,  // kill 命令
    syscall.SIGHUP,   // 挂起
    syscall.SIGUSR1,  // 用户自定义信号1
)

go func() {
    for sig := range sigCh {
        switch sig {
        case syscall.SIGINT, syscall.SIGTERM:
            log.Println("received shutdown signal, graceful shutdown...")
            // 清理逻辑
            os.Exit(0)
        case syscall.SIGHUP:
            log.Println("received SIGHUP, reloading config...")
            reloadConfig()
        case syscall.SIGUSR1:
            log.Println("received SIGUSR1, rotating log...")
            rotateLog()
        }
    }
}()

// 停止监听
// signal.Stop(sigCh)

常见信号

信号编号含义Go 默认行为
SIGINT2Ctrl+C退出程序
SIGTERM15终止请求退出程序
SIGKILL9强制终止退出(不可捕获)
SIGHUP1终端断开退出程序
SIGUSR110用户定义1
SIGUSR212用户定义2
SIGPIPE13管道断裂退出(部分场景)

SIGKILL 和 SIGSTOP 不可被捕获或忽略

优雅关闭模式

func main() {
    server := &http.Server{Addr: ":8080", Handler: mux}

    go func() {
        if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatal(err)
        }
    }()

    // 等待中断信号
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    sig := <-quit
    log.Printf("received signal: %v, shutting down...", sig)

    // 给 30 秒处理完现有请求
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()

    if err := server.Shutdown(ctx); err != nil {
        log.Printf("server shutdown error: %v", err)
    }

    // 关闭数据库连接等资源
    db.Close()
    redis.Close()

    log.Println("server exited")
}

syscall 包

syscall 包是底层系统调用封装,不同平台 API 不同。一般不需要直接使用,os 包已经封装了常用操作。

import "syscall"

// 文件操作
fd, err := syscall.Open("/path/to/file", syscall.O_RDONLY, 0)
syscall.Read(fd, buf)
syscall.Write(fd, data)
syscall.Close(fd)

// 获取文件信息
var stat syscall.Stat_t
syscall.Stat("/path/to/file", &stat)
fmt.Println(stat.Size)
fmt.Println(stat.Mode)
fmt.Println(stat.Uid)  // 文件所有者 UID

// 设置文件权限
syscall.Chmod("/path/to/file", 0644)
syscall.Chown("/path/to/file", uid, gid)

// 创建目录
syscall.Mkdir("/path/to/dir", 0755)

// 符号链接
syscall.Symlink("/target", "/link")

// 获取 hostname
hostname, _ := syscall.Hostname()

// 获取系统信息
var info syscall.Sysinfo_t
syscall.Sysinfo(&info)
fmt.Println("uptime:", info.Uptime)
fmt.Println("totalram:", info.Totalram)
fmt.Println("freeram:", info.Freeram)

Linux 特定操作

// 获取环境变量(底层)
env := syscall.Environ()

// 设置进程名
// Go 程序默认进程名为 os.Args[0]

// 获取 /proc 信息(Linux)
// 读取 /proc/self/status
data, _ := os.ReadFile("/proc/self/status")
// 解析 VmRSS、Threads 等

// 读取 /proc/self/cgroup
data, _ := os.ReadFile("/proc/self/cgroup")
// 在容器中获取 cgroup 信息

// inotify 文件监听
fd, _ := syscall.InotifyInit()
wd, _ := syscall.InotifyAddWatch(fd, "/path/to/watch",
    syscall.IN_MODIFY|syscall.IN_CREATE|syscall.IN_DELETE)

跨平台兼容

// 用 build tags 区分平台
// file_linux.go
//go:build linux
package main
import "syscall"
func getDiskUsage(path string) (uint64, uint64) {
    var stat syscall.Statfs_t
    syscall.Statfs(path, &stat)
    return stat.Bavail * uint64(stat.Bsize), stat.Blocks * uint64(stat.Bsize)
}

// file_windows.go
//go:build windows
package main
func getDiskUsage(path string) (uint64, uint64) {
    // Windows 实现
    return 0, 0
}

实用系统操作

获取系统信息

func SystemInfo() {
    hostname, _ := os.Hostname()
    fmt.Println("Hostname:", hostname)

    fmt.Println("OS:", runtime.GOOS)
    fmt.Println("Arch:", runtime.GOARCH)
    fmt.Println("CPUs:", runtime.NumCPU())
    fmt.Println("Go Version:", runtime.Version())
    fmt.Println("PID:", os.Getpid())
    fmt.Println("PPID:", os.Getppid())
    fmt.Println("WorkDir:", func() string {
        wd, _ := os.Getwd()
        return wd
    }())
}

执行 Shell 命令并实时输出

func RunCommand(name string, args ...string) error {
    cmd := exec.Command(name, args...)
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    cmd.Stdin = os.Stdin
    return cmd.Run()
}

// 实时读取输出行
func RunCommandStream(name string, args ...string) {
    cmd := exec.Command(name, args...)
    stdout, _ := cmd.StdoutPipe()
    cmd.Start()

    scanner := bufio.NewScanner(stdout)
    for scanner.Scan() {
        line := scanner.Text()
        fmt.Println("[cmd]", line)  // 实时输出
    }
    cmd.Wait()
}

检查命令是否存在

func CommandExists(cmd string) bool {
    _, err := exec.LookPath(cmd)
    return err == nil
}

// 使用
if !CommandExists("docker") {
    log.Fatal("docker not found in PATH")
}