질문1. 문법 관련 질문입니다.
include/linux/percpu-defs.h
#define __verify_pcpu_ptr(ptr) \
do { \
const void __percpu *__vpp_verify = (typeof((ptr) + 0))NULL; \
(void)__vpp_verify; \
} while (0)
1) (typeof((ptr) + 0))NULL; 구문의 의미가 궁금합니다.
2) const void __percpu *__vpp_verify 에서 "void __percpu*" 가 어떤 의미인지 궁금합니다.
질문2. Inline-Assembly 관련 질문입니다.
문C 블로그에 보면 constraint 사용 시
"ARM에서는 메모리 영역을 access 할 경우 clobber lists에서 “memory” 대신 “Q”를 사용한다."
라고 나와 있는데요,
arch/arm64/include/asm/percpu.h에 있는 함수 중
set_my_cpu_offset() 에서는 "memory"를 사용하고
__my_cpu_offset() 에서는 "Q"를 사용합니다.
두 함수에서 constraint를 사용하는 데 있어서 차이가 나는데, 특별한 이유가 있는지 궁금합니다.
질문3.
READ_ONCE / WRITE_ONCE를 사용하는 이유가 무엇인지 궁금합니다.
.
안녕하세요? 문c 블로그(https://jake.dothome.co.kr)의 문영일입니다.
질문들에 대한 답변을 달아보았습니다.
질문 1-1:
(typeof((ptr) + 0))NULL; 구문의 의미가 궁금합니다.
-> 잠재적으로 ptr이 포인터 배열일 수 있다고 판단하고, 즉 NULL을 ptr[0]의 타입으로 캐스팅하고 있습니다.
예) int *a[10]; -> (typeof(a + 0)) NULL -> (int *) NULL
질문 1-2:
const void __percpu *__vpp_verify 에서 "void __percpu*" 가 어떤 의미인지 궁금합니다.
-> sparse 툴을 사용하여 컴파일 시 잘못된 주소의 사용을 체크하기 위함입니다. __percpu를 사용하면 일반 커널 주소가 아니라 per-cpu에서 사용하는 주소여야 한다는 것을 의미합니다.
참고: Sparse 정적 분석 도구 | 문c - http://jake.dothome.co.kr/sparse
질문 2:
set_my_cpu_offset() 에서는 "memory"를 사용하고
__my_cpu_offset() 에서는 "Q"를 사용합니다.
두 함수에서 constraint를 사용하는 데 있어서 차이가 나는데, 특별한 이유가 있는지 궁금합니다.
-> asm [volatile] ( AssemblerTemplate
: OutputOperands
[ : InputOperands
[ : Clobbers ] ])
OutputOperands와 inputOperands가 C가 -> 인라인 어셈블리에게 주고 받는 변수 항목을 알려주는 것이고,
clobbers는 반대로 인라인 어셈블리에서 C 컴파일러에게 영향을 주는 항목을 지정합니다.
예를 들어 클로버 리스트에 “r9”, “cc”, “memory”를 지정하면 r9 레지스터와 플래그 및 메모리 어딘가를 변경할 수 있다고 c 컴파일러에게 알립니다. 그런데 ARM을 사용하는 최근 gcc 컴파일러는 Clobbes가 아니라 OutputOperands와 InputOperands에서 "Q"를 사용하여 메모리 어딘가가 아니라 특정 메모리 주소를 수정할 수 있다록 알릴 수 있게 하였습니다.
참고: Inline-Assembly | 문c - http://jake.dothome.co.kr/inline-assembly/
질문 3:
READ_ONCE / WRITE_ONCE를 사용하는 이유가 무엇인지 궁금합니다.
-> READ_ONCE()와 WRITE_ONCE() 매크로 함수는 컴파일러 및 아키텍처의 optimization을 막고, 지정한 메모리 주소를 대상으로 atomic하게 읽거나 쓰게할 목적으로 사용됩니다.
참고: Atomic Operation | 문c - http://jake.dothome.co.kr/atomic/
감사합니다.