[인프런 워밍업 클럽 3기 - CS] 2주차 발자국
학습 내용 회고: 이번 2주차는 운영체제를 공부하며 정말 재미를 느꼈습니다. 평소에 윈도우 설정을 보면서 그냥 넘어갔던 용어들 ex.조각 모음 같은 용어의 뜻을 알게 되어 너무 재밌게 수강했습니다. 1강 프로세스 간 통신프로세스 간 통신(Inter-Process Communication, IPC)은 같은 컴퓨터 안에서 프로세스들끼리 데이터를 주고받거나, 네트워크로 연결된 다른 컴퓨터의 프로세스와 데이터를 주고받는 기술이다.한 컴퓨터 내에서의 통신 방법파일(File): 여러 프로세스가 하나의 파일을 읽고 쓰면서 데이터를 공유하는 방식.파이프(Pipe): 운영체제가 제공하는 파이프를 통해 데이터를 주고받는 방식.쓰레드(Thread): 같은 프로세스 내에서 공유하는 CODE, DATA, HEAP 영역 중 DATA와 HEAP을 이용해 통신하는 방식.네트워크를 이용한 통신 방법소켓(Socket) 통신: 네트워크를 통해 데이터를 주고받는 방식.원격 프로시저 호출(RPC, Remote Procedure Call): 네트워크를 통해 다른 컴퓨터의 함수를 호출하는 방식.2강 공유자원과 임계구역프로세스 간 통신을 할 때 여러 프로세스가 공동으로 사용하는 변수나 파일을 공유자원(Shared Resource)이라고 한다.문제점: 동기화(Synchronization) 문제공유자원에 여러 프로세스가 동시에 접근하면 실행 순서에 따라 결과가 달라질 수 있다.컨텍스트 스위칭(Context Switching)으로 인해 실행 순서를 예측하기 어렵다.→ 이를 해결하려면 임계구역(Critical Section) 개념이 필요하다.공유자원 사용 시 발생하는 문제경쟁조건(Race Condition): 여러 프로세스가 동시에 공유자원에 접근하면 충돌이 발생할 수 있음.임계구역 문제 해결을 위한 상호 배제(Mutual Exclusion) 메커니즘임계구역에는 동시에 하나의 프로세스만 접근 가능해야 한다.여러 프로세스가 접근을 요청해도 한 번에 하나만 들어갈 수 있어야 한다.임계구역에 들어간 프로세스는 최대한 빨리 나와야 한다.3강 세마포어(Semaphore)세마포어는 상호 배제(Mutual Exclusion) 메커니즘 중 하나로, 공유자원에 대한 접근을 조절하는 역할을 한다.세마포어 개념프린터 방 예시:공유자원: 프린터프로세스: A, B대기 장소: 대기 큐열쇠 관리자: 운영체제열쇠: 세마포어세마포어는 프로세스가 공유자원에 접근하기 전에 획득하고, 사용이 끝나면 해제하는 방식으로 동작한다.4강 모니터(Monitor)세마포어의 단점을 보완한 상호 배제 메커니즘으로, 운영체제가 아닌 프로그래밍 언어 차원에서 지원하는 동기화 기법이다.모니터의 특징세마포어처럼 wait(), signal()을 직접 호출할 필요 없이 언어 차원에서 동기화 기능을 제공한다.더 안전하고 직관적으로 동기화 코드를 작성할 수 있다.예시: Java에서 synchronized 키워드를 사용하면 동시에 여러 프로세스가 같은 메서드를 실행할 수 없다.예시위 코드에서 프로세스 A가 increase() 메서드를 실행 중이면, 프로세스 B는 decrease()를 실행할 수 없다.결론 모니터는 세마포어보다 더 직관적이고 안전한 동기화 메커니즘을 제공하여, 동기화 코드를 쉽게 작성할 수 있도록 해준다. 데드락(교착상태)여러 프로세스가 서로 다른 프로세스의 작업이 끝나길 기다리다 아무도 작업을 진행하지 못하는 상황을 교착상태라 한다.ex. 프로세스A: 프로세스B가 자원을 줘야 실행 됨 프로세스B: 프로세스C가 자원을 주길 기다리고 있음. 프로세스C: 프로세스 A가 자원 주면 지원해주겠음. 교착상태의 발생 원인은 공유자원 때문이다.교착상태의 필요조건 4가지(모두 충족해야함)상호배제(어떤 프로세스가 리소스를 점유했다면 그 리소스는 다른 프로세스에게 공유 되면 안된다.)비선점(프로세스 A가 리소스를 점유하고 있는데 프로세스 B가 리소스를 빼앗을 수 없어야함.)점유와 대기(어떤 프로세스가 리소스 A를 점유하고 있는 상태에서 리소스 B를 원하는 상태)(오른쪽 포크를 쥔 상태에서 왼쪽 포크를 원하는 상태)원형 대기(점유와 대기를 하는 프로세스들의 관계가 원형을 이루고 있어야한다.)(1번은 자원이 공유될 수 있는지, 2번은 점유된 리소스를 빼앗을 수 있는지)교착상태를 예방하는 방법은 너무 비효율적이었다.그래서 교착상태에 빠졌을 때 해결방법을 알아보겠다.교착상태 회피(Deadlock avoidance)운영체제가 프로세스들에게 자원을 할당할 때 어느정도 자원을 할당해야 교착상태에 이르는지 파악하고 발생하지 않는 수준의 자원을 할당한다.교착상태 회피는 할당된 자원의 수를 기준으로 안정 상태와 불안정 상태로 나뉜다.운영체제는 최대한 안정 상태를 유지하려고 자원할당을 한다.불안정상태여도 교착상태에 무조건 빠지는 것이 아닌 확률이 높아지는 것.(은행원 알고리즘)운영체제는 프로세스들에게 자원을 할당하기 전에 자원을 얼마나 가지고 있는지 즉 총 자원을 알고있다.프로세스들은 자기가 필요한 자원의 최대 숫자를 운영체제에게 알려줘야함. 이를 최대 요구 자원 이라 함.안정상태시스템의 총 자원=14 사용가능한 자원=2프로세스 최대 요구 자원 현재 할당된 자원 요청이 예상되는 자원 P1 9 5 4 P2 6 4 2 P3 4 3 1이 상황에서 P1이 4개를 요구하면 요구를 취소하고 P2에게 빌려줌.그 후 P2가 작업을 마치고 6개를 반납함 so P1, P3 요구 전부 가능.불안정상태시스템의 총 자원=14 사용가능한 자원=1프로세스 최대 요구 자원 현재 할당된 자원 요청이 예상되는 자원 P1 9 7 2 P2 6 4 2 P3 4 2 2P1, P2, P3 의 요청 전부 불가능함.지금처럼 불안정상태라해도 모든 프로세스가 최대 요구 자원을 요구하지 않는 이상 교착상태에 빠지진 않지만 요구한다면 교착상태에 빠진다.이는 비효율적이다.교착상태를 어떻게 검출해낼까?가벼운 교착 상태 검출(프로세스가 일정시간 작업을 진행하지 않는다면 교착상태가 발생했다 간주& 해결)(해결: 일정 시점마다 체크포인트를 만들어 작업을 저장하고 타임아웃으로 교착상태가 발생했다면 마지막으로 저장했던 체크포인트로 롤백하는 것)무거운 교착 상태 검출(자원할당 그래프를 이용함, 현재 운영체제에서 프로세스가 어떤 자원을 사용하는지 지켜보고 교착상태가 발생했다면 해결한다. 그래프를 보고 HOW? ,그래프를 보고 순환 구조를 파악한 뒤 교착상태를 발생시킨 프로세스를 강제 종료 시킴. 다시 실행시킬 때 체크포인트로 롤백시킴)(이는 주기적으로 자원할당 그래프를 검사해야 하기 때문에 오버헤드가 발생함)(하지만 가벼운 교착 상태 검출로 검출 될 수 있는 억울하게 종료되는 프로세스는 발생하지 않는다.)컴파일과 프로세스1. 프로그래밍 언어: 컴파일 언어 vs 인터프리터 언어프로그래밍 언어는 컴파일 언어와 인터프리터 언어로 구분할 수 있다.컴파일 언어개발자가 작성한 코드를 컴파일 과정을 거쳐 실행 파일(기계어)로 변환한다.컴파일 과정에서 문법 오류를 검사하고, CPU에서 처리 가능한 기계어로 변환하므로 실행 속도가 빠르다.인터프리터 언어코드를 미리 기계어로 변환하지 않고, 실행 시 한 줄씩 해석하여 실행한다.실행 전 오류 검사를 하지 않으므로 실행 중 오류가 발생할 가능성이 높으며 속도가 느리다.2. 프로세스의 메모리 구조프로세스는 실행될 때 CODE, DATA, HEAP, STACK 영역으로 나뉜다.① 코드(Code) 영역실행해야 할 코드가 저장되는 공간.예시:ret = num1 + num2; ② 데이터(Data) 영역전역 변수나 정적 변수가 저장되는 공간.예시:int global_A = 1; int arr[10]; ③ 스택(Stack) 영역함수 호출 시 생성되는 지역 변수와 함수 호출 기록(스택 프레임)이 저장된다.함수가 종료되면 해당 스택 영역은 자동으로 해제된다.④ 힙(Heap) 영역실행 중 동적 메모리 할당이 이루어지는 공간.개발자가 직접 할당 및 해제해야 한다 (malloc, free 등 사용).3. 컴파일 언어의 실행 과정컴파일 언어로 작성된 코드가 실행 파일이 되기까지의 과정은 다음과 같다.#include <stdio.h> #define MY_NUMBER 100 int main() { int num1 = 50; printf("result: %d", num1 + MY_NUMBER); getchar(); return 0; } 실행 과정전처리기 (Preprocessor) → test.i#include <stdio.h>의 내용을 포함시키고, #define MY_NUMBER 100을 치환한다.코드에 있는 모든 주석을 제거한다.컴파일러 (Compiler) → test.sC 코드(test.i)를 어셈블리 코드로 변환한다.어셈블러 (Assembler) → test.o어셈블리 코드를 목적 파일(기계어)로 변환한다.링커 (Linker) → test.exe여러 목적 파일과 라이브러리를 합쳐 실행 파일(Executable)을 생성한다.실행 파일과 프로세스exe 파일에는 코드(Code) 영역, 데이터(Data) 영역이 명확하게 구분되어 있다.운영체제(OS)는 실행 파일을 읽어 새로운 프로세스를 생성하며:코드, 데이터를 프로세스 메모리에 로드.빈 상태의 스택(Stack)과 힙(Heap) 영역을 할당.PCB(Process Control Block) 생성 후 관리 가능하도록 설정.프로그램 카운터(PC)*를 코드 영역의 첫 번째 명령어 주소로 설정하여 실행을 시작한다.이러한 과정을 통해 프로세스가 실행되며, CPU가 이를 스케줄링하여 프로그램이 동작하게 된다. 메모리의 종류와 주소 체계1. 메모리의 종류컴퓨터에서 사용되는 주요 메모리는 다음과 같이 구분된다.1.1 레지스터 (Register)CPU 내부에 위치하며 가장 빠른 기억장소전원 차단 시 데이터가 사라지는 휘발성 메모리CPU의 비트 수(예: 32비트, 64비트)는 레지스터의 크기를 의미CPU는 연산 시 메인메모리(RAM)의 데이터를 레지스터로 가져와 계산 후 다시 메인메모리에 저장1.2 캐시 메모리 (Cache Memory)레지스터보다 느리지만 메인메모리보다 빠른 휘발성 메모리메인메모리 접근 속도를 개선하기 위해 사용CPU가 자주 사용할 것 같은 데이터를 미리 저장해 속도 차이를 줄임성능 향상을 위해 여러 단계(L1, L2 캐시 등)로 구성됨1.3 메인메모리 (RAM, Random Access Memory)운영체제와 프로세스가 실행되는 공간전원이 꺼지면 데이터가 사라지는 휘발성 메모리프로그램 실행 및 데이터 처리 속도에 직접적인 영향을 줌1.4 보조 저장장치 (HDD, SSD)전원이 차단되어도 데이터가 유지되는 비휘발성 메모리하드디스크(HDD)는 비교적 저렴하지만 속도가 느림솔리드스테이트드라이브(SSD)는 빠르지만 가격이 높음2. 메모리와 주소 체계운영체제는 메모리를 효과적으로 관리하기 위해 1바이트 단위로 구역을 나누고 각 구역에 **주소(Address)**를 부여한다.2.1 32비트 vs 64비트 CPU32비트 CPU레지스터 크기: 32비트연산 장치(ALU), 데이터 버스도 32비트다룰 수 있는 메모리 크기: 2^32=4GB64비트 CPU레지스터 크기: 64비트연산 장치(ALU), 데이터 버스도 64비트다룰 수 있는 메모리 크기 : 2^64 (거의 무한대)2.2 물리 주소와 논리 주소물리 주소 공간: 메모리를 실제로 연결하면 0x0번지부터 시작하는 주소 공간논리 주소 공간: 운영체제가 사용자 프로그램을 위해 제공하는 가상의 주소 공간사용자는 논리 주소만 인식하며, 운영체제가 이를 물리 주소로 변환운영체제 보호를 위해 **경계 레지스터(Boundary Register)**를 사용하여 프로세스가 메모리 접근 시 경계를 넘지 않도록 관리함2.3 절대 주소 vs 상대 주소절대 주소 (Absolute Address): 실제 메모리에서 사용되는 물리 주소상대 주소 (Relative Address): 프로그램이 실행될 때 기준이 되는 시작 주소를 기반으로 한 주소프로그램 실행 시 **재배치 레지스터(Relocation Register)**를 사용해 논리 주소를 절대 주소로 변환2.4 메모리 주소 변환 예시사용자가 논리 주소 0x100의 데이터를 요청CPU가 메모리 관리자에게 0x100번지 데이터를 요청메모리 관리자는 **재배치 레지스터 값(예: 0x4000)**을 더해 **절대 주소 0x4100*의 데이터를 가져옴프로그램 시작 주소가 변경되더라도 재배치 레지스터 값만 수정하면 됨메모리 할당 방식과거 유니프로그래밍 환경에서는 메모리보다 큰 프로그램을 실행할 때 필요한 부분만 메모리에 올리고, 나머지는 하드디스크에 저장하는 메모리 오버레이 기법을 사용했다. 이를 통해 큰 프로그램을 여러 조각으로 나누어 일부만 실행하며, 나머지는 하드디스크의 스왑 영역에 저장했다.현대의 멀티프로그래밍 환경에서는 메모리 할당 방식이 **가변 분할 방식(세그멘테이션)**과 **고정 분할 방식(페이징)**으로 나뉜다.1. 가변 분할 방식 (세그멘테이션)프로세스의 크기에 따라 메모리를 동적으로 나누는 방식하나의 프로세스가 메모리에 연속된 공간에 할당됨 (연속 메모리 할당)✅ 장점낭비되는 공간인 내부 단편화가 발생하지 않음❌ 단점외부 단편화가 발생할 수 있음2. 고정 분할 방식 (페이징)프로세스의 크기와 관계없이 일정한 크기의 블록(페이지)로 나누어 메모리에 할당하나의 프로세스가 메모리 내 비연속적인 공간에 할당됨 (비연속 메모리 할당)✅ 장점구현이 간단하고 오버헤드가 적음❌ 단점사용되지 않는 공간이 발생하여 내부 단편화가 많음메모리 단편화 문제와 해결 방법외부 단편화프로세스가 종료된 후 남은 메모리가 조각나서, 새로운 프로세스가 올라갈 수 없는 현상해결 방법: **조각 모음(Compaction)**을 통해 단편화된 메모리 공간을 정리하지만 조각 모음 과정에서 오버헤드(추가적인 연산 비용)가 발생내부 단편화프로세스 크기보다 큰 고정된 메모리 블록을 할당하면서 사용되지 않는 공간이 생기는 현상해결 방법:할당되는 블록 크기를 조정하여 내부 단편화를 최소화버디 시스템을 사용하여 효율적인 메모리 할당버디 시스템2의 승수 단위로 메모리를 분할하여 할당하는 방식프로세스의 요청 크기에 가장 적합한 크기의 블록을 할당하여 외부 단편화를 방지하지만 내부 단편화는 일부 발생할 수 있음예시2048B(2^11)의 메모리가 있음500B 크기의 프로세스 A가 메모리를 요청512B(2^9) 크기의 블록을 할당이처럼 프로세스 크기에 맞게 할당되며, 불필요한 공간 낭비를 줄일 수 있음.현대의 메모리 관리 방식현재는 가변 분할(세그멘테이션)과 고정 분할(페이징) 방식을 조합하여 사용한다.이를 통해 내부·외부 단편화 문제를 최소화하면서도 효율적인 메모리 활용이 가능하다.