2017-03-27 166 views
1

我幾天來一直在爲一個神祕的錯誤而掙扎。我使用PHP 7.1.0RC3(我用ZTS/pthreads啓用重新編譯自己)。最近,我一直在研究一個用Redis替換MySQL的重構,以優化我的應用程序中無磁盤數據的I/O。PHP + PThreads + Redis/Predis = zend_mm_heap是否損壞?

我有一個腳本爲每個加密貨幣市場創建一個線程(A)。線程(A)爲每個交易策略創建另一個線程(B)。 B線程始終在A線程之前同步。我不斷收到此錯誤:zend_mm_heap corrupted。每次運行腳本時,它都會在執行的不同位置發生。

我已經嘗試了所有建議的修補程序,100個Google網頁。垃圾收集,PHP配置/編譯,所有內容都經過詳細審查。我還沒有找到與「zend_mm_heap損壞」什麼,並行線程和Redis的/ Predis,而其他的「修復」有沒有效果...

所有的數據讀取並保存到Redis的(通過Predis) 。只有在線程A或線程B中使用Predis時纔會顯示錯誤消息(每次更改)。當兩個Predis方法調用在快速連續發生時發生的頻率更高(例如,設置得到)。

如果我使用shell_exec()直接通過CLI運行Redis命令,我看不到這個錯誤。但是,它顯着較慢,所以可能只是缺少這個邊緣情況?

如果任何人都可以給任何輸入/向以下任何想法,我將不勝感激:

  1. 我如何才能找到這個地方是從我的應用程序來嗎?
  2. 專用的Redis服務器可能會解決這個問題嗎?
  3. 我應該向https://github.com/krakjoe/pthreads提交缺陷報告嗎?
  4. 我應該向https://github.com/nrk/predis提交缺陷報告嗎?
  5. 有誰知道爲什麼zend_mm_heap corrupted可能會出現在一般的線程「安全」環境(又名:ZTS)?或者我應該如何防範呢?
  6. 我是否應該嘗試編譯甚至更​​多不同版本的PHP 7+? https://github.com/php/php-src

我沒有選擇,請有憐憫。我可以共享代碼示例,但在這裏粘貼它們並不會很有用。

編輯 我重新編譯PHP與./configure --enable-maintainer-zts --enable-pthreads --with-pthreads-sanitize --with-mysqli --enable-embedded-mysqli,我看到了一個新的錯誤之前,我還沒有看到:

==2716== ERROR: AddressSanitizer: heap-use-after-free on address 0x600800001c15 at pc 0xe25a87 bp 0x7f7a049feae0 sp 0x7f7a049fead0 
READ of size 1 at 0x600800001c15 thread T1632 
    #0 0xe25a86 (/usr/local/bin/php+0xe25a86) 
    #1 0xe953c8 (/usr/local/bin/php+0xe953c8) 
    #2 0xe4935b (/usr/local/bin/php+0xe4935b) 
    #3 0xcbede3 (/usr/local/bin/php+0xcbede3) 
    #4 0x98cfa9 (/usr/local/bin/php+0x98cfa9) 
    #5 0x99dc3c (/usr/local/bin/php+0x99dc3c) 
    #6 0x7f7b57e74a97 (/usr/lib64/libasan.so.0.0.0+0x19a97) 
    #7 0x7f7b5694edc4 (/usr/lib64/libpthread-2.17.so+0x7dc4) 
    #8 0x7f7b5667d73c (/usr/lib64/libc-2.17.so+0xf773c) 
0x600800001c15 is located 5 bytes inside of 48-byte region [0x600800001c10,0x600800001c40) 
freed by thread T1623 here: 
    #0 0x7f7b57e71009 (/usr/lib64/libasan.so.0.0.0+0x16009) 
    #1 0xe259fb (/usr/local/bin/php+0xe259fb) 
    #2 0xe953c8 (/usr/local/bin/php+0xe953c8) 
    #3 0xe4935b (/usr/local/bin/php+0xe4935b) 
    #4 0xcbede3 (/usr/local/bin/php+0xcbede3) 
    #5 0x98cfa9 (/usr/local/bin/php+0x98cfa9) 
    #6 0x99dc3c (/usr/local/bin/php+0x99dc3c) 
    #7 0x7f7b57e74a97 (/usr/lib64/libasan.so.0.0.0+0x19a97) 
previously allocated by thread T0 here: 
    #0 0x7f7b57e71129 (/usr/lib64/libasan.so.0.0.0+0x16129) 
    #1 0xdb0118 (/usr/local/bin/php+0xdb0118) 
    #2 0xe64f6c (/usr/local/bin/php+0xe64f6c) 
    #3 0xe68c5d (/usr/local/bin/php+0xe68c5d) 
    #4 0xcc4dcc (/usr/local/bin/php+0xcc4dcc) 
    #5 0xcc5972 (/usr/local/bin/php+0xcc5972) 
    #6 0x111f82c (/usr/local/bin/php+0x111f82c) 
    #7 0x44c781 (/usr/local/bin/php+0x44c781) 
    #8 0x7f7b565a7b34 (/usr/lib64/libc-2.17.so+0x21b34) 
Thread T1632 created by T0 here: 
    #0 0x7f7b57e65c3a (/usr/lib64/libasan.so.0.0.0+0xac3a) 
    #1 0x99e4cb (/usr/local/bin/php+0x99e4cb) 
    #2 0x975d2a (/usr/local/bin/php+0x975d2a) 
    #3 0x11105c1 (/usr/local/bin/php+0x11105c1) 
    #4 0xf71de2 (/usr/local/bin/php+0xf71de2) 
    #5 0x97b385 (/usr/local/bin/php+0x97b385) 
    #6 0x11126bc (/usr/local/bin/php+0x11126bc) 
    #7 0xf71de2 (/usr/local/bin/php+0xf71de2) 
    #8 0x97b385 (/usr/local/bin/php+0x97b385) 
    #9 0x11126bc (/usr/local/bin/php+0x11126bc) 
    #10 0xf71de2 (/usr/local/bin/php+0xf71de2) 
    #11 0x97b385 (/usr/local/bin/php+0x97b385) 
    #12 0x11126bc (/usr/local/bin/php+0x11126bc) 
    #13 0xf71de2 (/usr/local/bin/php+0xf71de2) 
    #14 0x97b385 (/usr/local/bin/php+0x97b385) 
    #15 0x11126bc (/usr/local/bin/php+0x11126bc) 
    #16 0xf71de2 (/usr/local/bin/php+0xf71de2) 
    #17 0x97b385 (/usr/local/bin/php+0x97b385) 
    #18 0x1110b28 (/usr/local/bin/php+0x1110b28) 
    #19 0xf71de2 (/usr/local/bin/php+0xf71de2) 
    #20 0x97b385 (/usr/local/bin/php+0x97b385) 
    #21 0x11126bc (/usr/local/bin/php+0x11126bc) 
    #22 0xf71de2 (/usr/local/bin/php+0xf71de2) 
    #23 0x97b385 (/usr/local/bin/php+0x97b385) 
    #24 0x111c28a (/usr/local/bin/php+0x111c28a) 
    #25 0xe52281 (/usr/local/bin/php+0xe52281) 
    #26 0xcc766f (/usr/local/bin/php+0xcc766f) 
    #27 0x1121d36 (/usr/local/bin/php+0x1121d36) 
    #28 0x44d202 (/usr/local/bin/php+0x44d202) 
    #29 0x7f7b565a7b34 (/usr/lib64/libc-2.17.so+0x21b34) 
Thread T1623 created by T0 here: 
    #0 0x7f7b57e65c3a (/usr/lib64/libasan.so.0.0.0+0xac3a) 
    #1 0x99e4cb (/usr/local/bin/php+0x99e4cb) 
    #2 0x975d2a (/usr/local/bin/php+0x975d2a) 
    #3 0x11105c1 (/usr/local/bin/php+0x11105c1) 
    #4 0xf71de2 (/usr/local/bin/php+0xf71de2) 
    #5 0x97b385 (/usr/local/bin/php+0x97b385) 
    #6 0x11126bc (/usr/local/bin/php+0x11126bc) 
    #7 0xf71de2 (/usr/local/bin/php+0xf71de2) 
    #8 0x97b385 (/usr/local/bin/php+0x97b385) 
    #9 0x11126bc (/usr/local/bin/php+0x11126bc) 
    #10 0xf71de2 (/usr/local/bin/php+0xf71de2) 
    #11 0x97b385 (/usr/local/bin/php+0x97b385) 
    #12 0x11126bc (/usr/local/bin/php+0x11126bc) 
    #13 0xf71de2 (/usr/local/bin/php+0xf71de2) 
    #14 0x97b385 (/usr/local/bin/php+0x97b385) 
    #15 0x11126bc (/usr/local/bin/php+0x11126bc) 
    #16 0xf71de2 (/usr/local/bin/php+0xf71de2) 
    #17 0x97b385 (/usr/local/bin/php+0x97b385) 
    #18 0x1110b28 (/usr/local/bin/php+0x1110b28) 
    #19 0xf71de2 (/usr/local/bin/php+0xf71de2) 
    #20 0x97b385 (/usr/local/bin/php+0x97b385) 
    #21 0x11126bc (/usr/local/bin/php+0x11126bc) 
    #22 0xf71de2 (/usr/local/bin/php+0xf71de2) 
    #23 0x97b385 (/usr/local/bin/php+0x97b385) 
    #24 0x111c28a (/usr/local/bin/php+0x111c28a) 
    #25 0xe52281 (/usr/local/bin/php+0xe52281) 
    #26 0xcc766f (/usr/local/bin/php+0xcc766f) 
    #27 0x1121d36 (/usr/local/bin/php+0x1121d36) 
    #28 0x44d202 (/usr/local/bin/php+0x44d202) 
    #29 0x7f7b565a7b34 (/usr/lib64/libc-2.17.so+0x21b34) 
Shadow bytes around the buggy address: 
    0x0c017fff8330: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 fa 
    0x0c017fff8340: fa fa 00 00 00 00 00 fa fa fa 00 00 00 00 00 fa 
    0x0c017fff8350: fa fa 00 00 00 00 00 fa fa fa 00 00 00 00 00 00 
    0x0c017fff8360: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 fa 
    0x0c017fff8370: fa fa 00 00 00 00 00 fa fa fa 00 00 00 00 00 00 
=>0x0c017fff8380: fa fa[fd]fd fd fd fd fd fa fa 00 00 00 00 00 00 
    0x0c017fff8390: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 00 
    0x0c017fff83a0: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 00 
    0x0c017fff83b0: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 00 
    0x0c017fff83c0: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 fa 
    0x0c017fff83d0: fa fa 00 00 00 00 00 fa fa fa 00 00 00 00 00 fa 
Shadow byte legend (one shadow byte represents 8 application bytes): 
    Addressable:   00 
    Partially addressable: 01 02 03 04 05 06 07 
    Heap left redzone:  fa 
    Heap righ redzone:  fb 
    Freed Heap region:  fd 
    Stack left redzone: f1 
    Stack mid redzone:  f2 
    Stack right redzone: f3 
    Stack partial redzone: f4 
    Stack after return: f5 
    Stack use after scope: f8 
    Global redzone:  f9 
    Global init order:  f6 
    Poisoned by user:  f7 
    ASan internal:   fe 
==2716== ABORTING 

回答

0

我找到了內存損壞的根源,而「修復」很簡單。我發現在pthreads中使用phpiredis $客戶端的唯一「非致命」方式是在當前本地作用域中顯式連接/執行命令/斷開連接。

概括起來,在內存損壞下面的結果時,在高負荷下:

  1. 嘗試一個phpiredis $客戶端通過參考在螺紋上下文傳遞給另一個對象
  2. 試圖傳遞phpiredis $客戶端在任何地方線程上下文另一目的是通過價值
  3. 試圖實例/使用phpiredis $客戶端除了在線程上下文本地範圍內(例如:在另一個類,_Redis,當我們目前在投資者::保存)
  4. 嘗試st原則上重新使用線程中的phpiredis連接

但是,當以相同的方式/上下文將MySQLi $客戶端傳遞給另一個對象時,不會發生內存損壞。

我希望這可以拯救別人我所目睹的絕望的一週。這是如何在pthreads內使用phpiredis的:

// Instantiate a local raw phpiredis $client and execute get/set commands using $client directly 
// @Result: This WORKS :D 
$client = phpiredis_connect('127.0.0.1', 6379); 
$key = "table_name-_-$this->id"; 
foreach($args as $prop=>$val) 
    if(isset($prop) && isset($val)) 
     phpiredis_command($client, "hset $key $prop $val"); 
$obj = phpiredis_command($client, "hgetall $key"); 
echo json_encode($obj); 
phpiredis_disconnect($client);