
인프런 워밍업 클럽 스터디 3기 - CS 전공지식 <1주차 발자국>
'그림으로 쉽게 배우는 운영체제'
[섹션 01]
운영체제란??
컴퓨터의 하드웨어적인 요소들과 소프트웨어적인 요소들을 효율적으로 운영하여 관리함으로써 사용자가 시스템을 이용하는데에 편리함을 제공하는 시스템 소프트웨어를 말합니다.
운영체제 종류 및 특징
유닉스/리눅스
대화식 운영체제
사용자가 명령을 입력하면 시스템이 명령을 수행
다중작업
한번에 하나 이상의 작업을 수행할 수 있음
다중 사용자
여러 사람이 동시에 각각 작업을 수행할 수 있음
이식성
90%이상 C언어로 구성, 시스템이 모듈화 되어 있어 다른 하드웨어 기종에 쉽게 이식 가능
계층적 파일 시스템 제공
계층적 트리 구조를 가짐
유닉스와 리눅스의 차이점
리눅스는 대부분 무료이지만 유닉스는 대부분 유료
리눅스는 개발자 및 일반 사용자가 사용, 유닉스는 대형 시스템 관리자가 사용
리눅스는 오픈 소스 개발, 유닉스는 사업자에 의해 배포 비용 수반
리눅스는 BASH 쉘 사용, 유닉스는 커맨드 기반
윈도우
그래픽 사용자 인터페이스(GUI) 제공
키보드없이 마우스로 아이콘이나 메뉴를 선택하여 작업 수행 가능
선점형 멀티 태스킹 방식 제공
동시에 여러개의 프로그램을 실행하며, 운영체제가 각 작업의 CPU 이용시간 제어
자동 감지 기능 (Plug & Play)
하필요한 시스템 환경을 운영체제가 자동으로 구성
OLE(Object Linking and Embedding) 사용
개체를 현재 작성 중인 문서에 자유롭게 연결 또는 삽입 가능
IOS
iOS는 사용자 경험과 인터페이스에 매우 중점을 둠
하드웨어 및 소프트웨어 최적화
강력한 보안 및 프라이버시 기능
OS는 앱스토어를 통해 다양한 애플리케이션을 제공하며, 개발자들에게 큰 수익 창출의 기회를 제공
운영체제 특징
사용자 편리성
한정된 시스템 자원을 효과적으로 사용할 수 있도록 관리 및 운영
인터페이스 기능
컴퓨터 시스템과 사용자를 연결
스케줄링
자원의 현재 상태를 파악, 자원 분배를 위한 스케줄링을 담당
자원관리
프로세스 관리
메모리 관리
하드웨어 관리
파일 시스템 관리
입출력 프로그램 관리
제어기능
입출력 장치와 사용자 프로그램을 제어
운영체제 역사
1940년도
애니악 개발
애니악 특징
운영체제가 없는 최초의 컴퓨터
응용 프로그램이 시스템 자원을 제어
스위치와 배선을 연결해서 프로그래밍
문제 발생 시 종이에 작업 후 테스트 마침 => 기간이 많이 소모되는 단점
30톤으로 수많은 진공관으로 구성되어 있기 때문에, 하드웨어 비용이 굉장히 비쌈
1950년대 초
진공관과 전선으로 만들어진 논리 회로를 아주 작은 크기로 만든 직접회로 개발
CPU와 메모리가 존재하지만 키보드와 모니토는 존재하지 않음
펀치 카드에 프로그래머가 카드의 구멍을 뚫어 프로그래밍
컴퓨터가 카드를 읽고 계산 후 결과를 라인 프린터로 출력
기존 스위치 배선 작업보다 편해짐
1950년도 중후반
이전 작업은 오퍼레이터의 오버헤드가 너무 큼
싱글 스트림 배치 시스템 개발
여러개의 프로그램을 순서대로 실행해서 결과를 한번에 확인할 수 있는 시스템
I/O Device 컨트롤러를 만들어 입출력 중에도 CPU가 계산할 수 있도록 만듦
입출력 작업이 끝나면 CPU에게 인터럽트 신호를 주고 인터럽트를 받은 CPU는 다시 처리를 하는 식으로 발전
입출력에도 CPU를 기다려야 하는 작업이 필요하다는 단점이 있음
1960년도 이후
메모리 침범 이슈 발생
다중 프로그램으로 인한 메모리 주소 이슈 발생
하드웨어적으로 베이스 레지스터라는 것을 추가해서 프로그램의 시작 주소를 저장하고 모든 프로그램은 영번지에서 실행한다고 가정
CPU의 사용률과 효율성을 중요한 문제로 인식
1970년도 이후
개인용 컴퓨터의 시대가 시작
저렴해진 컴퓨터의 개인 소유가 쉬워짐
애플의 매킨토시와 마이크로소프트의 MS-DOS가 많이 사용
매킨토시는 GUI를 도입해서 굉장한 인기
CPU 사용률과 비용 절감을 위한 노력으로 오늘날의 운영체제가 탄생
운영체제의 구조
커널
운영체제의 핵심 기능들이 모여있는 컴퓨터 프로그램
커널의 기능
프로세스 관리
기억 장치 관리
주변장치 관리
파일 관리
사용자로부터 자신을 보호하기 위한 시스템 콜이라는 인터페이스 보유
시스템 콜을 이용하면 커널에서 제공하는 write() 함수를 쓰게 되는데 하드디스크의 빈 공간에 저장
인터페이스
GUI
그래픽 사용자 인터페이스의 약자로 말 그대로 그래픽으로 된 인터페이스
윈도우나 맥OS와 같이 그래픽으로 커널과 상호작용하기 때문에 일반 사용자도 사용하기가 쉬움
CLI
명령줄 인터페이스의 약자
요즘은 리눅스는 GUI 환경을 제공하지만 많은 사용자들이 리눅스의 CLI를 선호
컴퓨터 하드웨어와 구조
현재 컴퓨터는 프로그램 내장 방식의 폰 노이만 구조
폰 노이만은 CPU와 메모리 사이를 버로 연결함
버스는 데이터를 전달하는 통로
프로그램 내장 방식은 프로그램을 메모리에 올려서 실행시키는 방식
메인보드
다른 하드웨어를 연결하는 장치
다양한 단자가 있음
ex) 출력단자, 그래픽 카드 단자, USB 단자, 사운드 단자 등등
장치간의 데이터를 전송하는건 메인보드의 버스가 담당
CPU
1. 산술논리 연산 장치
데이터 연산 담당
2. 제어 장치
모든 장치들의 동작 지시 및 제어
3. 레지스터
CPU 내에서 계산을 위해 임시적으로 보관하는 장소
RAM
랜덤으로 데이터를 읽어도 저장된 위치와 상관 없이 읽는 속도가 동일
전력이 끊기면 데이터를 잃어버리기 때문에 메인 메모리로 사용
ROM
전력이 끊겨도 데이터 영구 보관
데이터를 한번 쓰면 수정 불가
컴퓨터 부팅 과정
ROM에 저장된 BIOS 가 실행됨
바이오스 - BIOS (Basic Input Output System)
메모리와 CPU 레지스터를 초기화 시킨다.
디스크로부터 부트 로더를 불러 온다
바이오스는 롬에 저장되어 있기 때문에 램에 저장된 정보와는 달리, 컴퓨터를 끄더라도 그 내용이 지워지지 않음
주요 하드웨어에 이상이 없는지 확인 후 부팅
이상이 있을 경우 경고음을 내면서 부팅이되지 않음
운영체제가 2개 설치되어 있을 경우, 운영체제를 선택하는 화면이 활성화됨
부팅 후 실행되는 모든 프로그램은 램에 올라와서 운영체제에 의해 관리
폴딩
주기적으로 CPU가 입출력 관리자에게 주기적이고 지속적으로 입출력이 왔는지 확인
폴링 방식의 단점은 주기적으로 CPU가 확인해줘야 하니 성능이 좋지 않다는 단점이 존재
인터럽트
폴링 방식의 단점을 해결한 방식
입출력이 완료되었을 때 CPU에게 신호를 주어 인터럽트 서비스 루틴을 실행하는 방식
서비스루틴 이란?
특정 인터럽트가 들어오면 그 인터럽트를 처리하는 함수
[섹션 02]
프로그램이란?
하드디스크 등과 같은 저장장치에 저장된 명령문의 집합체
프로세스란?
하드디스크에 저장된 프로그램이 메모리에 올라갔을 때 실행중인 프로그램
메모리도 사용하고 운영체제의 CPU 스케줄링 알고리즘에 따라서 CPU도 사용하고 필요에 따라 입력과 출력을 하기 때문에 능동적인 존재
코드 영역, 데이터 영역, 스택 영역, 킵 영역을 갖고 있음
코드 영역
자신을 실행하는 코드가 저장되어 있고 데이터 영역은 전역 변수와 스태틱 변수가 저장
스택 영역
지역 변수와 함수 호출을 했을 때 필요한 정보들이 저장
힙 영역
프로그래머가 동적으로 메모리를 할당
ex) C언어에서 malloc, free 함수를 호출하면 힙 영역에 자원을 할당, 해제
컴파일 과정
고급 언어(예: C, C++)로 작성된 소스 코드를 기계어(바이너리 코드)로 변환하는 과정
4단계(전처리 → 컴파일 → 어셈블 → 링크)로 진행
1. 전처리 (Preprocessing)
소스 코드에서 전처리 지시문(#으로 시작하는 문장)을 처리하는 단계
생성 파일 : .i 로 끝남
2. 컴파일 (Compilation)
전처리된 코드를 어셈블리 코드(assembly language)로 변환하는 단계
생성 파일 : .s 로 끝남
주요 작업
문법 검사 및 오류 확인
최적화 수행
어셈블리 코드 생성
3.어셈블 (Assembling)
어셈블리 코드를 기계어(오브젝트 파일, .o 또는 .obj)로 변환하는 단계입니다.
생성 파일 : .o 로 끝남
주요 작업
명령어를 바이너리 코드로 변환
오브젝트 파일 생성
4. 링크 (Linking)
여러 개의 오브젝트 파일과 라이브러리를 결합하여 실행 파일(Executable, .exe 또는 a.out)을 생성하는 단계
생성 파일 : .exe, .out 로 끝남
멀티 프로그래밍과 멀티 프로세싱
멀티 프로그래밍
하나의 CPU가 여러 프로세스를 번갈아 실행
특징
CPU 이용률 극대화
비선점형 스케줄링에 많이 사용
병렬 처리가 아닌 시분할 방식
멀티 프로세싱
여러 개의 CPU가 동시에 여러 프로세스를 처리
특징
병렬 처리 가능
선점형 스케줄링 적용
다중 코어 시스템에서 사용
PCB (Process Control Block)
운영체제가 각 프로세스를 관리하기 위해 유지하는 데이터 구조체
프로세스의 상태, 메모리 정보, CPU 레지스터 정보 등을 저장
구성 요소
PID (Process ID)
프로세스 상태
프로그램 카운터
CPU 레지스터
메모리 관리 정보
입출력 상태 정보
우선순위
프로그램 카운터가 필요한 이유는 어떤 프로세스가 실행되다가 다른 프로세스에게 CPU를 뺏기고 다시 실행될 때 원래 실행하던 명령어가 실행되어야 하기 때문에 프로그램 카운터가 꼭 있어야 함
프로세스 상태
1. 생성
새로운 프로세스 생성
2. 준비
CPU 할당을 기다리는 상태
3.실행
CPU가 프로세스를 실행 중
4.대기
/O 작업 등으로 대기 중
5. 완료
실행이 끝난 상태
컨텍스트 스위칭 (Context Switching)
CPU가 현재 실행 중인 프로세스를 중단하고, 다른 프로세스로 전환하는 작업
PCB에 현재 프로세스의 상태 정보를 저장하고, 다음 프로세스의 상태 정보를 불러옴
절차
1. 현재 프로세스의 상태 저장 (PCB)
2. 새로운 프로세스의 PCB 불러오기
3. CPU 레지스터 및 메모리 정보 복원
4. 새로운 프로세스 실행
프로세스 생성과 종료
프로세스 생성 과정
1. 부모 프로세스가 fork() 또는 CreateProcess() 호출
2. 운영체제가 PCB 생성
3. 메모리 할당 및 초기화
4. Ready 상태로 전환
프로세스 종료 과정
1. 프로세스 작업 완료 또는 오류 발생
2. 운영체제가 자원 회수
3. PCB 제거
4. Terminated 상태로 전환
쓰레드 (Thread)
프로세스 내에서 실행 흐름 단위
독립적인 스택과 레지스터를 가짐
같은 프로세스의 쓰레드는 코드, 데이터, 힙 영역 공유
쓰레드 종류
커널 레벨 쓰레드: 운영체제에서 관리
유저 레벨 쓰레드: 사용자 공간에서 관리
[섹션 03]
CPU 스케줄링 이란?
CPU 스케줄링은 운영체제가 CPU를 여러 프로세스에게 효율적으로 할당하는 기법
이는 시스템 성능을 최적화하고 공정성을 보장하며, 응답 시간과 처리율을 개선하는 역할
2. 스케줄링 목표
CPU 이용률
CPU를 최대한 활용하도록 함
처리량
단위 시간당 처리할 수 있는 프로세스 수를 최대화
대기 시간 최소화
프로세스가 CPU를 기다리는 시간을 줄임
응답 시간 최소화
사용자의 요청에 빠르게 반응
공정성
모든 프로세스가 공정하게 CPU를 할당받도록 보장
다중 큐 (Multi-Queue)
다중 큐 스케줄링은 프로세스를 여러 개의 큐로 분류하고, 각 큐에 다른 스케줄링 정책을 적용하는 방식
ex) 시스템 프로세스는 높은 우선순위를 가진 큐에서 실행되고, 사용자 프로세스는 낮은 우선순위 큐에서 실행
FIFO (First In First Out)
먼저 들어온 프로세스가 먼저 실행됨 (큐의 구조와 유사)
장점: 구현이 간단함
단점: 짧은 작업보다 긴 작업이 먼저 실행되면 Convoy Effect 발생 (짧은 작업이 뒤에서 오래 대기하는 문제)
SJF (Shortest Job First)
실행 시간이 가장 짧은 프로세스를 먼저 실행
장점: 평균 대기 시간을 최소화
단점: 실행 시간이 긴 프로세스가 계속 뒤로 밀려 기아 현상(Starvation) 발생 가능
타임 슬라이스 (Time Slice)란?
타임 슬라이스는 CPU가 각 프로세스에 할당하는 최대 실행 시간을 의미
타임 퀀텀(Time Quantum) 이라고도 부르며, 보통 밀리초(ms) 단위로 설정
한 프로세스가 타임 슬라이스만큼 실행된 후, 문맥 전환(Context Switch) 을 통해 다른 프로세스로 넘어감
타임 슬라이스의 특징
짧은 타임 슬라이스
빠른 응답성을 제공하지만 문맥 전환 비용이 증가
긴 타임 슬라이스
문맥 전환 비용이 줄지만 응답 시간이 길어짐
적절한 타임 슬라이스 값을 설정하는 것이 중요
일반적으로 타임 슬라이스는 문맥 전환 비용보다 충분히 커야 효율적
타임 슬라이스가 적용되는 스케줄링 알고리즘
1. RR (Round Robin)
2. MLFQ (Multi-Level Feedback Queue)
RR (Round Robin)
일정한 시간동안 프로세스를 실행한 후, 다음 프로세스로 전환
장점: 공정성이 보장됨 (모든 프로세스가 CPU를 일정 시간 동안 사용할 기회 제공)
단점: 시간 할당량이 너무 크면 FIFO와 유사해지고, 너무 작으면 문맥 전환(Context Switch) 비용 증가
MLFQ (Multi-Level Feedback Queue)
여러 개의 큐를 사용하여, 우선순위를 동적으로 조정하는 방식
처음에는 높은 우선순위 큐에서 실행
CPU를 많이 사용하면 낮은 우선순위 큐로 이동
I/O 중심 프로세스는 높은 우선순위를 유지
장점: CPU 및 I/O 중심 프로세스를 균형 있게 처리할 수 있음
단점: 정책을 잘못 설계하면 특정 프로세스가 기아 상태에 빠질 가능성이 있음
'그림으로 쉽게 배우는 자료구조와 알고리즘(기본편)'
자료구조와 알고리즘이란?
자료구조(Data Structure): 데이터를 효율적으로 저장하고 관리하는 방법
알고리즘(Algorithm): 문제를 해결하는 절차나 방법
시간 복잡도(Time Complexity)란?
알고리즘이 실행되는 연산 횟수를 입력 크기(n)에 따라 분석한 것
주로 빅오 표기법(O-notation)으로 표현
배열(Array) 개념
같은 자료형의 연속된 메모리 공간을 차지하는 데이터 구조
빠른 접근(O(1)) 가능, 하지만 삽입/삭제(O(N))이 느림
배열 구현
#include <iostream>
using namespace std;
int main() {
int arr[5] = {1, 2, 3, 4, 5};
cout << "배열 요소 출력: ";
for(int i = 0; i < 5; i++) {
cout << arr[i] << " ";
}
return 0;
}
연결 리스트(Linked List) 개념
노드(Node)들이 포인터로 연결된 구조
삽입/삭제가 빠름(O(1)), 접근 속도가 느림(O(N))
단일 연결 리스트(Singly Linked List), 이중 연결 리스트(Doubly Linked List) 등이 있음
연결리스트 구현
#include <iostream>
using namespace std;
struct Node {
int data;
Node* next;
Node(int val) : data(val), next(nullptr) {}
};
class LinkedList {
public:
Node* head;
LinkedList() : head(nullptr) {}
void insert(int data) {
Node* newNode = new Node(data);
newNode->next = head;
head = newNode;
}
void remove(int data) {
Node* temp = head;
Node* prev = nullptr;
while (temp && temp->data != data) {
prev = temp;
temp = temp->next;
}
if (!temp) return; // 삭제할 노드 없음
if (prev) prev->next = temp->next;
else head = temp->next; // 첫 번째 노드 삭제 시
delete temp;
}
void display() {
Node* temp = head;
while (temp) {
cout << temp->data << " -> ";
temp = temp->next;
}
cout << "NULL\n";
}
};
int main() {
LinkedList list;
list.insert(10);
list.insert(20);
list.insert(30);
list.display();
list.remove(20);
list.display();
return 0;
}
스택(Stack) 개념
LIFO(Last In First Out) 구조
push(삽입), pop(제거), top(최상단 요소 확인)
스택 구현 (C++)
#include <iostream>
#include <stack>
using namespace std;
int main() {
stack<int> s;
s.push(10);
s.push(20);
s.push(30);
cout << "Top: " << s.top() << endl;
s.pop();
cout << "Top after pop: " << s.top() << endl;
return 0;
}
큐(Queue) 개념
FIFO(First In First Out) 구조
push(삽입), pop(제거), front(첫 요소), back(마지막 요소)
큐 구현 (C++)
#include <iostream>
using namespace std;
class Queue {
private:
struct Node {
int data;
Node* next;
Node(int val) : data(val), next(nullptr) {}
};
Node *frontNode, *rearNode;
public:
Queue() : frontNode(nullptr), rearNode(nullptr) {}
void enqueue(int data) {
Node* newNode = new Node(data);
if (!rearNode) {
frontNode = rearNode = newNode;
return;
}
rearNode->next = newNode;
rearNode = newNode;
}
void dequeue() {
if (!frontNode) return;
Node* temp = frontNode;
frontNode = frontNode->next;
if (!frontNode) rearNode = nullptr;
delete temp;
}
int front() {
return (frontNode) ? frontNode->data : -1;
}
bool isEmpty() {
return frontNode == nullptr;
}
};
int main() {
Queue q;
q.enqueue(10);
q.enqueue(20);
q.enqueue(30);
cout << "Front: " << q.front() << endl;
q.dequeue();
cout << "Front after dequeue: " << q.front() << endl;
return 0;
}
덱(Deque) 개념
양방향 삽입/삭제 가능한 자료구조
push_front(), push_back(), pop_front(), pop_back() 제공
덱 구현 (C++)
#include <iostream>
using namespace std;
class Deque {
private:
struct Node {
int data;
Node* next;
Node* prev;
Node(int val) : data(val), next(nullptr), prev(nullptr) {}
};
Node *frontNode, *rearNode;
public:
Deque() : frontNode(nullptr), rearNode(nullptr) {}
void push_front(int data) {
Node* newNode = new Node(data);
if (!frontNode) {
frontNode = rearNode = newNode;
} else {
newNode->next = frontNode;
frontNode->prev = newNode;
frontNode = newNode;
}
}
void push_back(int data) {
Node* newNode = new Node(data);
if (!rearNode) {
frontNode = rearNode = newNode;
} else {
newNode->prev = rearNode;
rearNode->next = newNode;
rearNode = newNode;
}
}
void pop_front() {
if (!frontNode) return;
Node* temp = frontNode;
frontNode = frontNode->next;
if (frontNode) frontNode->prev = nullptr;
else rearNode = nullptr;
delete temp;
}
void pop_back() {
if (!rearNode) return;
Node* temp = rearNode;
rearNode = rearNode->prev;
if (rearNode) rearNode->next = nullptr;
else frontNode = nullptr;
delete temp;
}
int front() {
return (frontNode) ? frontNode->data : -1;
}
int back() {
return (rearNode) ? rearNode->data : -1;
}
};
int main() {
Deque d;
d.push_back(10);
d.push_front(20);
cout << "Front: " << d.front() << ", Back: " << d.back() << endl;
d.pop_front();
cout << "Front after pop: " << d.front() << endl;
return 0;
}
해시 테이블(Hash Table) 개념
키-값(Key-Value) 쌍으로 데이터를 저장하는 자료구조
탐색 속도 O(1), 충돌 해결 방법 필요 (체이닝, 개방 주소법 등)
해시 테이블 구현 (C++)
#include <iostream>
#include <vector>
using namespace std;
class HashTable {
private:
static const int SIZE = 10;
vector<pair<int, int>> table[SIZE];
int hashFunction(int key) {
return key % SIZE;
}
public:
void insert(int key, int value) {
int hashIndex = hashFunction(key);
table[hashIndex].push_back({key, value});
}
int get(int key) {
int hashIndex = hashFunction(key);
for (auto &p : table[hashIndex]) {
if (p.first == key) return p.second;
}
return -1;
}
};
int main() {
HashTable ht;
ht.insert(1, 100);
ht.insert(11, 200);
cout << "Key 1: " << ht.get(1) << endl;
cout << "Key 11: " << ht.get(11) << endl;
return 0;
}
셋(Set) 개념
중복을 허용하지 않는 집합 자료구조
삽입/삭제 O(log N) (Balanced BST 사용)
셋 구현 (C++)
#include <iostream>
using namespace std;
class Set {
private:
struct Node {
int data;
Node* next;
Node(int val) : data(val), next(nullptr) {}
};
Node* head;
public:
Set() : head(nullptr) {}
void insert(int data) {
if (contains(data)) return;
Node* newNode = new Node(data);
newNode->next = head;
head = newNode;
}
bool contains(int data) {
Node* temp = head;
while (temp) {
if (temp->data == data) return true;
temp = temp->next;
}
return false;
}
};
int main() {
Set s;
s.insert(10);
s.insert(20);
s.insert(10);
return 0;
}
댓글을 작성해보세요.