linux、pthread、qemu 的一次 pthread create 失败的分析

前言:

qemu发生了crash。这种类型的问题比较少见,这里说一下这个问题的分析过程。

分析:

1、coredump

生成的coredump,一种是配置了/proc/sys/kernel/core_pattern并且配置了ulimit的情况,coredump文件会按照core pattern生成。

还有一种是apport的,生成的文件使用apport-unpack可以解压出来。

加载coredump文件,查看trace:

看到了原因是abort,那么还好,毕竟不是最难分析的。

2、frame

从trace中来看,应该是frame 3发生了错误,导致了qemu自己主动abort了。那么具体分析一下frame 3

再结合frame 2的第一个参数,可见,是pthread_create的返回值是11。

3、EAGAIN

打开/usr/include/asm-generic/errno-base.h可见,errno 11是EAGAIN

既然pthread_create的返回值是EAGAIN,那么只好继续分析glibc的nptl(glibc的pthread在nptl中实现)了。

同时,还要找到对应的glibc的版本。有两种办法供参考:

a,在gdb的命令行中敲info proc mappings

可以看到qemu当时映射了哪个glibc的文件,可以判断出来。

b,在shell中敲ldd /bin/qemu-system-x86_64 | grep libc,再通过symbolic link找到对应的文件

确定了glibc的版本是2.23,那么就可以去gnu下载对应的glibc的源代码了。

4、pthread_create

找到glibc-2.23/nptl/pthread_create.c,分析EAGAIN的具体原因。

在这里先大致说一下linux平台上pthread的实现:linux并不区分进程和线程,内核中只有task。多个task组成一个group,同一个组里面的task共享虚拟内存空间,fs,signal handler等等几乎所有的资源。但是,每个task都需要有自己的用户栈,所以就需要在创建线程之前先为task分配page size对齐的内存。分配好内存之后,就可以使用系统调用sys_clone创建线程了。

5、allocate stack

找到第一个可能返回EAGAIN的代码

如果在为新的线程分配栈内存的时候失败,那么就会返回EAGAIN。

stack用的内存比较大块,glibc维护了cache,尽量避免每次都需要syscall。

先确认cache是不是真的有,如果没有,很可能就是内存分配失败导致的。

再来确认stack_cache的地址。

最后确认pthread的handler的list的内容,通过上面的几个关键字段的地址,可以分析出来pthread的list双链表都是指向了stack_cache。

综上,可以判断出来,内存是分配成功的。一,当时的stack_cache有一个缓存,直接分配给了那次分配;二,当时的stack_cache是空的,向kernel要了内存,并且成功了,在后面执行失败的时候,把内存归还给了cache。

此二者,无论那种情况,都可以认为这条路径下,不会返回EAGAIN的

6、sys_clone

继续分析,看看还有哪里可能返回EAGAIN。

分析到了sys_clone,它的返回值可能是EAGAIN。继续分析linux-4.4/kernel/fork.c,重点do_fork函数中可能返回EAGAIN的可能性:

a,qemu的thread的个数超过了限制?从cat /proc/sys/kernel/threads-max和ulimit看进程的最大线程数,另外在gdb中info threads可以看到所有的线程数。对比之下,coredump进程的线程数远小于限制,排除。

b,copy process执行的时候遇到了ENOMEM?这个看起来也不太像,从host的dmesg中没有看到任何oom信息。

c,host的pid max不足cat /proc/sys/kernel/pid-max,发现只有32768。一来这个数值偏小,二来测试在host上跑过多线程模拟的测试,这里看起来可能性最大了。

7、复现

修改了pid-max之后,在原来的环境上一台虚拟机出现的问题,现在改用七台同时复现。目前七台虚拟机已经跑了超过了一天。还没有遇到。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏葡萄城控件技术团队

Winform文件下载之WinINet

在C#中,除了webclient我们还可以使用一组WindowsAPI来完成下载任务。这就是Windows Internet,简称 WinINet。本文通过一个...

1848
来自专栏大内老A

[WCF REST] 通过ASP.NET Output Caching实现声明式缓存

ASP.NET的输出缓存(Output Caching)机制允许我们针对整个Web页面或者页面的某个部分(主要针对用户控件)最终呈现的HTML进行缓存。对于后续...

1756
来自专栏程序员八阿哥

Python从入门到摔门(6):Python Web服务器Tornado使用小结

举例来说,假设某个银行网站有这样的 URL: http://bank.example.com/withdraw?amount=1000000&for=Eve ...

922
来自专栏大内老A

WCF技术剖析之二十三:服务实例(Service Instance)生命周期如何控制[下篇]

在[第2篇]中,我们深入剖析了单调(PerCall)模式下WCF对服务实例生命周期的控制,现在我们来讨轮另一种极端的服务实例上下文模式:单例(Single)模式...

1829
来自专栏大内老A

[WCF REST] 通过ASP.NET Output Caching实现声明式缓存

ASP.NET的输出缓存(Output Caching)机制允许我们针对整个Web页面或者页面的某个部分(主要针对用户控件)最终呈现的HTML进行缓存。对于后续...

1847
来自专栏iOS技术

YYWebImage 源码剖析:线程调度与缓存策略

在 iOS 开发中,异步网络图片下载框架可以说是很大的解放了生产力,通常情况下开发者只需要简单的代码就能将网络图片异步下载并显示到手机屏幕上,并且还带有缓存优化...

1114
来自专栏小白鼠

ZookeeperZNode基本命令四字命令SessionWatcherACLZookeeper集群Paxos算法ZAB协议Curator分布式锁

在Zookeeper中,ZNode可以分为持久节点和临时节点两类。所谓持久节点是指一旦这个ZNode被创建了,除非主动进行ZNode的移除操作,否则这个ZNod...

513
来自专栏IT笔记

使用elasticsearch遇到的一些问题以及解决方法

1.由gc引起节点脱离集群 因为gc时会使jvm停止工作,如果某个节点gc时间过长,master ping3次(zen discovery默认ping失败重试3...

3204
来自专栏恰同学骚年

自己动手写一个简单的MVC框架(第一版)

  路由(Route)、控制器(Controller)、行为(Action)、模型(Model)、视图(View)

1092
来自专栏别先生

基于jsp+servlet图书管理系统之后台用户信息修改操作

上一篇的博客写的是查询操作,且附有源码和数据库,这篇博客写的是修改操作,附有从头至尾写的代码(详细的注释)和数据库!  此次修改操作的源码和数据库:http:...

24110

扫码关注云+社区