草庐IT

PHP + PThreads + Redis/Predis = zend_mm_heap 已损坏?

coder 2023-11-07 原文

这几天我一直在为一个神秘的错误而苦苦挣扎。我使用的是 PHP 7.1.0RC3(我在启用ZTS/pthreads 的情况下重新编译了自己)。最近,我一直在进行重构,用 Redis 替换 MySQL 以优化我的应用程序中非磁盘值(value)的数据 I/O。

我有一个脚本可以为每个加密货币市场创建一个线程 (A)。线程 (A) 为每个交易策略创建另一个线程 (B)。 B 线程总是在 A 线程之前同步。

我不断收到此错误:zend_mm_heap 已损坏。每次我运行脚本时,它都会在不同的执行点发生。

我已经尝试了所有建议的修复、100 多个 Google 页面。垃圾收集、PHP 配置/编译,所有这些都经过了非常详细的审查。我没有发现任何与“zend_mm_heap 已损坏”、PThreads 和 Redis/Predis 相关的内容,而其他“修复”没有任何效果...

所有数据都从 Redis 读取并保存到 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

我别无选择,请怜悯。我可以共享代码示例,但将它们粘贴到此处不会很有用。

编辑 我用 ./configure --enable-maintainer-zts --enable-pthreads --with-pthreads-sanitize --with-mysqli --enable-embedded-mysqli 重新编译了 PHP,我看到了一个新的我以前从未见过的错误:

==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

最佳答案

我找到了内存损坏的根源,“修复”非常简单。我发现在 pthreads 中使用 phpiredis $client 的唯一“非致命”方式是在当前本地范围内显式连接/执行命令/断开连接。

总结起来,在高负载下会导致内存损坏,原因如下:

  1. 试图通过线程上下文中的引用将 phpiredis $client 传递给另一个对象
  2. 尝试在 Threaded 上下文中按值将 phpiredis $client 传递给另一个对象
  3. 尝试在 Threaded 上下文中除本地范围外的任何地方实例化/使用 phpiredis $client(例如:在另一个类 _Redis 中,当我们当前处于 Investor::save 时)
  4. 尝试在 Threaded 上下文中静态重用 phpiredis 连接

不过,当以相同的方式/上下文将 MySQLi $client 传递给另一个对象时,不会发生内存损坏。

我希望这能拯救其他人度过我目睹的绝望一周。这是在 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);

关于PHP + PThreads + Redis/Predis = zend_mm_heap 已损坏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43053652/

有关PHP + PThreads + Redis/Predis = zend_mm_heap 已损坏?的更多相关文章

  1. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  2. ruby-on-rails - 将 Ruby 中的日期/时间格式化为 YYYY-MM-DD HH :MM:SS - 2

    这个问题在这里已经有了答案:Railsformattingdate(4个答案)关闭4年前。我想格式化Time.Now函数以显示YYYY-MM-DDHH:MM:SS而不是:“2018-03-0909:47:19+0000”该函数需要放在时间中.现在功能。require‘roo’require‘roo-xls’require‘byebug’file_name=ARGV.first||“Template.xlsx”excel_file=Roo::Spreadsheet.open(“./#{file_name}“,extension::xlsx)xml=Nokogiri::XML::Build

  3. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  4. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  5. arrays - Ruby 数组 += vs 推送 - 2

    我有一个数组数组,想将元素附加到子数组。+=做我想做的,但我想了解为什么push不做。我期望的行为(并与+=一起工作):b=Array.new(3,[])b[0]+=["apple"]b[1]+=["orange"]b[2]+=["frog"]b=>[["苹果"],["橙子"],["Frog"]]通过推送,我将推送的元素附加到每个子数组(为什么?):a=Array.new(3,[])a[0].push("apple")a[1].push("orange")a[2].push("frog")a=>[[“苹果”、“橙子”、“Frog”]、[“苹果”、“橙子”、“Frog”]、[“苹果”、“

  6. += 的 Ruby 方法 - 2

    有没有办法让Ruby能够做这样的事情?classPlane@moved=0@x=0defx+=(v)#thisiserror@x+=v@moved+=1enddefto_s"moved#{@moved}times,currentxis#{@x}"endendplane=Plane.newplane.x+=5plane.x+=10putsplane.to_s#moved2times,currentxis15 最佳答案 您不能在Ruby中覆盖复合赋值运算符。任务在内部处理。您应该覆盖+,而不是+=。plane.a+=b与plane.a=

  7. ruby - Sinatra + Heroku + Datamapper 使用 dm-sqlite-adapter 部署问题 - 2

    出于某种原因,heroku尝试要求dm-sqlite-adapter,即使它应该在这里使用Postgres。请注意,这发生在我打开任何URL时-而不是在gitpush本身期间。我构建了一个默认的Facebook应用程序。gem文件:source:gemcuttergem"foreman"gem"sinatra"gem"mogli"gem"json"gem"httparty"gem"thin"gem"data_mapper"gem"heroku"group:productiondogem"pg"gem"dm-postgres-adapter"endgroup:development,:t

  8. ruby-on-rails - Assets 管道损坏 : Not compiling on the fly css and js files - 2

    我开始了一个新的Rails3.2.5项目,Assets管道不再工作了。CSS和Javascript文件不再编译。这是尝试生成Assets时日志的输出:StartedGET"/assets/application.css?body=1"for127.0.0.1at2012-06-1623:59:11-0700Servedasset/application.css-200OK(0ms)[2012-06-1623:59:11]ERRORNoMethodError:undefinedmethod`each'fornil:NilClass/Users/greg/.rbenv/versions/1

  9. ruby - Ruby 中字符串运算符 + 和 << 的区别 - 2

    我是Ruby和这个网站的新手。下面两个函数是不同的,一个在函数外修改变量,一个不修改。defm1(x)x我想确保我理解正确-当调用m1时,对str的引用被复制并传递给将其视为x的函数。运算符当调用m2时,对str的引用被复制并传递给将其视为x的函数。运算符+创建一个新字符串,赋值x=x+"4"只是将x重定向到新字符串,而原始str变量保持不变。对吧?谢谢 最佳答案 String#+::str+other_str→new_strConcatenation—ReturnsanewStringcontainingother_strconc

  10. ruby - rails 3.2.2(或 3.2.1)+ Postgresql 9.1.3 + Ubuntu 11.10 连接错误 - 2

    我正在使用PostgreSQL9.1.3(x86_64-pc-linux-gnu上的PostgreSQL9.1.3,由gcc-4.6.real(Ubuntu/Linaro4.6.1-9ubuntu3)4.6.1,64位编译)和在ubuntu11.10上运行3.2.2或3.2.1。现在,我可以使用以下命令连接PostgreSQLsupostgres输入密码我可以看到postgres=#我将以下详细信息放在我的config/database.yml中并执行“railsdb”,它工作正常。开发:adapter:postgresqlencoding:utf8reconnect:falsedat

随机推荐