책/컴퓨터 구조

컴퓨터 구조 Ch02_"Language of the Computer-4"

RyoTTa 2021. 1. 20. 21:44
반응형

2.11 병렬성과 명령어 : 동기화

 Task들이 종속 관계 및 협력 관계에 있을때 데이터 경쟁관계(Race Condition)의 위험이 있다. 

 즉, 이벤트의 일어나는 순서에 따라 프로그램의 결과가 달라질 수 있는 상황이다.

Race Condition

 동기화 메커니즘은 일반적으로 사용자 수준 소프트웨어 루틴에서 제공되는데 이 소프트웨어 루틴들은 하드웨어가 제공하는 동기화 명령을 사용한다. 여기서는 간단하게 lock, unlock 동기화를 사용한다.

 lock, unlock은 critical section이라 칭하는 하나의 공간을 생성하며 이 공간은 오직 하나의 프로세서만이 실행 가능하다.즉, Mutual Exclusion하게 실행된다는 의미이다.

lock, unlock
Code

이런 방법을 제공하기위해 원자적 교환(Atomic swap)을 사용하게된다. 이는 레지스터의 값을 메모리 값과 교환하는 것이다. lock은 0이면 사용 가능하고 1이면 사용할 수 없음을 표시한다고 가정한다. 프로세서는 레지스터에 있는 값 1과 메모리에 있는 lock을 바꿈으로 lock을 1로 만들고자 할 것이다. 만약 다른 프로세스가 이미 접근을 주장했다면 교환 명령어가 가져올 값은 1일것이며 그렇지 않다면 0일 것이다. 후자의 경우에는 메모리에 있는 값이 1로 바뀌어 경쟁 관계에 있는 다른 프로세서가 0을 읽어 가지 못하게 한다.

Atomic Swap

다른 방법으로 명령어 두개를 사용하는 방법이 있다. 두번째 명령어란 이 두 명령어가 원자적으로 실행되었는지 여부를 표시한다. 다른 프로세서에서 실행도니 모든 연산이 이 두 명령어보다 먼저 시작되어 먼저 끝나든지 이 명령어보다 나중에 시작되어 나중에 끝난 것 처럼 보인다면 이 한쌍의 명령어는 실질적으로 원자적으로 동작한 것이다.

 

2.12 프로그램 번역과 실행

Executing a Program

 

컴파일러

 컴파일러는 C프로그램을 어셈블리 언어 프로그램으로 바꾼다. 어셈블리 언어 프로그램이란 컴퓨터가 이해할 수 있는 심벌 형태이다. 상위 수준 언어 프로그램은 어셈블리 언어보다 문장 수가 훨씬 적으므로 프로그래머의 생산성이 높아진다. 

 

어셈블러

 어셈블리 언어는 상위 수준 소프트웨어와의 인터페이스이므로 원래는 없는 명령어를 어셈블러가 독자적으로 제공할 수도 있다. 이 명령어들은 하드웨어로 구현 되어있지 않더라도, 어셈블러가 처리하여 번역과 프로그래밍을 간편하게 해준다.

 e.g.) MOV X9, X10 -> ORR X9, XZR, X10

 MOV 명령어는 레지스터의 값을 다른 레지스터로 복사하는 명령어이지만 LEGv8에서 지원하지않는다. 하지만 XZR이라는 레지스터는 항상 0을 가지고있는 명령어 이므로 ORR X9, XZR, X10 명령어를 통해 MOV를 지원하게 된다.

 

 어셈블러는 이러한 편리한 기능을 제공하지만 주된 임무는 어셈블리 프로그램을 기계어로 번역하는 일이다. 즉, 어셈블러는 어셈블리 언어 프로그램을 목적파일(Object file)로 바꾼다. 각 명령어를 이진수로 바꾸기 위해서는 레이블에 해당하는 주소를 모두 알고있어야 한다. 따라서 분기나 데이터 전송 명령에서 사용된 모든 레이블을 심벌 테이블(Symbol table)에 저장한다. 

 

링커

 따로 어셈블된 기계어 프로그램을 하나의 프로그램으로 연결해 주는 일을 한다. 링커가 유용한 이유는 코드를 수정할 때 재컴파일하고 재어셈블하는 것보다 더 빠르기 때문이다.

 1. 코드와 데이터 모듈을 메모리에 심벌 형태로 올려놓는다.

 2. 데이터와 명령어 레이블의 주소를 결정한다.

 3. 외부 및 내부 참조를 해결한다.

 

 즉, 링커는 각 목적 묘듈의 재배치 정보와 심벌 테이블을 이용해서 미정의 레이블의 주소를 결정한다. 이후 외부 참조를 모두 해결하고 나면 각 모듈의 메모리 주소를 결정한다. 

 

로더

 1. 실행파일 헤더를 읽어 텍스트와 데이터 세그먼트의 크기를 알아낸다.

 2. 텍스트와 데이터가 들어갈 만한 주소 공간을 확보한다.

 3. 실행 파일의 명령어와 데이터를 메모리에 복사한다.

 4. 주 프로그램에 전달해야할 인수가 있으면 이를 스택에 복사한다.

 5. 레지스터를 초기화하고 스택포인터는 사용 가능한 첫 주소를 가리키게 한다.

 6. 기동루틴으로 분기한다. 이 기동 루틴에서는 인수를 인수 레지스터에 넣고 프로그램의주 루틴을 호출한다. 주 프로그램에서 기동 루틴으로 복귀하면 exit system call을 사용해 프로그램을 종료시킨다.

 

동적 링크 라이브러리

 정적 링크 : 라이브러리 루틴이 실행 프로그램의 일부가 된다. 즉, 실행파일에서 호출되는 라이브러리 루틴들은 이 호출의 실행 여부와 상관없이 전부 적재해야 한다. 즉 비대한 라이브러리때문에 프로그램이 비대해질 가능성이 있다,.

 

 동적 링크 : 프로그램 실행 전에 라이브러리가 링크되지 않고 적재되지도 않는다. 과거에는 로더가 호출 가능성 있는 모든 라이브러리 루틴을 링크시켜 모든 외부 참조를 갱신했다. 현재는 대신 Stub을 링크시켜 프로시저의 위치와 이름에 대한 정보를 가지고있다. 이후 라이브러리가 실행될때 Stub이 필요한 라이브러리 루틴을 실행한다. 

반응형