OS Signals and the Runtime

Updated

September 8, 2026

OS Signals and the Runtime

Overview

Unix signals interrupt processes asynchronously. Go multiplexes them through the runtime so user code can use os/signal channels instead of raw C handlers—while still coordinating with the scheduler and cgo.

Diagram: signal path (teaching)

  kernel signal
       │
       v
  runtime notes signal
       │
       v
  os/signal demux → Notify channel
       │
       v
  user G handles (e.g. Shutdown)

os/signal API

ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
<-ctx.Done()
// graceful shutdown
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGTERM)

Buffer the channel—signals can be missed if send would block.

Interactions

Area Note
Multiple Notify Same signal can fan out
Reset/Stop Undo registration
Windows Different signal story; Interrupt exists
cgo Foreign code may install handlers—careful
Panic dumps GOTRACEBACK, SIGQUIT often stack dump

Graceful shutdown pattern

sequence (top → bottom):
  actors: SIGTERM, main, http.Server
  SIGTERM --> main  : NotifyContext cancel
  main --> http.Server  : Shutdown(ctx)
  http.Server --> main  : drains then return

Experiment

# terminal 1
go run .
# terminal 2
kill -TERM $(pidof yourbin)
package main
import (
    "context"
    "fmt"
    "os"
    "os/signal"
    "time"
)
func main() {
    ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
    defer stop()
    fmt.Println("running; press Ctrl+C")
    select {
    case <-ctx.Done():
        fmt.Println("got", ctx.Err())
    case <-time.After(30 * time.Second):
        fmt.Println("timeout")
    }
}

What to notice: Cancel is process-wide coordination; pair with server Shutdown, not os.Exit mid-request.

Try next: Trap SIGTERM in a binary under Docker docker stop and confirm clean logs.