ILD

on same cacheline, arm may be strong memory order
作者:Yuan Jianpeng 邮箱:yuanjp89@163.com
发布时间:2026-6-13 站点:Inside Linux Development

众所周知arm是weakly ordered CPU. 参考1给出了详细的例子。


也就是说在

CPU0: X = 1; Y = 1;

CPU1: if (Y == 1) assert(x == 1);

由于X = 1; 可能发生在Y = 1; 后面。所以CPU1观察到Y = 1时,X可能还不是1,所以断言会失败。



例子1

写一个多线程程序验证:

#include <pthread.h>
#include <stdio.h>

#define assert(cond) if (!(cond)) printf("assert: %s failed\n", #cond)

volatile unsigned long x, y;

void *f1(void *)
{
    while (1) {
        if (y == 0) {
            x = 1;
            y = 1;
        }
    }
}

void *f2(void *)
{
    while (1) {
        if (y == 1) {
            assert(x == 1);
            x = 0;
            y = 0;
        }
    }
}

void main()
{
    pthread_t a, b;
    pthread_create(&a, NULL, f1, NULL);
    pthread_create(&b, NULL, f2, NULL);
    pthread_join(a, NULL);
    pthread_join(b, NULL);
}


在两台arm64上面都不能验证。


例子2

同时仿照参考1中,写一个例子也不能验证:

#include <pthread.h>
#include <stdio.h>

#define assert(cond) if (!(cond)) printf("assert: %s failed\n", #cond)

volatile unsigned long lock;
volatile unsigned long count;

void *f(void *)
{
        int i = 0;

        while (i < 1000000) {
                unsigned long zero = 0;
                if (__atomic_compare_exchange_n(&lock, &zero, 1, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
                        count++;
                        __atomic_store_n(&lock, 0, __ATOMIC_RELAXED);
                        i++;
                }

        }
}

void main()
{
        pthread_t a, b;
        pthread_create(&a, NULL, f, NULL);
        pthread_create(&b, NULL, f, NULL);
        pthread_join(a, NULL);
        pthread_join(b, NULL);
        printf("count: %lu\n", count);
}


这里relaxed模式,count++和__atomic_store_n(&lock, 0, __ATOMIC_RELAXED)乱序,就会导致保护失效。


问题定位

最后向社区求助,如参考2,原来是两个内存访问在同一个cache line,不会乱序,简单修改一下,使x,y间隔64个字节。

volatile unsigned long x __attribute__((aligned(64)));
volatile unsigned long y __attribute__((aligned(64)));


在ipq5018上面,打印出来了:

/ # ./a2.out 

assert: x == 1 failed

assert: x == 1 failed

assert: x == 1 failed

assert: x == 1 failed

assert: x == 1 failed

^C



仿照参考1的例子,修改为:

volatile unsigned long lock __attribute__((aligned(64)));
volatile unsigned long count __attribute__((aligned(64)));


/ # ./a.out 

count: 1999998

/ # ./a.out 

count: 1999998

/ # ./a.out 

count: 1999999

/ # ./a.out 

count: 1999997


乱序的概率为200万分之3。


总结:

在arm上面,不在同一个cacheline的内存访问更容易乱序。写例子的时候,尽量不在同一个cacheline.


参考

https://preshing.com/20121019/this-is-why-they-call-it-a-weakly-ordered-cpu/

https://stackoverflow.com/questions/79956349/arm64-weak-memory-model-why-this-assertx-1-not-failing


Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.