Đây không phải là một vấn đề cần thiết có thể giải quyết bằng cách thay đổi tùy chọn cấu hình.
Thay đổi tùy chọn cấu hình đôi khi sẽ có tác động tích cực, nhưng nó có thể dễ dàng làm mọi thứ tồi tệ hơn hoặc không làm gì cả.
Bản chất của lỗi là đây:
#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;
}
Mã ở trên có thể được biên dịch với:
gcc -g -o corrupt corrupt.c
Thực thi mã với valgrind, bạn có thể thấy nhiều lỗi bộ nhớ, đỉnh điểm là lỗi phân đoạn:
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
Nếu bạn không biết, bạn đã nhận ra đó mem
là bộ nhớ được phân bổ heap; Heap đề cập đến vùng bộ nhớ có sẵn cho chương trình khi chạy, bởi vì chương trình yêu cầu rõ ràng (với malloc trong trường hợp của chúng tôi).
Nếu bạn chơi xung quanh với mã khủng khiếp, bạn sẽ thấy rằng không phải tất cả các câu lệnh rõ ràng không chính xác đó đều dẫn đến lỗi phân đoạn (lỗi kết thúc nghiêm trọng).
Tôi đã rõ ràng mắc các lỗi đó trong mã ví dụ, nhưng các loại lỗi tương tự xảy ra rất dễ dàng trong môi trường được quản lý bộ nhớ: Ví dụ: nếu một số mã không duy trì số đếm của một biến (hoặc một số ký hiệu khác) theo cách chính xác nếu nó miễn phí quá sớm, một đoạn mã khác có thể đọc từ bộ nhớ đã có sẵn, nếu bằng cách nào đó lưu địa chỉ sai, một đoạn mã khác có thể ghi vào bộ nhớ không hợp lệ, nó có thể được miễn phí hai lần ...
Đây không phải là các vấn đề có thể được gỡ lỗi trong PHP, chúng hoàn toàn đòi hỏi sự chú ý của một nhà phát triển nội bộ.
Quá trình hành động nên là:
- Mở báo cáo lỗi trên http://bugs.php.net
- Nếu bạn có một segfault, hãy thử cung cấp một backtrace
- Bao gồm càng nhiều thông tin cấu hình có vẻ phù hợp, đặc biệt, nếu bạn đang sử dụng opcache bao gồm mức tối ưu hóa.
- Tiếp tục kiểm tra báo cáo lỗi để cập nhật, có thể yêu cầu thêm thông tin.
- Nếu bạn đã tải opcache, hãy tắt tối ưu hóa
- Tôi không chọn opcache, thật tuyệt, nhưng một số tối ưu hóa của nó đã được biết là gây ra lỗi.
- Nếu điều đó không hiệu quả, mặc dù mã của bạn có thể chậm hơn, trước tiên hãy thử tải opcache.
- Nếu bất kỳ điều này thay đổi hoặc khắc phục sự cố, hãy cập nhật báo cáo lỗi bạn đã thực hiện.
- Vô hiệu hóa tất cả các phần mở rộng không cần thiết cùng một lúc.
- Bắt đầu kích hoạt tất cả các tiện ích mở rộng của bạn một cách riêng lẻ, kiểm tra kỹ lưỡng sau mỗi lần thay đổi cấu hình.
- Nếu bạn tìm thấy phần mở rộng vấn đề, hãy cập nhật báo cáo lỗi của bạn với nhiều thông tin hơn.
- Lợi nhuận.
Có thể không có bất kỳ lợi nhuận nào ... Tôi đã nói khi bắt đầu, bạn có thể tìm cách thay đổi các triệu chứng của mình bằng cách làm rối cấu hình, nhưng điều này cực kỳ gây ra và bỏ lỡ, và không giúp ích gì cho lần sau bạn có Cùng một zend_mm_heap corrupted
thông điệp, chỉ có rất nhiều tùy chọn cấu hình.
Điều thực sự quan trọng là chúng tôi tạo báo cáo lỗi khi phát hiện thấy lỗi, chúng tôi không thể cho rằng người tiếp theo gặp lỗi sẽ làm điều đó ... nhiều khả năng hơn là không, độ phân giải thực tế không có gì bí ẩn, nếu bạn thực hiện đúng người nhận thức được vấn đề.
USE_ZEND_ALLOC
Nếu bạn đặt USE_ZEND_ALLOC=0
trong môi trường, điều này sẽ vô hiệu hóa trình quản lý bộ nhớ của Zend; Trình quản lý bộ nhớ của Zend đảm bảo rằng mỗi yêu cầu có một đống riêng, tất cả bộ nhớ đều miễn phí khi kết thúc yêu cầu và được tối ưu hóa để phân bổ các khối bộ nhớ có kích thước phù hợp cho PHP.
Vô hiệu hóa nó sẽ vô hiệu hóa các tối ưu hóa đó, quan trọng hơn là nó có thể sẽ tạo ra rò rỉ bộ nhớ, vì có rất nhiều mã mở rộng dựa trên Zend MM để giải phóng bộ nhớ cho chúng khi kết thúc yêu cầu (tut, tut).
Nó cũng có thể che giấu các triệu chứng, nhưng đống hệ thống có thể bị hỏng theo cách chính xác giống như đống của Zend.
Nó có vẻ khoan dung hơn hoặc kém khoan dung hơn, nhưng khắc phục nguyên nhân gốc rễ của vấn đề thì không thể .
Khả năng vô hiệu hóa nó, là vì lợi ích của các nhà phát triển nội bộ; Bạn không bao giờ nên triển khai PHP với Zend MM bị vô hiệu hóa.
USE_ZEND_ALLOC=0
để lấy stacktrace trong nhật ký lỗi và tìm thấy lỗi/usr/sbin/httpd: corrupted double-linked list
, tôi phát hiện ra rằng việc bình luận ra côngopcache.fast_shutdown=1
việc cho tôi.