Golang標(biāo)準(zhǔn)庫(kù)syscall詳解(什么是系統(tǒng)調(diào)用)
一、什么是系統(tǒng)調(diào)用
In computing, a system call is the programmatic way in which a computer program requests a service from the kernel of the operating system it is executed on. This may include hardware-related services (for example, accessing a hard disk drive), creation and execution of new processes, and communication with integral kernel services such as process scheduling. System calls provide an essential interface between a process and the operating system.
系統(tǒng)調(diào)用是程序向操作系統(tǒng)內(nèi)核請(qǐng)求服務(wù)的過(guò)程,通常包含硬件相關(guān)的服務(wù)(例如訪問(wèn)硬盤(pán)),創(chuàng)建新進(jìn)程等。系統(tǒng)調(diào)用提供了一個(gè)進(jìn)程和操作系統(tǒng)之間的接口。
二、Golang標(biāo)準(zhǔn)庫(kù)-syscall
syscall包包含一個(gè)指向底層操作系統(tǒng)原語(yǔ)的接口。
注意:該軟件包已被鎖定。標(biāo)準(zhǔn)以外的代碼應(yīng)該被遷移到golang.org/x/sys存儲(chǔ)庫(kù)中使用相應(yīng)的軟件包。這也是應(yīng)用新系統(tǒng)或版本所需更新的地方。 Signal , Errno 和 SysProcAttr 在 golang.org/x/sys 中尚不可用,并且仍然必須從 syscall 程序包中引用。有關(guān)更多信息,請(qǐng)參見(jiàn) https://golang.org/s/go1.4-syscall。
https://pkg.go.dev/golang.org/x/sys
該存儲(chǔ)庫(kù)包含用于與操作系統(tǒng)進(jìn)行低級(jí)交互的補(bǔ)充Go軟件包。
1. syscall無(wú)處不在
舉個(gè)最常用的例子, fmt.Println(“hello world”), 這里就用到了系統(tǒng)調(diào)用 write, 我們翻一下源碼。
func Println(a ...interface{}) (n int, err error) {
return Fprintln(os.Stdout, a...)
}
Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
func (f *File) write(b []byte) (n int, err error) {
if len(b) == 0 {
return 0, nil
}
// 實(shí)際的write方法,就是調(diào)用syscall.Write()
return fixCount(syscall.Write(f.fd, b))
}
2. syscall demo舉例:
go版本的strace Strace
strace 是用于查看進(jìn)程系統(tǒng)調(diào)用的工具, 一般使用方法如下:
strace -c 用于統(tǒng)計(jì)各個(gè)系統(tǒng)調(diào)用的次數(shù)
[root@localhost ~]# strace -c echo hello hello % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 0.00 0.000000 0 1 read 0.00 0.000000 0 1 write 0.00 0.000000 0 3 open 0.00 0.000000 0 5 close 0.00 0.000000 0 4 fstat 0.00 0.000000 0 9 mmap 0.00 0.000000 0 4 mprotect 0.00 0.000000 0 2 munmap 0.00 0.000000 0 4 brk 0.00 0.000000 0 1 1 access 0.00 0.000000 0 1 execve 0.00 0.000000 0 1 arch_prctl ------ ----------- ----------- --------- --------- ---------------- 100.00 0.000000 36 1 total [root@localhost ~]#
stace 的實(shí)現(xiàn)原理是系統(tǒng)調(diào)用 ptrace, 我們來(lái)看下 ptrace 是什么。
man page 描述如下:
The ptrace() system call provides a means by which one process (the “tracer”) may observe and control the execution of another process (the “tracee”), and examine and change the tracee's memory and registers. It is primarily used to implement breakpoint debuggingand system call tracing.
簡(jiǎn)單來(lái)說(shuō)有三大能力:
追蹤系統(tǒng)調(diào)用
讀寫(xiě)內(nèi)存和寄存器
向被追蹤程序傳遞信號(hào)
ptrace接口:
int ptrace(int request, pid_t pid, caddr_t addr, int data); request包含: PTRACE_ATTACH PTRACE_SYSCALL PTRACE_PEEKTEXT, PTRACE_PEEKDATA 等
tracer 使用 PTRACE_ATTACH 命令,指定需要追蹤的PID。緊接著調(diào)用 PTRACE_SYSCALL。
tracee 會(huì)一直運(yùn)行,直到遇到系統(tǒng)調(diào)用,內(nèi)核會(huì)停止執(zhí)行。 此時(shí),tracer 會(huì)收到 SIGTRAP 信號(hào),tracer 就可以打印內(nèi)存和寄存器中的信息了。
接著,tracer 繼續(xù)調(diào)用 PTRACE_SYSCALL, tracee 繼續(xù)執(zhí)行,直到 tracee退出當(dāng)前的系統(tǒng)調(diào)用。
需要注意的是,這里在進(jìn)入syscall和退出syscall時(shí),tracer都會(huì)察覺(jué)。
go版本的strace
了解以上內(nèi)容后,presenter 現(xiàn)場(chǎng)實(shí)現(xiàn)了一個(gè)go版本的strace, 需要在 linux amd64 環(huán)境編譯。
https://github.com/silentred/gosys
// strace.go
package main
import (
"fmt"
"os"
"os/exec"
"syscall"
)
func main() {
var err error
var regs syscall.PtraceRegs
var ss syscallCounter
ss = ss.init()
fmt.Println("Run: ", os.Args[1:])
cmd := exec.Command(os.Args[1], os.Args[2:]...)
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
cmd.Stdin = os.Stdin
cmd.SysProcAttr = &syscall.SysProcAttr{
Ptrace: true,
}
cmd.Start()
err = cmd.Wait()
if err != nil {
fmt.Printf("Wait err %v \n", err)
}
pid := cmd.Process.Pid
exit := true
for {
// 記得 PTRACE_SYSCALL 會(huì)在進(jìn)入和退出syscall時(shí)使 tracee 暫停,所以這里用一個(gè)變量控制,RAX的內(nèi)容只打印一遍
if exit {
err = syscall.PtraceGetRegs(pid, ®s)
if err != nil {
break
}
//fmt.Printf("%#v \n",regs)
name := ss.getName(regs.Orig_rax)
fmt.Printf("name: %s, id: %d \n", name, regs.Orig_rax)
ss.inc(regs.Orig_rax)
}
// 上面Ptrace有提到的一個(gè)request命令
err = syscall.PtraceSyscall(pid, 0)
if err != nil {
panic(err)
}
// 猜測(cè)是等待進(jìn)程進(jìn)入下一個(gè)stop,這里如果不等待,那么會(huì)打印大量重復(fù)的調(diào)用函數(shù)名
_, err = syscall.Wait4(pid, nil, 0, nil)
if err != nil {
panic(err)
}
exit = !exit
}
ss.print()
}
// 用于統(tǒng)計(jì)信息的counter, syscallcounter.go
package main
import (
"fmt"
"os"
"text/tabwriter"
"github.com/seccomp/libseccomp-golang"
)
type syscallCounter []int
const maxSyscalls = 303
func (s syscallCounter) init() syscallCounter {
s = make(syscallCounter, maxSyscalls)
return s
}
func (s syscallCounter) inc(syscallID uint64) error {
if syscallID > maxSyscalls {
return fmt.Errorf("invalid syscall ID (%x)", syscallID)
}
s[syscallID]++
return nil
}
func (s syscallCounter) print() {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 8, ' ', tabwriter.AlignRight|tabwriter.Debug)
for k, v := range s {
if v > 0 {
name, _ := seccomp.ScmpSyscall(k).GetName()
fmt.Fprintf(w, "%d\t%s\n", v, name)
}
}
w.Flush()
}
func (s syscallCounter) getName(syscallID uint64) string {
name, _ := seccomp.ScmpSyscall(syscallID).GetName()
return name
}
最后結(jié)果:
Run: [echo hello]
Wait err stop signal: trace/breakpoint trap
name: execve, id: 59
name: brk, id: 12
name: access, id: 21
name: mmap, id: 9
name: access, id: 21
name: open, id: 2
name: fstat, id: 5
name: mmap, id: 9
name: close, id: 3
name: access, id: 21
name: open, id: 2
name: read, id: 0
name: fstat, id: 5
name: mmap, id: 9
name: mprotect, id: 10
name: mmap, id: 9
name: mmap, id: 9
name: close, id: 3
name: mmap, id: 9
name: arch_prctl, id: 158
name: mprotect, id: 10
name: mprotect, id: 10
name: mprotect, id: 10
name: munmap, id: 11
name: brk, id: 12
name: brk, id: 12
name: open, id: 2
name: fstat, id: 5
name: mmap, id: 9
name: close, id: 3
name: fstat, id: 5
hello
name: write, id: 1
name: close, id: 3
name: close, id: 3
1|read
1|write
3|open
5|close
4|fstat
7|mmap
4|mprotect
1|munmap
3|brk
3|access
1|execve
1|arch_prctl
三、參考
Golang標(biāo)準(zhǔn)庫(kù)——syscall
參考URL: https://www.jianshu.com/p/44109d5e045b
Golang 與系統(tǒng)調(diào)用
參考URL: https://blog.csdn.net/weixin_33744141/article/details/89033990
以上就是Golang標(biāo)準(zhǔn)庫(kù)syscall詳解(什么是系統(tǒng)調(diào)用)的詳細(xì)內(nèi)容,更多關(guān)于Golang標(biāo)準(zhǔn)庫(kù)syscall的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Go語(yǔ)言操作mysql數(shù)據(jù)庫(kù)簡(jiǎn)單例子
- Golang的os標(biāo)準(zhǔn)庫(kù)中常用函數(shù)的整理介紹
- 完美解決golang go get私有倉(cāng)庫(kù)的問(wèn)題
- 解析Go 標(biāo)準(zhǔn)庫(kù) http.FileServer 實(shí)現(xiàn)靜態(tài)文件服務(wù)
- 深入解析Go語(yǔ)言的io.ioutil標(biāo)準(zhǔn)庫(kù)使用
- Go編寫(xiě)定時(shí)器與定時(shí)任務(wù)詳解(附第三方庫(kù)gocron用法)
- 解決Go語(yǔ)言數(shù)據(jù)庫(kù)中null值的問(wèn)題
- 一步步教你打造高效可靠的Go庫(kù)
相關(guān)文章
Go語(yǔ)言安裝和GoLand2021最全超詳細(xì)安裝教程
Go語(yǔ)言和GoLand的關(guān)系好比于java和idea、python和pycharm,因此我們需要先安裝好Go語(yǔ)言后才能安裝GoLand。它的安裝和java,python的安裝大同小異,好了,下面給大家?guī)?lái)了GoLand2021安裝教程,需要的朋友參考下吧2021-08-08
Go 1.22對(duì)net/http包的路由增強(qiáng)功能詳解
Go 1.22 版本對(duì) net/http 包的路由功能進(jìn)行了增強(qiáng),引入了方法匹配(method matching)和通配符(wildcards)兩項(xiàng)新功能,本文將給大家詳細(xì)的介紹一下Go 1.22對(duì)net/http包的路由增強(qiáng)功能,需要的朋友可以參考下2024-02-02
Go標(biāo)準(zhǔn)庫(kù)日志打印及同時(shí)輸出到控制臺(tái)與文件
Go語(yǔ)言?xún)?nèi)置的log包實(shí)現(xiàn)了簡(jiǎn)單的日志服務(wù),下面這篇文章主要給大家介紹了關(guān)于Go標(biāo)準(zhǔn)庫(kù)日志打印及同時(shí)輸出到控制臺(tái)與文件的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-11-11
Golang 中的直接依賴(lài)和間接依賴(lài)管理詳解
在 Golang 中,依賴(lài)管理是非常重要的,直接依賴(lài)是指項(xiàng)目代碼中明確引用的其他包的依賴(lài),而間接依賴(lài)是指直接依賴(lài)所引用的其他包的依賴(lài),這篇文章主要介紹了Golang 中的直接依賴(lài)和間接依賴(lài)管理,需要的朋友可以參考下2023-11-11
線上問(wèn)題排查之golang使用json進(jìn)行對(duì)象copy
這篇文章主要介紹了線上問(wèn)題排查之golang使用json進(jìn)行對(duì)象copy,文章圍繞golang使用json進(jìn)行對(duì)象copy的內(nèi)存溢出問(wèn)題排查展開(kāi)詳細(xì)內(nèi)容需要的小伙伴可以參考一下2022-06-06
Go語(yǔ)言實(shí)現(xiàn)請(qǐng)求超時(shí)處理的方法總結(jié)
這篇文章主要為大家詳細(xì)介紹了Go語(yǔ)言中實(shí)現(xiàn)請(qǐng)求的超時(shí)控制的方法,主要是通過(guò)timer和timerCtx來(lái)實(shí)現(xiàn)請(qǐng)求的超時(shí)控制,希望對(duì)大家有所幫助2023-05-05

