본문 바로가기
Development/운영체제

[운영체제] PintOS - Project 2. User Program (1)

by heondeam 2023. 6. 12.

✅ 주요 학습 내용

  • User Mode vs Kernel Mode
  • Process (+ PEB, PID)
  • User Stack
  • x86_64 calling convention
  • Argument Vector
  • ELF 파일
  • System Call
  • File System (+ FDT)

‼️ User Mode vs Kernel Mode

유저 모드와 커널 모드는 컴퓨터 시스템에서 실행 중인 프로세스의 권한 레벨을 나타낸다. 이 모드들은 프로세스가 시스템 자원에 접근하고 명령을 실행하는 데에 영향을 미친다.

 

즉, 커널은 중요한 자원들을 관리하기 때문에 사용자가 중요한 자원에 함부로 접근하지 못하도록 2가지 모드로 나누어 둔 것이라 할 수 있다.

 

한 프로세스가 실행되는 동안에 모드 전환이 빈번하게 일어난다. 일반적으로 유저 프로그램은 유저 모드에서 실행되고 자원이 필요할 때 시스템 콜을 사용하여 커널 모드로 전환한다.

 

  • 유저 모드
    • 유저 프로그램이 실행되는 권한 레벨이다.
    • 유저 모드에서는 시스템의 일부 자원과 기능에만 접근할 수 있으며, 특정 권한이 제한된다.
    • 응용 프로그램은 유저 모드에서 실행되며, 파일 시스템, 네트워크, 입출력 장치 등의 제한된 자원에 접근이 필요할 때 시스템 콜을 사용하여 커널 모드로 전환을 요청한다.
  • 커널 모드
    • 운영체제의 커널이 실행되는 권한 레벨이다.
    • 커널 모드에서는 시스템의 모든 자원과 기능에 접근할 수 있다.
    • 운영체제의 핵심 기능을 실행하고 하드웨어 제어 및 관리, 메모리 할당, 인터럽트 처리 등과 같은 시스템 수준 작업을 수행한다.
    • 커널 모드에서 실행되는 코드는 특권 명령어를 사용할 수 있다.

‼️ Process (+ PEB, PID)

프로세스에 대해 알기 전에 프로그램에 대해서 알고 넘어가자. 프로그램은 컴퓨터에서 실행할 수 있는 명령어들의 집합으로 일반적으로 디스크(보조 기억 장치) 또는 다른 저장 장치에 저장되어 있다. 그리고 사용자가 실행을 요청하게 되면 메모리(주 기억 장치)에 로드되어 실행된다. 크롬과 같은 웹 브라우저도 프로그램이라고 볼 수 있다.

 

그렇다면 프로세스란 무엇일까? 쉽게 말해서 프로세스란 실행 중인 프로그램이라고 할 수 있다. 웹 브라우저라는 프로그램을 실행하게 되면 명령어들이 메모리에 로드되고 해당 프로세스가 생성된다.

 

이 때 각각의 프로세스들은 고유한 프로세스 ID를 할당 받게 되는데 이를 PID(Process Identification)이라고 한다.

 

또한 PEB(Process Environment Block)란 운영체제에서 각 프로세스에 대한 정보를 저장하는 데이터 구조이다. 프로세스의 유저 모드 메모리 공간에 위치하며, 해당 프로세스와 관련된 정보를 포함하고 있다. (e g. 이미지 파일 정보, 메모리 관리 정보, 실행 환경 정보, 모듈 관리 정보 등)

 

운영체제는 보조 기억 장치에 저장되어 있는 프로그램을 주 기억 장치에 로드하여 프로세스를 생성하고 정해진 정책에 따라 스케줄링을 수행한다.

‼️ User Stack

 

‼️ x86_64 calling convention

x86-64 아키텍처에서 함수를 호출했을 때 어떻게 매개변수를 전달하고 반환 값을 처리하는지를 규정한 규칙. 이 규칙은 함수 호출 시 레지스터와 스택의 사용, 매개변수와 반환 값의 전달 방식 등을 포함한다. 대부분의 x86-64 컴파일러와 운영체제가 이 규칙을 준수하여 함수 호출을 처리한다.

다음은 x86-64 Calling Convention의 주요 특징이다.

 

  1. 레지스터 사용:
    • 호출자는 일부 레지스터를 함수 호출 전에 저장해야 하며, 호출된 함수는 이러한 레지스터를 변경하지 않아야 한다. 이를 "callee-saved" 또는 "non-volatile" 레지스터라고 한다.
    • 호출자는 매개변수를 전달하기 위해 특정 레지스터를 사용할 수 있다. 이러한 레지스터는 "parameter-passing" 또는 "volatile" 레지스터라고 한다.
    • 호출된 함수는 반환 값을 특정 레지스터에 저장해야 한다.
  2. 스택 프레임:
    • 호출된 함수는 로컬 변수와 다른 데이터를 저장하기 위해 스택 프레임을 사용한다. 스택 프레임은 스택 메모리의 일부를 할당하여 함수 호출 시 필요한 데이터를 저장하는 데 사용된다.
    • 스택 프레임에는 함수의 반환 주소, 호출자에 의해 저장된 레지스터 값, 매개변수, 로컬 변수 등이 저장된다.
  3. 매개변수 전달:
    • 매개변수는 주로 레지스터와 스택을 통해 전달된다.
    • 처음 몇 개의 매개변수는 레지스터를 통해 전달되며, 남은 매개변수는 스택을 통해 전달된다.
    • 매개변수의 순서와 위치는 규칙에 따라 결정된다.
  4. 반환 값 처리:
    • 반환 값은 특정 레지스터에 저장된다. 대부분의 경우, 64비트 값은 RAX 레지스터에 저장되며, 128비트 값은 RAX와 RDX 레지스터에 저장된다.
    • 더 큰 구조체 또는 클래스와 같은 복잡한 반환 값은 메모리를 통해 전달될 수 있다.

 

이러한 규칙들은 C 언어와 같은 고급 언어로 작성된 함수 호출을 효율적으로 처리하기 위해 설계되었다. 이는 컴파일러가 함수 호출과 관련된 작업을 최적화하는 데 도움을 준다. 그러므로 일반적으로 개발자는 x86-64 Calling Convention을 직접 다룰 필요가 없다. 하지만 저수준 어셈블리어나 운영체제 개발과 같은 특정 상황에서는 이러한 규칙에 대한 이해가 필요할 수 있다.

‼️ Argument Vector

 

‼️ ELF 파일

Executable Linkable Format(ELF)는 유닉스 계열의 운영체제에서의 실행파일, 오브젝트 파일, 공유 라이브러리 또는 코어 덤프 파일 및 커널 부트 이미지에서 사용하는 바이너리 파일이다. 간단히 말하자면 유닉스 계열의 운영체제에서 실행가능하고 링크가 가능한 파일이라고 생각하면 될 것 같다.

 

ELF는 프로그램이 실행될 때 메모리에 올라가야 할 각각의 부분들을 미리 정리하여 관리하다가 실행을 하게 되면 정리된 부분들(코드, 전역 데이터, 읽기 전용 데이터 등등)을 메모리에 올리게 된다. 그리고 메모리에 올라온 주소 공간의 text 섹션(코드 영역)의 instruction을 한 라인씩 실행하며 프로세스가 진행된다.

 

ELF는 2가지 View를 가질 수 있다.

 

  • Linking View - relocatable file 형식. 즉, Link가 이루어지기 전의 object file은 Linking View를 가짐.
  • Execution View - Link가 끝난 후에 완전히 실행 가능한 형태가 된 ELF 형식을 Execution View라고 한다.

그리고 각 ELF 파일은 하나의 ELF 헤더와 파일 데이터로 이루어진다.

 

  1. 파일 데이터
    • 프로그램 헤더
      • 프로그램 헤더 테이블은 바이너리 파일을 세그먼트 관점에서 볼 수 있게 해준다. 바이너리 파일을 섹션 관점에서 보는 것은 정적 링킹의 목적으로만 한정하는 것이고, 세그먼트의 관점으로 본다는 것은 운영체제와 동적 링킹 과정을 통해서 바이너리가 프로세스의 형태가 되고 그과 관련된 코드와 데이터들을 어떻게 처리할 것인지를 다룬다는 의미이다.
    • 섹션
      • ELF 파일의 데이터는 섹션으로 나누어져 있다.
      • 섹션의 구조는 각 섹션의 내용이 구성된 방식에 따라 다른데, 각 섹션 헤더에서 그 속성을 찾을 수 있다. 바이너리 파일 내부의 모든 섹션에 대한 헤더 정보는 섹션 헤더 테이블에서 찾을 수 있다.바이너리 파일을 실행할 때 내부의 코드와 데이터를 세그먼트라는 논리적인 영역으로 구분하는데, 세그먼트는 링크 시 사용되는 섹션과는 달리 런타임 시점에 사용된다.
      • 즉, 섹션은 링커가 바이너리를 편하게 해석하기 위한 단위인 것이다. 링킹이 필요하지 않는 경우에는 섹션 헤더 테이블이 불필요하며 이 때 e_shoff 필드는 0으로 표시된다.
    • 섹션 헤더
  2. ELF 헤더
    • ELF 헤더는 현재의 ELF 파일에 대해 정의한다. 즉 해당 파일에 대한 메타 정보를 제공한다. (Linking View이던 Execution View이던 무조건 가지고 있음.) ELF 헤더 구조체는 다음과 같은 필드를 포함한다.
      • e_ident (매직 넘버 그리고 ELF 파일에 대한 정보)
      • e_type (ELF 파일 형식)
        • ET_NONE : 아직 정의 되지 않았거나 알 수 없음.
        • ET_REL : 링킹 가능한 포맷 (재배열이 가능한 파일 형식 → 아직 실행 파일에 링킹되지 않은 상태)
        • ET_EXEC : 실행 가능한 포맷 (실행 파일 형식. 즉, 프로그램을 뜻함. → 프로세스 시작 지점인 엔트리 포인트가 존재.)
        • ET_DYN : 실행 가능하면서 링킹 가능한 포맷 (공유 오브젝트 파일 형식 → 동적 링킹이 가능한 오브젝트 파일 = 공유 라이브러리) (즉 런타임 중에 프로그램의 프로세스 이미지로 로드되고 링크된다.)
        • ET_CORE : 코어 파일 형식 (프로세스 이미지의 전체 덤프, 주로 프로그램이 비정상 종료되거나 SIGSEGV 시그널(= Segmentation Fault)로 인해서 프로세스가 종료된 경우 생성된다. GDB로 코어 파일을 분석해서 비정상 종료된 시점의 원인 분석이 가능하다.
      • e_machine (cpu 아키텍쳐)
      • e_version (ELF 파일 버전 (거의 무조건 0x1))
      • e_entry (엔드리 포인트의 주소)
      • e_phoff (프로그램 헤더 테이블 파일 오프셋)
      • e_shoff (섹션 헤더 테이블 파일 오프셋)
      • e_flags (processor specific flags)
      • e_ehsize (ELF header 사이즈)
      • e_phentsize (프로그램 헤더 테이블 엔트리 사이즈)
      • e_phnum (프로그램 헤더 테이블 엔트리 갯수)
      • e_shentsize (섹션 헤더 테이블 엔트리 사이즈)
      • e_shnum (섹션 헤더 테이블 엔트리 갯수)
      • e_shstrndx (섹션 헤더 스트링 테이블의 인덱스)ELF 헤더는 프로그램 헤더 테이블과 섹션 헤더 테이블의 위치를 결정하고 Zero offset에서 시작한다. ELF 헤더 구조체는 다음과 같은 필드를 포함한다.

‼️ System Call

 

‼️ File System(+ FDT)

 

 

✅ pintOS에서 User Program이 실행되는 원리

user program 동작원리

댓글