LINUX.ORG.RU

Виснет fork(), помогите...


0

0

Вот такая проблема: на системе с LinuxThreads один из тредов делает
fork(). В это время другой тред, пользуясь известным pid-ом этого треда,
делает связку

ptrace(PTRACE_ATTACH, pid, NULL, NULL)
waitpid(pid, status, __WALL)
ptrace(PTRACE_GETREGS, pid, NIL, &regs)
ptrace(PTRACE_DETACH, pid, NULL, NULL)

Приостановленный тред продолжает работу, но виснет в fork()! В GDB 
видно, что тред выполнил int 0x80 - системный вызов произошел. Дальше
ничего нет - fork() повис где-то, видимо, в ядре.

Как же такое может быть?... Что же делать? :(

версия ядра??

что возвращает fork()??

>Приостановленный тред продолжает работу, но виснет в fork()! 

ничего не понял. обьясните

>видно, что тред выполнил int 0x80 - системный вызов произошел.

тред не может выполнить fork(). форк выполняется для процесса тоесть для всех тредов сразу. 

И ещё ты уверен что делаеш :
ptrace(PTRACE_ATTACH, pid, NULL, NULL)
waitpid(pid, status, __WALL)
ptrace(PTRACE_GETREGS, pid, NIL, &regs)
ptrace(PTRACE_DETACH, pid, NULL, NULL)

ПОСЛЕ возврата из форка???? у тебя там мьютексы стоят или семафоры????

cvv ★★★★★
()
Ответ на: комментарий от cvv

> версия ядра??
2.4.18

> что возвращает fork()??
НЕ -1. Child исправно запускается, а parent виснет, из fork()-а не 
выходит.

>>Приостановленный тред продолжает работу, но виснет в fork()! 
>ничего не понял. обьясните

Сорри, неточно выразился. Я не знаю, что происходит. Ясно одно (видно 
из трейса GDB): тред A пытается сделать fork() - заходит в
libpthread::fork(), что-то там делает, затем заходит в
libc::__libc_fork(), тоже что-то делает, затем случается int 0x80. Всё.
Действия треда A прекращаются, он повис. Между тем child процесс 
рожается и начинает свои действия.

В процессе вызова тредом A функции fork() тред B (легковесный просесс
с другим pid-ом, ибо LinuxThreads!) делает связку ptrace-ptrace-waitpid-ptrace,
это совершенно точно. Почему ПОСЛЕ форка? Это происходит ВО ВРЕМЯ
форка...

jek_
() автор топика
Ответ на: комментарий от jek_

Небольшое дополнение. Стало понятно, что, видимо, форкающийся процесс,
зайдя в ядерный fork(), не замечает сигнала SIGCONT, который посылает
ему вызов ptrace(PTRACE_DETACH). Если из консоли послать его руками,
то намертво подвисший в форке тред оживает и вся прилада радостно едет
дальше, как будто ничего плохого не происходило.

jek_
() автор топика
Ответ на: комментарий от jek_

>В процессе вызова тредом A функции fork() тред B (легковесный просесс >с другим pid-ом, ибо LinuxThreads!) делает связку >ptrace-ptrace-waitpid-ptrace, это совершенно точно. >Почему ПОСЛЕ форка? Это происходит ВО ВРЕМЯ форка...

вот мы и розковыряли.

ты в связке ptrace-ptrace-waitpid-ptrace используеш пид ещё не существующего/не созданного процесса. следственно последствия неопределены. прочитай ман на ptrace и не занимайся ерундой

cvv ★★★★★
()
Ответ на: комментарий от cvv

эх... зря ты не читаешь вопрос внимательно. ptrace-ptrace-waitpid-ptrace
делается на pid существующего процесса, т.е. того, __который__ делает
fork! вовсе не child-a, которого еще нет, а именно парента!

jek_
() автор топика

Правильно ли я понимаю, что fork делает процесс, а не thread, и если процесс с двумя нитями внутри сделает fork, то будет два процесса по две нити в каждом?

anonymous
()
Ответ на: комментарий от jek_

прочитал внимательней.

ерундой ты страдаеш. ты не можешь делать ptrace для одного треда. ты можешь делать ptrace только для всего процесса сразу.

во вторых откудова ты знаеш пид процесса который соответствует потоку который ты пытаешся трассировать. ни одна ф-я не выдаёт его наружу ядра. при создании треда тебе возвращается Thread-ID который в обычно не имеет никакой связи с pid соответствующего ему процесса. причём этот "процесс" существенно отличается от процессов созданных при помощи fork().

отсюдова собственно и все твои грабли: делая ptrace на pid процесса соответсвующего твоему потоку ты фактически пытаешся остановить весь свой процесс тоесть все треды. система тебя не понимает и посылает SIGCONT процессу соответствующему главному треду.

несмотря на допущенные мною неточности ты наверное уразумел безсмысленность всех своих действий

cvv ★★★★★
()
Ответ на: комментарий от cvv

хотя если точнее так требует POSIX и прочие стандарты а на деле немного зависит от реализации.

cvv ★★★★★
()
Ответ на: комментарий от cvv

> абсолютно верно.
Это ты что, собственный монолог одобряешь? :)

Ты не въезжаешь, что такое LinuxThreads. LinuxThreads - это реализация
потоков (тредов, нитей, threads, как угодно) на базе т.н. легковесных
процессов, порождённых системным вызовом clone(). В этой реализации
_каждый_ тред имеет собственный pid, т.е., обращаться с каждым тредом
можно как с отдельным процессом, в том числе и делать ptrace() каждого
из них. Узнать этот pid для каждого индивидуального треда очень просто:
достаточно при создании каждого из них делать getpid() и записывать 
в глобальную для всех тредов хэш-таблицу или просто в массив.

Мне нужно сделать "очень простую" вещь: тред-монитор должен периодически
асинхронно по отношению к остальным тредам узнавать их контекст. Вот
для этого и делается ptrace(). Зная ThreadID, мы легко достаем из хэша
соответствующий PID и натравляем на данный тред (на самом деле отдельный
процесс, подчёркиваю!) связку вызовов ptrace и waitpid.

Почему при этом виснет ядерный fork() - и только fork()! - я не
понимаю, и наделся узнать у общественности...

jek_
() автор топика
Ответ на: комментарий от anonymous

>Правильно ли я понимаю, что fork делает процесс, а не thread, и если
>процесс с двумя нитями внутри сделает fork, то будет два процесса по
>две нити в каждом?

Неправильно. Если процесс делает fork(), вне зависимости от того,
сколько в нём тредов, случится ещё ровно один процесс с одним тредом.
"Сдублируется" тот тред, который делал fork().

jek_
() автор топика
Ответ на: комментарий от jek_

>>Правильно ли я понимаю, что fork делает процесс, а не thread, и если >>процесс с двумя нитями внутри сделает fork, то будет два процесса по >>две нити в каждом?

>Неправильно. Если процесс делает fork(), вне зависимости от того, >сколько в нём тредов, случится ещё ровно один процесс с одним тредом. >"Сдублируется" тот тред, который делал fork().

не вводи человека в заблуждение. если так реализован LinuxTreads то это очередной гвоздь в его могилу. все стандарты требуют что дублировались *ВСЕ* треды

cvv ★★★★★
()
Ответ на: комментарий от cvv

>не вводи человека в заблуждение. если так реализован LinuxTreads то это
>очередной гвоздь в его могилу. все стандарты требуют что дублировались
>*ВСЕ* треды

@#$@#$!!! Какие такие стандарты?! Вот стандарты:

http://www.opengroup.org/onlinepubs/009695399/functions/fork.html
http://bama.ua.edu/cgi-bin/man-cgi?fork1+2

  POSIX Threads
     In applications that use the POSIX threads API  rather  than
     the   Solaris   threads   API  (  applications  linked  with
     -lpthread, whether or not linked with -lthread), a  call  to
     fork() is like a call to  fork1(), which replicates only the
     calling thread. There is no call that forks a child with all
     threads and LWPs duplicated in the child.

     Note that if a program is  linked  with  both  libraries  (-
     lthread  and  -lpthread),  the POSIX semantic of fork() pre-
     vails.

jek_
() автор топика
Ответ на: комментарий от jek_

>Ты не въезжаешь, что такое LinuxThreads. >LinuxThreads - это реализация потоков (тредов, нитей, threads, как >угодно) на базе т.н. легковесных процессов, порождённых системным >вызовом clone().

это мы прошли в школе в третьем класе

>В этой реализации _каждый_ тред имеет собственный pid, т.е., >обращаться с каждым тредом можно как с отдельным процессом, в том >числе и делать ptrace() каждого из них.

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

>Узнать этот pid для каждого индивидуального треда очень просто: >достаточно при создании каждого из них делать getpid() и записывать >в глобальную для всех тредов хэш-таблицу или просто в массив.

ерунда. getpid ВСЕГДА возвращает pid процесса соответствующего главному/основному треду. не поленись разпечатать свою таблицу или массив.

>Мне нужно сделать "очень простую" вещь: тред-монитор должен >периодически асинхронно по отношению к остальным тредам узнавать их >контекст. Вот для этого и делается ptrace().

>Зная ThreadID, мы легко достаем из хэша соответствующий PID и >натравляем на данный тред (на самом деле отдельный процесс, >подчёркиваю!) связку вызовов ptrace и waitpid.

идеологически неверно. в ядре есть закладки на то что никто никогда из юзерспейса ничего не будет делать с pid "легковесных процессов" реализующих треды. следственно ....

>Почему при этом виснет ядерный fork() - и только fork()! - я не >понимаю, и наделся узнать у общественности...

потому что ты его останавливаеш при помощи ptrace из другого треда.

cvv ★★★★★
()
Ответ на: комментарий от cvv

> это мы прошли в школе в третьем класе
Неужто?.. ;)

> идеологически неверно. каждому треду соответствует TID который
> некоторым образом, известным только ядру, отображается на PID
> выполняющего его процесса. 
Не ядру, а библиотеке LinuxThreads. Ядро совершенно не при чём. Плиз,
объясни, что означает "идеологически неверно".

> ерунда. getpid ВСЕГДА возвращает pid процесса соответствующего
> главному/основному треду. не поленись разпечатать свою таблицу или
> массив.
Хм :) Не поленись написать маленький тестик и убедись сам, что в
LinuxThreads каждый тред имеет уникальный pid, - раз уж тебя так волнует
это обсуждение.

> идеологически неверно. в ядре есть закладки на то что никто никогда из
> юзерспейса ничего не будет делать с pid "легковесных процессов"
> реализующих треды. следственно .... 
Так что же значит "идеологически неверно"? А ты когда-нибудь работал
с отладчиками в Linux, с тем же GDB, например? Тебя не удивляет, что
они вообще-то работают?

>потому что ты его останавливаеш при помощи ptrace из другого треда.
ptrace(PTRACE_DETACH) посылает ему SIGCONT. По спецификации процесс
должен ожить и перейти в состояние Runnable.

jek_
() автор топика
Ответ на: комментарий от jek_

>ptrace(PTRACE_DETACH) посылает ему SIGCONT. По спецификации процесс должен ожить и перейти в состояние Runnable.

я думаю что сигнал уходит не тому процессу.

запусти под strace и посмотри чё выйдет

cvv ★★★★★
()

А что возвращает ptrace(PTRACE_DETACH, pid, NULL, NULL) ? До него вообще дело доходит? И еще я не понял, если трассировка асинхронная, то как она попадает именно в вызов fork, а не куда-то еще...
PS: Я думал, что прикладному программисту должно быть наплевать на реализацию тредов, однако вот этот код запашет только на linuxthreads ибо у них pids различны только.

Chumka ★★★
()
Ответ на: комментарий от Chumka

>однако вот этот код запашет только на linuxthreads ибо у них pids различны только.

а я и в этом сомневаюсь.

cvv ★★★★★
()
Ответ на: комментарий от Chumka

2cvv:

Под strace смотреть вообще говоря гиблое дело. Strace само использует
ptrace для трассировки, ну а ptrace под ptrace - это... :) Ничего не 
выйдет, короче говоря. Я пробовал :)

2Chumka:

Ptrace возвращает всегда 0, ошибок нет.

>то как она попадает именно в вызов fork, а не куда-то еще

Дык трассируемый тред может быть где угодно. А, я же не сказал: зависает
всё иногда, очень редко, именно в тех самых случаях, когда волею небес
тред оказывается в форке.

>Я думал, что прикладному программисту должно быть наплевать

Прикладному - согласен, а вот системному... 

>однако вот этот код запашет только на linuxthreads ибо у них pids
>различны только. 

Безусловно. Однако не составляет никакого труда сделать run-time
детектание, что перед нами именно LinuxThreads и с ним нужно вести 
себя соответственно.

jek_
() автор топика
Ответ на: комментарий от jek_

cvv:
> все стандарты требуют что дублировались *ВСЕ* треды

нет, такого не может быть никогда ...

jek_:
> which replicates only the calling thread.

??? тоже очень странно. в linux (и мне это кажется
очень правильным) будет просто создан новый процесс,
поток останется жив. это я о ядре говорю, что там
libpthread творит я не знаю, но интересно было бы
узнать, неужели поток завершится ????

по существу вопроса ничего не могу сказать ...

idle ★★★★★
()
Ответ на: комментарий от idle

>cvv:

>> все стандарты требуют что дублировались *ВСЕ* треды

>нет, такого не может быть никогда ...

не понимаю.

что это за копия процесса если в неё выгрызли только один тред??

чё он будет-то сам делать???? тем более без основного треда???

cvv ★★★★★
()
Ответ на: комментарий от cvv

> что это за копия процесса если в неё выгрызли только один тред?? чё он будет-то сам делать???? тем более без основного треда???

POSIX явным образом требует именно один, вызывающий поток.

hint: fork() небыл рассчитан на многопоточность. впрочем, как и многие другие вызовы.

// wbr

klalafuda ★☆☆
()

Думаю единственный выход, обновиться до 2.6 Я сравнил тут 2.6 и 2.4, там в do_fork() добавилось кое-что с PTRACE флаги, проверки и тд. Может повезет

Chumka ★★★
()
Ответ на: комментарий от Chumka

>Думаю единственный выход, обновиться до 2.6 Я сравнил тут 2.6 и 2.4,

Так-то оно так, я бы и не прочь, но что сказать пользователям, сидящим
на glibc 2.2.5?

jek_
() автор топика
Ответ на: комментарий от Chumka

>Думаю единственный выход, обновиться до 2.6 Я сравнил тут 2.6 и 2.4, там в do_fork() добавилось кое-что с PTRACE флаги, проверки и тд. Может повезет

нет не повезёт. там нету LinuxThreads. А на NTPL заниматся таким мазохизмом в принципе невозможно

cvv ★★★★★
()
Ответ на: комментарий от idle

2 idle: признаться, не совсем понял. Конечно, процесс создастся, но
ведь никакие потоки не должны умирать и не умирают... Не въезжаю, что
вас смущает.

jek_
() автор топика
Ответ на: комментарий от jek_

> Не въезжаю, что вас смущает.

блин, это я второпях 'replicate' прочитал как 'replace',
и решил, что thread делающий fork() должен по стандарту
умереть, что меня удивило чсрезвычайно :_)

idle ★★★★★
()
Ответ на: комментарий от idle

Возможно не совсем по теме... Интересует следующий вопрос.
Допустим один из тредов некоторого процесса сделал fork()
можно ли вызывать wait на потомка из другого треда этого же процесса.
На практике насколько я помню сделать это не получалось.
Интересно, стандартизирован ли данный случай в NPTL или нет?

Dead ★★★★
()
Ответ на: комментарий от Dead

>На практике насколько я помню сделать это не получалось.

в случае линукс-тредс скорее и невозможно а вот в случае НТПЛ надеюсь что прокатит.

cvv ★★★★★
()
Ответ на: комментарий от Dead

> можно ли вызывать wait на потомка из другого треда
> этого же процесса.

в 2.6 (судя по коду sys_wait4()) - да.

idle ★★★★★
()
Ответ на: комментарий от idle

Ок. Всем спасибо.
Надо будет как-нить на практике проверить.

Dead ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.