X : 컴퓨터 구조에 대한 세 번째 이야기
뇌를 자극하는 윈도우즈 시스템 프로그래밍 을 읽고 정리한 문서입니다 ;)
절차적 함수 호출 지원 CPU 모델
함수 호출 또한 CPU의 도움을 받아야만 가능한 일이다. 함수 호출은 소프트웨어 종속적인 기능으로 생각할 수 있지만 사실은 하드웨어 종속적인 부분이 상당수 존재한다.
함수 호출 과정에서 할당되는 메모리 블록을 스택 프레임이라고 한다.
함수 호출이 완료되면 기존에 선언된 지역변수에 접근이 불가능하다. 이는 할당되었던 메모리가 반환되었음을 의미하는 것이다.
지역변수를 위한 메모리 공간을 스택이라 이름붙인 이유는 메모리의 구조적 특성 때문이다. 가장 먼저 할당되면, 가장 나중에 반환된다.
계속해서 스택에 데이터를 쌓거나 반환하기 위해서는 현재 어느 위치까지 데이터를 저장했는지를 기억해야 한다. 따라서 CPU 내에 sp(stack pointer) 라는 레지스터가 존재한다.
호출된 함수가 종료될 경우, 스택 프레임 단위로 sp 레지스터값을 이동시켜야 한다. 호출된 함수가 종료될 경우 그 안에서 선언된 변수들을 동시에 모두 반환해야 하기 때문이다. 간단히 sp가 가리키는 위치를 아래로 이동시킴으로서 스택 프레임을 반환할 수 있게 된다.
하지만 정작 호출이 완료된 함수를 빠져 나오는 시점에, 앞서 얼마만큼의 메모리 공간을 할당했는지 알지 못한다. 따라서 이를 위해 프레임 포인터 레지스터가 존재한다.
함수 내에서 변수를 선언할 때마다 프레임 포인터의 사이즈를 증가시킨다면, 이는 매번 덧셈 연산을 필요로 한다. 따라서, 되돌아갈 sp 위치만 저장해 놓는 식으로 간단히 구현한다.
하지만 이를 그저 레지스터 하나만으로 구현하기에는 중첩된 함수 호출에는 적용할 수 없게 되기에, 이 프레임 포인터 레지스터 또한 스택에 매번 함수 호출 직전에 저장하고 불러오는 식으로 작동한다.
함수 호출 인자의 전달
전달되는 인자가 함수 내에서는 유효하고, 함수 호출이 끝나고 나면 사라지는 것으로봐서 지역변수와 마찬가지로 스택에 할당된다고 생각할 수 있고, 이는 대체로 맞는 말이긴 하다.
하지만 성능 향상을 위해서 일부 전달인자들은 레지스터를 할당해서 이곳에 저장하도록 제품의 표준을 정의하기도 한다.
하지만 레지스터는 제한적이기에 함수 호출 시 전달되는 인자들은 모두 스택에 저장하는 것을 기본 동작 원리로 하고, 이는 sp가 가리키는 현재 위치에 전달되는 인자값들을 저장하고 나서, sp를 증가시켜 다음 메모리 주소를 가리키게끔 한다.
함수 호출에 의한 실행의 이동
메모리 영역 중 코드 영역은 프로그램이 동작하기 위한 프로그램 코드가 올라가는 위치이다. 명령어의 실행은 세 단계로 이루어지고, Fetch, Decode, Execution 중 첫 번째인 Fetch 단계에서 명령어를 가져오게 되는 위치는 프로그램 코드가 존재하는 코드 영역이다.
어느 위치에 있는 명령어까지 가져와 실행했는지 기억하고 있어야만 다음 번에 실행할 명령어를 알 수 있기에, 이를 레지스터로 저장하고 PC 레지스터로 이름붙인다.
함수 호출이 가능하기 위해서는 순차적인 실행만으로는 부족하다. 특정 위치로의 이동이 가능토록 해야만 한다. 따라서 PC에 실행할 명령어의 주소를 넣는 연산을 수행하는 것으로 함수 호출이 이루어진다.
하지만 PC 레지스터에 함수 주소값을 넣는 것만으로는 완벽하지 않다. 현재의 PC 값을 백업해두어야만 함수 호출이 완료되었을 때 기존 실행하던 위치로 돌아올 수 있다. 따라서 이 또한 스택에 값을 저장해두는 식으로 동작한다.
함수 호출규약
함수 호출과정에서 할당된 스택 프레임을 반환하는 방법에는 두 가지가 존재한다. 함수 호출이 완료된 이후에 sp 레지스터를 복원하는 등의 동작을 함수 호출자가 수행할지, 호출된 함수가 수행할지로 나뉘는 것이다.
이러한 함수 호출 시 인자르르 전달하는 방식과 스택 프레임을 반환하는 방식을 약속해 놓은 것을 함수 호출규약이라고 한다.
__stdcall
: 이는 흔히WINAPI
,APIENTRY
,CALLBACK
이라는 다른 이름으로도 볼 수 있는, 초기부터 존재했던 호출규약으로서, 호출된 함수 내에서 스택 프레임을 반환하는 규약이다.__cdecl
: 일반 프로그래밍에 가장 많이 쓰이는 규약으로서 이는 함수를 호출한 측에서 함수 처리가 끝나면 스택 프레임을 반환하는 규약이다.__fastcall
: 이는 함수 인자의 전달을 스택이 아닌 레지스터를 우선적으로 사용함으로서 속도를 더욱 끌어올린 함수 호출 규약이다.
이것만은 알고 갑시다
- 스택이 관리되는 방법
- 특별한 용도로 지정되어 있는
SP
레지스터와FP
레지스터,PC
레지스터의 존재 이유와 동작 방식을 알아야 한다. - 스택은 단순히 지역변수들로만 이루어지는 것이 아니고, 함수가 호출되는 과정에서 기존의
FP
값과PC
값이 저장되었다가 복원되게 된다.
- 특별한 용도로 지정되어 있는
- 함수 호출규약
- 다양한 함수 호출 규약이 존재하며, 어떤 호출 규약을 따를 것인가는 사용할 라이브러리나 환경에 맞추어 적용해야 한다.
- 전달인자와 레지스터
- 64비트 CPU는 32비트 CPU와 다른 레지스터 구조를 지닌다. 따라서 32비트 환경과는 다른 함수 호출 규약이 존재한다. 새로운 함수 호출규약에서는 성능 향상을 위해 레지스터를 더욱 많이 활용하고 있다.
'공부한 이야기 > 윈도우 OS' 카테고리의 다른 글
XII : 쓰레드의 생성과 소멸 (0) | 2023.04.29 |
---|---|
XI : 쓰레드의 이해 (0) | 2023.04.29 |
IX : 스케줄링 알고리즘과 우선순위 (0) | 2023.04.29 |
VIII : 프로세스간 통신 (IPC) 2 (0) | 2023.04.29 |
VII : 프로세스간 통신 (IPC) 1 (0) | 2023.04.29 |