Ce face signal.Notify
Implicit, când apeși Ctrl+C (semnalul SIGINT, adică os.Interrupt) sau când sistemul trimite SIGTERM (de exemplu docker stop), programul Go se oprește imediat. Cu signal.Notify îi spui să pună semnalele pe un canal: programul nu se mai oprește singur și tu decizi ce faci (salvezi datele, închizi conexiunile, ieși).
Canalul trebuie să aibă buffer (de obicei make(chan os.Signal, 1)), pentru că Notify nu așteaptă: dacă nimeni nu e gata să primească, semnalul se pierde. Fără semnale în listă, sunt trimise toate.
signal.Stop(c) oprește trimiterea semnalelor pe canalul c; dacă niciun alt canal nu le mai așteaptă, semnalele revin la comportamentul implicit.
Parametri
| Parametru | Descriere |
|---|---|
c |
Canalul pe care sosesc semnalele; cu buffer. |
sig |
Semnalele urmărite: os.Interrupt, syscall.SIGTERM, syscall.SIGHUP etc. |
Ce returnează
Nimic.
Exemple
Exemplul 1: Prinderea lui Ctrl+C
Programul își trimite singur semnalul de întrerupere, ca să-l putem vedea în rezultat.
package main
import (
"fmt"
"os"
"os/signal"
)
func main() {
semnale := make(chan os.Signal, 1)
signal.Notify(semnale, os.Interrupt)
fmt.Println("serverul rulează...")
proces, _ := os.FindProcess(os.Getpid())
proces.Signal(os.Interrupt) // ca și cum ai apăsa Ctrl+C
s := <-semnale
fmt.Println("am primit:", s)
fmt.Println("salvez datele și închid frumos")
}
Rezultat
serverul rulează...
am primit: interrupt
salvez datele și închid frumos
Exemplul 2: Tratarea mai multor semnale
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
)
func main() {
semnale := make(chan os.Signal, 1)
signal.Notify(semnale, syscall.SIGHUP, syscall.SIGTERM)
proces, _ := os.FindProcess(os.Getpid())
for _, trimis := range []os.Signal{syscall.SIGHUP, syscall.SIGTERM} {
proces.Signal(trimis)
switch <-semnale {
case syscall.SIGHUP:
fmt.Println("SIGHUP: recitesc configurarea")
case syscall.SIGTERM:
fmt.Println("SIGTERM: opresc serverul")
}
}
}
Rezultat
SIGHUP: recitesc configurarea
SIGTERM: opresc serverul
Exemplul 3: signal.Stop
După Stop, semnalul SIGWINCH (schimbarea mărimii terminalului, ignorat implicit) nu mai ajunge pe canal.
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGWINCH)
proces, _ := os.FindProcess(os.Getpid())
proces.Signal(syscall.SIGWINCH)
fmt.Println("primit:", <-c)
signal.Stop(c)
proces.Signal(syscall.SIGWINCH)
select {
case s := <-c:
fmt.Println("primit din nou:", s)
case <-time.After(200 * time.Millisecond):
fmt.Println("după Stop: niciun semnal pe canal")
}
}
Rezultat
primit: window size changes
după Stop: niciun semnal pe canal
Greșeli frecvente
- Folosești un canal fără buffer:
make(chan os.Signal). Dacă semnalul vine când nu aștepți pe canal, se pierde.go vetavertizează pentru asta. - Încerci să prinzi
SIGKILLsauSIGSTOP. Aceste semnale nu pot fi prinse; sistemul oprește procesul oricum. - Apelezi
Notifyși uiți să ieși din program după tratare. Ctrl+C nu mai oprește programul, iar utilizatorul trebuie să foloseascăkill -9.
Comenzi înrudite
signal.NotifyContext: Returnează un context care se anulează când programul primește unul dintre semnalele date (de exemplu Ctrl+C).os.Exit: Oprește imediat programul cu codul de ieșire dat; funcțiile amânate cu defer nu mai rulează.exec.CommandContext: Ca exec.Command, dar procesul este oprit automat când contextul expiră sau este anulat.