Xeon上如何在不同核之间同步数据(linux如何使用内存屏障)
Posted
技术标签:
【中文标题】Xeon上如何在不同核之间同步数据(linux如何使用内存屏障)【英文标题】:How Can synchronize data between differernt cores on Xeon (linux how to use memory barriers) 【发布时间】:2013-12-31 13:38:22 【问题描述】:我写了一个简单的程序来测试内存同步。使用全局队列与两个共享 进程,并将两个进程绑定到不同的核心。我的代码被炸毁了。
#include<stdio.h>
#include<sched.h>
#define __USE_GNU
void bindcpu(int pid)
int cpuid;
cpu_set_t mask;
cpu_set_t get;
CPU_ZERO(&mask);
if (pid > 0)
cpuid = 1;
else
cpuid = 5;
CPU_SET(cpuid, &mask);
if (sched_setaffinity(0, sizeof(mask), &mask) == -1)
printf("warning: could not set CPU affinity, continuing...\n");
#define Q_LENGTH 512
int g_queue[512];
struct point
int volatile w;
int volatile r;
;
volatile struct point g_p;
void iwrite(int x)
while (g_p.r == g_p.w);
sleep(0.1);
g_queue[g_p.w] = x;
g_p.w = (g_p.w + 1) % Q_LENGTH;
printf("#%d!%d", g_p.w, g_p.r);
void iread(int *x)
while (((g_p.r + 1) % Q_LENGTH) == g_p.w);
*x = g_queue[g_p.r];
g_p.r = (g_p.r + 1) % Q_LENGTH;
printf("-%d*%d", g_p.r, g_p.w);
int main(int argc, char * argv[])
//int num = sysconf(_SC_NPROCESSORS_CONF);
int pid;
pid = fork();
g_p.r = Q_LENGTH;
bindcpu(pid);
int i = 0, j = 0;
if (pid > 0)
printf("call iwrite \0");
while (1)
iread(&j);
else
printf("call iread\0");
while (1)
iwrite(i);
i++;
两个进程Intel(R) Xeon(R) CPU E3-1230和两个核心之间的数据不同步。
CPU:Intel(R) Xeon(R) CPU E3-1230 操作系统:3.8.0-35-generic #50~precise1-Ubuntu SMP
我想知道 IPC 之外的我如何在用户的不同内核之间同步数据 空间?
【问题讨论】:
如果您希望两个进程能够相互通信,您将需要使用某种形式的进程间通信。这里有一个关于进程间通信的好答案 -> ***.com/questions/2682462/c-fork-and-sharing-memory。另外,只是一个观察,你没有初始化你的点结构实例。 全局初始化默认为0。谢谢你的链接。 我想知道 IPC 之外的东西,是一种从用户空间控制缓存内存的方法。或任何系统调用,如“rmb()、wmb()” 【参考方案1】:如果您希望您的应用程序操纵 cpus 共享缓存以完成 IPC,我认为您无法做到。
《Linux Kernel Development Second Edition》第 9 章有关于同步多线程应用程序的信息(包括原子操作、半量、屏障等...): http://www.makelinux.net/books/lkd2/ch09
所以你可能会得到一些关于你在那里寻找什么的想法。
这里是英特尔® 智能缓存“共享缓存多核系统的软件技术”的一篇不错的文章:http://archive.is/hm0y
这里有一些 *** 问题/答案可以帮助您找到您正在寻找的信息:
Storing C/C++ variables in processor cache instead of system memory
C++: Working with the CPU cache
Understanding how the CPU decides what gets loaded into cache memory
很抱歉用链接轰炸你,但如果没有更清楚地了解你想要完成的事情,这是我能做的最好的事情。
【讨论】:
谢谢!是我的问题是我没有明确我想知道的。 我阅读了这个链接,我认为共享的 chache 很酷,而且它是starmt。我的 cpu 得到了 L3 可以共享,但它没有工作。如何检查系统上的缓存配置,如何修改?【参考方案2】:我建议阅读“Volatile: Almost Useless for Multi-Threaded Programming”,了解为什么应从示例代码中删除 volatile。相反,使用 C11 或 C++11 原子操作。另请参阅 TBB 设计模式手册中的 Fenced Data Transfer 示例。
下面我展示了我更改为使用 C++11 原子的问题示例的部分内容。我用 g++ 4.7.2 编译它。
#include <atomic>
...
struct point g_p;
struct point
std::atomic<int> w;
std::atomic<int> r;
;
void iwrite(int x)
int w = g_p.w.load(std::memory_order_relaxed);
int r;
while ((r=g_p.r.load(std::memory_order_acquire)) == w);
sleep(0.1);
g_queue[w] = x;
w = (w+1)%Q_LENGTH;
g_p.w.store( w, std::memory_order_release);
printf("#%d!%d", w, r);
void iread(int *x)
int r = g_p.r.load(std::memory_order_relaxed);
int w;
while (((r + 1) % Q_LENGTH) == (w=g_p.w.load(std::memory_order_acquire)));
*x = g_queue[r];
g_p.r.store( (r + 1) % Q_LENGTH, std::memory_order_release );
printf("-%d*%d", r, w);
主要变化是:
我到处都删除了“volatile”。 struct point 的成员被声明为 std::atomic g_p.r 和 g_p.w 的一些加载和存储被隔离。其他人被吊起。 加载由另一个线程修改的变量时,代码会将其“快照”到局部变量中。代码使用“松弛加载”(无栅栏),其中线程加载没有其他线程修改的变量。我将这些负载从旋转循环中取出,因为重复它们没有意义。
代码使用“获取负载”,其中线程加载由另一个线程设置的“消息准备就绪”指示器,并使用“释放存储”存储“消息准备就绪”指示器“以供读取由另一个线程。释放是确保“消息”(队列数据)写入之前写入“就绪”指示符(g_p 的成员)所必需的。获取同样需要确保在看到“就绪”指示器之后读取“消息”。
使用快照以便 printf 报告线程实际使用的值,而不是稍后出现的一些新值。一般来说,我喜欢使用快照风格有两个原因。首先,接触共享内存可能会很昂贵,因为它通常需要高速缓存行传输。其次,该样式为我提供了一个稳定的值,可以在本地使用,而不必担心重新读取可能会返回不同的值。
【讨论】:
我发现我的代码有错误,我认为 fork 会让进程共享相同的 mm_struct 这意味着 porcesses 正在运行内存空间,但我检查了内核代码,从 fork 进程中内存是“写入时复制”,因此每个进程在自己的内存空间中都有自己的副本(通过页面错误实现)。 并且使用 vfork 和 memory barriy 可以完成我想要的。我想尽可能快地运行代码,多核可以,一个生产,消费,没有任何 IPC,没有任何 atmic,没有任何管道中断,并尽可能高地命中缓存。以上是关于Xeon上如何在不同核之间同步数据(linux如何使用内存屏障)的主要内容,如果未能解决你的问题,请参考以下文章