운영체제 스터디 5주차

낚시하는 곰·2025년 9월 30일
1

운영체제 스터디

목록 보기
4/4

참고 자료

https://os2024.jeju.ai/week05/vm-intro.html

https://os2024.jeju.ai/week05/vm-api.html

https://os2024.jeju.ai/week05/lab.html


주소 공간의 개념

📌 핵심 내용 요약

  1. 가상 메모리의 필요성
    • 여러 프로그램이 동시에 실행될 때, 각 프로그램이 물리 주소를 직접 쓰면 충돌 위험이 있음.
    • → 따라서 각 프로세스마다 독립적인 주소 공간을 제공해야 함.
  2. 주소 변환 (Address Translation)
    • 프로그램은 가상 주소(virtual address)를 사용.
    • MMU가 이를 물리 주소(physical address)로 변환.
    • 덕분에 같은 가상 주소라도, 프로세스마다 다른 물리 주소에 매핑될 수 있음.
  3. 보호 (Protection)
    • 프로세스가 다른 프로세스의 메모리에 접근하지 못하게 차단.
    • 커널 영역과 사용자 영역도 분리해서 안전 보장.
  4. 편리함 (Convenience)
    • 프로그래머는 “0번지부터 쭉 이어진 메모리”처럼 단순하게 생각하면 됨.
    • 실제 물리 메모리가 흩어져 있어도 운영체제가 알아서 매핑.

💡 쉽게 비유

  • 가상 주소 = 호텔 방 열쇠 번호
  • 물리 주소 = 실제 호텔 방 위치
  • 투숙객(프로세스)은 방 열쇠(가상 주소)만 보면 되고, 그게 실제 어디 방(물리 주소)에 연결되는지는 호텔 직원(운영체제 + MMU)이 알아서 처리하는 구조.

메모리 관리 API

핵심 내용 요약

  1. 메모리 공간의 종류
    • 스택 (stack): 자동으로 할당/해제됨 (지역 변수).
    • 힙 (heap): 프로그래머가 직접 malloc, free 같은 API로 관리해야 함.
  2. 메모리 할당 API
    • malloc(size): 원하는 크기만큼 메모리를 요청.
    • free(ptr): 사용이 끝난 메모리를 반환.
    • calloc(n, size): 초기화된 배열 메모리 요청.
    • realloc(ptr, new_size): 기존 메모리 크기 조정.
  3. 실수하기 쉬운 점
    • free를 깜빡하거나, 같은 포인터를 두 번 free하면 오류 발생.
    • malloc한 메모리를 쓰지 않고 잊어버리면 메모리 누수(memory leak).
  4. 현대적 의미
    • 실제 물리 메모리가 아니라, 가상 메모리 주소 공간에서의 요청/해제.
    • 운영체제가 가상 주소를 실제 물리 메모리에 매핑해줌.

가상 메모리의 필요성

초기에 시분할 시스템을 이용해서 여러 사용자가 동시에 컴퓨터를 사용할 수 있도록 했지만 문제가 발생했다. 시분할을 구현하는 방법 중 하나인 프로세스를 아주 짧은 시간 동안만 실행시키되, 그 순간에는 메모리 전체에 대한 접근 권한을 부여하는 것이었습니다. 그 후 프로세스를 중단하고 그때의 모든 상태를 디스크 같은 곳에 저장한 뒤, 다른 프로세스의 정보를 불러와 또 잠깐 실행하는 식이었죠.

이 방식의 문제점은 레지스터 값을 저장하고 복원하는 건 빨랐지만 메모리 전체를 디스크에 옮기는 건 너무 느렸다고 한다. 이 문제를 해결하기 위해 메모리 가상화가 탄생했다.

가상 주소와 물리 주소는 어떤 관계를 가질까?

가상 주소는 프로세스마다 가지는 독립된 논리 주소 공간을 말하며, 물리 주소는 실제 메모리 하드웨어의 주소를 말한다. 가상 주소는 물리 주소로 변환되어 메모리에 실제로 저장된다.

프로세스마다 주소 공간을 독립적으로 두는 이유는 무엇인가?

이전의 저장된 데이터를 다른 프로세스가 덮어쓰지 말라고.

컨텍스트 스위칭 시 가상 메모리가 어떤 역할을 하는가?

기존의 데이터는 저장된 채로 프로세스만 전환할 수 있다.

가상 메모리를 사용하지 않는다면 어떤 문제가 생길 수 있는가?

우리가 malloc을 호출할 때 실제로는 운영체제의 어떤 동작이 일어날까?

malloc을 쓴다면 가상 메모리 주소를 생성하고 MMU가 실제 메모리 주소랑 매핑해준다.

운영체제가 malloc 요청을 받으면 곧바로 물리 메모리 페이지를 다 붙여줄까, 아니면 필요할 때까지 미뤄두는 방식(지연 할당)도 가능할까?

지연 할당 방식도 가능하다. 요청할 때 물리 메모리 일부를 다른 프로세스가 못 쓰게 막는다면 메모리 낭비로 이어질 것.

malloc(1GB) 같은 큰 요청을 해도 그 순간 1GB 물리 메모리를 다 주는 게 아니라, 가상 주소 공간만 예약해두고 실제 물리 메모리는 접근할 때(페이지 폴트가 날 때) 채워줘.

만약 프로그래머가 malloc만 하고 free를 안 한다면, 지연 할당 방식에서는 어떤 문제가 생길까?

일단 malloc을 했을 경우 가상 메모리 공간을 일부 예약해 놓기 때문에 실제 메모리 공간이 사용이 안되고 있더라고 메모리 부족 현상이 발생할 수 있다.

malloc만 하고 free하지 않으면,

가상 주소 공간이 계속 잡혀 있는 상태가 돼.

  • 처음엔 지연 할당 덕분에 실제 물리 메모리 사용은 적을 수 있지만, 프로그램이 그 영역을 실제로 접근하기 시작하면 점점 물리 메모리까지 소비하게 돼.
  • 그럼 결국 운영체제 입장에서는 "다른 프로그램에게 줄 수 있는 여유 메모리"가 줄어들어서, 시스템이 느려지거나 심하면 OOM(Out Of Memory) 에러까지 날 수 있어.

내가 네 이해도를 확인하고 싶은데, 혹시 malloc으로 할당한 후 free하지 않고 프로그램이 끝까지 실행을 마치고 종료한다면, 그 메모리는 어떻게 될까? 운영체제가 계속 잡고 있을까, 아니면 자동으로 회수할까?

C는 garbage 컬렉션이 없기 때문에 회수하지 않고, 여전히 메모리를 할당한 채로 남아있을 것 같아…

프로세스 실행 중에는 free하지 않으면 계속 잡혀 있어서 메모리 부족 문제가 생김.

  • 프로세스가 종료될 때는 운영체제가 "이제 이 프로세스 필요 없네" 하고 통째로 정리해버림.

이런 동작을 생각했을 때, 왜 굳이 우리가 free를 해줘야 할까? “어차피 끝나면 OS가 회수해주는데…”라는 생각도 들 수 있잖아?

여유 메모리가 줄어서 시스템이 느려질 수 있기 때문에

  • 메모리 부족 → 디스크 스와핑 증가 → 시스템 전체가 느려짐
  • 장시간 실행되는 서버 프로그램 같은 경우, 누수가 쌓이면 결국 다운될 수도 있음

정리

  • malloc → 가상 주소 공간에 예약
  • 실제 물리 메모리 할당은 지연(lazy)
  • free 안 하면 실행 중에 메모리 낭비 발생
  • 프로세스 종료 시 운영체제가 통째로 회수

지금까지 malloc을 써온 상황을 떠올려 보면, calloc이나 realloc어떤 게 있으면 더 편했을 것 같다는 생각이 들어?

realloc이야. 왜냐하면 malloc으로 메모리 공간을 할당했어도 추가적으로 공간이 필요할 경우가 생기기 때문이야. python 같은 경우 array를 생성해도 추가적으로 데이터를 append할 수 있으니까 realloc을 쓸 것 같아

  • 내부적으로는 새 메모리를 할당하고, 기존 데이터를 복사하고, 옛 공간을 해제하는 식으로 동작할 수도 있어.
  • 그래서 realloc 쓰면 Python 리스트의 자동 확장과 비슷한 효과를 흉내낼 수 있는 거지.

만약 네가 malloc으로 int 5개짜리 배열을 만들었는데, 나중에 10개로 늘리고 싶어졌어. 이때 realloc을 안 쓰고 직접 하려면 어떤 과정을 밟아야 할까?

기존에 만들었던 5개짜리 배열을 삭제하고 다시 10개로 만들어야 하지 않아?

  • malloc(5 * sizeof(int)) 으로 5칸짜리 배열을 만든다.
  • 새로운 배열을 위해 malloc(10 * sizeof(int)) 으로 10칸짜리를 만든다.
  • memcpy 같은 함수로 기존 5칸짜리의 데이터를 새 배열로 복사한다.
  • 기존 5칸짜리 배열을 free한다.

정리: 가상 메모리 API 학습

📌 기본 개념

  • malloc: 가상 주소 공간을 예약하는 함수. 실제 물리 메모리는 접근할 때 할당된다(지연 할당, lazy allocation).
  • free: 예약한 메모리를 운영체제에 반환하는 함수.
  • calloc: malloc과 유사하지만, 할당된 메모리를 0으로 초기화해준다.
  • realloc: 기존 메모리 블록의 크기를 조정하는 함수. 내부적으로 새 블록을 만들고 복사 후 기존 블록을 해제할 수도 있음.

⚙️ 동작 원리

  • malloc가상 주소 공간 예약
  • 실제 물리 메모리는 페이지 폴트 발생 시 할당
  • free하지 않으면 실행 중 메모리 누수 발생 → 시스템 전체 성능 저하
  • 프로그램이 종료되면 운영체제가 해당 프로세스의 모든 메모리를 자동으로 회수

🚨 주의할 점

  • C는 garbage collection이 없다 → 반드시 직접 free 해줘야 함
  • realloc 사용 시 기존 포인터 대신 반환된 새 포인터를 사용해야 함 (이전 포인터는 무효화될 수 있음)

💡 비유

  • malloc: 창고에 빈 칸 예약
  • free: 예약 취소하고 다른 사람도 쓰게 해줌
  • calloc: 창고 빈 칸을 예약하고 모두 청소까지 완료
  • realloc: 더 큰 창고로 이사 가면서 기존 물건을 옮겨주는 과정
  • Python 리스트의 append = 내부적으로 realloc과 유사 (여유분까지 크게 늘려서 효율적으로 관리)

가상 메모리 실습

📌 핵심 내용 요약

  1. malloc/free 실습
    • 작은 크기, 큰 크기 메모리를 할당해 보고 시스템이 어떻게 반응하는지 확인.
    • malloc은 가상 주소 공간만 예약하므로, 실제 물리 메모리는 접근해야 할당됨을 확인.
  2. 메모리 접근 실험
    • 할당만 하고 접근하지 않을 때와, 실제로 데이터를 채울 때 차이를 비교.
    • 페이지 폴트가 발생하는 시점을 관찰.
  3. 메모리 해제 실험
    • free를 했을 때 OS의 메모리 사용량이 바로 줄어드는지 확인.
    • 운영체제가 내부적으로 free된 메모리를 어떻게 처리하는지 살펴봄.
  4. 도구 활용
    • top, ps, /proc/[pid]/maps 등을 이용해 실제 프로세스 메모리 사용량 관찰.
    • "눈에 보이는 메모리 사용량"과 "실제로 할당된 가상 공간"의 차이를 느끼는 게 목적.

🧩 학습 포인트

  • malloc과 실제 물리 메모리 사용이 다르다는 걸 실험으로 체득.
  • free를 안 하면 어떻게 되는지 직접 관찰.
  • 운영체제가 lazy allocation을 쓰는 이유를 이해.

코드

va.c

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    printf("location of code : %p\n", main);
    printf("location of heap : %p\n", malloc(100e6));
    int x = 3;
    printf("location of stack: %p\n", &x);
    return 0;
}

makefile

.DEFAULT_GOAL := help

.PHONY: help
help: ## Display this help
	@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-25s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)

##@ Build

all: va ## Build all targets

va: ## Build target va
	gcc -o va va.c -Wall

##@ Run

run: ## Run the lottery program
	./va

##@ Clean-up

clean: ## Remove all build artifacts
	rm -f va

결과

location of code : 0x5d338ef01189
location of heap : 0x7d275b4a1010
location of stack: 0x7fff9eded3d4

스택 주소가 힙보다 더 높게 있는 이유는 뭐라고 생각해?

스택은 데이터 공간이 많이 필요할 것이다. 프로그램이 실행될 때 데이터를 스택에 저장해 놓을테니까. 그리고 힙은 실행되면서 동적으로 데이터의 크기가 커질 경우에 사용함으로 스택보다 작아도 된다.

📌 왜 스택이 더 높은 주소에 있을까?

  1. 스택은 위에서 아래로 성장
    • 함수 호출할 때마다 새로운 stack frame이 위 주소에서 아래로 내려옴.
    • 그래서 운영체제는 스택을 가장 높은 주소에 배치해 두고, 필요할 때 아래로 확장시키는 구조를 씀.
  2. 힙은 아래에서 위로 성장
    • malloc 같은 호출을 할 때마다 점점 더 높은 주소 쪽으로 커짐.
    • 따라서 낮은 주소대(코드/데이터 영역)와 높은 주소대(스택) 사이의 “중간 지대”를 차지.
  3. 서로 충돌 방지
    • 스택은 위→아래, 힙은 아래→위로 커지니까, 둘 사이에 넓은 빈 공간을 두고 시작해.
    • 만약 충돌할 정도로 커지면 → stack overflowout of memory 같은 에러 발생.

만약 어떤 프로그램에서 재귀 호출을 엄청 깊게 해서 스택이 엄청 커진다면, 힙과 부딪힐 가능성이 생기겠지? 이 경우 운영체제는 어떤 식으로 문제를 처리할까?

일단 힙은 스택보다 낮은 주소에 있고 스택은 낮은 주소에서 높은 주소로 이동을 하는데 어떻게 힙이랑 부딪히는 지 모르겠어.… → 스택은 높은 주소에서 낮은 주소로 성장하기 때문에 만날 수 있음

운영체제가 segmentation faultstack overflow 같은 에러를 발생시켜 프로그램을 종료해버려.

profile
취업 준비생 낚곰입니다!! 반갑습니다!!

0개의 댓글