컴퓨터구조(2) - 명령어: 컴퓨터 언어(2)
‘컴퓨터 구조 및 설계’를 공부하여 정리한 내용입니다.
MIPS의 32비트 수치를 위한 주소지정 및 복잡한 주소 지정 방식
- 다양한 방법의 필요성
- 단순한 방법으로는 분기할 주소값을 표현하지 못할 가능성이 생긴다.
- 간단한 방법으로는 분기할 주소 전에 분기점을 하나 더 만들어 두번 거쳐서 분기하는 방법이다.
- 이외에도 아래와 같은 방법이 있다.
수치 주소지정 | - 피연산자는 명령어 내에 있는 상수이다. |
레지스터 주소지정 | - 피연산자는 레지스터로, 분기할 주소값은 그 레지스터에 저장된 값이다. |
베이스(base) 또는 변위(displacement) 주소지정 | - 우선 메모리 주소는 레지스터와 명령어 내에 주어진 상수를 더해서 구한다. - 지정된 메모리의 내용이 피연산자가 된다. |
PC 상대 주소지정 | - PC 값과 명령어 내 상수의 합을 더해서 주소를 구한다. - 이 때, 파이프라이닝을 사용하게 된다면 PC 값을 잘 지정해야 한다는 것을 유의하자. |
의사직접(pseudodirect) 주소지정 | - 명령어 내의 하위 26비트를(opcode 6bit를 제외한 나머지) PC의 상위 비트들과 연접하여 주소를 구한다. - PC의 상위 비트란 상위 6비트를 의미한다. |
병렬성과 명령어: 동기화
- 병렬성에서 동기화의 필요성
- 두가지 이상의 프로세서가 한 메모리 공간에 수정권한을 가지고 동시에 접근할 때, 경쟁 상태(race condition)에 빠질 가능성이 있다
- 그래서 특정 메모리 공간에 접근할 때, 여러 프로세서가 한번에 들어오는 것을 막아주는 기능이 필요하다.
- 어떤 방법을 제공하는가?
- 위에서 설명했듯이, 특정 메모리 공간에 접근할 때, 이를 Lock하고 Unlock하는 기능이 필요하다
- 원자적으로 처리할 능력을 가진 하드웨어 프리미티브가 필요하다.
- 여기서 원자적이라는 의미는 ‘한 순간에’ 모든 일을 마친다는 의미로, 읽고 수정하고 다시 저장하는 등의 일련의 과정이 무조건 연결되어 일어난다는 의미다.
- 이 일련의 과정 동안에는 프로세서의 전환이나 인터럽트가 끼어들지 못하게 하는 primitive 기능이 필요하다는 의미로 받아들여질 수 있다.
- 혹시 끼어들었다면 아토믹 연산의 중간 결과는 모두 취소된다.
프로그램 번역과 실행
- 컴파일러
- C 등의 상위 언어 프로그램을 어셈블리 언어 프로그램으로 바꾼다.
- 어셈블러
- 어셈블리 프로그램을 기계어로 번역하는 일을 한다.
- 여기에는 하드웨어에서 제공하는 기능을 바로 매핑하는 명령어들도 있지만, 하드웨어로 구현이 되어있지 않아도 사람이 이해하기에 편한 형태의 의사명령어(pseudoinstruction)를 제공하여 사용자의 편의를 도모하기도 한다: 예를들어 blt(branch and less than)라는 명령어가 의사명령어로 제공된다면 어셈블러에서는 이를 알아서 slt, bne 명령어를 연속적으로 사용한다고 파악하고 사용하는 식이다.
- 결과적으로 어셈블리 언어 프로그램을 -> 목적 파일(object file)로 바꾼다.
- 목적파일
- 목적 파일 헤더: 각 주요 부분 크기 및 위치 정보
- 텍스트 세그먼트: 기계어
- 정적 데이터 세그먼트: 정적 데이터와 동적 데티어를 프로그램에서 사용할 수 있게 한다.
- 재배치 정보: 프로그램이 메모리에 적재될 때 절대 주소에 의존하는 명령어와 데이터 워드 표시
- 심벌 테이블(symbol table): 분기나 데이터 저장 공간에 사용된 모든 레이블을 저장하는 테이블
- 디버깅 정보: 각 모듈이 어떻게 번역되었는지에 대한 설명, 디버거가 움직일 때 필요한 정보를 제공한다.(gdb 등)
- 링커
- 프로그램을 여러개로 나누어 구현하였을 때, 이를 적절하게 각각 컴파일, 어셈블하고 이 어셈블된 기계어 프로그램을 하나로 연결해 주는 일을 한다.
- 결과물로는 실행 파일(executable file)을 생성한다. 이는 목적 파일과 같은 형식을 갖는데, 차이점이 있다면 미해결된 참조가 없다는 것이다.
- 다만 라이브러리 루틴같이 일부만 링크된 파일이 있을 수도 있다. 이런 파일은 미해결 주소를 갖고 있기에, 목적 파일에 속한다.
- 로더
- 운영체제가 디스크에 있는 실행 파일을 메모리에 넣고 이를 실행시킬때 사용하는 시스템 프로그램
- 동적 링크 라이브러리의 필요성
- 정적라이브러리를 사용하면 한번 컴파일해서 프로그램을 만들고나면 이 프로그램은 이미 라이브러리에 대한 내용이 모두 링크 되어있는 상태가 되기 때문에 라이브러리가 업데이트 되어도, 이미 링크되어 있는 실행파일 내부의 오래된 라이브러리 정보를 사용해서 프로그램을 실행하게 된다. (다시 프로그램을 링커로 링크하면 당연히 업데이트 된다. 이 경우는 한번 만든 실행파일을 그대로 사용할 때의 이야기이다.)
- 정적라이브러리를 사용하게 되면 실제로 실행 파일에서는 호출되지 않는 루틴에 대해서도 모두 미리 컴파일되어 있어야 해서, 이 부분들도 모두 적재되는 낭비가 일어난다.
- 그래서 코드가 수정되어 최신 코드가 나오거나 했을 때, 바로 적용될 수 있도록, 실행할 때 필요한 루틴만 골라서, 그 때 그 때 업데이트된 최신 코드에 대해서 라이브러리를 링크하여 프로그램 실행에 사용하는 동적 링크가 필요하다고 판단하게 된다.
- 동적 링크 라이브러리(DLL: dynamicallu linked library)
- 초기: 로더가 프로그램을 메모리에 넣을 때, 파일에 저장된 추가 정보를 이용해서 적절한 라이브러리를 찾고 모든 외부 참조를 갱신했다.
- 초기의 단점: 호출되는 것을 기준으로 링크가 되는 것이 아니라, 호출될 가능성이 있는 모든 라이브러리 루틴이 링크된다.
- 후기: 프로그램 실행중에 호출되는 라이브러리를 실시간으로 링크시키는 지연(Lazy) 프로시저 링키지형의 DLL이 개발되었다. (실행전에는 링크되지 않고 실행 중에 필요하다는 것이 판단되면 최초로 링크되면 그 이후에 다시 호출되면 이전에 링크시킨 정보를 이용한다: 이 작업을 통해 호출되는 라이브러리 루틴만 링크되게 된다)
- 후기 DLL에서 사용하는 더미 엔트리란?: 프로그램이 실행하는 중에 특정 라이브러리 루틴을 처음 호출하면 이에 대해 미리 만들어둔 맵(표) 형태의 더미 엔트리 테이블로 먼저 뛴다. 그리고 해당하는 루틴의 엔트리를 확인한다. 아직 한번도 링크를 해본적이 없다면, 그 엔트리 공간에는 링크해야 하는 Obj 파일의 위치가 있고, 이를 찾아가서 라이브러리 루틴을 메인 메모리에 사상한다. 그리고 그 위치를 더미 엔트리 공간에 저장한다. 후에 다시 그 라이브러리를 다시 호출하여 더미 엔트리 테이블로 넘어가면 해당하는 엔트리 공간에 사상된 라이브러리 루틴의 위치를 가리키는 정보가 저장되어 있고 코드를 찾아가는 작업없이 바로 루틴으로 뛰는 것이 가능하다.
- 어찌되었건, 링커를 통해 실행프로그램이 한번 만들어졌다고 해도, 아직 실행파일은 링크가 되지 않은 라이브러리 루틴을 가지게되고, 로더가 프로그램을 실행하는 순간에 필요할 때 라이브러리(주로 obj 파일)를 링크하기 때문에 실행 파일이 만들어진 이후에 라이브러리, obj파일이 업데이트되면 실행 파일일이 실제 실행되는 순간의 라이브러리는 업데이트 된 라이브러리이기 때문에 업데이트 된 라이브러리를 사용해서 프로그램을 구동하는 것이 가능하게 된다. (새로 링커를 이용해서 실행파일을 만들 필요가 사라진다.)
Leave a Comment