Skip to content

Commit d92dd22

Browse files
lifubangcyphar
authored andcommitted
performance improvement: setup signal notify in a new go routine
There is a big loop(at least 65 times) in `signal.Notify`, it costs as much time as `runc init`, so we can call it in parallel ro reduce the container start time. In a general test, it can be reduced about 38.70%. Signed-off-by: lifubang <lifubang@acmcoder.com> (cyphar: move signal channel definition inside goroutine) Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
1 parent 202ca99 commit d92dd22

File tree

2 files changed

+20
-11
lines changed

2 files changed

+20
-11
lines changed

signals.go

+18-10
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,30 @@ const signalBufferSize = 2048
1818
// while still forwarding all other signals to the process.
1919
// If notifySocket is present, use it to read systemd notifications from the container and
2020
// forward them to notifySocketHost.
21-
func newSignalHandler(enableSubreaper bool, notifySocket *notifySocket) *signalHandler {
21+
func newSignalHandler(enableSubreaper bool, notifySocket *notifySocket) chan *signalHandler {
2222
if enableSubreaper {
2323
// set us as the subreaper before registering the signal handler for the container
2424
if err := system.SetSubreaper(1); err != nil {
2525
logrus.Warn(err)
2626
}
2727
}
28-
// ensure that we have a large buffer size so that we do not miss any signals
29-
// in case we are not processing them fast enough.
30-
s := make(chan os.Signal, signalBufferSize)
31-
// handle all signals for the process.
32-
signal.Notify(s)
33-
return &signalHandler{
34-
signals: s,
35-
notifySocket: notifySocket,
36-
}
28+
handler := make(chan *signalHandler)
29+
// signal.Notify is actually quite expensive, as it has to configure the
30+
// signal mask and add signal handlers for all signals (all ~65 of them).
31+
// So, defer this to a background thread while doing the rest of the io/tty
32+
// setup.
33+
go func() {
34+
// ensure that we have a large buffer size so that we do not miss any
35+
// signals in case we are not processing them fast enough.
36+
s := make(chan os.Signal, signalBufferSize)
37+
// handle all signals for the process.
38+
signal.Notify(s)
39+
handler <- &signalHandler{
40+
signals: s,
41+
notifySocket: notifySocket,
42+
}
43+
}()
44+
return handler
3745
}
3846

3947
// exit models a process exit status with the pid and

utils_linux.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ func (r *runner) run(config *specs.Process) (int, error) {
246246
// Setting up IO is a two stage process. We need to modify process to deal
247247
// with detaching containers, and then we get a tty after the container has
248248
// started.
249-
handler := newSignalHandler(r.enableSubreaper, r.notifySocket)
249+
handlerCh := newSignalHandler(r.enableSubreaper, r.notifySocket)
250250
tty, err := setupIO(process, r.container, config.Terminal, detach, r.consoleSocket)
251251
if err != nil {
252252
return -1, err
@@ -285,6 +285,7 @@ func (r *runner) run(config *specs.Process) (int, error) {
285285
return -1, err
286286
}
287287
}
288+
handler := <-handlerCh
288289
status, err := handler.forward(process, tty, detach)
289290
if err != nil {
290291
r.terminate(process)

0 commit comments

Comments
 (0)