今天遇到了工作以来最深入的问题,就是关于 abort 函数的多线程安全性问题,应该写一篇文章,纪念一下,希望自己以后能够学到更多关于 Linux 内核的知识,祝愿自己以后工作越来越顺利。
首先,需要说明一下,什么是多线程安全以及异步信号安全。
所谓多线程安全,我们称为 MT-Safe ,就是指同一个函数,同时被多个线程并行调用时,不会出现问题,也就是说,其执行结果就和该函数被串行执行多次的结果一样。打个比方,如果一个函数在不加锁的情况下使用了全局变量或静态变量,那么,当它被多个线程同时调用时,有可能出现结果不一致的情况,这样的函数就不是多线程安全的,我们称为 unsafe 。
所谓异步信号安全,就是指一个函数可以被一个信号处理函数不加限制地调用,而不会出现问题。这个概念很模糊,一开始我也没有弄懂。要说清楚这个问题,就必须看看什么样的函数不能被信号处理函数安全地调用。
这里谈论的信号,主要是指异步信号。所谓异步信号,就是随时都有可能出现的信号。从宏观上说,如果一个进程正执行一个函数 func() ,执行到一半还没有退出的时候,收到一个异步信号,此时,处理器必须转而执行相应的信号处理函数 handle() ,如果正巧, handle 也调用了 func() ,那就有可能出现问题。例如, func 使用了全局变量,如果不加锁,那就和上面提到的多线程安全性问题一样,结果会不一致;如果加锁,试想,在进入 handle 之前, func 已经在该全局变量获得了锁,还没有释放,就进入了 handle , handle 再次调用 func ,又一次申请同一个锁,这就形成了死锁。所以,这种情况下,无论加不加锁,也就是无论是否多线程安全,这个函数 func 都不是异步信号安全的。
有了上面的铺垫,我们就可以来看一下 abort() 函数的源码了。由于源码太长,我们只列出对本次讨论有帮助的一部分,完整的源码可以在 glibc 源码中 stdlib 文件夹下的 abort.c