Study/TIL(Today I Learned)

24.11.25 CS, C++

에린_1 2024. 11. 25. 23:00
728x90

이것이 컴퓨터 과학이다.

Chapter 2. 운영체제

  • 운영체제의 핵심 기능을 담당하는 부분을 커널(kernel)이라고 한다.
  • 운영체제에는 크게 2가지의 핵심 기능이 있다.
    • 자원 할당 및 관리
    • 프로세스 및 스레드 관리

운영체제의 역할

  • 자원(resource)이란 프로그램 실행에 마땅히 필요한 요소를 의미한다. 실행에 필요한 데이터를 자원이라고 하기도 하고, 실행에 필요한 부품을 자원이라고 하기도 한다.
  • 운영체제는 사용자가 실행하는 응용 프로그램을 대신하여 CPU, 메모리, 보조기억장치 등의 컴퓨터 부품에 접근하고, 각각의 부품들이 효율적으로 사용되도록 관리한다.
  • 응용 프로그램이 컴퓨터 부품들을 효율적으로 할당받아 문제 없이 실행할 수 있도록 응용 프로그램에게 자원을 할당한다.

프로세스와 스레드

  • 프로세스의 유형으로는 사용자가 보는 공간에서 사용자와 상호작용하며 실행되는 포그라운드 프로세스, 사용자가 보지 못하는 곳에서 실행되는 백그라운드 프로세스가 있다.
  • 백그라운드 프로세스 중에서도 사용자와 별다른 상호작용 없이 주어진 작업만 수행하는 특별한 백그라운드 프로세스인 데몬(daemon)도 있다. 윈도우에서는 데몬을 서비스(service)라고 한다.
  • 프로세스 정보가 저장되는 메모리 영역 중 사용자 영역
    • 코드 영역(code segment)
      • 실행 가능한 명령어가 저장되는 공간으로, 텍스트 영역(text segment)이라고도 부른다.
      • CPU가 읽고 실행할 명령어가 담겨 있기 때문에 쓰기가 금지되어 있는 읽기 전용(read-only) 공간이다.
    • 데이터 영역(data segment)
      • 프로그램이 실행되는 동안 유지할 데이터가 저장되는 공간이다. 데이터 영역에 저장되는 데이터는 정적 변수나 전역 변수가 대표적이다.
    • 코드 영역과 데이터 영역은 프로그램 실행 도중 크기가 변하지 않기 때문에 정적 할당 영역이라고 부른다. 반면, 크기가 변할 수 있는 힙 영역과 스택 영역은 동적 할당 영역이라고 부른다.
    • 힙 영역(heap segment)
      • 프로그램을 만드는 사용자가 직접 할당 가능한 저장 공간이다. 프로그램 실행 도중 비교적 자유롭게 할당하여 사용 가능한 메모리 공간이라 볼 수 있다.
      • 힙 영역에 메모리 공간을 할당했다면 언젠가는 공간을 반환해야한다. 메모리 공간을 반환하지 않으면 할당한 공간이 계속 메모리 내에 남아 메모리를 낭비하는 메모리 누수(memory leak) 문제를 초래할 수 있다. 때로는 이러한 문제를 해결하기 위해 프로그래밍 언어에서 자체적으로 힙 메모리를 해제하는 가비지 컬렉션(garbage collection) 기능을 제공하기도 한다.
    • 스택 영역(stack segment)
      • 데이터 영역에 담기는 값과는 달리 일시적으로 사용할 값들이 저장되는 공간이다. 함수의 실행이 끝나면 사라지는 매개변수, 지역 변수, 함수 복귀 주소등이 스택 영역에 저장되는 대표적인 데이터들이다.
      • 스택 영역에는 스택 트레이스 형태의 함수 호출 정보가 저장될 수 있다. 스택 트레이스(stack trace)란 특정 시점에 스택 영역에 저장된 함수 호출 정보를 말한다. 스택 트레이스로는 문제의 발생 지점을 추적할 수 있어, 디버깅에 매우 유용하게 사용된다.
  • PCB와 문맥 교환
    • 운영체제가 메모리에 적재된 다수의 프로세스를 관리하려면 프로세스를 식별할 수 있는 커널 영역 내의 정보가 필요하다. 이 정보가 바로 프로세스 제어 블록(PCB)이다. PCB는 프로세스와 관련된 다양한 정보를 관리하는 구조체의 일종으로, 새로운 프로세스가 메모리에 적재됐을 때 커널 영역에 만들어지고, 프로세스의 실행이 끝나면 폐기된다.
    • PCB에 담기는 정보는 운영체제마다 차이가 있지만, 대표적으로 프로세스 식별 번호인 프로세스 ID(PID)와 프로세스가 실행 과정에서 사용한 레지스터 값, 프로세스가 현재 어떤 상태인지를 나타내는 프로세스 상태, 프로세스가 언제, 어떤 순서로 CPU 할당받을지 나타내는 CPU 스케줄링(우선순위) 정보, 프로세스의 메모리 상 적재 위치를 알 수 있는 메모리 관련 정보, 프로세스가 사용한 파일 및 입출력장치 관련 정보가 명시된다.
    • 이때 여러 PCB들은 커널 내에 프로세스 테이블의 형태로 관리되는 경우가 많다. 프로세스 테이블은 실행 중인 PCB의 모음을 의미한다. 새롭게 실행되는 프로세스가 있다면 해당 프로세스의 PCB를 프로세스 테이블에 추가하고, 필요한 자원을 할당한다. 반대로, 종료되는 프로세스가 있다면 사용 중이던 자원을 해제하고 PCB도 프로세스 테이블에서 삭제된다.
    • 일반적으로 메모리에 적재된 프로세스들은 한정된 시간 동안 번갈아 가며 실행된다. 이때 프로세스가 실행된다는 말은 운영체제에 의해 CPU의 자원을 할당받았다는 말과 같다.
    • 프로세스의 CPU 사용 시간은 타이머 인터럽트에 의해 제한된다. 타이머 인터럽트(time interrupt)란 시간이 끝났음을 알리는 인터럽트로, 타임아웃 인터럽트라고도 불린다. 프로세스는 자신의 차례가 되면 정해진 시간만큼 CPU를 이용하고, 타이머 인터럽트가 발생하면 자신의 차례를 양보하고 다음 차례가 올 때까지 기다린다.
    • 프로세스 A가 CPU를 할당받아 실행되다가, 타이머 인터럽트가 발생하여 프로세스 B로 CPU 사용을 양보한다고 가정해보자.
      • 프로세스 A는 프로그램 카운터를 비롯한 각종 레지스터 값과 메모리 정보, 실행을 위해 열었던 파일, 사용한 입출력장치 등 지금까지의 정보를 백업해야 한다. 여기서 백업 대상이 되는 중간 정보, 즉 프로세스의 수행을 재개하기 위해 기억해야 할 정보를 문맥(context)라고 한다.
      • 프로세스의 문맥은 해당 프로세스의 PCB에 명시된다. 프로세스가 CPU를 사용할 수 있는 시간이 다 되거나 인터럽트가 발생하면 운영체제는 해당 프로세스의 PCB에 문맥을 백업한다. 그리고 뒤이어 실행할 프로세스의 문맥을 복구한다. 이처럼 기존 프로세스의 문맥을 PCB에 백업하고, PCB에서 문맥을 복구하여 새로운 프로세스를 실행하는 것을 문맥 교환(context switching)이라고 한다.
    • 프로세스 간 문맥 교환이 자주 발생하면 캐시 미스가 발생할 가능성이 높아져 메모리로부터 실행할 프로세스 내용을 가져오는 작업이 빈번해지고, 이는 큰 오버헤드로 이어질 수 있다.
  • 프로세스 상태
    • 프로세스는 여러 상태를 거치며 실행된다. 운영체제는 PCB를 통해 프로세스의 상태를 인식하고 관리한다.
    • 생성 상태(new)
      • 프로세스를 생성 중인 상태로, 메모리에 적재되어 PCB를 할당받은 상태이다. 생성 상태를 거쳐 실행할 준비가 완료된 프로세스는 준비 상태가 되어 CPU의 할당을 기다린다.
    • 준비 상태(ready)
      • 당장이라도 CPU를 할당받아 실행할 수 있지만, 아직 자신의 차례가 아니기 떄문에 기다리고 있는 상태를 말한다. 준비 상태인 프로세스가 CPU를 할당받으면 실행 상태가 되며, 준비 상태인 프로세스가 실행 상태로 전환되는 것을 디스패치(dispatch)라고 한다.
    • 실행 상태(running)
      • 실행 상태는 CPU를 할당받아 실행 중인 상태로, 일정 시간 동안만 CPU를 사용할 수 있다. 타이머 인터럽트가 발생하여 프로세스가 할당된 시간을 모두 사용하면 다시 준비 상태가 되고, 실행 도중 입출력 장치를 사용하여 입출력장치의 작업이 끝날 때까지 기다려야 하면 대기 상태가 된다.
    • 대기 상태(blocked)
      • 프로세스가 입출력 작업을 요청하거나 바로 확보할 수 없는 자원을 요청하는 등 곧장 실행이 불가능한 조건에 놓이는 경우 대기 상태가 된다. 대기 상태로 전환되는 상황은 다양하지만, 입출력 작업을 요청하는 경우가 대표적이다. 대기 상태였던 해당 프로세스는 입출력작업이 완료되는 등 실행 가능한 상태가 되면 다시 준비 상태가 되어 CPU 할당을 기다린다.
    • 종료 상태(terminated)
      • 프로세스가 종료된 상태를 말한다. 프로세스가 종료되면 운영체제는 PCB와 프로세스가 사용한 메모리를 정리한다.
  • 멀티프로세스와 멀티스레드
    • 동시에 여러 프로세스가 실행되는 것을 멀티프로세스(multi-process)라고 한다. 각기 다른 프로세스들이 기본적으로 자원을 공유하지 않고, 독립적으로 실행된다. 같은 작업을 수행하더라도 각각의 PID값이 다르고, 프로세스별로 파일과 입출력장치 등의 자원이 독립적으로 할당되어 다른 프로세스에 영향을 거의 끼치지 않는다.
    • 한 프로세스를 구성하는 코드를 동시에 실행하는 방법에는 여러 스레드를 이용하는 방법도 있다. 이때 프로세스를 동시에 실행하는 여러 스레드를 멀티스레드(multi-thread)라고 한다. 하나의 스레드는 스레드를 식별할 수 있는 고유 정보인 스레드 ID와 프로그램 카운터, 레지스터 값, 스택 등으로 구성된다. 스레드마다 각각의 프로그램 카운터 값과 스택을 가지고 있기 때문에 스레드마다 다음에 실행할 주소를 가질 수 있고, 연산 과정의 임시 저장 값을 가질 수 있다.
  • 프로세스 간 통신
    • 프로세스는 기본적으로 자원을 공유하지 않지만, 프로세스 간에도 자원을 공유하고 데이터를 주고 받을 수 있는 방법이 있다. 이를 프로세스 간 통신(IPC, Inter-Process Communication)이라고 부르는데요. 프로세스 간 통신이 이루어지는 방식에는 크게 2가지 유형, 각각 공유 메모리와 메시지 전달이 있다.
      • 공유 메모리
        • 말 그대로 데이터를 주고받는 프로세스가 공통적으로 사용할 메모리 영역을 두는 방식이다. 공유 메모리 기반 IPC의 가장 중요한 특징은 통신을 주고받는 각 프로세스가 마치 자신의 메모리 영역을 읽고 쓰는 것처럼 통신한다는 점이다.
        • 프로세스가 데이터를 주고받는 과정에 커널의 개입이 거의 없다는 점도 중요한 특징이다. 다시 말해 프로세스가 주고받는 데이터는 커널 영역을 거치지 않는 경우가 많다.
        • 각 프로세스가 각자의 메모리 영역을 읽고 쓰는 것뿐이므로 메시지 전달 방식보다 통신 속도가 빠르지만 레이스 컨디션이 발생할 수 있다.
      • 메시지 전달
        • 프로세스 간에 주고받을 데이터를 메시지의 형태로 주고받는 방식을 말한다. 프로세스 간에 주고받을 데이터가 커널을 거쳐 송수신된다. 메시지를 보내는 시스템 콜인 send(), 메시지를 받는 recv() 시스템 콜을 이용해 메시지를 송수신한다. 메시지 전달 기반 IPC를 위한 대표적인 수단으로는 파이프, 시그널, 소켓, 원격 프로시저 호출(RPC)등이 있다.
        • 파이프란 단방향 프로세스 간의 통신 도구를 말한다. 단방향으로만 읽고 쓸 수 있는 파이프로 양방향 통신을 수행할 경우, 읽기용 파이프와 쓰기용 파이프 2개를 이용해 양방향으로 통신하는 경우가 많다.
        • 시그널은 프로세스에게 특정 이벤트가 발생했음을 알리는 비동기적인 신호이다. 프로세스는 시그널이 발생하면 여느 인터럽트 처리 과정과 유사하게 하던 일을 중단하고, 시그널 처리를 위한 시그널 핸들러를 실행한 뒤 실행을 재개한다. 시그널을 이용하는 방법은 앞선 IPC 기법들과 다르게 직접적으로 메시지를 주고받지는 않지만, 비동기적으로 원하는 동작을 수행할 수 있는 좋은 수단이다.

C++

동적할당

malloc(Memory Allocation)

  • 메모리를 동적으로 할당한다.
  • 할당된 메모리는 초기화되지 않고 쓰레기 값을 가진다.
  • 크기를 명시적으로 지정해야 하며, 반환 값은 void* 로 반환되므로 형 변환이 필요하다.
  • C에서 제공되며 C++에서도 사용 가능하지만, C++에서는 일반적으로 new를 선호한다.

calloc(Contiguous Allocation)

  • malloc과 유사하지만, 메모리를 0으로 초기화한다.
  • 연속된 블록의 메모리를 할당하며, 각 블록은 0으로 초기화된다.
  • 장점
    • 메모리가 자동으로 0으로 초기화되므로 초기화 과정을 생략 가능하다.
    • 연속된 메모리 블록을 할당하기에 유용하다.
  • 단점
    • 초기화 과정이 포함되어 malloc보다 느리다.

realloc(Reallocate Memory)

  • 기존에 할당된 메모리를 크기 조정(확장 또는 축소)한다.
  • 새로운 크기로 할당된 메모리가 기존 할당 메모리와 동일한 위치를 보장하지 않는다. 필요 시, 새로운 메모리 블록을 할당하고 기존 데이터를 복사한다.
  • 장점
    • 기존 데이터를 유지하면서 크기를 조정할 수 있다.
    • 메모리를 효율적으로 관리 가능하다.
  • 단점
    • 메모리 위치가 변경될 수 있어 기존 포인터는 무효화될 수 있다.
    • 초기화되지 않은 공간은 쓰레기 값을 가진다.

new(C++에서의 동적 메모리 할당)

  • C++에서 객체를 포함한 모든 타입의 메모리를 동적으로 할당한다.
  • 생성자를 호출하며, 클래스 객체를 할당할 때 적합하다.
  • 메모리 할당 실패 시 예외(std::bad_alloc)를 던진다.
  • 해제할 떄는 반드시 delete를 사용해야 한다.
  • 장점
    • 객체의 생성자와 소멸자를 자동으로 호출한다.
    • C++ 객체와 잘 통합되어 객체 지향 프로그래밍에 적합하다.
  • 단점
    • 초기화된 메모리를 할당하므로, malloc보다 느릴 수 있다.
    • delete와 함께 사용하지 않으면 메모리 누수가 발생한다.
728x90

'Study > TIL(Today I Learned)' 카테고리의 다른 글

24.11.27 서버, AWS  (0) 2024.11.27
24.11.26 C++, 책  (0) 2024.11.26
24.11.24 C++  (0) 2024.11.24
24.11.23 C++  (0) 2024.11.23
24.11.22 JavaScript, C++  (0) 2024.11.22