Как можно контролировать монитор?

У меня гипотетическая ситуация:

  1. Скажем, у нас есть два процесса S1 и S2, которые просто контролируют друг друга.
    Как это может быть возможным?
    Ну, в параметрах командной строки для strace -p PID – это способ передать требуемый PID, который (в нашем случае) еще не известен, когда мы выдаем команду strace. Мы могли бы изменить исходный код strace, так что -P 0 означает, спросите пользователя о PID. Например, read () из STDIN. Когда мы можем запускать два процесса strace в двух сеансах оболочки и находить PID в третьей оболочке, мы можем предоставить этот вход S1 и S2 и позволить им контролировать друг друга.
    Будет ли S1 & S2 застревать? Или, переходите в бесконечные петли или немедленно сбивайтесь или …?

  2. Опять же, скажем, у нас есть еще один процесс S3, связанный с -p -1 , который, изменяя исходный код, мы используем, чтобы сообщить S3 о себе. Например, используйте getpid () без использования STDIN. Сбой S3? Или, возможно, он зависает без дальнейшей обработки? Подождет ли какое-то событие, но, потому что он ждет, ничего не произойдет?

В man-странице strace говорится, что мы не можем контролировать процесс init. Есть ли какое-либо другое ограничение, принуждаемое strace или ядром, чтобы избежать циклической зависимости или цикла?

Некоторые специальные случаи:
S4 контролирует S5, S5 мониторы S6, S6 мониторы S4.
S7 и S8 контролируют друг друга, где S7 является родителем S8.
Возможны более частные случаи.

EDIT (после комментариев @Ralph Rönnquist & @pfnuesel):
https://github.com/bnoordhuis/strace/blob/master/strace.c#L941

 if (pid <= 0) { error_msg_and_die("Invalid process id: '%s'", opt); } if (pid == strace_tracer_pid) { error_msg_and_die("I'm sorry, I can't let you do that, Dave."); } 

В частности, что произойдет, если strace.c не проверяет pid == strace_tracer_pid или какие-либо другие особые случаи? Есть ли какие-либо технические ограничения (в ядре) над одним процессом мониторинга? Как насчет группы из 2 (или 3 или более) процессов мониторинга себя? Повреждена ли система или повесить ее?

 % sh -c 'exec strace -p $$' strace: I'm sorry, I can't let you do that, Dave. 

🙂

Просто хочу показать вам реальный пример круговой зависимости или цикла, который может вызвать зависание системы.

В своем X-сеансе и графическом эмуляторе терминала запустите эту команду, чтобы получить Xorg.bin pid:

 [xiaobai@xiaobai tmp]$ pgrep Xorg 1780 [xiaobai@xiaobai tmp]$ 

Затем выполните:

 [xiaobai@xiaobai tmp]$ sudo strace -p 1780 

Он закроет весь рабочий стол через несколько секунд (~ 5), по крайней мере, в моем Fedora 21 gnome 3.14.0, я должен отключить кнопку питания.

Но если вы попробуете либо запустить sudo strace -p 1780 в другом tty с помощью Ctrl-Alt-F1 | 7 или sudo strace -p 1780 2>/tmp/strace.log , оба не будут sudo strace -p 1780 2>/tmp/strace.log .

Таким образом, мы можем заключить, что strace получает выход Xorg, а затем печатает его на Xorg и вызывает бесконечную петлю и замораживание.

Я отвечу только на Linux.

Удивительно, но в новых ядрах системный вызов ptrace , который используется strace для фактического выполнения трассировки, позволяет отслеживать процесс init. На странице руководства написано:

  EPERM The specified process cannot be traced. This could be because the tracer has insufficient privileges (the required capability is CAP_SYS_PTRACE); unprivileged processes cannot trace pro‐ cesses that they cannot send signals to or those running set- user-ID/set-group-ID programs, for obvious reasons. Alterna‐ tively, the process may already be being traced, or (on kernels before 2.6.26) be init(8) (PID 1). 

подразумевая, что начиная с версии 2.6.26 вы можете проследить init , хотя, конечно, вы все равно должны быть root, чтобы сделать это. Бинарный файл strace в моей системе позволяет мне отслеживать init , и на самом деле я могу даже использовать gdb для присоединения к init и его уничтожения. (Когда я это сделал, система сразу же остановилась).

ptrace не может использоваться процессом для прослеживания, поэтому, если strace не проверял, он, тем не менее, не смог бы отслеживать себя. Следующая программа:

 #include <sys/ptrace.h> #include <stdio.h> #include <unistd.h> int main() { if (ptrace(PTRACE_ATTACH, getpid(), 0, 0) == -1) { perror(NULL); } } 

распечатки Operation not permitted ( т. е. результатом является EPERM ). Ядро выполняет эту проверку в ptrace.c :

  retval = -EPERM; if (unlikely(task->flags & PF_KTHREAD)) goto out; if (same_thread_group(task, current)) // <-- this is the one goto out; 

Теперь возможно, что два процесса strace могут отслеживать друг друга; ядро этого не предотвратит, и вы сами можете наблюдать результат. Для меня последнее, что первый strace процесс (PID = 5882) печатает:

 ptrace(PTRACE_SEIZE, 5882, 0, 0x11 

тогда как второй процесс strace (PID = 5890) вообще ничего не печатает. ps показывает оба процесса в состоянии t , которые, согласно странице руководства proc(5) , означают остановку.

Это происходит потому, что трассировка останавливается всякий раз, когда она вводит или выходит из системного вызова и всякий раз, когда сигнал должен быть доставлен на него (кроме SIGKILL ).

Предположим, что процесс 5882 уже отслеживает процесс 5890. Затем мы можем вывести следующую последовательность событий:

  1. Процесс 5890 входит в системный вызов ptrace , пытаясь выполнить трассировку процесса 5882. Процесс 5890 вводит трассировку.
  2. Процесс 5882 принимает SIGCHLD чтобы сообщить ему, что его трассировка, процесс 5890 остановлен. (Процесс остановки трассировки выглядит так, как будто он получил сигнал SIGTRAP.)
  3. Процесс 5882, увидев, что его трассировка выполнила системный вызов, добросовестно распечатывает информацию о syscall, который должен выполнить процесс 5890, и аргументы. Это последний результат, который вы видите.
  4. Процесс 5882 вызывает ptrace(PTRACE_SYSCALL, 5890, ...) чтобы продолжить процесс 5890.
  5. Процесс 5890 оставляет трассировку и выполняет ее ptrace(PTRACE_SEIZE, 5882, ...) . Когда последний возвращается, процесс 5890 входит в trace-stop.
  6. Процесс 5882 отправляется SIGCHLD так как его трассировка снова остановилась. Поскольку он отслеживается, получение сигнала заставляет его вводить трассировку.

Теперь оба процесса остановлены. Конец.

Как видно из этого примера, ситуация двух трассировок процессов друг с другом не создает никаких явных логических трудностей для ядра, поэтому, вероятно, поэтому код ядра не содержит проверку, чтобы предотвратить эту ситуацию. Это просто не очень полезно для двух процессов, чтобы проследить друг друга.