- 前言
- Go 与操作系统
- Go 内部机制
- Go 基本数据类型
- 4 组合类型的使用
- 5 数据结构
- 6 Go package 中不为人知的知识
- 7 反射和接口
- 8 Go UNIX 系统编程
- 08.1 关于 UNIX 进程
- 08.2 flag 包
- 8.2 flag 包
- 08.3 io.Reader 和 io.Writer 接口
- 08.4 bufio 包
- 08.5 读取文本文件
- 08.6 从文件中读取所需的数据量
- 08.7 为什么我们使用二进制格式
- 08.8 读取 CSV 文件
- 08.9 写入文件
- 08.10 从磁盘加载和保存数据
- 08.11 再看strings包
- 08.12 关于bytes包
- 08.13 文件权限
- 08.14 处理 Unix 信号
- 08.15 Unix 管道编程
- 08.16 遍历目录树
- 08.17 使用 ePBF
- 08.18 关于 syscall.PtraceRegs
- 08.19 跟踪系统调用
- 08.20 User ID 和 group ID
- 08.21 其他资源
- 08.22 练习
- 08.23 总结
- 9 并发 Goroutines、Channel 和 Pipeline
- 10 Go 并发-进阶讨论
- 11 代码测试、优化及分析
- 12 Go 网络编程基础
- 13 网络编程 - 构建服务器与客户端
08.19 跟踪系统调用
本节将介绍一种非常先进的技术,它使用syscall
包,并允许你监视在Go
程序中执行的系统调用。
本节不包含在我的《
Go
系统编程》书籍中,Packt
出版社,2017年
Go
程序名为traceSyscall.go
,并分为五部分。traceSyscall.go
第一部分代码如下:
package main
import (
"bufio"
"fmt"
"os"
"os/exec"
"strings"
"syscall"
)
var maxSyscalls = 0
const SYSCALLFILE = "SYSCALLS"
你将很快学习SYSCALLFILE
变量的作用。
traceSyscall.go
第二部分代码如下:
func main() {
var SYSTEMCALLS []string
f, err := os.Open(SYSCALLFILE)
defer f.Close()
if err != nil {
fmt.Println(err)
return
}
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
line = strings.Replace(line, " ", "", -1)
line = strings.Replace(line, "SYS_", "", -1)
temp := strings.ToLower(strings.Split(line, "=")[0])
SYSTEMCALLS = append(SYSTEMCALLS, temp)
maxSyscalls++
}
请注意,SYSCALLS
文件的信息取自syscall
包的文档,它将每个系统调用与一个数字相关联,该数字是系统调用的内部Go
表示形式。该文件主要用于打印被跟踪程序所使用的系统调用的名称。
SYSCALLS
文件的格式如下:
SYS_READ = 0
SYS_WRITE = 1
SYS_OPEN = 2
SYS_CLOSE = 3
SYS_STAT = 4
在读取文本文件后,程序创建名为SYSTEMCALLS
的切片来存储信息。
traceSyscall.go
第三部分代码如下:
COUNTER := make([]int, maxSyscalls)
var regs syscall.PtraceRegs
cmd := exec.Command(os.Args[1], os.Args[2:]...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.SysProcAttr = &syscall.SysProcAttr{Ptrace: true}
err = cmd.Start()
err = cmd.Wait()
if err != nil {
fmt.Println("Wait:", err)
}
pid := cmd.Process.Pid
fmt.Println("Process ID:", pid)
COUNTER
切片存储在被跟踪的程序中每个系统调用的次数。
traceSyscall.go
的第四部分代码如下:
before := true
forCount := 0
for {
if before {
err := syscall.PtraceGetRegs(pid, ®s)
if err != nil {
break
}
if regs.Orig_rax > uint64(maxSyscalls) {
fmt.Println("Unknown:", regs.Orig_rax)
return
}
COUNTER[regs.Orig_rax]++
forCount++
}
err = syscall.PtraceSyscall(pid, 0)
if err != nil {
fmt.Println("PtraceSyscall:", err)
return
}
_, err = syscall.Wait4(pid, nil, 0, nil)
if err != nil {
fmt.Println("Wait4:", err)
return
}
before = !before
}
syscall.PtraceSyscall()
函数的作用是:告诉Go
继续执行正在被跟踪的程序,但是当程序进入或退出系统调用时停止执行,这正是我们想要的!由于每个系统调用在被调用之前和完成其工作之后都会被跟踪,因此我们使用before
变量来计算每个系统调用仅一次。
traceSyscall.go
的最后一部分代码如下:
for i, x := range COUNTER {
if x != 0 {
fmt.Println(SYSTEMCALLS[i], "->", x)
}
}
fmt.Println("Total System Calls:", forCount)
}
在这一部分中,我们打印切片COUNTER
的内容。切片SYSTEMCALLS
用于在知道系统调用的Go
数字表示时,来查找系统调用的名称。
在macOS High Sierra
机器上执行traceSyscall.go
会创建如下的输出:
$ go run traceSyscall.go
# command-line-arguments
./traceSyscall.go:36:11: undefined: syscall.PtraceRegs
./traceSyscall.go:57:11: undefined: syscall.PtraceGetRegs
./traceSyscall.go:70:9: undefined: syscall.PtraceSyscall
同样,traceSyscall.go
程序不能在macOS
和Mac OS X
上运行。
在Debian Linux
机器上执行程序会创建如下的输出:
$ go run traceSyscall.go ls /tmp/
Wait: stop signal: trace/breakpoint trap
Process ID: 5657
go-build084836422 test.go upload_progress_cache
read -> 11
write -> 1
open -> 37
close -> 27
stat -> 1
fstat -> 25
mmap -> 39
mprotect -> 16
munmap -> 4
brk -> 3
rt_sigaction -> 2
rt_sigprocmask -> 1
ioctl -> 2
access -> 9
execve -> 1
getdents -> 2
getrlimit -> 1
statfs -> 2
arch_prctl -> 1
futex -> 1
set_tid_address -> 1
openat -> 1
set_robust_list -> 1
Total System Calls: 189
在程序结束时,traceSyscall.go
打印程序中调用每个系统调用的次数!traceSyscall.go
的正确性通过strace -c
程序的输出进行验证。
$ strace -c ls /tmp
test.go upload_progress_cache
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
0.00 0.000000 0 11 read
0.00 0.000000 0 1 write
0.00 0.000000 0 37 13 open
0.00 0.000000 0 27 close
0.00 0.000000 0 1 stat
0.00 0.000000 0 25 fstat
0.00 0.000000 0 39 mmap
0.00 0.000000 0 16 mprotect
0.00 0.000000 0 4 munmap
0.00 0.000000 0 3 brk
0.00 0.000000 0 2 rt_sigaction
0.00 0.000000 0 1 rt_sigprocmask
0.00 0.000000 0 2 ioctl
0.00 0.000000 0 9 9 access
0.00 0.000000 0 1 execve
0.00 0.000000 0 2 getdents
0.00 0.000000 0 1 getrlimit
0.00 0.000000 0 2 2 statfs
0.00 0.000000 0 1 arch_prctl
0.00 0.000000 0 1 futex
0.00 0.000000 0 1 set_tid_address
0.00 0.000000 0 1 openat
0.00 0.000000 0 1 set_robust_list
------ ----------- ----------- --------- --------- -------------
100.00 0.000000 189 24 total
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论