스택큐힙리스트

x86 명령어 캐시는 어떻게 동기화되나요? 본문

카테고리 없음

x86 명령어 캐시는 어떻게 동기화되나요?

스택큐힙리스트 2023. 8. 26. 00:08
반응형

저는 예시를 좋아해서 C 언어로 자기 수정 코드를 조금 작성해보았습니다...

#@!'#include

#include // linux

int main(void) {

unsigned char *c = mmap(NULL, 7, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|

MAP_ANONYMOUS, -1, 0); // get executable memory

c[0] = 0b11000111; // mov (x86_64), immediate mode, full-sized (32 bits)

c[1] = 0b11000000; // to register rax (000) which holds the return value

// according to linux x86_64 calling convention

c[6] = 0b11000011; // return

for (c[2] = 0; c[2] < 30; c[2]++) { // incr immediate data after every run

// rest of immediate data (c[3:6]) are already set to 0 by MAP_ANONYMOUS

printf("%d ", ((int (*)(void)) c)()); // cast c to func ptr, call ptr

}

putchar('\n');

return 0;

}

'#@!

...which works, apparently:

...물론, 작동하는 것 같습니다.

#@!'>>> gcc -Wall -Wextra -std=c11 -D_GNU_SOURCE -o test test.c; ./test

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

'#@!

하지만 솔직히 말해서, 전혀 작동할 것으로 기대하지 않았습니다. 나는 첫 호출 시에 # $ @ & @ $ $ * $ & # @ ! 를 포함하는 명령이 캐시될 것으로 예상하였고 그 후에는 # $ *! # $ ^ $ & # @! 에 대한 모든 연속 호출이 # $ *! # $ ^ $ & # @! 에 대한 반복적인 변경 사항을 무시할 것으로 예상하였습니다 (캐시를 명시적으로 무효화하지 않는 한). 다행히도, 나의 CPU는 그것보다 똑똑한 것 같습니다.

나는 CPU가 RAM과 명령어 캐시를 비교하는 것 같다. (RAM에 !@##@!'c'!@##@!이 있다고 가정한다면) 명령어 포인터가 큰 점프를 할 때마다 (위의 맵된 메모리 호출과 같은 경우) 캐시를 무효화하고, 일치하지 않을 때 (전부인가요?) 알려져있는 캐시 동작에 대해 더 구체적인 정보를 얻고 싶습니다. 특히 하드웨어와 운영 체제의 차이를 제외하고 이 동작이 예측 가능하고 의존할 수 있는지 알고 싶습니다.

(난 아마도 인텔 매뉴얼을 참고해야 할 것 같은데, 그건 수천 페이지로 구성되어 있고 난 그 안에서 길을 잘못 찾을 때가 많아서...)

답변 1

당신이 하는 것은 일반적으로 자가 수정 코드라고 합니다. Intel 플랫폼 (아마도 AMD도)은 매뉴얼에서 가리키는 대로 i/d 캐시 일관성을 유지하는 작업을 대신해 줍니다. ( !@##@!'Manual 3A, System Programming'!@##@! )

11.6 자가 수정 코드

현재 프로세서의 캐시에 캐시된 코드 세그먼트의 메모리 위치에 쓰기를 하면, 관련된 캐시 라인(또는 라인들)이 무효화됩니다.

하지만 동일한 선형 주소가 수정 및 검색에 사용되는 한 이 주장은 올바릅니다. 하지만 디버거 및 이진로더의 경우는 다른 주소 공간에서 실행되므로 이 주장은 적용되지 않습니다.

자기 수정 코드를 포함한 애플리케이션은 수정 및 명령 검색을위한 동일한 선형 주소를 사용합니다. 명령을 가져오는 데 사용되는 주소와 다른 선형 주소를 사용하여 명령을 수정할 수있는 시스템 소프트웨어 (예 : 디버거)는 수정된 명령을 실행하기 전에 CPUID 명령과 같은 직렬화 작업을 실행하여 명령 캐시와 프리페치 큐를 자동으로 동기화합니다.

예를 들어, 직렬화 작업은 항상 다른 아키텍처(PowerPC 등)에서 요청되며 명시적으로 수행해야 합니다. ( !@##@!'E500 Core Manual'!@##@! )

3.3.1.2.1 자기 수정 코드

프로세서가 명령을 포함할 수 있는 모든 메모리 위치를 수정할 때, 소프트웨어는 명령 캐시를 데이터 메모리와 일치시키고 수정 사항을 명령을 가져오는 메커니즘에 표시해야 합니다. 이 작업은 캐시가 비활성화되었거나 페이지가 캐싱이 금지되어 있더라도 수행되어야 합니다.

파워PC가 캐시를 비활성화하였을 때에도 컨텍스트 동기화 명령을 발급하는 것은 흥미로운 점입니다. 로드/스토어 버퍼와 같은 깊은 데이터 처리 유닛의 비우기를 강제하는 것으로 의심됩니다.

당신이 제안한 코드는 스누핑 또는 고급 캐시 일관성 기능이 없는 아키텍처에서 신뢰할 수 없기 때문에 실패할 가능성이 높습니다.

이것이 도움이 되기를 희망합니다.

답변 2

x86 명령어 캐시는 어떻게 동기화되는가?

안녕하세요! 오늘 우리는 x86 아키텍처에서 명령어 캐시가 어떻게 동기화되는지 알아보고자 합니다. x86 프로세서는 많은 현대 컴퓨터 시스템에서 사용되며, 명령어 캐시는 프로세서 성능을 향상시키기 위해 중요한 역할을 합니다. 이제 우리는 어떻게 이 명령어 캐시가 동기화되는지 살펴보겠습니다.

첫째로, x86 명령어 캐시는 다중 코어 프로세서에서 발생하는 동기화 문제를 다루는 데 중요한 역할을 합니다. 다중 코어 프로세서는 여러 개의 코어로 구성되어 동시에 여러 개의 명령어를 실행할 수 있습니다. 그러나 코어 간에 데이터 및 명령어의 일관성을 유지하기 위해 캐시 동기화가 필요합니다. 이를 위해 x86 아키텍처에서는 캐시 일관성 프로토콜(Coherence Protocol)을 사용합니다.

캐시 일관성 프로토콜은 다양한 메모리 동기화 기법을 포함하는 복잡한 매커니즘입니다. 하지만 기본 아이디어는 간단합니다. 캐시는 일관성 있는 데이터를 유지하기 위해 어떤 데이터가 수정되면 해당 데이터를 사용하는 모든 캐시 라인을 유효하지 않은(invalidate) 상태로 설정합니다. 그러면 해당 캐시 라인을 다시 사용할 때 무효화된 데이터를 가져와 새로운 값을 업데이트합니다. 이렇게 함으로써 프로세서 간에 캐시 일관성이 유지됩니다.

뿐만 아니라, x86 아키텍처는 캐시 일관성을 보장하기 위해 메모리 배리어(Memory Barrier)와 같은 특별한 명령어도 제공합니다. 메모리 배리어는 명령어가 실행되는 동안 캐시를 동기화하는 데 사용되는 동기화 포인트를 정의합니다. 이것은 특히 다중 쓰레드 환경에서 데이터 동기화를 보장하고 잠재적인 경쟁 조건을 방지하는 데 유용합니다.

따라서 x86 아키텍처의 명령어 캐시는 캐시 일관성 프로토콜과 메모리 배리어를 통해 동기화됩니다. 이를 통해 프로세서 간에 데이터 일관성을 유지하고 동기화 문제를 해결할 수 있습니다.

이 글을 마치며, x86 아키텍처의 명령어 캐시 동기화에 대해 알아보았습니다. x86 프로세서의 성능을 향상시키기 위해 명령어 캐시가 어떻게 동기화되는지를 이해하는 것은 중요합니다. 캐시 일관성 프로토콜과 메모리 배리어는 프로세서 간의 데이터 일관성을 보장하는 역할을 하고, 다중 코어 프로세서에서 발생하는 동기화 문제를 해결할 수 있습니다.

반응형
Comments