리눅스 코드 원자

/ *
 * 2020/12/4 15:07 qing
 *
 * 원자 적 연산은 하드웨어로 구현해야합니다
 * /

/ *
 * atomic
 * /
Linux 커널에는 다양한 원자 연산 기능이 제공됩니다. 가장 원시적 인 읽기 및 설정 외에도 다양한 작업, 비트 작업 등이 포함됩니다. 또한 일부 원자 연산은 연산 후 변수 값
을 반환해야 하고 일부는 연산 전에 변수 값을 반환해야합니다. 메모리 장벽이 관련되어 있으면 이러한 요소를 결합하면 원자 연산 함수가 많이 있습니다. .

/ *
 * 버스 잠금 (버스 잠금)
 * /

    CPU가 원자 적 명령어를 실행할 때 버스를 잠 가서 다른 CPU가 메모리 작업을 해제하지 못하도록 차단합니다.

/ *
 * 캐시 잠금
 * /

    IO (예 : MMIO)와 밀접한 관련이있는 경우를 제외하고 대부분의 메모리를 캐시 할 수 있습니다. 위에서 소개 한 캐시 일관성 원칙에 따라 캐시 라인이 Exclusive 또는 Modified 일
    때 현재 CPU 만이 변수에 대한 데이터를 캐시 한다는 것을 알고 있습니다. 따라서 원자 적 연산을 수행하면 다른 CPU의 캐시를 무효화하기 위해 Read Invalidate 메시지가 전송되고 캐시 라인은 Exclusive 상태가되고

    캐시 라인이 잠기면 데이터를 가져 와서 수정하고 캐시 라인에 쓸 수 있습니다. 이때 다른 CPU에서도 원자 적 작업을 수행하는 경우 읽기 무효화 메시지가 전송됩니다. 그러나 현재 CPU
    캐시 라인이 잠겨 있으므로 현재 CPU가 완료되고 캐시 라인이 잠금 해제 될 때까지 다른 CPU가 대기 중입니다.

/ *
 * 기본 유형
 * /

    기본 유형에는 비 RMW (읽기 수정 쓰기) 읽기 및 쓰기 작업과 RMW 산술 및 비트 작업이 포함됩니다.

    RMW가 아닌 작업은 매우 간단합니다. 읽기에는 atomic_read (), 쓰기에는 atomic_set () 만 있습니다.
    일반적으로 단일 변수 자체의 읽기 또는 쓰기 작업은 원자 적입니다. 코드가 단일 변수를 읽고 쓰기 만하고 RMW 작업을 사용하지 않는 경우 일반적으로 잘못 사용하고 있다는 의미이므로
    필요하지 않습니다. 제공된 커널 원자 작업을 사용합니다.

    산술 연산 : 더하기, 빼기, 증감 포함, 이름 지정 형식은 atomic_ {add, sub, inc, dec} ();
    비트 연산 : AND, OR, XOR 및 NAND 포함, 이름 지정 형식은 atomic_ {and, 또는 , xor, andnot} ();
    교환 작업 : 성공 또는 실패를 반환하는 교환 (atomic_xchg ()), 비교 교환 (atomic_cmpxchg ()) 및 비교 교환 (atomic_try_cmpxchg ()) 포함.

    atomic_t 유형 외에도 Linux 커널은 64 비트 및 long 정수에 대한 atomic64_t 및 atomic_long_t 유형도 지원합니다. 또한 해당 기본 원자 연산 함수가
    있지만 함수 접두사 이름은 각각 atomic64_ 및 atomic_long_입니다.

정적 인라인 void atomic_add (int i, atomic_t * v)
{     atomic_add_return (i, v); }

#ifndef atomic_add_return
#define atomic_add_return (...) \
    __atomic_op_fence (atomic_add_return, __VA_ARGS__)
#endif

#ifndef __atomic_op_fence
#define __atomic_op_fence (op, args ...) \
({\
    typeof (op ## _ relaxed (args)) __ret; \
    smp_mb__before_atomic (); \
    __ret = op ## _ relaxed (args); \
    smp_mb__after_atomic () ; \
    __ret; \
})
#endif


/ *
 * 수정 된 값 반환
 * /

    원자 연산 함수가 수정 된 원자 변수의 값을 반환하려는 경우 함수 이름은 "_return"문자열을 포함하고 특정 연산 문자열 뒤에 있습니다.
    
    예 : atomic_add_return, atomic_dec_return_relaxed 등.


/ *
 * 수정 전 값을 반환하려면
 * /

    원자 연산 함수가 수정 전에 원자 변수 값을 반환하려는 경우 함수 이름에는 "_fetch"문자열이 포함되며 특정 연산 문자열 앞에 있습니다.
    
    예를 들어, atomic_fetch_and, atomic_fetch_or_acquire 등입니다.


/ *
 * Acquire and Release 단방향 장벽 의미가 있습니다
 * /

    원자 연산 함수에 Acquire 또는 Release 단방향 장벽 의미 체계도 포함되어야하는 경우 함수 이름에 "_acquire"또는 "_release"접미사가 있습니다.
    
    예 : atomic_xchg_acquire, atomic_cmpxchg_release 등.


    반대로 원자 연산 함수 이름에 "_relaxed"접미사가 있으면 함수가 메모리 장벽에 의해 보호되지 않으며 임의로 재정렬 될 수 있음을 의미합니다.
    
    예를 들어, atomic_add_return_relaxed, atomic_xchg_relaxed 등입니다.


    원자 연산 함수가 값을 반환하려는 경우에만 _acquire, _release 및 _relaxed 접미사를 추가 할 수 있습니다.


/ *
 * 원자 적 연산을위한 재정렬 규칙
 * /

    Linux 커널의 원자 적 연산은 단일 변수에 대한 연산이 원자적임을 보장 할 뿐이며 여러 CPU가 동시에 작동 할 때 중간 오류 상태가 없습니다. 그러나 이러한 원자 적 연산 자체
    의 경우 실행 순서가 반드시 보장되는 것은 아니며 SMP 시스템에서는 재정렬 문제가 발생할 수 있습니다.
    
    따라서 앞에서 언급했듯이 일부 원자 연산 함수에는 자체 메모리 장벽 의미가 있습니다. 복용 여부와 복용량은 함수명으로 알 수 있으며 구체적인 규칙은 다음과 같다.

    비 RMW 원자 연산은 임의로 재정렬 될 수 있습니다.

    RMW 원자 연산은 반환 값이없는 경우 임의로 재정렬 될 수 있습니다
    .RMW 원자 연산은 반환 값이 있고 _relaxed, _acquire 및 _release 접미사가없는 경우 메모리 장벽으로 보호되며 보장 될 수 있습니다. 재정렬되지 않음;
    RMW의 원자 연산은 메모리 장벽 보호없이 반환 값과 _relaxed 접미사가 있으면 임의로 재정렬 될 수 있습니다 .RMW
    의 원자 연산에 반환 값이 있고 접미사가 _acquire 인 경우, RMW에서 읽기를 의미합니다 .R) 작업은 단방향 획득 장벽에 의해 보호됩니다
    .RMW의 원자 적 작업은 반환 값과 접미사가 _release 인 경우 쓰기 (RMW에서 W) 작업을 의미합니다. 단방향 해제 장벽에 의해 보호됩니다
    .RMW의 원자 적 작동 조건부 판단이있는 경우 조건의 일부가 임의로 재정렬됩니다.

    메모리 배리어 의미를 제공하지 않는 원자 적 연산의 경우 순서가 변경되지 않도록하기 위해 메모리 배리어 앞뒤에 명시 적으로 메모리 배리어를 사용해야합니다.
    Linux 커널은 두 가지 기능을 제공합니다. 원자 연산 함수 앞에는 smp_mb__before_atomic ()이 사용되고 원자 연산 함수 뒤에는 smp_mb__after_atomic ()이 사용됩니다.

/ *
 * ARMv8 아키텍처에서 원자 연산 구현
 * /

#define ATOMIC_INIT (i) {(i)}

 / * atomic_read * /

#define atomic_read (v) READ_ONCE ((v)-> 카운터)

#define __READ_ONCE (x, check) \
({\
    union {typeof (x) __val; char __c [1];} __u; \
    if (check) \
        __read_once_size (& (x), __u .__ c, sizeof (x) ); \
    else \
        __read_once_size_nocheck (& ​​(x), __u .__ c, sizeof (x)); \
    __u .__ val; \
})
#define READ_ONCE (x) __READ_ONCE (x, 1)

static __always_inline
void __read_once_size (const volatile void * p, void * res, int size)
{     __READ_ONCE_SIZE; }

#define __READ_ONCE_SIZE \
({\
    switch (size) {\
    case 1 : * (__ u8 *) res = * (volatile __u8 *) p; break; \
    case 2 : * (__ u16 *) res = * (volatile __u16 *) p; break; \
    case 4 : * (__ u32 *) res = * (volatile __u32 *) p; break; \
    case 8 : * (__ u64 *) res = * (volatile __u64 *) p; break; \
    default : \
        barrier (); \
        __builtin_memcpy ((void *) res, (const void *) p, size); \
        barrier (); \
    } \
})

/ * atomic_set * /
#define atomic_set (v, i) WRITE_ONCE (((v)-> 카운터), (i))

#define WRITE_ONCE (x, val) \
({\
    union {typeof (x) __val; char __c [1];} __u = \
        {.__ val = (__force typeof (x)) (val)}; \
    __write_once_size (& (x), __u .__ c, sizeof (x)); \
    __u .__ val; \
})

static __always_inline void __write_once_size (volatile void * p, void * res, int size)
{     switch (size) {     case 1 : * (volatile __u8 *) p = * (__ u8 *) res; 단절;     사례 2 : * (휘발성 __u16 *) p = * (__ u16 *) res; 단절;     사례 4 : * (휘발성 __u32 *) p = * (__ u32 *) res; 단절;     사례 8 : * (휘발성 __u64 *) p = * (__ u64 *) res; 단절;     기본값 :         barrier ();         __builtin_memcpy ((void *) p, (const void *) res, size);         장벽();     } }










 

/ *
 * 사용되지 않는 함수, C 구문 및 지침에 대해 알아보기
 * /

     static inline int
    __ll_sc_atomic_add_return_acquire (int i, atomic_t * v)
    {         unsigned long tmp;         int result;         asm volatile (     "// Prefetch v-> counter to the CPU cache \ n"\     "prfm pstl1strm, % 2 \ n"\     " // v-> counter를 결과로 독점적으로 읽습니다. \ n "\     "1 : ldaxr % w0, % 2 \ n "\     "// result = result + i \ n "\     "add % w0, % w0, % w3 \ n "\     "// v-> counter에만 결과 값 저장 \ n "\


     








    "// 저장할 때 배타적 표시가 지워지면 tmp를 1로 설정합니다. \ n"\
    "stxr % w1, % w0, % 2 \ n"\
    "// tmp를 1로 설정하면 처음부터 다시 실행합니다. \ n "\
    "cbnz % w1, 1b \ n "\
            ) \
        :"= & r "(결과),"= & r "(tmp),"+ Q "(v-> 카운터) \
        : __stringify (I)" r "(i) \
        :"메모리 "); \
                                        \
        반환 결과; \
    }

    static inline int \
    __ll_sc_atomic_fetch_or_release (int i, atomic_t * v) \
    {\
        unsigned long tmp; \
        int val, result; \
                                        \
        asm volatile (\
    "// prefetch v-> counter to the CPU cache \ n"\
    "prfm pstl1strm, % 3 \ n "\
    "// v-> counter를 결과로 독점적으로 읽습니다. \ n "\
    "1 : ldxr % w0, % 3 \ n "\
    "// val = result | i \ n"\
    "orr % w1, % w0, % w4 \ n"\
    "// val 값을 v-> counter에 배타적으로 저장 \ n"\
    "// 저장시 배타적 플래그가 지워집니다. tmp를 1로 설정 \ n "\
    "stlxr % w2, % w1, % 3 \ n "\
    "// tmp가 1로 설정되면
    처음부터 다시 실행합니다. \ n "\ " cbnz % w2, 1b \ n "\
        :"= & r "(결과),"= & r "(발),"= & r "(tmp),"+ Q "(v-> 카운터) \
        : __stringify (K) "r"(i) \
        : "메모리 "); \
                                        \
        반환 결과; \
    }

/ * arch_cmpxchg * /
#define __cmpxchg_wrapper (sfx, ptr, o, n) \
({\
    __typeof __ (* (ptr)) __ret; \
    __ret = (__typeof __ (* (ptr))) \
        __cmpxchg ## sfx ((ptr ), (unsigned long) (o), \
                (unsigned long) (n), sizeof (* (ptr))); \
    __ret; \
})

#define arch_cmpxchg (...) __cmpxchg_wrapper (_mb, __VA_ARGS__)

arch_cmpxchg는 마지막으로 매크로에 의해 __cmpxchg_wrapper로 정의되고 __cmpxchg_mb 함수를 호출합니다.

#define __CMPXCHG_GEN (sfx) \
static __always_inline unsigned long __cmpxchg ## sfx (volatile void * ptr, \
                       unsigned long old, \
                       unsigned long new, \
                       int size) \
{\
    switch (size) {\
    case 1 : \
        return __cmpxchg_case ## sfx ## _ 8 (ptr, 이전, 새); \
    case 2 : \
        return __cmpxchg_case ## sfx ## _ 16 (ptr, old, new); \
    사례 4 : \
        return __cmpxchg_case ## sfx ## _ 32 (ptr, old, new); \
    case 8 : \
        return __cmpxchg_case ## sfx ## _ 64 (ptr, old, new); \
    기본값 : \
        BUILD_BUG (); \
    } \
                                    \
    unreachable (); \
}
 
__CMPXCHG_GEN ()
__CMPXCHG_GEN (_acq)
__CMPXCHG_GEN (_rel)
__CMPXCHG_GEN (_mb)
 
#undef __CMPXCHG_GEN

추천

출처blog.csdn.net/xiaozhiwise/article/details/111215857