Слишком много звонков в mprotect - PullRequest
3 голосов
/ 11 мая 2009

Я работаю над параллельным приложением (C, pthread). Я проследил системные вызовы, потому что в какой-то момент у меня были плохие параллельные работы. Мои следы показали, что моя программа вызывает mprotect() много-много раз ... достаточно, чтобы значительно замедлить мою программу.

Я выделяю много памяти (с malloc()), но есть только разумное количество вызовов brk() для увеличения размера кучи. Так почему так много звонков на mprotect()?!

Ответы [ 4 ]

3 голосов
/ 12 мая 2009

Вы создаете и уничтожаете множество тем?

Большинство реализаций pthread добавляют «защитную страницу» при выделении стека потоков. Это защищенная от доступа страница памяти, используемая для обнаружения переполнения стека. Я бы ожидал, что по крайней мере один вызов mprotect будет создаваться или прерываться каждый раз, чтобы (не) защитить защитную страницу. Если это так, есть несколько очевидных стратегий:

  1. Установите размер страницы защиты на ноль, используя pthread_attr_setguardsize() перед созданием темы.
  2. Используйте пул потоков (столько потоков, сколько говорят процессоры). После завершения потока с задачей верните его в пул, чтобы получить новую задачу, а не завершать ее и создавать новый поток.

Другим объяснением может быть то, что вы находитесь на платформе, где стек потока будет увеличен в случае обнаружения переполнения. Я не думаю, что это реализовано в Linux с GCC / Glibc, но в последнее время были сделаны некоторые предложения в этом направлении. Если вы используете много места в стеке во время обработки, вы можете явно увеличить начальный / минимальный размер стека, используя pthread_attr_setstacksize.

Или это может быть что-то совсем другое!

2 голосов
/ 10 октября 2011

Библиотека glibc, имеющая ptmalloc2 для своего malloc, использует mprotect () для микроуправления кучей для потоков, отличных от основного потока (для основного потока используется sbrk ().) Malloc () сначала выделяет большой кусок памяти с помощью mmap () для потока, если область кучи, кажется, имеет конфликт, и затем он изменяет защитные биты ненужной части, чтобы сделать ее доступной с помощью mprotect (). Позже, когда ему нужно увеличить кучу, он снова меняет защиту на чтение / запись с помощью mprotect (). Эти вызовы mprotect () предназначены для увеличения кучи и сжатия в многопоточных приложениях.

http://www.blackhat.com/presentations/bh-usa-07/Ferguson/Whitepaper/bh-usa-07-ferguson-WP.pdf объясняет это немного более подробно.

2 голосов
/ 11 мая 2009

Если вы можете, запустите вашу программу под отладкой libc и остановитесь на mprotect (). Посмотрите на стек вызовов, посмотрите, что делает ваш код, который приводит к вызовам mprotect ().

0 голосов
/ 14 августа 2011

В наборе 'valgrind' есть инструмент под названием 'callgrind', который скажет вам, что называется, что. Если вы запускаете приложение в «callgrind», вы можете просмотреть полученные данные профиля с помощью «kcachegrind» (оно может анализировать профили, созданные «cachegrind» или «callgrind»). Затем просто дважды щелкните «mprotect» на левой панели, и он покажет вам, какой код его вызывает и сколько раз.

...