게시글
블로그
전체 102025. 03. 23.
0
3주차 미션
운영체제 메모리의 종류는 어떤것들이 있나요? 각 메모리의 특징도 함께 적어주세요.레지스터(Register)CPU 내부에 있는 가장 빠른 메모리. 크기는 작지만 처리 속도가 빠름캐시 메모리(Cache)CPU와 메인 메모리 사이에 위치. 자주 사용하는 데이터를 저장해서 접근 속도를 높인다.주기억장치(Main Memory, RAM)현재 실행 중인 프로그램과 데이터를 저장. 휘발성(전원이 꺼지면 데이터가 사라짐).보조기억장치(Secondary Storage)HDD, SSD 같은 저장장치. 비휘발성. 데이터를 영구적으로 저장한다.가상 메모리(Virtual Memory)물리적 메모리보다 더 큰 주소 공간을 제공하기 위해 하드디스크의 일부를 메모리처럼 사용. 페이지 교체가 발생하면 성능이 저하될 수 있음.ROM(Read Only Memory)읽기 전용 메모리로, 주로 부팅 시 필요한 기본적인 정보를 저장. 비휘발성. BIOS가 여기에 저장됨.플래시 메모리(Flash Memory)빠른 읽기/쓰기 가능. SSD나 USB 메모리에 사용됨. 사용자 프로세스가 메모리의 운영체제 영역에 침범하지 못하도록 만든 레지스터는 어떤 레지스터일까요? 경계 레지스터(Bound Register) 또는 기저 레지스터(Base Register) 를 사용한다Base Register는 사용자 프로세스가 접근 가능한 메모리 시작 주소를 지정Bound Register는 접근 가능한 메모리의 끝 주소(범위)를 지정한다 메모리 할당 방식에서 가변 분할 방식과 고정 분할 방식의 장단점은 뭔가요?고정 분할은 공간을 미리 나눠놓기 때문에 비어 있는 부분이 생겨도 다른 프로세스가 못 쓰는 경우가 많아서 비효율적.가변 분할은 필요한 만큼만 나눠서 공간 활용은 좋은데, 빈 공간들이 흩어져서 외부 단편화 문제가 생길 수 있다 CPU 사용률을 올리기 위해 멀티프로그래밍을 올렸지만 스왑이 더 많이 이루어져 CPU 사용률이 0%에 가까워 지는 것을 뭐라고 할까요?스레싱프로세스가 너무 많아지면 메모리가 부족해서 페이지 폴트가 빈번하게 발생하고,때문에 계속 스왑 인/아웃만 반복하게 돼서 CPU가 실질적인 작업을 거의 못 하는 상황이다결과적으로 CPU는 일 안 하고, 하드디스크만 바쁘게 움직이는 상태가 된다 HDD나 SSD는 컴퓨터를 실행시키는데 꼭 필요한 걸까요?꼭 필요하진 않다. HDD나 SSD는 운영체제와 데이터를 저장하는 역할을 하지만, ROM이나 USB 같은 외부 저장장치에서 OS를 부팅하면 HDD/SSD 없이도 동작 가능하다하지만 HDD/SSD가 없으면 운영체제를 어디다 저장하고 부팅할지 문제 때문에 필수로 여겨진다메모리는 휘발성이라 전원이 꺼지면 모든 데이터가 날아가서 영구 저장 장치가 필요하기 때문임 파일을 삭제해도 포렌식으로 파일을 복구할 수 있는 이유가 무엇일까요?파일을 삭제해도 실제 데이터는 안 지워지고, 파일 시스템에서 그 파일에 대한 포인터(주소 정보)만 삭제되기 때문이다삭제하면 운영체제는 "이 공간은 비어 있다" 하고 표시할 뿐 실제 데이터는 그대로 하드디스크에 남아 있게 된다 덮어쓰기가 되지 않는 한, 복구 프로그램이나 포렌식 기술로 지워진 파일을 다시 읽어올 수 있다 자료구조 알고리즘 지금까지 배운 5개의 정렬 알고리즘의 장단점과 시간 복잡도를 적어주세요. 버블 정렬복잡도 : O(n²) / O(n²)O(1) 특징구현이 매우 간단함자료가 거의 정렬된 경우 최적화 가능느린 속도선택 정렬복잡도 : O(n²) / O(n²)O(1) 특징구현이 간단함메모리 적게 사용 (제자리 정렬)항상 O(n²)데이터 양이 많아지면 비효율적 삽입 정렬복잡도 : O(n²) / O(n²), (최선 O(n))O(1)특징거의 정렬된 데이터일 경우 빠름구현이 간단함데이터가 많거나 무작위일 경우 비효율적 병합 정렬복잡도 : O(n log n) / O(n log n) O(n) 특징안정적인 시간 복잡도안정 정렬(같은 값 순서 보장)큰 데이터 정렬에 강점추가 메모리 공간 필요리스트 병합 과정이 오버헤드 퀵 정렬복잡도 : O(n log n) / O(n²)O(log n) (호출 스택)특징평균적으로 매우 빠르고 효율적추가 메모리 거의 안 씀 (제자리 정렬)최악의 경우 느림 (피벗 선택 실패 시)불안정 정렬 (같은 값 순서 보장 X) 메모리가 부족한 시스템에서 어떤 문제를 해결하는데 재귀로 쉽게 구현이 가능할 것 같습니다. 여러분이라면 메모이제이션과 타뷸레이션 중 어떤 걸 이용하실 건가요? 이유를 함께 적어주세요.타뷸레이션으로 구현합니다우리는 항상 메모리가 부족하지까 재귀 호출을 많이 하면 스택 오버플로우 위험이 있음메모이제이션은 재귀 호출을 기반으로 동작하기 때문에 호출 스택이 깊어지면 메모리 문제가 발생할 수 있다반면에 타뷸레이션은 반복문 기반으로 동작하고, 스택을 사용하지 않아서 스택 오버플로우 걱정이 없다그리고 타뷸레이션은 미리 테이블을 할당해서 사용하는 메모리 양이 예측 가능하고 고정적이다메모이제이션은 필요할 때마다 캐시를 쌓아가지만, 그만큼 캐시 관리가 필요하고 메모리를 더 많이 사용할 수도 있음.
2025. 03. 23.
1
3주차 발자국 운영체제
가상메모리 개요정의: 가상 메모리는 물리 메모리(RAM)의 용량이 부족할 때 보조 저장 장치(디스크)를 마치 메모리처럼 사용하는 기술이야.목적: 한정된 물리 메모리 자원을 효과적으로 활용하고, 프로그램이 실제 메모리 크기를 몰라도 실행되도록 해 줌.장점: 여러 프로그램이 동시에 실행 가능하고, 물리 메모리보다 큰 프로그램도 실행할 수 있음.가상메모리의 메모리 분할 방식 1: 세그멘테이션(Segmentation)설명: 프로그램을 의미 있는 단위(세그먼트)로 나누는 방식. 보통 코드, 데이터, 스택 등으로 나눔.방식: 가변 분할 방식 → 각 세그먼트 크기가 서로 다름.장점:논리적인 구조 반영이 용이함 (코드, 데이터 등을 따로 관리).필요한 메모리만큼만 할당하므로 메모리 낭비가 적음.단점:외부 단편화 발생 → 메모리가 여기저기 나뉘어져 있어서 충분한 메모리가 있어도 연속 공간이 부족하면 사용하지 못하는 문제.가상메모리의 메모리 분할 방식 2: 페이징(Paging)설명: 메모리를 고정된 크기의 페이지(Page) 단위로 나눔.방식: 고정 분할 방식 → 모든 페이지 크기가 동일함.장점:외부 단편화가 없음 → 빈 공간이 여기저기 흩어져 있어도 사용 가능.메모리 관리가 간단함.단점:내부 단편화 발생 → 페이지 크기보다 작은 데이터는 빈 공간이 생김.가상메모리의 메모리 분할 방식 3: 페이지드 세그멘테이션(Paged Segmentation)설명: 세그멘테이션과 페이징을 결합한 방식.특징:세그먼트는 논리적인 단위지만, 각 세그먼트 내부는 페이지로 나누어서 관리.장점:외부 단편화와 내부 단편화 문제를 모두 어느 정도 해결.효율적인 메모리 할당 가능.디맨드 페이징(Demand Paging)설명: 필요한 페이지가 메모리에 없을 때만 디스크에서 불러오는 방식. 프로그램 실행 시 전체를 메모리에 올리지 않아도 됨.근거 이론: 지역성(Locality)의 원리시간적 지역성: 최근에 참조한 데이터는 가까운 시점에 다시 참조될 가능성이 높다.공간적 지역성: 현재 참조한 주소 근처가 곧 참조될 가능성이 높다. 가상 메모리 주소가 참조되는 상황스왑이 필요 없는 경우→ 필요한 페이지가 이미 물리 메모리에 존재. 바로 접근 가능.스왑 영역에 있는 데이터를 참조하는 경우→ 페이지 폴트(Page Fault)가 발생하여 스왑 영역에서 페이지를 불러와야 함.물리 메모리가 꽉 찼을 때 스왑 영역 참조→ 기존에 있는 페이지를 스왑 아웃(교체)하고 새 페이지를 불러와야 함.페이지 교체 정책(Page Replacement Algorithm)목적: 메모리가 꽉 찼을 때 어떤 페이지를 제거하고 새 페이지를 가져올지 결정.정책 종류:Random: 무작위 선택 → 구현은 쉽지만 비효율적.FIFO(First-In-First-Out): 가장 먼저 들어온 페이지를 제거 → 오래된 게 덜 쓸 것 같아서 사용하지만, 성능이 안 좋을 때도 있음(벨라디의 이상 현상 발생 가능).Optimal(OPT): 앞으로 가장 오래 쓰지 않을 페이지를 제거 → 이상적이지만 미래를 예측할 수 없어서 현실에서는 불가능.LRU(Least Recently Used): 최근에 사용하지 않은 페이지를 제거 → 지역성 원리를 반영하여 성능이 좋음.단점: 시간 정보 기록 필요 → 하드웨어 지원이 없으면 구현이 비효율적(비트 오버플로우 문제 등).Clock 알고리즘 (Second Chance)LRU를 근사화한 방식 → 참조 비트(Reference Bit)를 사용하여 순환하면서 교체.Enhanced Clock: 참조 비트와 수정 비트를 함께 사용하여 더 효율적으로 교체.스레싱(Thrashing)과 워킹셋(Working Set)스레싱: 과도한 페이지 폴트로 CPU가 페이지 교체에만 매달리는 현상 → CPU 사용률 저하.원인: 물리 메모리가 부족해서 자주 교체가 발생.워킹셋 모델:현재 프로세스가 일정 시간 동안 자주 사용하는 페이지 집합을 워킹셋이라 함.워킹셋을 메모리에 유지하면 페이지 폴트율을 낮출 수 있음.운영체제가 페이지 폴트율을 기준으로 페이지를 할당하거나 회수하는 방식으로 스레싱 완화 가능.주변장치I/O 디바이스와 저장장치 구분캐릭터 디바이스: 데이터가 순차적으로 처리(키보드, 마우스, 프린터 등).블록 디바이스: 대량의 데이터 블록 단위 처리(하드디스크, SSD 등).연결 방식시스템 버스와 입출력 제어기로 연결됨.DMA(Direct Memory Access): CPU 개입 없이 메모리와 직접 데이터 전송 → 고속 입출력 지원.Memory Mapped IO: 입출력 장치를 메모리처럼 접근. 마우스와 키보드 동작광학 마우스초당 수천 번 촬영한 사진을 DSP가 분석해서 위치 변화를 계산.클릭/움직임 감지 후 인터럽트 신호를 CPU에 전달.키보드키가 눌릴 때 신호 발생 → 디바이스 컨트롤러가 감지 후 CPU에 인터럽트 발생. 하드디스크 vs 플래시 메모리(SSD)하드디스크기계적 방식 → 회전하는 플래터와 읽기/쓰기 헤드 사용.자성 매체라서 외부 충격, 자기장에 민감함.트랙과 섹터로 구분, 밀리초 단위 접근 시간.SSD전자적 방식 → 반도체 기반으로 전기 신호로 데이터 저장.속도가 빠르고 충격에 강함.수명 제한(지울 수 있는 횟수 제한).파일 시스템(파일 관리자)기능파일 생성, 수정, 삭제, 검색.접근 권한 관리, 암호화, 백업 및 복구.파일 관리파일 테이블과 파일 제어 블록(FCB) 사용 → 파일 속성 및 위치 정보 저장.전송 단위저장 장치에서는 블록 단위, 사용자 관점에서는 바이트 단위 접근.파일 구성헤더: 속성 정보(이름, 크기, 시간 등).데이터: 실제 내용.파일 구조순차 파일: 순서대로 접근 → 수정과 삽입이 비효율적.직접 파일: 해시 기반 → 빠른 접근.인덱스 파일: 인덱스를 통해 빠르게 검색 가능 → 음악 목록 같은 구조. 디렉토리와 파일 디스크디렉토리도 파일이지만 파일 정보를 저장하는 특수한 구조.블록 단위로 디스크 관리, 페이지와 유사하지만 파일 시스템에선 블록.할당 방식연속 할당: 빠르지만 외부 단편화 문제.불연속 할당:연결 할당: 링크드 리스트 방식 → 순차 접근 효율.인덱스 할당: 인덱스 블록으로 블록 정보를 관리 → 랜덤 액세스에 유리.
2025. 03. 23.
0
(3주차) 발자국 자료구조 알고리즘
삽입 정렬 (Insertion Sort)개념삽입 정렬은 리스트를 앞에서부터 차례로 정렬된 부분과 비교해서 적절한 위치에 삽입하는 방식의 정렬 알고리즘손으로 카드를 정렬하는 방법과 비슷하다이미 정렬된 부분은 항상 유지되고, 새로운 값을 적절한 자리에 넣는 방식 동작 방식두 번째 요소부터 시작해서 이전 요소들과 비교크기를 비교해서 알맞은 위치에 삽입끝까지 반복시간 복잡도평균/최악: O(n²)최선(이미 정렬된 경우): O(n)장점구현이 간단함거의 정렬된 데이터에서는 빠름추가 메모리 필요 없음 (제자리 정렬, In-place)단점데이터가 많으면 비효율적 (시간 복잡도 때문에) const Insertion = (arr) => { for (let i = 1; i = 0; j--) { if (arr[j] > insertingData) { arr[j + 1] = arr[j]; } else { break; } arr[j + 1] = insertingData; } } }; 병합 정렬 (Merge Sort)개념병합 정렬은 분할 정복(Divide and Conquer) 방법을 사용하는 정렬 알고리즘데이터를 반으로 나누고, 각각을 정렬한 후, 다시 병합하면서 정렬하는 방식동작 방식리스트를 반으로 쪼갬각각 재귀적으로 병합 정렬 수행정렬된 두 리스트를 병합시간 복잡도최선/평균/최악: O(n log n)장점시간 복잡도가 안정적 (데이터 상태에 관계없이 O(n log n))안정 정렬(같은 값의 순서 유지)단점추가 메모리 공간 필요 (배열을 나누고 병합하는 과정에서) const Merge = (arr, leftIndex, midIndex, rightIndex) => { let leftAreaIndex = leftIndex; let rightAreaIndex = midIndex + 1; let tempArr = []; tempArr.length = rightIndex + 1; tempArr.fill(0, 0, rightIndex + 1); let tempArrIndex = leftIndex; while (leftAreaIndex midIndex) { for (let i = rightAreaIndex; i { if (leftIndex 퀵 정렬 (Quick Sort)개념퀵 정렬도 분할 정복(Divide and Conquer) 방식의 정렬기준(pivot)을 하나 정해서, 그보다 작은 값과 큰 값으로 나누고, 각각 정렬한 후 합치는 방식동작 방식피벗 선택피벗을 기준으로 작은 값/큰 값으로 나누기각 부분 배열에 대해 퀵 정렬 재귀 호출시간 복잡도평균: O(n log n)최악(피벗을 잘못 잡았을 때): O(n²)장점평균적으로 매우 빠름추가 메모리 거의 필요 없음 (제자리 정렬)단점최악의 경우 성능 저하 가능불안정 정렬(같은 값의 순서 보장 안 됨) const swap = (arr, index1, index2) => { let temp = arr[index1]; arr[index1] = arr[index2]; arr[index2] = temp; }; const divide = (arr, left, right) => { let pivot = arr[left]; let leftStartIndex = left + 1; let rightStartIndex = right; while (leftStartIndex = arr[leftStartIndex]) { leftStartIndex++; } while (rightStartIndex >= left + 1 && pivot { if (left 메모이제이션 (Memoization)개념메모이제이션은 이미 계산한 값을 저장해서 재사용하는 최적화 기법주로 재귀함수와 함께 사용된다중복 계산을 피해서 성능을 높여준다왜 필요할까?재귀함수는 같은 값을 계속 계산하는 경우가 많아.예를 들어 피보나치 수열을 재귀로 구현하면, 같은 계산을 수없이 반복하게 돼서 비효율적이다어떻게 동작할까?계산하려는 값이 이미 계산된 값인지 검사(검색)있다면 저장된 값을 바로 반환없다면 계산하고 결과를 저장보통 어디에 저장?보통 해시테이블(객체, Map 등)에 저장키: 계산하려는 값값: 계산 결과장점중복 계산 제거 → 성능 향상단점메모리 사용량 증가 (저장 공간 필요)타뷸레이션 (Tabulation)개념타뷸레이션은 상향식(bottom-up) 방식의 동적 프로그래밍작은 문제부터 차근차근 해결해서 테이블에 값을 저장하고, 그 값을 이용해서 큰 문제를 푸는 방식차이점 vs 메모이제이션메모이제이션: 탑다운(top-down) → 재귀함수 + 캐싱타뷸레이션: 바텀업(bottom-up) → 반복문 사용해서 테이블 채우기피보나치 예시fib[0], fib[1]을 먼저 저장fib[2]부터 fib[n]까지 차례로 계산장점재귀 호출 스택을 사용하지 않아서 스택 오버플로우 방지보통 메모이제이션보다 메모리를 적게 사용단점모든 값을 전부 다 계산해야 해서 필요 없는 값까지 구할 수도 있음동적 프로그래밍에서 어떤 방식 더 좋을까메모이제이션간단하게 재귀함수 수정만으로 구현 가능부분 문제 중 일부만 필요한 경우 적합타뷸레이션반복문으로 구현해서 스택 문제 없음대부분의 경우 메모리 효율이 좋고 속도도 빠름const fibonacci1 = (n) => { if (n === 0 || n === 1) return n; return fibonacci1(n - 2) + fibonacci1(n - 1); }; const fibonacci2 = (n, memo) => { if (n === 0 || n === 1) return n; // 검색 if (memo[n] === null) { memo[n] = fibonacci2(n - 2, memo) + fibonacci2(n - 1, memo); } return memo[n]; }; const fibonacci3 = (n) => { if (n
2025. 03. 16.
0
(2주차) 자료구조 알고리즘 미션
재귀함수에서 기저조건을 만들지 않거나 잘못 설정했을 때 어떤 문제가 발생할 수 있나요?재귀가 끝나지 않으므로 스택 오버플로가 발생한다 함수가 호출될 때마다 콜 스택(call stack)에 쌓이는데, 끝없이 호출되면 모든 메모리를 사용하게 된다 0부터 입력 n까지 홀수의 합을 더하는 재귀 함수를 만들어보세요.const sumOdd = (n) => { // 기저 조건: n이 0보다 작으면 0을 반환하고 끝낸다 if (n 다음 코드는 매개변수로 주어진 파일 경로(.는 현재 디렉토리)에 있는 하위 모든 파일과 디렉토리를 출력하는 코드입니다. 다음 코드를 재귀 함수를 이용하는 코드로 변경해보세요.const fs = require("fs"); const path = require("path"); const traverseDirectoryRecursive = (directory) => { const files = fs.readdirSync(directory); // 디렉토리 안의 파일/폴더 목록 가져온다 for (const file of files) { const filePath = path.join(directory, file); // 경로를 합친다 const fileStatus = fs.statSync(filePath); // 파일/디렉토리 상태 확인 if (fileStatus.isDirectory()) { console.log('Directory : ', filePath); // 재귀 호출: 하위 디렉토리를 다시 순회 traverseDirectoryRecursive(filePath); } else { console.log('File : ', filePath); } } } traverseDirectoryRecursive("."); // 현재 경로부터 시작
2025. 03. 16.
0
(2주차) 운영체제 미션 제출
FIFO 스케줄링의 장단점이 뭔가요?은행에 줄 서서 기다리는 상황이라고 비유 했을 때, 먼저 온 사람이 먼저 창구에서 업무를 보고 뒤에 온 사람은 무조건 앞사람이 끝나야 한다.장점누가 먼저 왔는지만 따지기 때문에 누구한테도 특혜가 없으므로 공정하다고 느껴진다구현이 간단하다 : 큐에 넣고 순서대로 꺼내면 되니까 코드도 쉽고 시스템 자원도 적게 쓴다.단점CPU를 오래 쓰는 프로세스가 앞에 있으면 뒤의 짧은 작업들도 기다려야 한다앞사람이 대출 상담(시간 오래 걸리는 업무)을 보고 있다면 뒤에 온 사람들(단순 이체 업무)이 빨리 끝날 수 있는데도, 무조건 기다려야 한다.짧은 작업이 불필요하게 오래 기다리는 경우가 많아서 전체적인 대기 시간이 늘어난다. SJF를 사용하기 여러운 이유가 뭔가요?프로세스의 CPU Burst Time(실행 시간)을 미리 알기 어렵다 -> 예측이 쉽지 않아서 실시간 시스템에서는 비현실적 잘못 예측하면 기대 성능이 안 나오지 않는다비선점형으로 하면 긴 프로세스가 무한정 대기할 수 있다RR 스케줄링에서 타임 슬라이스가 아주 작으면 어떤 문제가 발생할까요?Context Switch이 자주 발생한다 -> 오버헤드가 커져서 CPU 시간의 낭비처리 시간이 짧은 프로세스에도 효율이 떨어진다운영체제가 MLFQ에서 CPU Bound Process와 I/O Bound Process를 어떻게 구분할까요?행동 패턴을 기준으로 자동 분류한다 -> CPU를 오래 쓰면 낮은 우선순위로 강등, 입출력(I/O) 위주로 빠르게 종료하면 높은 우선순위를 유지한다즉, CPU 사용 시간과 빈도에 따라 레벨이 조정된다공유자원이란무엇인가요?여러 프로세스/스레드가 동시에 접근하려는 자원 -> CPU, 메모리, 파일, 프린터 등동시에 접근하면 경쟁이 발생하고 동기화가 필요하다 교착상태에 빠질 수 있는 조건은 어떤 것들을 충족해야할까요? 상호 배제: 자원을 하나의 프로세스만 사용 가능하다점유와 대기: 자원을 가진 상태에서 다른 자원을 기다린다비선점: 자원을 강제로 빼앗을 수 없다순환 대기: 프로세스 간에 원형 대기 상태위 조건을 모두 충족해야 교착상태에 빠진다
2025. 03. 16.
0
운영체제 2주차
프로세스 간 통신(IPC)서로 다른 프로세스가 데이터를 주고받기 위한 기법. 각 방법마다 동기화 방식, 속도, 구현 난이도에서 차이가 있다.방법특징장점단점파이프 (Pipe)한 방향 통신 (익명 파이프), 부모-자식 간 사용간단하고 구현 쉬움양방향 어렵고 제한적임메시지 큐메시지 형태로 데이터 전송, 큐를 통해 비동기 처리비동기 지원, 독립적 통신커널 리소스 필요, 크기 제한공유 메모리메모리 공간을 공유, 가장 빠른 통신 방식속도 빠름동기화(락) 필수, 복잡함소켓네트워크 기반 통신, 원격 프로세스와 통신 가능네트워크를 통한 유연한 통신프로토콜 관리 복잡, 느림[프로세스 A] --| 파이프 |--> [프로세스 B] [프로세스 A] --| 메시지 큐 |--> [프로세스 B] [프로세스 A] | | 공유 메모리 (Shared Memory) | [프로세스 B] [프로세스 A] [프로세스 B]공유 자원과 임계 구역공유 자원은 여러 프로세스나 스레드가 접근하는 데이터나 자원이다. 임계 구역은 그 자원에 접근하는 코드 영역으로, 동시에 접근하면 문제 발생 가능성이 있다.용어설명공유 자원여러 프로세스나 스레드가 동시에 접근하는 자원 (예: 파일, 메모리)임계 구역공유 자원에 접근하는 코드 블록으로, 동시에 접근하면 데이터 일관성 문제가 생김동기화 도구임계 구역을 보호하기 위해 사용 (락, 세마포어, 모니터 등)세마포어동기화 객체로, 카운터 값을 통해 자원 접근을 제어한다. 임계 구역에 여러 프로세스가 접근하지 못하게 조정하며, 두 가지 연산으로 관리한다.wait() (P 연산): 세마포어 값을 감소. 값이 0보다 작으면 대기signal() (V 연산): 세마포어 값을 증가. 대기 중인 프로세스가 있으면 깨움종류설명카운팅 세마포어자원의 개수를 관리. 값이 0 이상일 때만 접근 허용이진 세마포어0 또는 1 값을 가짐. 뮤텍스와 유사하게 동작 // wait() 연산 (P) wait(S): while S 모니터공유 자원과 그 자원을 사용하는 메서드를 하나의 단위로 캡슐화하고 동기화를 자동으로 처리하는 구조이다. 특정 언어나 플랫폼에서 구현 방식이 다르다.언어/플랫폼구현 방법설명Javasynchronized 키워드 사용메서드나 블록 단위로 동기화 처리C#lock 키워드, Monitor 클래스Monitor.Enter/Exit 또는 lock 문법 사용.NETC# 동일 + Task, async/await병렬 처리와 비동기 지원을 강화 데드락(교착 상태)서로 자원을 점유하고 대기하는 상황이 꼬여서 아무도 작업을 진행하지 못하는 상태.[P1] ----(점유중)---> [R1] ^ | | | | (대기중) | | [P4] 데드락 발생 조건 (4가지 필요조건)조건설명상호 배제자원을 한 번에 하나의 프로세스만 점유 가능비선점자원을 강제로 빼앗을 수 없음점유와 대기자원을 점유한 채로 다른 자원을 기다리는 상태순환 대기프로세스들이 원형으로 자원을 점유하고 대기하는 구조데드락 해결 방법방법설명장점단점예방데드락 발생 조건 중 하나를 제거하여 사전에 차단단순하고 명확함시스템 자원 활용도 낮음회피자원 할당 시 안전 상태인지 판단 후 할당 (은행원 알고리즘)교착상태 회피 가능복잡하고 오버헤드 큼검출 및 복구교착 상태 발생 후 감지 및 해결 (롤백, 강제 종료 등)유연성 있음오버헤드, 데이터 손실 가능성회피 전략 - 은행원 알고리즘안정 상태(Safe): 자원 요청이 들어와도 교착 상태가 발생하지 않는 상태불안정 상태(Unsafe): 교착 상태가 발생할 가능성이 있는 상태지만, 아직 교착 상태는 아님검출 방법방식설명특징가벼운 검출 (타임아웃)일정 시간 동안 작업이 없으면 교착 상태로 간주하고 복구 시도간단하지만 오탐 가능성 있음무거운 검출 (자원 할당 그래프)자원/프로세스 관계를 그래프로 그려 순환 존재 여부 탐지정확하나 지속적 검사로 인한 오버헤드 발생컴파일과 프로세스언어 구분구분설명언어 예시컴파일 언어소스 코드를 기계어로 변환 후 실행 파일 생성C, C++, C#, Go인터프리터 언어소스 코드를 실행할 때 한 줄씩 해석하여 실행Python, JavaScript, Ruby컴파일 과정 (C 기준)복사편집test.c → 전처리기 → test.i test.i → 컴파일러 → test.s test.s → 어셈블러 → test.o test.o → 링커 → test.exe 실행 파일이 프로세스로링커가 생성한 실행 파일은 OS에 의해 메모리에 적재되고, 프로세스로 생성되어 실행된다.메모리 종류메모리 유형설명특징레지스터CPU 내부, 가장 빠른 속도CPU 연산용, 소량캐시 메모리CPU와 메인 메모리 사이에 위치L1(가장 빠름) ~ L3 존재메인 메모리(RAM)실행 중인 프로그램과 데이터를 저장휘발성, 속도 중간보조 저장장치HDD, SSD 등비휘발성, 대용량 저장비트 수에 따른 주소 공간32비트: 2³² → 4GB 주소 공간 한계64비트: 2⁶⁴ → 이론상 수십 기가바이트 이상의 메모리 접근 가능메모리와 주소주소 구분설명물리 주소실제 메모리 주소, 하드웨어가 직접 접근논리 주소사용자 프로세스가 접근하는 가상의 주소경계 레지스터커널과 사용자 공간을 구분하며, 보호 기능 수행메모리 할당 방식방식설명장점단점가변 분할(세그멘테이션)프로세스 크기에 맞게 메모리 공간 할당공간 효율적외부 단편화 발생고정 분할(페이징)일정 크기 페이지 단위로 메모리 할당관리 간단, 단편화 적음내부 단편화 가능성버디 시스템2의 거듭제곱으로 메모리 블록을 분할 및 병합분할/병합 유연함일부 메모리 낭비 가능버디 시스템 예시메모리 전체 크기: 2048바이트요청 크기: 500바이트2048 → 1024 → 512바이트로 분할 후 500바이트 할당나머지 12바이트만 낭비, 효율적 공간 사용 가능 회고..배운 점IPC 방식의 차이점과 적절한 사용처를 구분할 수 있게 되었다.세마포어와 모니터의 개념적 차이와 구현 방식을 언어별로 정리했다.메모리 할당 방식에서 외부 단편화, 내부 단편화 개념이 헷갈렸는데 확실히 정리했다.아쉬운 점은행원 알고리즘은 개념적으로만 접근했고, 실제 코드 구현이나 응용 사례가 부족했다. IPC도 실습 위주로 보완이 필요하다.더 해보고 싶은 것세마포어, 모니터를 직접 구현 해본 코드 검색해보기데드락 검출을 위한 자원 할당 그래프를 찾아보기
2025. 03. 16.
0
자료구조 알고리즘 2주차
재귀 (Recursion)재귀 구현함수가 자기 자신을 호출하는 방식기본적으로 종료 조건(Base case) 과 반복 호출(Recursive case) 로 구성됨const myFunc = (number) => { if (number > 10) return; console.log(number); myFunc(number + 1); }; myFunc(1); 종료가 없는데 자동으로 실행이 종료된 이유?종료 조건이 없거나 충족되지 않으면 무한 반복콜스택(Call Stack)이 계속 쌓여서 메모리 한계를 넘으면 스택 오버플로우(Stack Overflow) 발생 후 프로그램 강제 종료됨콜스택의 개념함수 호출 정보를 저장하는 메모리 구조 (LIFO)재귀 호출이 일어날 때마다 스택에 함수 상태(매개변수, 지역변수 등) 가 쌓임호출이 끝나면 스택에서 빠져나가며 이전 상태로 돌아감재귀를 사용하는 이유문제를 더 작은 하위 문제로 나눠서 풀기 좋을 때 유리예시 : 팩토리얼, 피보나치 수열, 트리 탐색, 하노이탑 등반복문보다 코드가 간결하고 직관적일 수 있음재귀적으로 생각하기재귀의 여러 가지 패턴 분석하기단순 반복 구현반복문(for/while)으로 충분한데 재귀를 쓰면 비효율적 (콜스택 부담)하위 문제 결과를 기반으로 현재 문제 계산예시: 팩토리얼(n!) = n * (n-1)!하향식(Top-down): 큰 문제에서 시작 → 하위 문제를 해결하며 내려감상향식(Bottom-up): 작은 문제부터 차례로 풀어서 큰 문제를 해결 (재귀보다는 반복문과 DP에 주로 사용)어떤 종류의 재귀 함수인지 구분하기단순 재귀: 하위 문제를 그대로 호출 (ex. 팩토리얼)const power = (x, n) => { if (n === 0) return 1; return power(x, n - 1) * x; }; console.log(power(2, 5)); const strLength = (arr) => { if (arr[0] === undefined) return 0; return strLength(arr.slice(0, -1)) + 1; }; let str = "abcde"; let len = strLength(str); console.log(len); const factorial = (num) => { if (num === 1 || num === 0) return 1; return num * factorial(num - 1); }; console.log(factorial(5));다중 재귀: 여러 번 호출하는 경우 (ex. 피보나치) 트리 재귀: 트리 구조 탐색 (ex. DFS)꼬리 재귀: 마지막에 자기 자신 호출 → 반복문으로 최적화 가능재귀의 위력을 느낄 수 있는 문제하노이탑 트리 탐색 (DFS)백트래킹 (경로 탐색, 조합, 순열 문제)재귀를 쉽게 사용할 수 있는 방법종료 조건을 명확히 설계스택 오버플로우 방지 위해 입력값 제한 또는 반복문 전환 고려메모이제이션(Memoization) 으로 중복 계산 제거하노이탑가장 대표적인 재귀 문제원판을 한 개씩 이동시키는 재귀적 패턴이동 순서가 명확하고, 규칙에 따라 분할 정복 가능최소 이동 횟수: 2ⁿ - 1// 원반의 갯수, 시작 기둥(from), 이동할 기둥(to), 임시로 사용할 수 있는 기동(temp) const hanoi = (count, from, to, temp) => { if (count === 0) return; hanoi(count - 1, from, temp, to); console.log(`원반 ${count}를 ${from}에서 ${to}로 이동`); hanoi(count - 1, temp, to, from); }; hanoi(3, "A", "C", "B"); 버블 정렬 (Bubble Sort)핵심 개념인접한 두 값을 비교하고 교환하며 정렬매번 가장 큰 값을 뒤로 밀어내는 방식 → 버블처럼 부상하는 느낌for문 두 개가 중첩됨외부 루프: 전체 패스 반복 (n-1회)내부 루프: 인접 요소 비교 및 교환 (n-i-1회)시간 복잡도O(n²)등차수열의 합: (n-1) + (n-2) + … + 1 = n(n-1)/2데이터가 많아지면 급격히 느려짐const bubbleSort = (arr) => { for (let i = 0; i arr[j + 1]) { let temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } }; 선택 정렬 (Selection Sort)핵심 개념최솟값을 찾아서 맨 앞과 교환하는 방식한 번 선택한 요소는 다시 고려하지 않음 → 안정성은 없음성능for문 두 개가 중첩외부 루프: 정렬 범위 축소내부 루프: 최솟값 탐색시간 복잡도 O(n²) → 버블 정렬과 비슷한 비효율성const selectronSort = (arr)=>{ for(let i = 0 ; i 회고..배운 내용 & 내 생각재귀재귀는 단순히 "함수가 자기 자신을 호출하는 것"이라고만 알고 있었는데,이번에 콜스택과 메모리 구조를 이해하고 나니까 재귀 호출이 어떻게 돌아가는지 시각화가 됐다.특히 종료 조건(Base case)이 왜 중요한지 체감했다.종료 조건을 제대로 못 걸면 콜스택이 무한히 쌓이다가 스택 오버플로우가 발생한다는 걸 코드로 실험해보며 확인했다.팩토리얼이나 피보나치 같이 작은 문제로 쪼개서 풀어가는 방식은 하향식(Top-down),반복문이나 동적 계획법으로 밑에서부터 쌓아가는 건 상향식(Bottom-up)이라는 개념이 머릿속에 좀 더 명확해졌다.정렬 알고리즘버블 정렬과 선택 정렬은 사실 비슷한 느낌인데, 다시 보면 비효율적인 방식이라는 걸 알게 됐다.특히 시간 복잡도 O(n²)를 등차수열의 합으로 직접 계산해보니 훨씬 더 와닿았다.그럼에도 불구하고 이런 정렬부터 배우는 이유는, 기초적인 반복과 비교 구조를 이해하기 위해서라는 생각이 들었다.어려웠던 점과 해결 방법재귀에서 하향식과 상향식 사고가 헷갈렸는데,직접 코드를 그려보고 함수 호출 순서와 콜스택 흐름을 손으로 써보니까 감이 잡혔다.또, 하노이탑 문제를 풀면서 "문제를 나누고, 나눈 문제를 어떻게 다시 합칠지"를 생각하는 패턴이 중요한 걸 느꼈다.적용과 앞으로의 계획재귀적인 사고를 백트래킹 문제에 적용해보고 싶다.예를 들어 DFS 기반 조합, 순열, 경로 탐색 문제를 풀어보면서 패턴을 익힐 예정이다.정렬 알고리즘은 이제 퀵 정렬, 병합 정렬 같은 효율적인 방식으로 넘어가야겠다.또한, 재귀는 단순한 반복의 다른 표현이 아니라, 문제를 쪼개서 푸는 사고법이다.이걸 익히면 알고리즘 문제에서 트리 구조나 백트래킹을 자연스럽게 생각할 수 있다.
2025. 03. 09.
0
미션 1주차
운영체제 while(true){ wait(1); // 1초 멈춤 bool isActivated = checkSkillActivated(); // 체크 } 1. 위 코드는 1초 마다 플레이어가 스킬을 사용했는지 체크하는 코드입니다. 이 방식은 폴링방식입니다. 1초마다 체크하기 때문에 성능에 좋지 않습니다. 이를 해결하기 위한 방식으로 어떤 걸 이용해야 할까요? -> 인터럽트를 사용한다. 인터럽트는 자신의 상태가 바뀔 때 CPU에 통보해주는 기법이다 프로그램과 프로세스가 어떻게 다른가요?-> 프로그램 : 저장장치(HDD,SSD 등)에 저장되어 특정 작업을 수행하는 명렁어 모음 프로세스 : 메모리에 탑재되어 실행중인 프로그램 멀티프로그래밍과 멀티프로세싱이 어떻게 다른가요?->멀티프로그래밍 : 메모리 위에 여러 개의 프로그램을 실행시켜 처리하는 것 멀티프로세싱 : 두 개 이상의 프로세서(CPU)가 각 프로세스를 처리하는 것 운영체제는 프로세스를 관리하기 위해서 어떤 것을 사용하나요?-> 관리하고자 하는 프로세스의 정보를 가지고 있는 PCB를 만들고 저장한다 컨텍스트 스위칭이란 뭔가요?->실행되어야할 프로세스를 변경하기 위해 기존 프로세스의 정보를 PCB에 저장하고 실행될 프로세스의 PCB 내용대로 작업하는 것 자료구조와 알고리즘 여러분은 교실의 학생 정보를 저장하고 열람할 수 있는 관리 프로그램을 개발하려고 합니다. 이 때 여러분이라면 학생의 정보를 저장하기 위한 자료구조를 어떤 걸 선택하실 건가요? 이유를 함께 적어주세요. -> 한번 정해진 교실의 학생들은 추가/삭제가 빈번하지 않기 때문에 연결리스트가 효율적이다 교실의 학생 수는 언제든지 바뀔 수 있기 때문에 미리 많은 공간을 마련해둬야 하는 해시테이블과 크기가 고정되어 있는 배열은 비효율적이다 여러분은 고객의 주문을 받는 프로그램을 개발하려고 합니다. 주문은 들어온 순서대로 처리됩니다. 이 때 여러분이라면 어떤 자료구조를 선택하실 건가요? 이유를 함께 적어주세요.-> Queue를 사용한다 FIFO방식으로 먼저 온 손님을 먼저 계산해줄 수 있기 때문 우리가 구현한 스택은 0번 인덱스, 즉 입구쪽으로 데이터가 삽입되고 나오는 구조입니다. 반대로 마지막 인덱스, 즉 출구쪽으로 데이터가 삽입되고 나오는 구조로 코드를 변경해주세요.-> LinkedList의 insertLast , deleteLast 함수를 사용한다import { LinkedList } from "./linkedList.mjs"; class Stack { constructor() { this.list = new LinkedList(); } push(data) { this.list.insertAt(0, data); } insertLast(data) { this.list.insertLast(data); } deleteLast() { return this.list.deleteLast(); } pop() { try { return this.list.deleteAt(0); } catch (e) { return null; } } peek() { return this.list.getNodeAt(0); } isEmpty() { return this.list.count === 0; } } export { Stack }; 해시테이블의 성능은 해시 함수에 따라 달라집니다. 수업 시간에 등번호를 이용해 간단한 해시 함수를 만들어봤습니다. 이번엔 등번호가 아닌 이름을 이용해 데이터를 골고루 분산시키는 코드로 수정해주세요. 힌트: charCodeAt() 함수를 이용 예시: name1 = "이운재"; name1.charCodeAt(0); // 51060 이운재의 0번 인덱스 ‘이’의 유니코드 출력hashFunction(name){ return name.charAt(0) % 10; }
2025. 03. 09.
0
자료구조 및 알고리즘 1주차
1. 자료구조와 알고리즘1.1 자료구조(Data Structure)정의: 데이터를 어떻게 저장하고, 어떻게 활용할 것인지에 대한 구조적인 방법.예시: 배열(Array), 연결리스트(Linked List), 스택(Stack), 큐(Queue), 해시테이블(Hash Table) 등.주요 포인트: 올바른 자료구조 선택은 코드의 효율성(성능)과 가독성, 유지보수성 등에 큰 영향을 미침.1.2 알고리즘(Algorithm)정의: 어떤 문제를 해결하기 위한 단계적 절차나 방법.예시: 정렬(Sort), 탐색(Search), 재귀(Recursion) 등.주요 포인트: 문제 상황에 맞게 알고리즘을 선택하고, 시간복잡도와 공간복잡도 등을 고려해 효율성을 판단. 2. 시간복잡도(Time Complexity)2.1 빅오(Big-O), 빅오메가, 빅세타빅오(Big-O): 알고리즘의 평균/최악 시간복잡도를 간단히 표현 (대개 평균을 Big-O로 표기하는 경우가 많지만, 엄밀히는 최악의 경우를 나타냄).빅오메가(Ω): 최선의 시간 복잡도.빅세타(Θ): 평균의 시간 복잡도.개발 현장에서는 빅오 위주로 효율성을 평가하는 경우가 많음. 2.2 대표적인 시간복잡도 예시O(1): 상수 시간 → 입력 크기에 관계없이 일정한 실행 시간.O(n): 선형 시간 → 입력 크기에 비례해 실행 시간 증가.O(log n): 로그 시간 → 데이터가 커질수록 증가 폭이 완만.O(n log n): 선형로그 시간 → 정렬 알고리즘(퀵소트, 머지소트 등)에서 자주 등장.O(n^2): 이차 시간 → 2중 반복문 등이 대표적.3. 자료구조3.1 배열(Array)정의: 연속된 메모리 공간에 데이터를 순차적으로 저장하는 자료구조.장점: 인덱스를 통해 임의 접근이 가능하므로 조회(access)가 O(1).단점:중간에 삽입/삭제가 빈번하면 비효율적(O(n)).배열 크기가 고정되어 있어, 공간을 재할당해야 하는 경우가 있음. // 배열 선언 및 접근 const arr = [10, 20, 30]; console.log(arr[0]); // 10 -> O(1) // 삽입 arr.push(40); // 배열 끝에 추가 -> O(1) // 중간 삭제 arr.splice(1, 1); // 인덱스 1부터 1개 삭제 -> O(n) console.log(arr); // [10, 30, 40] 3.2 연결리스트(Linked List)정의: 데이터를 담은 노드(Node)들이 포인터를 통해 서로 연결된 형태의 자료구조.장점:삽입/삭제가 빈번한 경우 효율적 (다음 노드 포인터만 변경하면 됨).동적 메모리 할당으로, 배열처럼 크기가 고정되지 않음.단점:임의 접근이 불가능 → 특정 노드를 찾으려면 처음부터 순회(O(n)).JavaScript로 간단 구현 예시 (클래스 기반으로 표현)class Node constructor(data) { this.data = data; this.next = null; } } class LinkedList { constructor() { this.head = null; } append(data) { const newNode = new Node(data); if (!this.head) { this.head = newNode; return; } let current = this.head; while (current.next) { current = current.next; } current.next = newNode; } } // 사용 예시 const list = new LinkedList(); list.append(10); list.append(20); console.log(list.head.data); // 10 console.log(list.head.next.data); // 20 3.3 스택(Stack)정의: LIFO(Last In, First Out), 즉 “나중에 들어온 데이터가 먼저 나가는” 구조.특징:주로 ‘쌓는다’는 개념.push(삽입), pop(삭제) 연산이 대표적. const stack = []; // push stack.push(10); stack.push(20); // pop console.log(stack.pop()); // 20 -> 가장 나중에 들어온 데이터부터 꺼냄 console.log(stack.pop()); // 10 용도 예시: 함수 호출 스택, 되돌리기(undo) 기능, 웹 브라우저 뒤로가기 등. 3.4 큐(Queue)정의: FIFO(First In, First Out), 즉 “먼저 들어온 데이터가 먼저 나가는” 구조.특징:주로 “줄을 선다”는 개념.enqueue(삽입), dequeue(삭제) 연산이 대표적. const queue = []; // enqueue queue.push(10); queue.push(20); // dequeue console.log(queue.shift()); // 10 -> 먼저 들어온 데이터부터 꺼냄 console.log(queue.shift()); // 20 용도 예시: 프린터 작업 대기열, 네트워크 패킷 처리 등. 3.5 덱(Deque)정의: Double Ended Queue의 약자로, 양쪽(앞뒤) 모두에서 삽입과 삭제가 가능한 자료구조.특징:스택과 큐의 특성을 모두 가짐.자바스크립트 Array에서도 unshift, shift, push, pop 조합으로 덱 기능을 구현 가능. 3.6 해시테이블(Hash Table)정의: 해시 함수를 통해 Key → 해시값(인덱스)으로 매핑하여 데이터를 저장하는 구조.장점:탐색, 삽입, 삭제 등이 평균적으로 O(1).Key-Value 형태로 데이터에 접근하기 편리.단점:해시 충돌(Collision)이 발생하면 연결리스트 등으로 관리되어, 최악의 경우 O(n)까지 갈 수 있음.적절한 해시 함수 선택 및 해시 테이블 크기 관리가 중요.자바스크립트에선 기본 객체(Object)가 해시 개념const map = {}; map["apple"] = 1000; // 해시 함수로 문자열 "apple"을 인덱스로 변환 map["banana"] = 2000; console.log(map["apple"]); // 1000 console.log(map["banana"]); // 2000 실제 구현은 자바스크립트 엔진 내부에서 해시테이블 구조로 동작함.3.7 집합(Set)정의: 중복을 허용하지 않는 자료구조.특징:해시테이블을 내부적으로 사용(중복 여부를 빠르게 확인).JavaScript의 Set 객체 사용 예javascript복사편집const mySet = new Set(); mySet.add(10); mySet.add(20); mySet.add(10); // 중복, 실제로는 추가되지 않음 console.log(mySet); // Set { 10, 20 }
2025. 03. 09.
0
운영체제 1주차
운영체제 섹션1 1. 운영체제가 하는 일운영체제(OS, Operating System)는 하드웨어와 소프트웨어 자원을 효율적으로 관리하고 사용자에게 서비스를 제공하는 소프트웨어 계층입니다.프로세스 관리여러 프로그램(프로세스)을 동시에 실행하도록 스케줄링(우선순위 관리 등)프로세스 간 문맥 교환(Context Switching) 및 자원 할당메모리 관리프로세스에 필요한 메모리 공간을 할당/회수, 가상 메모리 등으로 메모리 활용을 극대화하드웨어 관리CPU, 입출력 장치(I/O), 저장장치 등의 자원 할당디바이스 드라이버를 통해 하드웨어와 통신 중재파일 시스템 관리디스크 등에 파일을 읽고, 쓰고, 삭제 등 수행2. 운영체제의 구조커널(Kernel)운영체제의 핵심 영역으로, 프로세스 관리, 메모리 관리, 저장장치 관리 등 주요 기능 담당인터페이스를 통한 커널 접근GUI: 아이콘, 창, 마우스 등 그래픽 요소로 소통CLI: 터미널에 명령어로 소통 (예: Bash, Powershell 등)시스템 콜(System Call)사용자 프로그램(애플리케이션)이 OS 커널 서비스를 요청하는 인터페이스예) open(), fork(), socket() 등(코드 예시)#include #include #include #include int main() { int fd = open("example.txt", O_RDONLY); // 시스템 콜을 통해 파일 오픈 if (fd 하드웨어 드라이버OS와 실제 하드웨어를 연결하는 소프트웨어새 하드웨어 인식이나 장치 제어 시 필요3. 폰 노이만 구조 (Von Neumann Architecture)탄생 배경프로그램마다 하드웨어 배선을 일일이 교체하던 비효율을 개선하고자 등장CPU와 메모리가 버스로 연결, 프로그램과 데이터를 같은 메모리에 저장구성 요소CPU: 산술 논리 연산장치(ALU), 제어 장치(CU), 레지스터메모리(RAM, ROM), 버스(데이터/주소/제어 버스)프로그램 내장 방식프로그램(기계어 코드)을 메모리에 올린 뒤 CPU가 순차적으로 명령어를 읽어 실행4. CPU의 처리 방식폴링(Polling)CPU가 주기적으로 I/O 완료 여부를 확인하는 방식(코드 예시 / 의사코드)while (1) { if (device_status == READY) { // 처리 로직 } // 다른 작업 } 비효율적일 수 있음인터럽트(Interrupt)I/O가 완료되면 하드웨어가 CPU에 신호(Interrupt Request) 전송 -> CPU가 즉시 처리 루틴으로 이동 운영체제 섹션2 : 프로세스와 쓰레드1. 프로그램과 프로세스프로그램: 저장장치에 있는 명령어 집합 (정적)프로세스: 메모리에 적재되어 실행 중인 프로그램 (동적)프로세스 구조코드 영역: 실행할 명령어데이터 영역: 전역 변수, 정적 변수스택 영역: 함수 호출 시 지역 변수, 반환 주소 등힙 영역: 동적 메모리 할당 영역코드가 프로세스가 되는 과정전처리 → 컴파일 → 링킹 → 실행 파일 생성 → 메모리에 적재 → 프로세스2. 멀티프로그래밍과 멀티프로세싱멀티프로그래밍(Multiprogramming)메모리에 여러 프로그램을 동시에 올려두고, CPU가 I/O 대기 시 다른 프로그램 실행멀티태스킹(Multitasking)CPU 시간을 쪼개서 여러 프로세스를 빠르게 교차 실행 -> 동시에 돌아가는 것처럼 보임멀티프로세싱(Multiprocessing)여러 CPU(코어)가 각각 다른 프로세스를 병렬로 처리3. PCB (Process Control Block)정의프로세스를 관리하기 위해 운영체제가 사용하는 자료구조프로세스 식별자, 상태, 레지스터 값, 메모리 정보 등 포함(ASCII 다이어그램 예시)+-----------------------------------+ | Process ID (PID) = 1234 | | State = RUNNING | | Program Counter = 0x100057 | | CPU Registers = [... ] | | Memory Info (Base, Limit ...) | | Open File List, etc. | +-----------------------------------+ PCB 관리프로세스 생성 시 PCB를 만들고, 프로세스 종료 시 PCB를 제거 4. 프로세스의 상태생성(New) → 준비(Ready) → 실행(Running) → 대기(Waiting) → 완료(Terminated) +-----+ | New | (프로세스 생성 상태) +-----+ | | (OS가 프로세스를 준비 큐로 편입) v +--------+ (준비 상태; CPU 할당 대기) | Ready | +--------+ | | (CPU 스케줄러가 프로세스에게 CPU 할당) v +--------+ (실행 상태; CPU를 점유 중) |Running | +--------+ | \ | \ (입출력 또는 이벤트가 필요) | \ | v | +--------+ (대기 상태; I/O 완료 등 이벤트를 기다림) | |Waiting | | +--------+ | | | | (I/O 혹은 이벤트 완료 시) | v | +--------+ | | Ready | (다시 CPU 할당 대기 상태로 복귀) | +--------+ | | (프로세스 실행이 종료될 때) v +-----------+ (완료 상태; 프로세스 종료) |Terminated | +-----------+5. 컨텍스트 스위칭 (Context Switching)현재 실행 중인 프로세스의 레지스터, PC 등을 PCB에 저장새롭게 실행할 프로세스의 PCB 정보를 불러와 레지스터, PC 설정빈번하면 오버헤드(시간 낭비) 증가// 단순 의사코드 save_state_of(curr_process); // 레지스터, PC -> curr_process.PCB curr_process.state = READY; curr_process = next_process_from_ready_queue(); load_state_of(curr_process); // 레지스터, PC 6. 프로세스의 생성과 종료프로세스 생성리눅스의 경우 fork() 시스템 콜로 부모 프로세스를 복제 #include #include int main() { pid_t pid = fork(); if (pid == 0) { // 자식 프로세스 printf("Child Process: PID=%d\n", getpid()); } else { // 부모 프로세스 printf("Parent Process: PID=%d, Child PID=%d\n", getpid(), pid); } return 0; } 좀비 프로세스(Zombie)자식이 종료되었지만 부모가 wait()로 회수하지 않아 PCB가 남아있는 상태 7. 쓰레드(Thread)등장 배경탭마다 프로세스를 새로 만들면 자원 낭비하나의 프로세스 내 여러 실행 흐름(쓰레드)을 사용공유 자원같은 프로세스 내부의 쓰레드들은 코드, 데이터, 힙을 공유하지만 스택은 개별 #include #include #include void* thread_func(void* arg) { printf("Thread %ld is running!\n", (long)arg); return NULL; } int main() { pthread_t threads[2]; for (long i = 0; i 프로세스 vs 쓰레드안정성: 프로세스는 서로 독립, 쓰레드는 한 프로세스 내부 공유속도/자원: 프로세스 생성/소멸 오버헤드 > 쓰레드 생성/소멸 오버헤드운영체제 섹션3 : CPU 스케줄링1. 스케줄링의 개념CPU가 여러 프로세스를 언제, 어떻게 실행할지 결정하는 정책목표: CPU 활용도 극대화, 공평성, 처리량 극대화, 대기/응답시간 최소화 2. 다중큐 (Multi-Queue)여러 개의 대기열(Queue)을 두고, 우선순위 또는 도착 순서에 따라 프로세스를 배치(이미지 예시)여러 줄(큐)에 프로세스가 나누어져 있는 그림예시 이미지 링크3. 대표적인 CPU 스케줄링 알고리즘FIFO (First In First Out)도착 순서대로 CPU 할당구현은 단순하지만, 긴 작업이 뒤의 작업들을 오래 기다리게 할 수 있음SJF (Shortest Job First)실행 시간이 짧은 작업부터 먼저 처리실제 실행 시간을 미리 알기 어려우며, 긴 작업이 계속 밀릴 위험(Starvation)RR (Round Robin)Time Slice(할당 시간)만큼 CPU를 할당, 시간 만료 시 다음 프로세스로 전환 while (ready_queue is not empty) { process = dequeue(ready_queue) run(process, TIME_SLICE) if (process not finished) { enqueue(ready_queue, process) } } MLFQ (Multi-Level Feedback Queue)여러 단계(레벨)의 큐를 두고, CPU 사용량에 따라 우선순위를 변경I/O 바운드라면 높은 우선순위 유지, CPU 바운드라면 우선순위가 점점 낮아짐