[운영체제] 컴파일

지니🧸·2023년 4월 7일
0

CS 저장소

목록 보기
10/48

🪕 컴파일 과정이란?

컴파일: 소스를 기계어로 번역

컴파일러: 고급언어로 작성된 프로그램을 기계어로 번역하는 것

  • 개발자가 고급언어로 프로그래밍한 코드를 CPU가 이해할 수 있게 변환해주는 것

빌드: 컴파일을 포함해 프로그램을 실행 가능한 파일로 만들어주는 과정

고급언어 > 어셈블리어 > 기계어 > 실행파일 > 실행

  • 컴파일러가 고급언어를 어셈블리어 언어로 번역
  • 어셈블러가 어셈블리 언어를 기계어로 번역
  • 링커가 어셈블러에 의해 생성된 객체코드를 결합하여 실행 가능한 모듈 생성
  • 로더가 링커에서 생성된 실행 가능한 모듈(파일)을 메인 메모리에 로드하여 실행

🪕 링커 vs. 로더

링커:

  • 어셈블러가 생성한 소스 프로그램의 객체 코드를 받아 프로그램의 실행 코드 생성
  • 프로그램에 내장 라이브러리 함수가 있으면 내장 라이브러리에 연결
    • 내장 라이브러리가 발견되지 않으면 컴파일러에 알림 > 컴파일러는 오류 생성함
  • 모든 개체 모듈을 결합/연결하여 원본 프로그램의 단일 실행 파일 생성
    • 모듈: 큰 프로그램은 때때로 모듈이라고 하는 서브 프로그램으로 나뉨
  • 두 가지 유형:
    • 링키지 편집기: 재배치 가능한 실행 가능 모듈 생성
    • 다이나믹 링커: 로드 모듈/실행 모듈이 생성될때까지 일부 외부 모듈의 연결을 연기
      • 링크는 로드 시간/런타임 중에 수행

로더:

  • 운영체제의 프로그램
  • 기능:
    • 할당, allocation: 실행 프로그램을 실행시키기 위해 기억장치 내에 옮겨놓을 공간을 확보
    • 연결, linking: 부 프로그램 호출 시 그 부 프로그램이 할당된 기억장소의 시작주소를 호출한 부분에 등록하여 연결
    • 재배치, relocation: 디스크 등의 보조기억장치에 저장된 프로그램이 사용하는 각 주소들을 할당된 기억장소의 실제 주소로 배치
    • 적재, loading: 실행 프로그램을 할당된 기억공간에 실제로 옮김
  • 로더의 종류:
    • Compile and Go 로더:
      • 별도의 로더 없이 언어 번역 프로그램이 로더의 기능까지 수행
        • 연결 기능 수행 X
        • 할당, 재배치, 적재 작업 담당
    • 절대로더:
      • 목적 프로그램을 기억장소에 적재시키는 기능만 수행
      • 매번 같은 주 메모리 위치에 프로그램의 실행파일 로드
      • 단점: 프로그램에서 일부 삽입/삭제 등 프로그램을 수정해야 하는 경우 프로그램의 모든 주소 변경해야 함
    • 직접 연결 로더
      • 로드의 기본 기능 네가지 모두 수행
      • aka 재배치 로더, 상대 로더
    • 동적 적재 로더:
      • 매우 유연
      • 프로그램에 한꺼번 적재 X. 실행시 필요한 부분만 적재
        • 호출시 적재
      • 실행중인 프로그램은 중간에 중단되거나 디스크로 스왑 아웃 될 수도
      • 프로그램의 크기가 주기억장치의 크기보다 큰 경우 유리

🪕 컴파일 언어와 인터프리터 언어

컴파일 언어: 프로그래머가 작성한 소스코드를 모두 기계어로 변환 후 기계에 넣어 기계어 코드 실행
(예) C, C++

  • 소스코드를 기계어로 번역하는 빌드 과정 존재
    • 빌드 과정: 소스파일을 실행파일로 생성하는 과정
  • 런타임에는 이미 기계어로 모든 소스코드가 변환되어 있어 빠르게 실행 가능

인터프리터 언어: 프로그래머가 작성한 소스코드를 기계어로 변환하는 과정없이 한줄씩 해석하여 바로 명령어를 실행하는 언어
(예) R, Python, Ruby

  • 인터프리터가 직접 한줄씩 읽고 따로 기계어로 변환하지 않음 > 빌드 시간 없음
  • 런타임에 실시간으로 읽어서 실행 > 컴파일언어에 비해 속도 느림
  • 빌드 과정이 없음 > 서버를 다시 시작하지 않아도 변경사항 반영

🪕 Just in Time (JIT)

Just In Time 컴파일: 프로그램을 실행하는 시점에서 필요한 부분을 즉석으로 컴파일

  • 같은 코드를 매번 해석하는 대신 처음 실행될 때 인터프리트 하면서 자주 쓰이는 코드 캐싱
    • 이후에는 캐싱된 코드 가져다 씀 > 인터프리터의 느린 실행 속도 개선
  • 고속 코드 실행 및 다중 플랫폼 지원을 위해 설계
  • 단점:
    • 초기 구동 시에는 소스코드를 실행 단계에서 컴파일하는데 시간 & 메모리 소모
      • 정적 컴파일된 프로그램에 비해 실행 속도 느림
    • 캐시 메모리 사용 증가
    • 여러 프로세스에서 코드 공유 불가
    • 보안 문제:
      • 런타임에 동적으로 코드 생성 & 실행
      • JIT 컴파일러의 버그는 보안취약점으로 직결

🪕 Java의 컴파일과 실행 과정

  1. 개발자가 자바 소스코드(.java) 작성
  2. 자바 컴파일러가 자바 소스파일 컴파일
  • 자바 바이트코드(.class)파일 생성됨
    • 컴퓨터는 읽을 수 없는 코드
    • 자바 가상 머신은 이해할 수 있는 코드
  • 바이트 코드의 각 명령어는 1바이트 크기의 Opcode와 추가 피연산자(Operand)로 이루어져있음
  1. 컴파일된 바이트 코드를 JVM의 클래스로더(Class Loader)에게 전달
  2. 클래스 로더는 동적 로딩을 통해 필요한 클래스들을 로딩 및 링크하여 런타임 데이터 영역(=JVM의 메모리)에 올림
  • 클래스 로더 세부 동작
    1. 로드: 클래스 파일을 가져와서 JVM의 메모리에 로드
    2. 검증: 자바 언어 명세 및 JVM 명세에 명시된 대로 구성되어 있는지 검사
    3. 준비: 클래스가 필요로 하는 메모리 할당
    4. 분석: 클래스의 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경
    5. 초기화: 클래스 변수를 적절한 값으로 초기화
  1. 실행 엔진은 JVM 메모리에 올라온 바이트 코드를 명령어 단위로 하나씩 가져와 실행
  • 인터프리터: 바이트 코드 명령어를 하나씩 읽어서 해석하고 실행
    • 하나하나의 실행은 빠르나 전체적 실행 속도가 느림
  • JIT 컴파일러:
    • 인터프리터의 단점 보완을 위해 도입
    • 바이트 코드 전체를 컴파일하여 바이너리 코드로 변경 > 해당 메서드를 더이상 인터프리팅하지 않고, 바이너리 코드로 직접 실행
    • 하나씩 인터프리팅하여 실행하지 않고 바이트 코드 전체가 컴파일된 바이너리 코드 실행
    • 전체적인 실행 속도는 인터프리팅보다 빠름

참고:

profile
우당탕탕

0개의 댓글