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基于viper實(shí)現(xiàn)配置文件熱更新及其源碼分析
這篇文章主要介紹了詳解go基于viper實(shí)現(xiàn)配置文件熱更新及其源碼分析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06利用golang的字符串解決leetcode翻轉(zhuǎn)字符串里的單詞
這篇文章主要介紹了利用golang的字符串解決leetcode翻轉(zhuǎn)字符串里的單詞,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12golang interface指針實(shí)現(xiàn)示例
本文主要介紹了golang interface指針實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08golang中實(shí)現(xiàn)給gif、png、jpeg圖片添加文字水印
這篇文章主要介紹了golang中實(shí)現(xiàn)給gif、png、jpeg圖片添加文字水印,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-04-04go高并發(fā)時(shí)append方法偶現(xiàn)錯(cuò)誤解決分析
這篇文章主要為大家介紹了go高并發(fā)時(shí)append方法偶現(xiàn)錯(cuò)誤解決分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10