Тупик (вилка + malloc) libc (glibc-2.17, glibc-2.23)

Я столкнулся с довольно неприятной проблемой: у меня есть программа, которая создает один поток в начале, этот поток запускает другие вещи во время своего выполнения (fork () сразу же, за которым следует execve ()).

Вот bt обоих потоков в точке, где моя программа достигла (я думаю) тупика:

Поток 2 (LWP 8839):

# 0 0x00007ffff6cdf736 в __libc_fork () по адресу ../sysdeps/nptl/fork.c:125

# 1 0x00007ffff6c8f8c0 в _IO_new_proc_open (fp = fp @ entry = 0x7ffff00031d0, command = command @ entry = 0x7ffff6c26e20 "ps -u brejon | grep \" cvc \ "

# 2 0x00007ffff6c8fbcc в _IO_new_popen (command = 0x7ffff6c26e20 "ps -u user | grep \" cvc \ "| wc -l", mode = 0x42c7fd "r") на iopopen.c: 296

#3-4 ...

# 5 0x00007ffff74d9434 в start_thread (arg = 0x7ffff6c27700) в pthread_create.c: 333

# 6 0x00007ffff6d0fcfd в clone () по адресу ../sysdeps/unix/sysv/linux/x86_64/clone.S:109

Поток 1 (LWP 8835):

# 0 __lll_lock_wait_private () в ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:95

# 1 0x00007ffff6ca0ad9 в malloc_atfork (sz = 140737337120848, caller =) на arena.c: 179

# 2 0x00007ffff6c8d875 в __GI__IO_file_doallocate (fp = 0x17a72230) в filedoalloc.c: 127

# 3 0x00007ffff6c9a964 в __GI__IO_doallocbuf (fp = fp @ entry = 0x17a72230) на genops.c: 398

# 4 0x00007ffff6c99de8 в _IO_new_file_overflow (f = 0x17a72230, ch = -1) в fileops.c: 820

# 5 0x00007ffff6c98f8a в _IO_new_file_xsputn (f = 0x17a72230, data = 0x17a16420, n = 682) на fileops.c: 1331

# 6 0x00007ffff6c6fcb2 в _IO_vfprintf_internal (s = 0x17a72230, format =, ap = ap @ entry = 0x7fffffffcf18) по адресу vfprintf.c: 1632

# 7 0x00007ffff6c76a97 в __fprintf (stream =, format =) в fprintf.c: 32

#8-11 ...

# 12 0x000000000042706e в main (argc = 3, argv = 0x7fffffffd698, envp = 0x7fffffffd6b8) в mains / ignore / .c: 146

Оба остаются здесь навсегда с glibc-2.17 и glibc-2.23

Приветствуется любая помощь: 'D

РЕДАКТИРОВАТЬ :

Вот минимальный пример:

  1 #include <stdlib.h>
  2 #include <pthread.h>
  3 #include <unistd.h>
  4 
  5 void * thread_handler(void * args)
  6 {
  7         char * argv[] = { "/usr/bin/ls" };
  8         char * newargv[] = { "/usr/bin/ls", NULL };
  9         char * newenviron[] = { NULL };
 10         while (1) {
 11                 if (vfork() == 0) {
 12                         execve(argv[0], newargv, newenviron);
 13                 }
 14         }
 15 
 16         return 0;
 17 }
 18 
 19 int main(void)
 20 {
 21         pthread_t thread;
 22         pthread_create(&thread, NULL, thread_handler, NULL);
 23 
 24         int * dummy_alloc;
 25 
 26         while (1) {
 27                 dummy_alloc = malloc(sizeof(int));
 28                 free(dummy_alloc);
 29         }
 30 
 31         return 0;
 32 }

Среда: пользователь: тупик $ cat / etc / redhat-release

Версия 7.3 Scientific Linux (Азот)

пользователь: deadlock $ ldd --version

ldd (GNU libc) 2.17

РЕДАКТИРОВАТЬ 2: версия пакета rpm: glibc-2.17-196.el7.x86_64

Я не могу получить номера строк с помощью пакета rpm. Вот BT, использующий glibc из дистрибутива: решено с помощью debuginfo.

(gdb) поток применить все bt

Тема 2 (Тема 0x7ffff77fb700 (LWP 59753)):

# 0 vfork () в ../sysdeps/unix/sysv/linux/x86_64/vfork.S:44

# 1 0x000000000040074e в thread_handler (args = 0x0) в deadlock.c: 11

# 2 0x00007ffff7bc6e25 в start_thread (arg = 0x7ffff77fb700) в pthread_create.c: 308

# 3 0x00007ffff78f434d в clone () в ../sysdeps/unix/sysv/linux/x86_64/clone.S:113

Тема 1 (Тема 0x7ffff7fba740 (LWP 59746)):

# 0 0x00007ffff7878226 в _int_free (av = 0x7ffff7bb8760, p = 0x602240, have_lock = 0) на malloc.c: 3927

# 1 0x00000000004007aa в main () в deadlock.c: 28


person bob boby    schedule 23.10.2017    source источник
comment
Можете ли вы опубликовать минимальный, полный и проверяемый пример?   -  person mgarey    schedule 23.10.2017
comment
Пожалуйста, разместите код.   -  person Andrew Henle    schedule 23.10.2017
comment
Привет, вызов fork в многопоточных программах может привести к проблемам, при вызове в потоке он копирует все адресное пространство, но не клонирует ни один поток, кроме вызвавшего его потока. Что привело бы к постоянной блокировке ресурсов. Это поведение определено в POSIX.   -  person NishanthSpShetty    schedule 23.10.2017
comment
В исходном коде используется fork, но в вашем примере используется vfork, поэтому он не репрезентативен. Какая у вас среда? Интересно, на какой именно блокировке блокируется __libc_fork (т.е. что находится в строке 125?). Всего две темы? Что-нибудь, использующее потоки C ++? Или звонит fflush (NULL)? Мы, безусловно, исправили некоторые ошибки в этой области на протяжении многих лет.   -  person Florian Weimer    schedule 23.10.2017
comment
Я читал, что вызов fork в многопоточной программе будет копировать только вызывающий поток в дочернем процессе, меня полностью устраивает такое поведение. И, как вы можете видеть в минимальном примере, я не вызываю никаких функций между fork () и execve ().   -  person bob boby    schedule 23.10.2017
comment
@FlorianWeimer Это тоже происходит с fork (). Моя среда It a Scientific Linux 7.3. В строке 125: вызов этого макроса: 24 #define ARCH_FORK () \ 25 INLINE_SYSCALL (clone, 4, \ 26 CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD, 0, \ 27 NULL, & THREAD_SELF- ›tid) Нет, только два потока. И только код C.   -  person bob boby    schedule 23.10.2017
comment
@bob, а как насчет других моих вопросов? Пожалуйста, также добавьте в свой пост точную версию RPM glibc.   -  person Florian Weimer    schedule 23.10.2017
comment
@bob, я сомневаюсь, что опубликованная обратная трассировка относится к среде Scientific Linux 7.3. Red Hat Enterprise Linux 7 по крайней мере не имеет nptl/sysdeps очистки, поэтому в нем нет sysdeps/nptl/fork.c файла. Вам действительно нужно предоставить точную информацию о вашей среде, чтобы мы могли разобраться в обратной трассировке.   -  person Florian Weimer    schedule 23.10.2017
comment
@FlorianWeimer, это libc, которую я скомпилировал сам, я не могу гарантировать, что она была сделана безупречно. Но это Scientific Linux 7.3: cat / etc / redhat-release Scientific Linux версии 7.3 (азот)   -  person bob boby    schedule 23.10.2017
comment
@NishanthShetty Привет, вызов fork в многопоточных программах может привести к проблемам, при вызове в потоке он копирует все адресное пространство, но не клонирует ни один поток, кроме вызвавшего его потока. Что привело бы к постоянной блокировке ресурсов. Это поведение определено в POSIX. POSIX определяет fork() как безопасное для асинхронных сигналов, что является гораздо более строгим, чем просто безопасное для многопоточности. Это не проблема POSIX.   -  person Andrew Henle    schedule 23.10.2017


Ответы (1)


Это специально скомпилированный файл glibc. Возможно, что-то пошло не так с установкой. Обратите внимание, что Red Hat Enterprise Linux 7.4 поддерживает исправление тупиковой ситуации между malloc и fork, и вам этого не хватает, потому что вы скомпилировали свой собственный glibc. исправление ошибки основной ветки вошло только в исходную версию 2.24, поэтому, если вы основываете на этом свою кастомную сборку, возможно, у вас нет этого исправления. (Хотя для этого обратная трассировка выглядела бы иначе.)

Думаю, мы исправили по крайней мере еще одну ошибку тупика, связанную с libio после версии 2.17.

ИЗМЕНИТЬ. Я слишком долго имел дело с взаимоблокировками, связанными с вилкой. Как указано в сообщении, существует несколько проблем с воспроизведением:

  • Нет waitpid вызова для PID. В результате таблица процессов быстро заполнится зомби.
  • Нет проверки ошибок для execve. Если /usr/bin/ls путь не существует (например, в системе, которая не подвергалась UsrMove) , execve вернется, и следующая итерация цикла запустит другой vfork вызов.

Я исправил обе проблемы (потому что отлаживать то, что приближается к вилочной бомбе, совсем не весело), ​​но я не могу воспроизвести зависание с glibc-2.17-196.el7.x86_64.

person Florian Weimer    schedule 23.10.2017
comment
Тупиковая ситуация возникает и с libc (2.17) из дистрибутива (7.3), поэтому у меня должно быть исправление с обратным переносом (что не является исправлением ошибки, которую я получил, если это так). Попробую 2.24. - person bob boby; 24.10.2017
comment
Опубликуйте трассировку с номерами строк из дистрибутива и точной версией пакета RPM, чтобы мы могли сопоставить трассировку с исходным кодом. - person Florian Weimer; 24.10.2017
comment
Я отредактировал первый пост с этой информацией, но не смог получить номера строк для дистрибутива libc. - person bob boby; 24.10.2017
comment
Попробуйте установить debuginfo пакеты - сначала установите yum-utils, а затем запустите debuginfo-install glibc. Спасибо. - person Florian Weimer; 24.10.2017
comment
Спасибо за помощь, вы можете найти обновленную трассировку с номерами строк в первом посте. - person bob boby; 24.10.2017
comment
Спасибо, с тех пор я понял, что у опубликованного репродуктора есть собственные проблемы, поэтому он на самом деле не показывает ошибку glibc. - person Florian Weimer; 24.10.2017
comment
Я не могу воспроизвести ошибку после исправления репродуктора предложениями @FlorianWeimer. Спасибо за ваше время ... Думаю, я просто писал плохой код ... - person bob boby; 24.10.2017