Nie jest to problem, który można koniecznie rozwiązać poprzez zmianę opcji konfiguracyjnych.
Zmiana opcji konfiguracji będzie czasami miała pozytywny wpływ, ale równie łatwo może pogorszyć sytuację lub w ogóle nic nie robić.
Natura błędu jest następująca:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
void **mem = malloc(sizeof(char)*3);
void *ptr;
/* read past end */
ptr = (char*) mem[5];
/* write past end */
memcpy(mem[5], "whatever", sizeof("whatever"));
/* free invalid pointer */
free((void*) mem[3]);
return 0;
}
Powyższy kod można skompilować za pomocą:
gcc -g -o corrupt corrupt.c
Wykonując kod za pomocą valgrind można zobaczyć wiele błędów pamięci, których kulminacją jest błąd segmentacji:
krakjoe@fiji:/usr/src/php-src$ valgrind ./corrupt
==9749== Memcheck, a memory error detector
==9749== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==9749== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==9749== Command: ./corrupt
==9749==
==9749== Invalid read of size 8
==9749== at 0x4005F7: main (an.c:10)
==9749== Address 0x51fc068 is 24 bytes after a block of size 16 in arena "client"
==9749==
==9749== Invalid read of size 8
==9749== at 0x400607: main (an.c:13)
==9749== Address 0x51fc068 is 24 bytes after a block of size 16 in arena "client"
==9749==
==9749== Invalid write of size 2
==9749== at 0x4C2F7E3: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9749== by 0x40061B: main (an.c:13)
==9749== Address 0x50 is not stack'd, malloc'd or (recently) free'd
==9749==
==9749==
==9749== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==9749== Access not within mapped region at address 0x50
==9749== at 0x4C2F7E3: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9749== by 0x40061B: main (an.c:13)
==9749== If you believe this happened as a result of a stack
==9749== overflow in your program's main thread (unlikely but
==9749== possible), you can try to increase the size of the
==9749== main thread stack using the --main-stacksize= flag.
==9749== The main thread stack size used in this run was 8388608.
==9749==
==9749== HEAP SUMMARY:
==9749== in use at exit: 3 bytes in 1 blocks
==9749== total heap usage: 1 allocs, 0 frees, 3 bytes allocated
==9749==
==9749== LEAK SUMMARY:
==9749== definitely lost: 0 bytes in 0 blocks
==9749== indirectly lost: 0 bytes in 0 blocks
==9749== possibly lost: 0 bytes in 0 blocks
==9749== still reachable: 3 bytes in 1 blocks
==9749== suppressed: 0 bytes in 0 blocks
==9749== Rerun with --leak-check=full to see details of leaked memory
==9749==
==9749== For counts of detected and suppressed errors, rerun with: -v
==9749== ERROR SUMMARY: 4 errors from 3 contexts (suppressed: 0 from 0)
Segmentation fault
Jeśli nie wiedziałeś, już zorientowałeś się, że mem
jest to pamięć przydzielona na stos; Sterta odnosi się do obszaru pamięci dostępnego dla programu w czasie wykonywania, ponieważ program jawnie go zażądał (w naszym przypadku z malloc).
Jeśli będziesz bawić się okropnym kodem, okaże się, że nie wszystkie z tych oczywiście niepoprawnych instrukcji powodują błąd segmentacji (krytyczny błąd zakończenia).
Jawnie popełniłem te błędy w przykładowym kodzie, ale te same rodzaje błędów zdarzają się bardzo łatwo w środowisku zarządzanym pamięcią: jeśli jakiś kod nie obsługuje liczby referencyjnej zmiennej (lub innego symbolu) we właściwy sposób, na przykład jeśli zwolni to za wcześnie, inny fragment kodu może odczytać z już wolnej pamięci, jeśli w jakiś sposób zapisze nieprawidłowy adres, inny fragment kodu może zapisać do niewłaściwej pamięci, może być zwolniony dwukrotnie ...
Nie są to problemy, które można debugować w PHP, absolutnie wymagają one uwagi wewnętrznego programisty.
Sposób postępowania powinien być:
- Otwórz raport o błędzie na http://bugs.php.net
- Jeśli masz segfault, spróbuj podać ślad
- Uwzględnij tyle informacji konfiguracyjnych, ile wydaje się właściwe, w szczególności, jeśli używasz poziomu optymalizacji dołączania opcache.
- Sprawdzaj raport o błędzie pod kątem aktualizacji, możesz poprosić o więcej informacji.
- Jeśli załadowałeś opcache, wyłącz optymalizacje
- Nie czepiam się opcache, jest świetny, ale niektóre z jego optymalizacji są znane z powodowania błędów.
- Jeśli to nie zadziała, nawet jeśli twój kod może być wolniejszy, spróbuj najpierw zwolnić opcache.
- Jeśli któraś z tych zmian lub rozwiąże problem, zaktualizuj zgłoszony błąd.
- Wyłącz wszystkie niepotrzebne rozszerzenia jednocześnie.
- Zacznij włączać wszystkie rozszerzenia indywidualnie, dokładnie testując po każdej zmianie konfiguracji.
- Jeśli znajdziesz rozszerzenie powodujące problem, zaktualizuj swój raport o błędzie, podając więcej informacji.
- Zysk.
Może nie być żadnego zysku ... Powiedziałem na początku, że możesz być w stanie znaleźć sposób na zmianę swoich objawów, mieszając konfigurację, ale jest to bardzo trafione i chybione i nie pomaga następnym razem ta sama zend_mm_heap corrupted
wiadomość, jest tylko tyle opcji konfiguracyjnych.
Naprawdę ważne jest, abyśmy tworzyli raporty o błędach, gdy znajdziemy błędy, nie możemy zakładać, że zrobi to następna osoba, która trafi błąd ... bardziej prawdopodobne, że rzeczywista rozdzielczość nie jest w żaden sposób tajemnicza, jeśli zrobisz właściwi ludzie świadomi problemu.
USE_ZEND_ALLOC
Jeśli ustawisz USE_ZEND_ALLOC=0
w środowisku, wyłącza to własnego menedżera pamięci Zend; Menedżer pamięci Zend zapewnia, że każde żądanie ma własną stertę, że cała pamięć jest zwolniona na końcu żądania i jest zoptymalizowany pod kątem przydzielania fragmentów pamięci o rozmiarze odpowiednim dla PHP.
Wyłączenie go wyłączy te optymalizacje, co ważniejsze, prawdopodobnie spowoduje wycieki pamięci, ponieważ istnieje wiele kodów rozszerzeń, które zależą od Zend MM, aby zwolnić pamięć dla nich pod koniec żądania (tut, tut).
Może również ukryć objawy, ale sterta systemowa może zostać uszkodzona dokładnie w taki sam sposób, jak sterta Zend.
To może wydawać się być bardziej tolerancyjne lub mniej tolerancyjny, ale ustalić przyczynę problemu, to nie mogę .
Możliwość wyłączenia go w ogóle jest korzystna dla programistów wewnętrznych; Nigdy nie powinieneś wdrażać PHP z wyłączonym Zend MM.
USE_ZEND_ALLOC=0
pobierałem ślad stosu w dzienniku błędów I znalazłem błąd/usr/sbin/httpd: corrupted double-linked list
, dowiedziałem się, że komentowanieopcache.fast_shutdown=1
działało dla mnie.