操作系统交互
操作系统交互
包总览
| 包 | 核心功能 | 用途 |
|---|---|---|
os | 文件/环境变量/进程 | OS 基础(详见 01-IO与文件系统) |
os/exec | Cmd, Run, Start, Output | 执行外部命令 |
os/signal | Notify, 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 默认行为 |
|---|---|---|---|
| SIGINT | 2 | Ctrl+C | 退出程序 |
| SIGTERM | 15 | 终止请求 | 退出程序 |
| SIGKILL | 9 | 强制终止 | 退出(不可捕获) |
| SIGHUP | 1 | 终端断开 | 退出程序 |
| SIGUSR1 | 10 | 用户定义1 | 无 |
| SIGUSR2 | 12 | 用户定义2 | 无 |
| SIGPIPE | 13 | 管道断裂 | 退出(部分场景) |
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")
}