在Apache HTTP Server 2.x中使用任何线程mpms时,从Apache调用的每个函数都是线程安全的,这一点很重要。链接第3方扩展时,可能很难确定结果服务器是否是线程安全的。随便测试通常也不会告诉您这两种情况,因为线程安全性问题可能导致微妙的竞争状况,而这种竞争状况仅在重载下的某些状况下才会出现。
在编写模块或尝试确定模块或第三方库是否是线程安全的时,请记住一些常见的事项。
首先,您需要认识到在线程模型中,每个单独的线程都有其自己的程序计数器,堆栈和寄存器。局部变量位于堆栈上,因此可以。您需要当心任何静态或全局变量。这并不意味着绝对不允许您使用静态或全局变量。有时候您确实希望某些东西影响所有线程,但是如果您希望代码是线程安全的,通常就需要避免使用它们。
如果您有一个需要全局变量并被所有线程访问的全局变量,则在更新它时要非常小心。例如,如果它是一个递增计数器,则需要自动对其进行递增,以避免与其他线程发生竞争。您可以使用互斥锁(互斥)来执行此操作。锁定互斥锁,读取当前值,将其递增并写回,然后解锁互斥锁。任何其他想要修改该值的线程都必须首先检查互斥量并阻塞,直到将其清除为止。
如果您使用的是APR,请查看功能和
功能。apr_atomic_*
apr_thread_mutex_*
这是一个通用全局变量,用于保存最近发生的错误的错误号。如果一个线程调用设置errno的低级函数,然后另一个线程对其进行检查,则我们会将错误号从一个线程渗入另一个线程。要解决此问题,请确保您的模块或库定义_REENTRANT
或使用编译
-D_REENTRANT
。这将使errno成为每个线程的变量,并有望对代码透明。它通过执行以下操作来做到这一点:
#define errno (*(__errno_location()))
这意味着访问errno将调用
__errno_location()
libc提供的内容。设置
_REENTRANT
还会强制将某些其他功能重新定义为其*_r
等效功能,有时还会将common getc
/ putc
宏更改为更安全的函数调用。检查您的libc文档以了解详细信息。相反的,或除了_REENTRANT
可能影响这个是符号
_POSIX_C_SOURCE
,_THREAD_SAFE
,
_SVID_SOURCE
,和_BSD_SOURCE
。
事情不仅必须是线程安全的,而且还必须是可重入的。strtok()
是显而易见的。您第一次使用分隔符来调用它,然后它会记住该分隔符,并且在随后的每次调用中,它将返回下一个标记。显然,如果有多个线程在调用它,您将遇到问题。大多数系统具有函数的可重入版本,称为strtok_r()
,您可以在其中传递一个额外的参数,该参数包含一个分配的值char *
,该函数将使用该分配的值代替该函数自身的静态存储来维护令牌化状态。如果您使用的是APR,则可以使用apr_strtok()
。
crypt()
是另一个倾向于不可重入的函数,因此,如果您在库中遇到对该函数的调用,请当心。在某些系统上,它是可重入的,因此并不总是一个问题。如果您的系统有crypt_r()
机会,您应该使用它,或者如果可能的话,只需使用md5来避免整个混乱。
以下是第三方Apache模块使用的常见库的列表。您可以检查,看看你的模块使用一个潜在的不安全库通过使用工具,如ldd(1)
和
nm(1)
。例如,对于PHP,请尝试以下操作:
% ldd libphp4.so
libsablot.so.0 => /usr/local/lib/libsablot.so.0 (0x401f6000)
libexpat.so.0 => /usr/lib/libexpat.so.0 (0x402da000)
libsnmp.so.0 => /usr/lib/libsnmp.so.0 (0x402f9000)
libpdf.so.1 => /usr/local/lib/libpdf.so.1 (0x40353000)
libz.so.1 => /usr/lib/libz.so.1 (0x403e2000)
libpng.so.2 => /usr/lib/libpng.so.2 (0x403f0000)
libmysqlclient.so.11 => /usr/lib/libmysqlclient.so.11 (0x40411000)
libming.so => /usr/lib/libming.so (0x40449000)
libm.so.6 => /lib/libm.so.6 (0x40487000)
libfreetype.so.6 => /usr/lib/libfreetype.so.6 (0x404a8000)
libjpeg.so.62 => /usr/lib/libjpeg.so.62 (0x404e7000)
libcrypt.so.1 => /lib/libcrypt.so.1 (0x40505000)
libssl.so.2 => /lib/libssl.so.2 (0x40532000)
libcrypto.so.2 => /lib/libcrypto.so.2 (0x40560000)
libresolv.so.2 => /lib/libresolv.so.2 (0x40624000)
libdl.so.2 => /lib/libdl.so.2 (0x40634000)
libnsl.so.1 => /lib/libnsl.so.1 (0x40637000)
libc.so.6 => /lib/libc.so.6 (0x4064b000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x80000000)
除了这些库之外,您还需要查看静态链接到模块中的所有库。您可以用来nm(1)
在模块中查找单个符号。
如果您对此列表进行了补充或更正,请在dev@httpd.apache.org上添加注释。
图书馆 | 版 | 线程安全吗? | 笔记 |
---|---|---|---|
拼写/拼写 | ? | ||
伯克利DB | 3.x,4.x | 是 | 跨线程共享连接时要小心。 |
bzip2 | 是 | 低级和高级API都是线程安全的。但是,高级API需要对errno进行线程安全的访问。 | |
数据库 | ? | ||
客户 | 也许 | c客户端使用strtok() ,
gethostbyname() 并且在大多数C库实现中都不是线程安全的。c客户端的静态数据旨在跨线程共享。如果strtok() 和
gethostbyname() 在您的操作系统上是线程安全的,则c-client
可能是线程安全的。 | |
libcrypt的 | ? | ||
外籍人士 | 是 | 每个线程需要一个单独的解析器实例 | |
FreeTDS | ? | ||
自由类型 | ? | ||
GD 1.8.x | ? | ||
GD 2.0.x | ? | ||
gdbm | 没有 | 通过静态gdbm_error
变量返回的错误 | |
图像魔术 | 5.2.2 | 是 | ImageMagick文档声称它从5.2.2版本开始是线程安全的(请参阅更改日志)。 |
Imlib2 | ? | ||
libjpeg | v6b | ? | |
libmysql客户端 | 是 | 使用mysqlclient_r库变量来确保线程安全。有关更多信息,请阅读http://dev.mysql.com/doc/mysql/en/Threaded_clients.html。 | |
明 | 0.2a | ? | |
网络SNMP | 5.0.x | ? | |
OpenLDAP | 2.1.x | 是 | 使用ldap_r 库变体以确保线程安全。 |
的OpenSSL | 0.9.6克 | 是 | 要求正确使用CRYPTO_num_locks ,
CRYPTO_set_locking_callback ,
CRYPTO_set_id_callback |
liboci8(Oracle 8+) | 8.x,9.x | ? | |
pdflib | 5.0.x | 是 | PDFLib文档声称它是线程安全的;changes.txt表示自V1.91起它已部分处于线程安全状态:http ://www.pdflib.com/products/pdflib-family/pdflib/ 。 |
libpng | 1.0.x | ? | |
libpng | 1.2.x | ? | |
libpq(PostgreSQL) | 8.x | 是 | 不要跨线程共享连接并当心
crypt() 电话 |
萨勃龙 | 0.95 | ? | |
zlib | 1.1.4 | 是 | 依赖于线程安全的zalloc和zfree函数默认情况下使用线程安全的libc的calloc / free。 |
可用语言: zh