1

我用 C/C++ 编写了一个软件,使用了很多 BIAS/Profil,一个区间代数库。在我的算法中,我有一个主控,它划分一个域并将它的一部分提供给从属进程。那些返回关于这些域部分的 int 法规。有共同的数据可供阅读,仅此而已。

我需要并行化我的代码,但是只要 2 个从属线程正在运行(或者我猜想更多)并且都在调用这个库的函数,它就会出现段错误。这些段错误的特殊之处在于,gdb 很少从两个构建中指示相同的错误行:它取决于线程的速度,如果一个更早开始,等等。我尝试让线程屈服,直到从主人,它“稳定”了错误。我相当确定它来自对库的 memcpy 的调用(在 gdb 回溯之后,我总是以调用 memcpy 的 BIAS/Profil 函数结束。公平地说,几乎所有函数都临时调用 memcpy返回结果之前的对象...)。从我在网上阅读的内容来看,memcpy() 似乎不是线程安全的,)。(对于一个应该只读取共享数据的函数来说似乎很奇怪......或者在编写线程数据时,两个线程都使用相同的内存空间?)

为了解决这个问题,我想“替换”(至少用于测试行为是否发生变化)对 memcpy 的调用以进行互斥体框架调用。(类似于 mtx.lock();mempcy(...);mtx.unlock();)

第一个问题:我根本不是开发/代码工程师,并且缺乏很多基础知识。我认为当我使用预构建的 BIAS/Profil 库时,调用的 memcpy 是构建库的系统之一,对吗?如果是这样,如果我尝试从系统上的源代码构建库,它会改变什么吗?(我不确定我是否可以构建这个库,因此提出了这个问题。)

第二个问题:在我的 string.h 中,memcpy 由以下方式声明: #ifndef __HAVE_ARCH_MEMCPY extern void * memcpy(void *,const void *,__kernel_size_t); #endif并且在其他一些字符串头文件(string_64.h,string_32.h)中,定义了以下形式:#define memcpy(dst, src, len) __inline_memcpy((dst), (src), (len))或更明确的定义,或者只是像引用的声明一样的声明。它开始变得丑陋,但理想情况下,我想创建一个预处理器变量#define __HAVE_ARCH_MEMCPY 1,以及一个void * memcpy(void *,const void *,__kernel_size_t)可以使用被解雇的 memcpy 执行互斥框架的 memcpy。这里的想法是避免弄乱库并使其与 3 行代码一起工作;)

有更好的主意吗?(这会让我很开心......)

4

3 回答 3

2

恕我直言,您不应该专注于 memcpy()s,而应该专注于更高级别的功能。

如果并行运行线程的处理内存间隔不重叠,则memcpy()是线程安全的。实际上,在 memcpy() 中只有一个 for(;;) 循环(有很多优化)[至少在 glibc 中],这是原因,为什么要声明它。

如果你想知道你的并行 memcpy() 线程会做什么,你应该想象 for(;;) 循环通过 longint 指针复制内存。

于 2013-11-13T16:04:48.193 回答
1

鉴于您的观察,并且 Profil 库来自上个千年,并且文档(主页和Profil2.ps)甚至不包含“线程”一词,我会假设该库不是线程安全的。

第一个:不,通常memcpy是动态链接的 libc 的一部分(至少现在是这样)。在 linux 上,检查ldd NAMEOFBINARY,它应该给出一个类似libc.so.6 => /lib/i386-linux-gnu/libc.so.6或类似的行。如果没有:重建。如果是的话:重建无论如何都会有所帮助,因为还有许多其他因素。

除此之外,我认为memcpy只要您从不写回数据,线程安全(即使写回未修改的数据也会受到伤害:https ://blogs.oracle.com/dave/entry/memcpy_concurrency_curiosities )。

2nd:如果事实证明你必须使用修改过的memcpy,也考虑一下LD_PRELOAD

于 2013-11-13T16:48:36.110 回答
0

通常,您必须使用临界区互斥锁或其他一些保护技术来防止多个线程同时访问非线程安全(不可重入)函数。有些 ANSI C 实现memcpy()不是线程安全的,有些是。(安全不安全

编写线程安全的函数和/或编写可以安全地容纳非线程安全函数的线程程序是一个重要主题。非常可行,但需要阅读该主题。有很多写的。 至少会帮助您开始提出正确的问题。

于 2013-11-13T16:04:03.717 回答