Прошу помощи у знающих людей.Создаю процессы с помощью clone() с флагами: CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD | CLONE_SYSVSEM.
Если добавить флаг CLONE_THREAD, то все работает нормально, но этот флаш не нужен.При этом когда они начиют конкурировать между собой, возникает deadlock в функциях malloc()/free()
используется clone(), а не fork() потому что в приложении нужно шарить очень много данных.
используется clone(), а не pthread_create() потому что при смерти треда (seg fault) завершается сразу все приложение.
То есть хочется что бы и процессы между собой все делили, и при этом были независимы друг от друга в случае смерти.
В функциях glibc есть внутренние блокировки, непонятно только почему они заходят в deadlock.
Код для демонстрации:int func(void *p)
{
while (1) {
p = (char *)malloc(6);
memcpy(p, "12345\0", 5);
free(p);
}
}#define STACK_SIZE 1024*1024
int main()
{
int i;
for (i=0; i<10; i++) {
void *stack = malloc(STACK_SIZE);
int flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD | CLONE_SYSVSEM;
int id = clone(func, (char *)stack + STACK_SIZE, flags, NULL);
}sleep(10000);
return 0;
}
так в чём проблема-то? Какие дедлоки? Все потоки крутятся, ничего не залочено.
> так в чём проблема-то? Какие дедлоки? Все потоки крутятся, ничего не залочено.Только что скомпилировал код на CentOS 4.4 / 5 / 5.6
Все версии выдали:
*** glibc detected *** *** glibc detected *** ./a.out: double free or corruption (fasttop): 0x00002af4240008c0 ***
В некоторых случаях появлялся deadlock и процессы просто висели в ожидании. gdb показал что ожидание внутри free.
>> так в чём проблема-то? Какие дедлоки? Все потоки крутятся, ничего не залочено.
> Только что скомпилировал код на CentOS 4.4 / 5 / 5.6
> Все версии выдали:
> *** glibc detected *** *** glibc detected *** ./a.out: double free or
> corruption (fasttop): 0x00002af4240008c0 ***ну это логично вполне, он-же CLONE_VM делает и никак это не лечит.
> В некоторых случаях появлялся deadlock и процессы просто висели в ожидании. gdb
> показал что ожидание внутри free.это больша на багу glibc похоже, хотя с таким кодом я не удивлён.
>>> так в чём проблема-то? Какие дедлоки? Все потоки крутятся, ничего не залочено.
>> Только что скомпилировал код на CentOS 4.4 / 5 / 5.6
>> Все версии выдали:
>> *** glibc detected *** *** glibc detected *** ./a.out: double free or
>> corruption (fasttop): 0x00002af4240008c0 ***
> ну это логично вполне, он-же CLONE_VM делает и никак это не лечит.С одной стороны это логично, и это тоже первое о чем я подумал. Но тот же pthread_create тоже использует CLONE_VM и проблем никаких.
Да и malloc использует внутреннюю блокировку, что бы быть thread-safe, непонятно почему эта блокировка не спасает.
Если обернуть внешний вызов malloc/free в mutex то проблема исчезает.
>> В некоторых случаях появлялся deadlock и процессы просто висели в ожидании. gdb
>> показал что ожидание внутри free.
> это больша на багу glibc похоже, хотя с таким кодом я не
> удивлён.Про код опишите, пожалуйста, что именно в нем плохо? Специально синтетический тест на конкуренцию malloc/free.
>>>> так в чём проблема-то? Какие дедлоки? Все потоки крутятся, ничего не залочено.
>>> Только что скомпилировал код на CentOS 4.4 / 5 / 5.6
>>> Все версии выдали:
>>> *** glibc detected *** *** glibc detected *** ./a.out: double free or
>>> corruption (fasttop): 0x00002af4240008c0 ***
>> ну это логично вполне, он-же CLONE_VM делает и никак это не лечит.
> С одной стороны это логично, и это тоже первое о чем я
> подумал. Но тот же pthread_create тоже использует CLONE_VM и проблем никаких.так вы-ж его не иначе с -pthread собираете? :)
> Да и malloc использует внутреннюю блокировку, что бы быть thread-safe,
без флага pthread? или с флагом но без pthread_create? серьезно? :)
> непонятно почему
> эта блокировка не спасает.
> Если обернуть внешний вызов malloc/free в mutex то проблема исчезает.
>>> В некоторых случаях появлялся deadlock и процессы просто висели в ожидании. gdb
>>> показал что ожидание внутри free.
>> это больша на багу glibc похоже, хотя с таким кодом я не
>> удивлён.
> Про код опишите, пожалуйста, что именно в нем плохо? Специально синтетический тест
> на конкуренцию malloc/free.вы думаете он знает зачем его создали? :) ему вообщем плевать, он и malloc/malloc и free/free - поконкурирует на раз. И если там есть бага - легко поймает. Вы хотели багу рэйса - вы поймали багу рэйса (возможно, требует проверки все-же). Вы удивлены? Вас не Вова зовут?(шучу) :)
>[оверквотинг удален]
>>>> Только что скомпилировал код на CentOS 4.4 / 5 / 5.6
>>>> Все версии выдали:
>>>> *** glibc detected *** *** glibc detected *** ./a.out: double free or
>>>> corruption (fasttop): 0x00002af4240008c0 ***
>>> ну это логично вполне, он-же CLONE_VM делает и никак это не лечит.
>> С одной стороны это логично, и это тоже первое о чем я
>> подумал. Но тот же pthread_create тоже использует CLONE_VM и проблем никаких.
> так вы-ж его не иначе с -pthread собираете? :)
>> Да и malloc использует внутреннюю блокировку, что бы быть thread-safe,
> без флага pthread? или с флагом но без pthread_create? серьезно? :)Я собирал и без -pthread и с ней. добавлял -D_REENTRANT - все без результатно.
но... по вашему "намеку" попробовал добавить в код pthread_create(), который создает тред и сразу его закрывает. все заработало.
Очевидно что pthread_create что то проинициализировал. Я смотрел его код, но инициализацией связанных с памятью или локами не нашел (кроме стека).Спасибо Вам за помощь.
Если возможно, подскажите, что именно инициализирует pthread_create()? что бы не использовать его как "хак".
>[оверквотинг удален]
>>> подумал. Но тот же pthread_create тоже использует CLONE_VM и проблем никаких.
>> так вы-ж его не иначе с -pthread собираете? :)
>>> Да и malloc использует внутреннюю блокировку, что бы быть thread-safe,
>> без флага pthread? или с флагом но без pthread_create? серьезно? :)
> Я собирал и без -pthread и с ней. добавлял -D_REENTRANT - все
> без результатно.
> но... по вашему "намеку" попробовал добавить в код pthread_create(), который создает тред
> и сразу его закрывает. все заработало.
> Очевидно что pthread_create что то проинициализировал. Я смотрел его код, но инициализацией
> связанных с памятью или локами не нашел (кроме стека).не, я имел ввиду pthread_create для создания потока вместо clone. Вы показали ваш собственный фокус. (весьма занятный, кстати. но я-бы не рассчитывал, что этот код будет работать всегда)
> Спасибо Вам за помощь.
незачто и судя по-тому, что вы хотели clone() "потому что при смерти треда (seg fault) завершается сразу все приложение." - вы еще не пробовали посылать sigsegv ни одному из ваших "работающих" клонов. SIGSEGV, кстати, маскируется и обрабатывается.
> Если возможно, подскажите, что именно инициализирует pthread_create()? что бы не использовать
> его как "хак".это "хак" и я-бы его не использовал.
> незачто и судя по-тому, что вы хотели clone() "потому что при смерти
> треда (seg fault) завершается сразу все приложение." - вы еще не
> пробовали посылать sigsegv ни одному из ваших "работающих" клонов. SIGSEGV, кстати,
> маскируется и обрабатывается.Этот момент я хорошо тестировал. SIGSEGV вызывал разными способами.
В случае с клонами (с указанными флагами) SIGSEGV ловится слоном, вызывается exit() и клон умирает. Родитель обрабатывает waitpid и продолжает работать.
В случае с тредами (pthread_create или флаг CLONE_THREAD) SIGSEGV тоже ловится, но ловит его родитель и продолжать работать не может. Умирает родитель и умирают все поражденные треды.
Блин, вы С учить будете? А то glibc, треды... придумали тоже... :)У Вас указатель *stack ВСЕ ВРЕМЯ указывает на один и тот же сегмент,
над которым затем трахаются по 10 копий malloc и free!!!
Да, на уровне ядра есть блокировки, собственно благодаря которым вы и получаете SIGSEGV,
а не смерть всего компа.Ниже я показал, что надо юзать указатель на указатель (можно массив указателей)
> Блин, вы С учить будете? А то glibc, треды... придумали тоже... :)Буду рад, если Вы подскажите что почитать для получения новых знаний.
> У Вас указатель *stack ВСЕ ВРЕМЯ указывает на один и тот же
> сегмент,
> над которым затем трахаются по 10 копий malloc и free!!!Мой код:
for (i=0; i<10; i++) {
void *stack = malloc(STACK_SIZE);
}переменной stack присывается новое значение, так что *stack никак не может указывать на одинаковый сегмент памяти.
> Ниже я показал, что надо юзать указатель на указатель (можно массив указателей)Кусок Вашего кода:
stack = malloc(STACK_SIZE);
for (i = 0; i < 10; i++) {
*stack = (void *)(STACK_SIZE + (char *)stack);
clone(func, stack, FLAGS, NULL);Вы выделили память размером STACK_SIZE,
затем в первый байт записали ссылку на конец выделенной памяти
передали в clone ссылку на выделенную память.У доке четко написано:
The child_stack argument specifies the location of the stack used by the child process. ... The calling process must therefore set up memory space for the child stack and pass a pointer to this space to clone().
В переводе это означает что передавать нужно ссылку на память, а не ссылку на ссылку на память.
Даже, если предположить что нужно передавать последнее, то Ваш код все равно не работает так как во все 10 клонов он передаст один и тот же указатель.
P.S. Ваш код я скомпилировал и запустил, как результат:
kernel: a.out[28746]: segfault at 0000000000000000 rip 0000000000000000 rsp 00002b18a9b79008 error 14
>[оверквотинг удален]
> В переводе это означает что передавать нужно ссылку на память,
> а не ссылку на ссылку на память.Чуток не правильно понимаешь свойства работы с указателем на указатель.
Ну на, в твоем виде
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sched.h>
#include <signal.h>
#define STACK_SIZE (1024*1024)
#define FLAGS (CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_SYSVSEM|SIGCHLD)int func(void *p) {
while (1) {
p = (char *)malloc(6);
memcpy(p, "12345\0", 6);
free(p);
}
}int main() {
int i;
void **stack;
for (i = 0; i < 10; i++) {
stack = (void **)malloc(STACK_SIZE);
clone(func, stack, FLAGS, NULL);
}sleep(10);
return 0;
}
>>[оверквотинг удален]
>> В переводе это означает что передавать нужно ссылку на память,
>> а не ссылку на ссылку на память.
> Чуток не правильно понимаешь свойства работы с указателем на указатель.
> Ну на, в твоем видеС Вашего позволения, тоже перейду на "ты".
Объясни, мне пожалуйста, что я неправильно понимаю. Можно ссылкой в ман, гугл или куда угодно.
Что касается кода, правильно ли я понял фразу "Ну на, в твоем виде" - означает, что 2 кода которые ты привел они идентичны, только "вид" разный?
Для наглядности:первый:
void **stack;
for (i = 0; i < 10; i++) {
stack = (void **)malloc(STACK_SIZE);
clone(func, stack, FLAGS, NULL);
}
второй:
void **stack;
stack = malloc(STACK_SIZE);
for (i = 0; i < 10; i++) {
*stack = (void *)(STACK_SIZE + (char *)stack);
clone(func, stack, FLAGS, NULL);
}
>>>[оверквотинг удален]Короче не ..би моск
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;int func(void *ptr) {
char *msg = (char *) ptr;
while (1) {
pthread_mutex_lock(&lock);
msg = malloc( 6);
strncpy(msg, "12345\0", 6);
free(msg);
pthread_mutex_unlock(&lock);
}
return 0;
}Кстати, размер стека лучше узнавать у getrlimit(RLIMIT_STACK, rlim) ...
int func(void *p)
{
while (1) {
p = (char *)malloc(6);
memcpy(p, "12345\0", 6);
free(p);
}
}#define STACK_SIZE (1024*1024)
#define FLAGS (CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_SYSVSEM|SIGCHLD)int main()
{
int i;
void **stack;stack = malloc(STACK_SIZE);
for (i = 0; i < 10; i++) {
*stack = (void *)(STACK_SIZE + (char *)stack);
clone(func, stack, FLAGS, NULL);
}
sleep(10000);
return 0;
}Криво, ибо пример такой.
Давай реальную задачу.
> При этом когда они начиют конкурировать между собой, возникает deadlock в функциях
> malloc()/free()Конечно возникает. Потому что память одного процесса неожиданно редактирует другой процесс.
Если какой-то поток вызывает SIGSEGV - значит вся память испорчена. Общая, всего процесса!!
Ничего хорошего в работе программы с испорченной памятью не может быть.
>> При этом когда они начиют конкурировать между собой, возникает deadlock в функциях
>> malloc()/free()
> Конечно возникает. Потому что память одного процесса неожиданно редактирует другой процесс.Так вопрос-то был соответствующий - почему так. malloc/free они ведь thread-safe :)
> Если какой-то поток вызывает SIGSEGV - значит вся память испорчена. Общая, всего
> процесса!!Кстати, это неправильное утверждение.
SIGSEGV is the signal sent to a process when it makes an invalid memory reference, or segmentation fault.