Вопрос или проблема
Я начал изучать linux tty и сигналы и столкнулся с некоторыми проблемами.
Я читаю и использую The TTY demystified в качестве справочника.
Я создал две простые программы на golang.
Родитель:
package main
import (
"fmt"
"syscall"
"time"
)
func main() {
attr := &syscall.ProcAttr{
Files: []uintptr{0, 1, 2},
}
_, err := syscall.ForkExec("./child/child", []string{"child"}, attr)
if err != nil {
fmt.Println("Ошибка:", err)
return
}
for {
fmt.Println("Привет от родителя")
time.Sleep(time.Second * 10)
}
}
Дочерняя программа:
package main
import (
"fmt"
"os/signal"
"syscall"
"time"
)
func main() {
signal.Ignore(syscall.SIGTSTP)
for {
fmt.Println("Привет от дочерней программы")
time.Sleep(time.Second * 5)
}
}
Они довольно просты. Обе просто выводят сообщение на tty каждые 5/10 секунд. Единственное отличие в том, что дочерняя программа игнорирует сигнал SIGTSTP (ctrl-z). Таким образом, когда я нажимаю ctrl-z, это приостанавливает родителя, но не дочернюю программу. Именно этого я и ожидаю. Однако, чего я не ожидал, так это что вся группа переносится из переднего плана в группу фона. Это противоречит The TTY demystified. В частности:
Когда все процессы в выполнении в переднем плане были приостановлены,
лидер сеанса читает текущую конфигурацию с устройства TTY,
и сохраняет ее для последующего восстановления. Лидер сеанса устанавливает
себя как текущую группу процессов на переднем плане для TTY
с помощью вызова ioctl. Затем он выводит что-то вроде “[1]+ Остановлено”, чтобы
сообщить пользователю, что задача была приостановлена.
Говорится, что только когда все процессы в выполнении в переднем плане были приостановлены, лидер сеанса (шелл/bash) перемещает группу в фоновую задачу…
Результат ps l -t /dev/pts/0
до и после ctrl-z:
yakog@yakog-computer:~/goprojects/parent$ ps l -t /dev/pts/0
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
0 1000 1747467 1747441 20 0 14288 5632 do_wai Ss pts/0 0:00 bash
0 1000 1747496 1747467 20 0 1225432 1792 ep_pol Sl+ pts/0 0:00 ./parent
0 1000 1747501 1747496 20 0 1225424 1664 ep_pol Sl+ pts/0 0:00 child
yakog@yakog-computer:~/goprojects/parent$ ps l -t /dev/pts/0
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
0 1000 1747467 1747441 20 0 14288 5632 do_sel Ss+ pts/0 0:00 bash
0 1000 1747496 1747467 20 0 1225432 1792 do_sig Tl pts/0 0:00 ./parent
0 1000 1747501 1747496 20 0 1225680 1792 ep_pol Sl pts/0 0:00 child
Если я переношу игнорирование (signal.Ignore(syscall.SIGTSTP)
) из дочерней программы в родительскую, то все работает так, как и должно (с моей точки зрения). Дочерняя программа приостанавливается (T), родительская возобновляется нормально (R/S), но группа все еще находится в переднем плане.
yakog@yakog.computer:~/goprojects/parent$ ps l -t /dev/pts/0
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
0 1000 1749437 1749410 20 0 14420 5632 do_wai Ss pts/0 0:00 bash
0 1000 1749957 1749437 20 0 1225448 1920 ep_pol Sl+ pts/0 0:00 ./parent
0 1000 1749962 1749957 20 0 1225412 1664 ep_pol Sl+ pts/0 0:00 child
yakog@yakog.computer:~/goprojects/parent$ ps l -t /dev/pts/0
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
0 1000 1749437 1749410 20 0 14420 5632 do_wai Ss pts/0 0:00 bash
0 1000 1749957 1749437 20 0 1225448 1920 ep_pol Sl+ pts/0 0:00 ./parent
0 1000 1749962 1749957 20 0 1225668 1792 do_sig Tl+ pts/0 0:00 child
Почему это происходит, что я упускаю?
Ответ или решение
Ваша проблема связана с тем, как сигнал SIGTSTP обрабатывается и как это влияет на группы процессов в Unix-подобных системах. Давайте разберем это подробнее, рассматривая теоретическую основу, пример вашего кода и практическое применение.
Теория
В Unix/Linux системах терминал ассоциирован с сессией, в рамках которой процессы образуют группы. Когда вы выполняете команду с терминала, она запускается в группе процессов, которая связана с управляющим терминалом (TTY). SIGTSTP — это сигнал, который обычно используется для приостановки процесса. Он высылается процессам в текущей группе терминала, которая находится на переднем плане (foreground process group), когда пользователь нажимает Ctrl-Z.
Согласно управлению сессиями и группами процессов, когда все процессы в передней группе приостановлены или завершены, оболочка (например, Bash) может передвигать фокус терминала на заднюю группу процессов. Оболочка контролирует, какие процессы имеют прямой доступ к вводу-выводу через терминал, и переключает эти привилегии между заранее объявленными состояниями передней и задней частей.
Пример
Ваши Golang программы демонстрируют следующее поведение:
- Родительский процесс создает дочерний процесс и работает в цикле, выводя сообщения. Оба процесса находятся в одной процессной группе, связанной с текущим терминалом.
- В дочернем процессе игнорируется сигнал SIGTSTP, поэтому при нажатии Ctrl-Z, только родительский процесс приостанавливается.
- Несмотря на то что дочерний процесс продолжает выполнение, оболочка сообщает терминалу, что передняя группа была перемещена на задний план.
Ваше наблюдение верно: перемещение группы на фон происходит из-за приостановки процесса, от которого ожидается взаимодействие с терминалом, то есть родительского процесса, а не обязательно всех процессов в группе.
Применение
Ваше решение использовать signal.Ignore(syscall.SIGTSTP)
в родительском процессе и ожидать, что дочерний процесс будет приостановлен, действительно изменяет поведение. Это связано с тем, что при игнорировании SIGTSTP родительским процессом, он не будет приостановлен, что позволяет ему оставаться в передней группе терминала, поскольку основная цель — сохранить интерактивную сессию в том виде, в котором она активно управляет I/O через терминал.
Заключение
Основным фактором, способствующим этому поведению, является организация управления процессами и сессиями в Unix-подобных системах, которая гарантирует, что только одна процессная группа может иметь прямой доступ к терминалу в любой момент времени. При этом, приостановка процессов данной группы является триггером для перемещения группы на задний план. Понимание и правильное манипулирование этим поведением может помочь лучше управлять задачами и процессами в сценариях, связанных с управлением терминалами и сигналами.