Кто-то добавил к Википедии "ptrace" статью, утверждая, что на Linux процесс ptraced не мог самостоятельно ptrace другой процесс. Я пытаюсь определить, имеет ли (и раз так почему) это место. Ниже простая программа, я умудрился тестировать это. Мои сбои программы (sub подпроцесс не работает правильно), но я довольно убежден, что это - моя ошибка и не что-то фундаментальное.
В сущности начальная буква обрабатывает, ветвления обрабатывают B который в свою очередь ветвления C. ptraces его ребенок B, B ptraces его ребенок C. После того как они настраиваются, все три процесса записаны, чтобы просто распечатать A
,B
, или C
к stdout один раз во второй.
На практике то, что происходит, - то, что A и B хорошо работают, но C печатает только однажды и затем застревает. Сверение ps -eo pid,cmd,wchan
шоу C всунутая функция ядра ptrace_stop
в то время как остальные находятся в hrtimer_nanosleep
где я ожидал бы, что все три будут.
Достаточно редко все три действительно работают (таким образом, программа печатает Cs, а также As и бакалавра наук), который приводит меня полагать, что в начальной настройке существует некоторое состояние состязания.
Мои предположения относительно того, что могло бы быть неправильным:
SIGCHLD
связанный с B, видящим a SIGCHLD
сделать с сигналом к C и ожидать (2) создание отчетов об обоих как прибывающий из B (но hacky вызов PTRACE_CONT обоим pids не чинит вещи)?Кто-либо может выяснить то, что я делаю неправильно?Спасибо.
#include
#include
#include
#include
#include
#include
static void a(){
while(1){
printf ("A\n");
fflush(stdout);
sleep(1);
}
}
static void b(){
while(1){
printf ("B\n");
fflush(stdout);
sleep(1);
}
}
static void c(){
while(1){
printf ("C\n");
fflush(stdout);
sleep(1);
}
}
static void sigchld_handler(int sig){
int result;
pid_t child_pid = wait(NULL); // find who send us this SIGCHLD
printf("SIGCHLD on %d\n", child_pid);
result=ptrace(PTRACE_CONT, child_pid, sig, NULL);
if(result) {
perror("continuing after SIGCHLD");
}
}
int main(int argc,
char **argv){
pid_t mychild_pid;
int result;
printf("pidA = %d\n", getpid());
signal(SIGCHLD, sigchld_handler);
mychild_pid = fork();
if (mychild_pid) {
printf("pidB = %d\n", mychild_pid);
result = ptrace(PTRACE_ATTACH, mychild_pid, NULL, NULL);
if(result==-1){
perror("outer ptrace");
}
a();
}
else {
mychild_pid = fork();
if (mychild_pid) {
printf("pidC = %d\n", mychild_pid);
result = ptrace(PTRACE_ATTACH, mychild_pid, NULL, NULL);
if(result==-1){
perror("inner ptrace");
}
b();
}
else {
c();
}
}
return 0;
}
Вы действительно наблюдаете состояние гонки. Вы можете вызвать его повторяемость, поставив sleep(1);
непосредственно перед вторым вызовом fork()
.
Состояние гонки вызвано тем, что процесс A неправильно передает сигналы процессу B. Это означает, что если процесс B начинает отслеживать процесс C после того, как процесс A начал отслеживать процесс B, процесс B никогда не получит сигнал SIGCHLD
, указывающий на то, что процесс C остановился, поэтому он никогда не сможет его продолжить.
Чтобы решить эту проблему, нужно просто исправить обработчик SIGCHLD
:
static void sigchld_handler(int sig){
int result, status;
pid_t child_pid = wait(&status); // find who send us this SIGCHLD
printf("%d received SIGCHLD on %d\n", getpid(), child_pid);
if (WIFSTOPPED(status))
{
result=ptrace(PTRACE_CONT, child_pid, 0, WSTOPSIG(status));
if(result) {
perror("continuing after SIGCHLD");
}
}
}