Preface
언어 번역 프로그램 관련 내용 중 컴파일러를 사용하는 프로그래밍 언어는 사용할 변수를 먼저 선언한 후 코드를 작성한다는 문장이 있었다.
해당 문장으로 보아 인터프리터 방식은 코드 작성 시 변수를 먼저 선언하지 않는 것으로 보이는데, 이렇게 되면 프로그램의 오류가 발생할 가능성이 커질 뿐만 아니라 오류 발생 위치 추적도 어려울 것이라 생각한다.
물론, 본문에서 서술하듯 인터프리터는 작성된 코드를 바로 실행하므로 빠르게 프로그램을 설계할 수 있겠지만, 그로 인해 발생할 문제점이 이점보다 많을 것 같다.
인터프리터 방식의 단점을 생각하다보니 문득 소프트웨어 공학에서 공부했던 내용이 떠올랐다.
소프트웨어의 개발은 유지보수에서 사용되는 금액이 실제 개발 관련 공정과정(계획, 요구사항 분석, 설계, 구현 시험 등)에서 사용되는 금액 보다 크기 때문에 처음 만들 때 최대한 완벽하게 만드는 것이 좋다고 한다.
이렇게만 보면 단지 빠른 개발 속도를 위해 인터프리터 방식을 사용한다는 것을 이해할 수 없지만, 책에서 소개하지 않은 다른 이유가 있지 않을까?
→ 인터프리터 방식은 전체를 컴파일 하는 것이 아닌, 한 줄씩 실행하므로 컴파일러에 비해 디버깅이 쉽다고 한다. 나는 전체를 실행한 후 오류를 수정하는 것으로 생각했기 때문에 한 줄씩 실행하며 오류가 있으면 즉시 수정한다는 생각을 하지 못했던 것 같다. 완전히 잘못 생각했다.
+ 컴파일러와 인터프리터는 프로그래밍 언어의 수준에 따라 사용!
→ 이번에 작성한 서문이 전체적으로 오류가 있는 것 같아 다시 한 번 정리하여 작성한다.
컴파일러는 코드를 작성한 후 컴파일을 진행한다.
이 때 산출된 아웃풋은 기계어일 수도, 어셈블리어일 수도 혹은 다른 규격일 수도 있다.
만약 기계어라면 곧바로 실행시킬 수 있고, 어셈블리어라면 해당 OS의 도움을 받아 기계어로 번역한 후 실행시킬 수 있다. 또한, 다른 규격일 땐 어셈블리어로 변환시킨 후 기계어로 변환하거나, 바로 기계어로 변환하여 실행하면 된다.
즉, 컴파일러란 컴파일과 실행의 개념이 분리된 것이다.
그러므로 컴파일 된 아웃풋이 존재한다면, 이것을 실행만 하면 되므로 실행 속도가 빠르다.
그러나 소스 수정이 일어난 경우엔 다시 컴파일을 해야 한다는 단점이 존재한다.
이와 반대로 인터프리터는 컴파일과 실행의 개념이 묶여있다.
그렇기에 소스가 수정되더라도 별도의 컴파일 과정 없이 수정된 내용을 즉시 반영할 수 있지만, 한 줄씩 명령을 수행하므로 아웃풋에 대한 실행 속도가 느리다.
간단히 정리하자면, 컴파일이란 빌드와 실행의 개념이 분리되어 있어 컴파일된 아웃풋이 존재한다면 실행 속도가 빠르지만 소스코드 수정 시 재컴파일이 필요하고, 인터프리터란 빌드와 실행의 개념이 묶여 있어 개발 속도가 빠르지만 명령을 한 줄씩 수행하므로 실행 속도가 다소 느리다.
+ 컴파일된 언어는 다른 OS에서 실행되지 않는데, 이를 위해 고안된 개념이 Java이다.
Java는 가상 머신 위에서 동작하며, Java를 컴파일하면 어셈블리어보다 한 수준 고차원이지만 고수준 언어보단 한 수준 저차원인 아웃풋을 만든다.
그러므로 Java 가상 머신을 이용하면 어느 운영체제에서든 컴파일된 아웃풋을 실행시킬 수 있다.
이처럼 Java는 머신(OS) 간 확장성이 좋지만, 가상머신에서 재번역을 하므로 속도가 느리다.
1. 메모리 관리의 개요
- 메모리 구조는 1Byte(8bit) 크기로 나뉜다.
- 메모리 주소 레지스터(MAR) : CPU가 메모리에 있는 내용을 가져오거나 작업 결과를 메모리에 저장할 때 사용
- 메모리 관리 시스템(MMS) : 복잡한 메모리 관리
- 메모리 관리의 이중성 : 프로세스 입장에선 메모리를 독차지하려 하고, 메모리 관리자 입장에선 관리를 효율적으로 하고 싶어 하는 것
→ 프로세스 입장에서 작업의 편리함과 관리자 입장에서 관리의 편안함이 충돌
- 프로그래밍 언어
1) 저급 언어 : 컴퓨터의 동작을 가장 직접적으로 표현한 언어
→ 기계어, 어셈블리어 etc.
2) 고급 언어 : 사용자가 이해하기 쉽게 프로그래밍할 수 있는 언어
→ C 언어, 자바 etc.
- 언어 번역 프로그램 : 고급 언어로 작성한 소스코드를 기계어로 번역하는 프로그램
1) 컴파일러 : 작성된 코드를 기계어로 우선 번역한 뒤 한꺼번에 실행
→ C 언어, 자바 etc.
2) 인터프리터 : 소스코드를 한 행씩 바로 번역하여 실행
→ 자바스크립트, 베이직 etc.
- 컴파일러의 목적
1) 오류 발견
→ 심벌 테이블(변수 선언부에 명시한 각 변수의 이름과 종류를 모아놓은 테이블) 사용
2) 코드 최적화
→ 작고 빠른 실행 파일 제작
- 컴파일러를 사용하는 프로그래밍 언어 : 사용할 변수를 먼저 선언한 후 코드 작성
→ 즉, 컴파일러는 크고 복잡한 프로그램에, 인터프리터는 간단한 프로그램에 사용
- 컴파일 과정 : 사용자가 작성한 소스코드를 목적코드로 변환한 후 라이브러리를 연결
1) 목적 코드 : 사용자가 작성한 소스코드를 컴파일러로 일차 번역한 코드
2) 라이브러리 : 자주 사용하는 함수를 시스템 내에 미리 만들어둔 것
→ 프로그래머가 직접 만들기 어렵거나 제작 시간이 많이 걸리는 함수를 파일 형태로 모아놓은 것
cf. <stdio.h> 라이브러리 : 입출력 함수를 미리 만들어놓은 것
- 과거엔 printf( ) 문에 새로운 기능이 추가되었을 때 해당 printf( ) 문의 정의가 담긴 <stdio.h> 라이브러리를 가져와 다시 컴파일했지만, 최근엔 printf( ) 문의 자리를 비워 놓고 컴파일한 후 실행할 때 prinf( ) 문의 실행 코드를 라이브러리에서 가져와 실행한다.
- 이 때 삽입되는 함수를 가진 라이브러리 : 동적 라이브러리
1) 함수가 변경되어도 새로 컴파일할 필요 X
2) 윈도우에선 동적 라이프러리 파일을 DLL(Dynamic Link Loader)이라고 부름
- 메모리 관리자 : 메모리 관리 유닛(MMU)이라는 하드웨어
1) 가져오기 작업 : 프로세스와 데이터를 메모리로 가져오는 작업
① 프로세스가 요청할 때 가져옴
② 필요하다고 예상되는 데이터를 미리 가져옴
2) 배치 작업 : 가져온 프로세스와 데이터를 메모리의 어떤 부분에 올릴지 결정하는 작업
① 페이징(paging) : 메모리를 같은 크기로 자르는 것
② 세그먼테이션(segmentation) : 메모리를 프로세스의 크기에 맞게 자르는 것
3) 재배치 작업 : 메모리에 새로운 프로세스를 가져오기 위해 오래된 프로세스를 내보내는 작업
① 교체 알고리즘 : 앞으로 사용하지 않을 프로세스를 찾아서 내보내는 알고리즘
2. 메모리 주소
- CPU의 비트 : 한 번에 다룰 수 있는 데이터의 최대 크기
→ 예를 들어 32bit CPU는 한 번에 32bit를 다룰 수 있으며, 레지스터, ALU, 버스, 대역폭 등의 크기도 동일함
- 단순 메모리 구조
1) 일괄 처리 시스템에서 볼 수 있음
2) 운영체제 영역과 사용자 영역으로 나뉨
3) 사용자 프로세스는 운영체제 영역을 피하여 메모리에 적재
- 메모리를 최상위 부터 사용하는 방법
1) 메모리를 최상위에서 운영체제 방향으로 내려오며 사용
2) 운영체제 크기에 상관없이 사용자 영역의 시작점 결정 가능
→ but, 주소 변경이 복잡하여 잘 쓰이지 않음
- 경계 레지스터 : 사용자 영역이 운영체제 영역으로 침범하는 것을 막는 레지스터
- 컴파일 방식을 사용하는 프로그램은 컴파일 시 변수의 주소를 0번지부터 배정한다.
- 메모리 주소
1) 절대 주소 : 실제 물리 주소
① 관리자 입장에서 바라본 주소
② 메모리 주소 레지스터가 사용하는 주소로
③ 램 메모리의 실제 주소
2) 상대 주소 : 사용자의 영역이 시작되는 번지를 0번지로 변경하여 사용하는 주소 지정 방식
① 사용자 프로세스 입장에서 바라본 주소
② 절대 주소와 관계없이 항상 0번지부터 시작
- 메모리 주소 공간
1) 물리 주소 공간 : 절대 주소를 사용하는 주소 공간
→ 하드웨어 입장에서 바라본 주소 공간
2) 논리 주소 공간 : 상대 주소를 사용하는 주소 공간
→ 사용자 입장에서 바라본 주소 공간
1) 사용자 프로세스가 상대 주소 40번지에 있는 데이터 요청
2) CPU는 메모리 관리자에게 40번지에 있는 내용을 가져오라고 명령
3) 메모리 관리자는 재배치 레지스터를 사용하여 상대 주소를 절대 주소로 변환
→ 상대주소 값 + 재배치 레지스터 값 = 절대 주소
- 재배치 레지스터 : 주소 변환의 기본이 되는 주소값을 가진 레지스터
→ 사용자 영역의 시작 주소값이 저장됨
3. 단일 프로그래밍 환경에서의 메모리 할당
- 메모리 오버레이(overlay) : 프로그램의 크기가 실제(물리) 메모리보다 클 때 전체 프로그램을 메모리에 가져오는 대신 적당한 크기로 잘라 가져오는 기법
1) 프로그램을 몇 개의 모듈로 나누어 필요할 때마다 메모리에 가져와 사용
2) 어떤 모듈을 가져오거나 내보낼지는 프로그램 카운터(PC)가 결정
3) 프로그램의 일부만 메모리에 올라와도 프로그램 실행이 가능함
- 스왑 영역 : 메모리가 모자라서 쫓겨난 프로세스를 모아두는 저장장치의 별도 공간
1) 재사용할 수도 있고, 작업이 끝나지 않았으므로 원래의 하드디스크가 아닌 스왑 영역에 보관
2) 스왑 영역은 디스크 관리자가 아닌, 메모리 관리자가 관리함
- 스왑 영역 용어
1) 스왑 인 : 스왑 영역에서 메모리로 데이터를 가져오는 작업
2) 스왑 아웃 : 메모리에서 스왑 영역으로 데이터를 내보내는 작업
참고 문헌 : 조성호, 『쉽게 배우는 운영체제』(한빛아카데미), 2020, p.329~350.
'CS > 쉽게 배우는 운영체제' 카테고리의 다른 글
운영체제 글 (0) | 2022.09.14 |
---|
댓글