網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
Golang不是像C語(yǔ)言的系統(tǒng)級(jí)編程語(yǔ)言,但仍提供了以下特性幫助開(kāi)發(fā)者與底層操作系統(tǒng)進(jìn)行交互,如信號(hào)(singals),os/singals包實(shí)現(xiàn)了這些功能。相對(duì)于其他語(yǔ)言處理OS信號(hào)采用復(fù)雜或冗余的方法,Golang內(nèi)置OS包提供了易用的方法處理Unix信號(hào),非常直接、簡(jiǎn)單。事實(shí)上,類unix系統(tǒng)中通常處理singal是必要的,本文介紹singal概念,并通過(guò)示例說(shuō)明其應(yīng)用場(chǎng)景。
從示例開(kāi)始
假設(shè)場(chǎng)景:Golang應(yīng)用在關(guān)閉時(shí)打印消息:“Thank you for using Golang.” 首先新建main函數(shù),函數(shù)體模擬執(zhí)行一些業(yè)務(wù),直到接收到結(jié)束命令。
func main() { for { fmt.Println("Doing Work") time.Sleep(1 * time.Second) } }
當(dāng)運(yùn)行應(yīng)用,然后從OS發(fā)送執(zhí)行kill信號(hào)(Ctrl + C),可能輸出結(jié)果類似這樣:
Doing Work Doing Work Doing Work Process finished with exit code 2
現(xiàn)在我們希望攔截kill信號(hào)并定義處理邏輯:打印必要的關(guān)閉信息。
接收信號(hào)
首先創(chuàng)建channe接收來(lái)自操作系統(tǒng)的命令,os包提供了signal接口處理基于OS規(guī)范信號(hào)實(shí)現(xiàn)。
killSignal := make(chan os.Signal, 1)
為了通知killSignal,需使用signal包中提供的Notify函數(shù),第一個(gè)參數(shù)為Signal類型的channel,下一個(gè)參數(shù)接收一組發(fā)給通道的信號(hào)列表。
Notify(c chan<- os.Signal, sig ...os.Signal)
我們也可以使用syscall包通知特定命令的信號(hào):
signal.Notify(c chan<- os.Signal, syscall.SIGINT, syscall.SIGTERM)
為了處理信號(hào),我們?cè)趍ain函數(shù)中使用killSignal通道等待interrupt信號(hào),一旦接收到來(lái)自O(shè)S的命令,則打印結(jié)束消息并接收應(yīng)用。現(xiàn)在把處理業(yè)務(wù)的循環(huán)代碼移入獨(dú)立的goroute中,這里定義一個(gè)匿名函數(shù):
go func() { for { fmt.Println("Doing Work") time.Sleep(1 * time.Second) } }()
因?yàn)闃I(yè)務(wù)代碼運(yùn)行在獨(dú)立routine中,main函數(shù)實(shí)現(xiàn)等待killSignal信號(hào)并在結(jié)束之前打印消息。
<-killSignal fmt.Println("Thanks for using Golang!")
完整代碼
下面把幾個(gè)部分組裝在一起,完整代碼如下:
package main import ( "fmt" "os" "os/signal" "time" ) func main() { killSignal := make(chan os.Signal, 1) signal.Notify(killSignal, os.Interrupt) go func() { for { fmt.Println("Doing Work") time.Sleep(1 * time.Second) } }() <-killSignal fmt.Println("Thanks for using Golang!") }
運(yùn)行程序,看到一直執(zhí)行業(yè)務(wù)循環(huán),直到接收到interrupt信號(hào),打印消息并結(jié)束:
Doing Work
Doing Work
Doing Work
Thanks for using Golang!
常見(jiàn)信號(hào)
對(duì)于命令行程序,當(dāng)按CTRL+C
時(shí),會(huì)發(fā)送SIGINT
信號(hào),SIGINT
是與指定特定信號(hào)數(shù)字相關(guān)聯(lián)的名稱。信號(hào)實(shí)際是可以有程序生成的軟件中斷,也是一種異步處理事件機(jī)制。通常為了安全起見(jiàn),信號(hào)采用名稱而不是數(shù)值生成。
大多數(shù)信號(hào)及其具體動(dòng)作都是由操作系統(tǒng)定義的,并還在操作系統(tǒng)權(quán)限范圍內(nèi)執(zhí)行相應(yīng)功能。因此能夠處理某些事件并不能讓用戶自由地使用系統(tǒng),有些信號(hào)即使生成了也會(huì)被操作系統(tǒng)忽略,而由操作系統(tǒng)決定程序允許處理哪些信號(hào)。
舉例,SIGKILL 和 SIGSTOP 信號(hào)也可以被捕獲、阻塞或忽略。因?yàn)檫@些信號(hào)對(duì)于保持操作系統(tǒng)功能的"健全"至關(guān)重要,在極端條件下為內(nèi)核和root用戶提供了停止進(jìn)程的方法。與SIGKILL相關(guān)聯(lián)的數(shù)字是9,Linux提供了kill命令發(fā)送SIGTERM信號(hào),終止正在運(yùn)行的程序。可以使用kill -l命令查看完整的支持信號(hào):
圖片
有許多信號(hào)可用,但并非所有信號(hào)都能被程序處理。像SIGKILL和SIGSTOP這樣的信號(hào)既不能被程序調(diào)用,也不能被程序忽略。原因很簡(jiǎn)單:它們太重要了,不允許被惡意程序?yàn)E用。
系統(tǒng)信號(hào)分為同步信號(hào)和異步信號(hào)。其中同步信號(hào)是程序執(zhí)行中的錯(cuò)誤觸發(fā)的信號(hào),如SIGBUS, SIGFPE, SIGSEGV,在 Golang 程序中,同步信號(hào)通常會(huì)被轉(zhuǎn)換為 runtime panic。同步信號(hào)需要事件及事件時(shí)間,相反異步信號(hào)不需要于時(shí)鐘同步,異步信號(hào)是系統(tǒng)內(nèi)核或其它程序發(fā)送的信號(hào)。舉例,SIGHUP表示它控制的終端被關(guān)閉,常用的是當(dāng)按CTRL+C
時(shí),會(huì)發(fā)送SIGINT
信號(hào)。當(dāng)SIGINT
信號(hào)有鍵盤產(chǎn)生時(shí),實(shí)際上不是終止應(yīng)用程序,而是生成特定鍵盤中斷,并且處理該中斷的程序開(kāi)始執(zhí)行,其缺省行為時(shí)關(guān)閉應(yīng)用程序,但我們可以覆蓋缺省行為,寫我們自己的業(yè)務(wù)處理邏輯。
事實(shí)上許多信號(hào)處理都可以被覆蓋,Go開(kāi)發(fā)者可以編寫自己的處理程序來(lái)自定義信號(hào)處理行為。然而,除非有非常好的理由,否則改變信號(hào)的默認(rèn)行為并不是明智的想法。這里引用列舉常用linux信號(hào):
SIGHUP: ‘HUP’ = ‘hung up’. This signal is generated when a controlling process dies or hangs up detected on the controlling terminal.
SIGINT: When a process is interrupted from keyboard by pressing CTRL+C
SIGQUIT: Quit from keyboard
SIGILL: Illegal instruction. A synonym for SIGPWR() – power failure
SIGABRT: Program calls the abort() function – an emergency stop.
SIGBUS: Bad memory access. Attempt was made to access memory inappropriately
SIGFPE: FPE = Floating point exception
SIGKILL: The kill signal. The process was explicitly killed.
SIGUSR1: This signal is open for programmers to write a custom behavior.
SIGSEGV: Invalid memory reference. In C when we try to access memory beyond array limit, this signal is generated.
SIGUSR2: This signal is open for programmers to write a custom behavior.
SIGPIPE: This signals us open for programmers to write a custom behavior.
SIGALRM: Process requested a wake up call by the operating system such as by calling the alarm() function.
SIGTERM: A process is killed
處理多個(gè)信號(hào)
下面再看一個(gè)示例,較上面的示例增加了對(duì)SIGTERM信號(hào)的處理:
package main import ( "fmt" "os" "os/signal" "syscall" ) func handler(signal os.Signal) { if signal == syscall.SIGTERM { fmt.Println("Got kill signal. ") fmt.Println("Program will terminate now.") os.Exit(0) } else if signal == syscall.SIGINT { fmt.Println("Got CTRL+C signal") fmt.Println("Closing.") os.Exit(0) } else { fmt.Println("Ignoring signal: ", signal) } } func main() { sigchnl := make(chan os.Signal, 1) signal.Notify(sigchnl) // signal.Notify(sigchnl, os.Interrupt, syscall.SIGTERM) exitchnl := make(chan int) go func() { for { s := <-sigchnl handler(s) } }() exitcode := <-exitchnl os.Exit(exitcode) }
首先創(chuàng)建通道接收響應(yīng)信號(hào),我們也可以指定信號(hào)類型:signal.Notify(sigchnl, os.Interrupt, syscall.SIGTERM),否則會(huì)接收所有信號(hào),包括命令窗口改變大小信號(hào)。然后在handle函數(shù)判斷信號(hào)類型并分別進(jìn)行處理。
程序運(yùn)行后,linux可以通過(guò)ps -h 查看go程序,然后執(zhí)行kill命令。
NotifyContext示例
Go.1.16提供了signal.NotifyContext函數(shù),可以實(shí)現(xiàn)更優(yōu)雅的關(guān)閉功能。下面示例實(shí)現(xiàn)簡(jiǎn)單web服務(wù)器,示例handle需要10s處理時(shí)間,如果正在運(yùn)行的web服務(wù),客戶端通過(guò)postman或cURL發(fā)送請(qǐng)求,然后在服務(wù)端立刻通過(guò)ctrl+C發(fā)送終止信號(hào)。
我們希望看到服務(wù)器在終止之前通過(guò)響應(yīng)終止請(qǐng)求而優(yōu)雅地關(guān)閉。如果關(guān)閉時(shí)間過(guò)長(zhǎng),可以發(fā)送另一個(gè)中斷信號(hào)立即退出,或者暫停將在5秒后開(kāi)始。
package main import ( "context" "fmt" "net/http" "os" "os/signal" "time" ) var ( server http.Server ) func main() { // Create context that listens for the interrupt signal from the OS. ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) defer stop() server = http.Server{ Addr: ":8080", } // Perform application startup. http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { time.Sleep(time.Second * 10) fmt.Fprint(w, "Hello world!") }) // Listen on a different Goroutine so the application doesn't stop here. go server.ListenAndServe() // Listen for the interrupt signal. <-ctx.Done() // Restore default behavior on the interrupt signal and notify user of shutdown. stop() fmt.Println("shutting down gracefully, press Ctrl+C again to force") // Perform application shutdown with a maximum timeout of 5 seconds. timeoutCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := server.Shutdown(timeoutCtx); err != nil { fmt.Println(err) } }
總結(jié)
本文介紹了信號(hào)的概念及常用信號(hào),并給出了應(yīng)用廣泛的幾個(gè)示例,例如優(yōu)雅地關(guān)閉應(yīng)用服務(wù)、在命令行應(yīng)用中接收終止命令。
原文鏈接:https://blog.csdn.net/neweastsun/article/details/128812053
相關(guān)推薦
- 2022-11-15 Flutter異步操作實(shí)現(xiàn)流程詳解_Android
- 2022-10-05 redis?stream?實(shí)現(xiàn)消息隊(duì)列的實(shí)踐_Redis
- 2022-09-14 Python詳解如何動(dòng)態(tài)給對(duì)象增加屬性和方法_python
- 2021-12-18 ECommerceCrawlers項(xiàng)目分析(十二)
- 2022-12-12 C語(yǔ)言中帶頭雙向循環(huán)鏈表基本操作的實(shí)現(xiàn)詳解_C 語(yǔ)言
- 2022-07-09 kernel劫持modprobe?path內(nèi)容詳解_C 語(yǔ)言
- 2022-09-08 python?字典生成樹(shù)狀圖的實(shí)例_python
- 2022-09-24 關(guān)于R語(yǔ)言包的升級(jí)與降級(jí)問(wèn)題_R語(yǔ)言
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支