운영체제 개요

운영체제 개요

개요

운영체제(Operating System, OS)는 컴퓨터 하드웨어를 관리하고 응용 프로그램에 서비스를 제공하는 시스템 소프트웨어입니다. 이 레슨에서는 운영체제의 정의, 역할, 발전 역사, 그리고 커널 구조에 대해 학습합니다.


목차

  1. 운영체제란?
  2. 운영체제의 역할
  3. 운영체제 발전 역사
  4. 커널 구조
  5. 시스템 콜
  6. 인터럽트 처리
  7. 연습 문제

1. 운영체제란?

정의

운영체제 = 하드웨어와 사용자 사이의 중재자

┌─────────────────────────────────────────┐
│            사용자/응용 프로그램           │
├─────────────────────────────────────────┤
│              운영체제 (OS)               │
│  - 자원 관리자 (Resource Manager)        │
│  - 제어 프로그램 (Control Program)       │
├─────────────────────────────────────────┤
│              하드웨어                    │
│    CPU, 메모리, 디스크, I/O 장치         │
└─────────────────────────────────────────┘

운영체제의 두 가지 관점

┌────────────────────────────────────────────────────┐
│                   관점 1: 자원 관리자               │
├────────────────────────────────────────────────────┤
│  - CPU 시간 분배                                   │
│  - 메모리 공간 할당                                │
│  - 디스크/I/O 장치 관리                            │
│  - 여러 프로그램 간 자원 충돌 해결                  │
└────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────┐
│                   관점 2: 서비스 제공자             │
├────────────────────────────────────────────────────┤
│  - 파일 시스템 접근                                │
│  - 프로세스 실행 환경                              │
│  - 사용자 인터페이스 (CLI/GUI)                     │
│  - 네트워크 통신                                   │
└────────────────────────────────────────────────────┘

2. 운영체제의 역할

핵심 기능

┌───────────────────────────────────────────────────────┐
│                    운영체제 핵심 기능                   │
├───────────────┬───────────────────────────────────────┤
│  프로세스 관리  │ 프로세스 생성/종료/스케줄링            │
├───────────────┼───────────────────────────────────────┤
│  메모리 관리   │ 메모리 할당/회수, 가상 메모리           │
├───────────────┼───────────────────────────────────────┤
│  파일 시스템   │ 파일 생성/삭제/읽기/쓰기               │
├───────────────┼───────────────────────────────────────┤
│  I/O 관리     │ 장치 드라이버, 버퍼링, 스풀링           │
├───────────────┼───────────────────────────────────────┤
│  보안/보호    │ 접근 제어, 사용자 인증                  │
├───────────────┼───────────────────────────────────────┤
│  네트워킹     │ 프로토콜 스택, 소켓 인터페이스          │
└───────────────┴───────────────────────────────────────┘

자원 관리 예시

// 프로세스가 파일을 읽는 과정
// (OS가 중재하는 방식)

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    // 1. 파일 열기 (OS에 요청)
    int fd = open("data.txt", O_RDONLY);

    // 2. 파일 읽기 (OS가 디스크 I/O 처리)
    char buffer[1024];
    ssize_t bytes = read(fd, buffer, sizeof(buffer));

    // 3. 파일 닫기 (OS가 자원 해제)
    close(fd);

    return 0;
}

/*
사용자 프로그램         운영체제            하드웨어
     │                    │                  │
     │── open() ──────────▶                  │
     │                    │── 디스크 접근 ────▶
     │                    │◀── 데이터 ────────│
     │◀── fd 반환 ─────────│                  │
*/

3. 운영체제 발전 역사

세대별 발전

┌─────────────────────────────────────────────────────────────┐
│                     운영체제 발전 역사                        │
├───────────┬─────────────────────────────────────────────────┤
│   시대     │                    특징                         │
├───────────┼─────────────────────────────────────────────────┤
│ 1세대     │ 운영체제 없음, 수동 조작                         │
│ 1940-50s  │ 프로그래머가 직접 기계어 입력                     │
├───────────┼─────────────────────────────────────────────────┤
│ 2세대     │ 배치 시스템 (Batch System)                       │
│ 1950-60s  │ 작업 자동화, 상주 모니터                         │
├───────────┼─────────────────────────────────────────────────┤
│ 3세대     │ 멀티프로그래밍, 시분할                           │
│ 1960-70s  │ 여러 프로그램 동시 실행, 대화형 사용             │
├───────────┼─────────────────────────────────────────────────┤
│ 4세대     │ 분산 시스템, 네트워크 OS                         │
│ 1970-현재  │ GUI, 멀티프로세서, 클라우드                      │
└───────────┴─────────────────────────────────────────────────┘

배치 시스템 (Batch System)

┌───────────────────────────────────────────────────────┐
│              배치 시스템 동작 방식                       │
├───────────────────────────────────────────────────────┤
│                                                       │
│   작업1 ──▶ 작업2 ──▶ 작업3 ──▶ 작업4 ──▶ ...         │
│                                                       │
│   • 한 번에 하나의 작업만 실행                          │
│   • CPU 유휴 시간 발생 (I/O 대기 중)                    │
│   • 자동 작업 전환으로 효율성 향상                       │
│                                                       │
└───────────────────────────────────────────────────────┘

시간축:
┌──────┬──────────────────┬──────┬──────────────────┐
│작업1  │   I/O 대기 (낭비)  │작업2  │   I/O 대기       │
└──────┴──────────────────┴──────┴──────────────────┘
   CPU        CPU 유휴         CPU        CPU 유휴

멀티프로그래밍 (Multiprogramming)

┌───────────────────────────────────────────────────────┐
│             멀티프로그래밍 동작 방식                     │
├───────────────────────────────────────────────────────┤
│                                                       │
│   메모리에 여러 프로그램 동시 적재                       │
│                                                       │
│   ┌─────────┐                                         │
│   │  OS     │                                         │
│   ├─────────┤                                         │
│   │ 프로그램1│ ◀── CPU가 실행 중                        │
│   ├─────────┤                                         │
│   │ 프로그램2│ ◀── I/O 대기 중                          │
│   ├─────────┤                                         │
│   │ 프로그램3│ ◀── 준비 상태                            │
│   └─────────┘                                         │
│                                                       │
└───────────────────────────────────────────────────────┘

시간축:
┌──────┬──────┬──────┬──────┬──────┬──────┐
│작업1  │작업2  │작업3  │작업1  │작업3  │작업2  │
└──────┴──────┴──────┴──────┴──────┴──────┘
   CPU가 쉬지 않고 계속 다른 작업 수행

시분할 시스템 (Time-Sharing)

┌───────────────────────────────────────────────────────┐
│              시분할 시스템 동작 방식                     │
├───────────────────────────────────────────────────────┤
│                                                       │
│   여러 사용자가 동시에 컴퓨터 사용                       │
│   각 사용자에게 짧은 시간(Time Slice) 할당              │
│                                                       │
│   사용자A ──┬── 10ms ──┬── 10ms ──┬── ...             │
│   사용자B ──┼── 10ms ──┼── 10ms ──┼── ...             │
│   사용자C ──┴── 10ms ──┴── 10ms ──┴── ...             │
│                                                       │
│   → 각 사용자는 컴퓨터를 독점하는 것처럼 느낌           │
│   → 응답 시간 중요 (대화형 시스템)                      │
│                                                       │
└───────────────────────────────────────────────────────┘

4. 커널 구조

커널이란?

커널(Kernel) = 운영체제의 핵심 부분
            = 하드웨어와 직접 상호작용
            = 항상 메모리에 상주

┌─────────────────────────────────────────┐
│           사용자 공간 (User Space)        │
│  ┌───────┐ ┌───────┐ ┌───────┐          │
│  │ App1  │ │ App2  │ │ App3  │          │
│  └───────┘ └───────┘ └───────┘          │
├─────────────────────────────────────────┤
│           커널 공간 (Kernel Space)        │
│  ┌───────────────────────────────────┐  │
│  │           커널 (Kernel)            │  │
│  │  • 프로세스 관리                   │  │
│  │  • 메모리 관리                     │  │
│  │  • 파일 시스템                     │  │
│  │  • 장치 드라이버                   │  │
│  └───────────────────────────────────┘  │
├─────────────────────────────────────────┤
│              하드웨어                    │
└─────────────────────────────────────────┘

모놀리식 커널 (Monolithic Kernel)

┌───────────────────────────────────────────┐
│              모놀리식 커널                  │
├───────────────────────────────────────────┤
│                                           │
│  ┌─────────────────────────────────────┐  │
│  │             커널                     │  │
│  │  ┌──────┬──────┬──────┬──────────┐  │  │
│  │  │프로세스│메모리 │파일   │ 네트워크  │  │  │
│  │  │ 관리  │ 관리  │시스템 │          │  │  │
│  │  ├──────┴──────┴──────┴──────────┤  │  │
│  │  │      장치 드라이버              │  │  │
│  │  └────────────────────────────────┘  │  │
│  └─────────────────────────────────────┘  │
│                                           │
│  장점: 성능 우수 (직접 호출)               │
│  단점: 유지보수 어려움, 버그 영향 큼        │
│  예시: Linux, Unix, MS-DOS                │
│                                           │
└───────────────────────────────────────────┘

마이크로커널 (Microkernel)

┌───────────────────────────────────────────┐
│              마이크로커널                   │
├───────────────────────────────────────────┤
│                                           │
│  사용자 공간:                              │
│  ┌──────┐ ┌──────┐ ┌──────┐ ┌──────────┐ │
│  │파일   │ │장치   │ │네트워크│ │프로세스   │ │
│  │서버   │ │드라이버│ │서버   │ │서버      │ │
│  └──────┘ └──────┘ └──────┘ └──────────┘ │
│            ▲          ▲          ▲        │
│            │    IPC   │          │        │
│            ▼          ▼          ▼        │
│  ┌─────────────────────────────────────┐  │
│  │      마이크로커널 (최소 기능)         │  │
│  │   - 기본 IPC                        │  │
│  │   - 기본 스케줄링                    │  │
│  │   - 기본 메모리 관리                 │  │
│  └─────────────────────────────────────┘  │
│                                           │
│  장점: 안정성, 유지보수 용이               │
│  단점: IPC 오버헤드로 성능 저하            │
│  예시: Minix, QNX, L4                     │
│                                           │
└───────────────────────────────────────────┘

하이브리드 커널 (Hybrid Kernel)

┌───────────────────────────────────────────┐
│              하이브리드 커널                │
├───────────────────────────────────────────┤
│                                           │
│  마이크로커널 + 모놀리식의 장점 결합        │
│                                           │
│  사용자 공간:                              │
│  ┌──────┐ ┌──────┐                        │
│  │ App  │ │서브시스템│                      │
│  └──────┘ └──────┘                        │
│                                           │
│  ┌─────────────────────────────────────┐  │
│  │           하이브리드 커널             │  │
│  │  ┌──────────────────────────────┐   │  │
│  │  │ 파일시스템 │ 네트워크 │ 그래픽  │   │  │
│  │  └──────────────────────────────┘   │  │
│  │  ┌──────────────────────────────┐   │  │
│  │  │       마이크로커널 코어        │   │  │
│  │  └──────────────────────────────┘   │  │
│  └─────────────────────────────────────┘  │
│                                           │
│  예시: Windows NT, macOS (XNU)            │
│                                           │
└───────────────────────────────────────────┘

커널 구조 비교

┌─────────────┬──────────┬──────────┬──────────┐
│    특성      │ 모놀리식  │마이크로커널│ 하이브리드 │
├─────────────┼──────────┼──────────┼──────────┤
│ 성능        │ 높음     │ 낮음     │ 중간     │
│ 안정성      │ 낮음     │ 높음     │ 중간     │
│ 유지보수    │ 어려움   │ 쉬움     │ 중간     │
│ 모듈성      │ 낮음     │ 높음     │ 높음     │
│ 코드 크기   │ 큼       │ 작음     │ 중간     │
├─────────────┼──────────┼──────────┼──────────┤
│ 예시        │ Linux    │ Minix    │ Windows  │
│             │ FreeBSD  │ QNX      │ macOS    │
└─────────────┴──────────┴──────────┴──────────┘

5. 시스템 콜

시스템 콜이란?

시스템 콜(System Call) = 사용자 프로그램이 OS 서비스를 요청하는 인터페이스

┌───────────────────────────────────────────┐
│              사용자 모드                   │
│                                           │
│    ┌─────────────────────────────────┐    │
│    │      사용자 프로그램              │    │
│    │                                 │    │
│    │   read(fd, buffer, size);       │    │
│    │          │                      │    │
│    │          ▼                      │    │
│    │   ┌──────────────────┐          │    │
│    │   │ 시스템 콜 인터페이스│          │    │
│    │   │ (라이브러리 함수)  │          │    │
│    │   └──────────────────┘          │    │
│    └─────────────│───────────────────┘    │
├──────────────────│────────────────────────┤
│              커널 모드 (트랩)              │
│                  ▼                        │
│    ┌─────────────────────────────────┐    │
│    │      시스템 콜 핸들러             │    │
│    │                                 │    │
│    │   → 실제 파일 읽기 수행           │    │
│    │   → 결과를 사용자에게 반환        │    │
│    └─────────────────────────────────┘    │
└───────────────────────────────────────────┘

시스템 콜 처리 과정

단계:
1. 사용자 프로그램이 시스템  호출
2. 라이브러리가 레지스터에 파라미터 설정
3. 소프트웨어 인터럽트(트랩) 발생
4. 사용자 모드  커널 모드 전환
5. 시스템  번호로 핸들러 찾기
6. 핸들러 실행
7. 커널 모드  사용자 모드 복귀
8. 결과 반환

┌────────────────────────────────────────────────┐
                시스템  테이블                  
├────────┬───────────────────────────────────────┤
 번호                  시스템                  
├────────┼───────────────────────────────────────┤
   0     read()                                
   1     write()                               
   2     open()                                
   3     close()                               
   ...   ...                                   
  39     fork()                                
  60     exit()                                
└────────┴───────────────────────────────────────┘

시스템 콜 분류

// 1. 프로세스 제어
fork();     // 프로세스 생성
exec();     // 프로그램 실행
exit();     // 프로세스 종료
wait();     // 자식 프로세스 대기

// 2. 파일 관리
open();     // 파일 열기
read();     // 파일 읽기
write();    // 파일 쓰기
close();    // 파일 닫기

// 3. 장치 관리
ioctl();    // 장치 제어
read();     // 장치에서 읽기
write();    // 장치에 쓰기

// 4. 정보 유지
getpid();   // 프로세스 ID 얻기
time();     // 시간 얻기

// 5. 통신
socket();   // 소켓 생성
send();     // 데이터 전송
recv();     // 데이터 수신

시스템 콜 예제

// Linux에서 파일 쓰기 예제
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int main() {
    const char *msg = "Hello, OS!\n";

    // 시스템 콜 1: open()
    int fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd < 0) {
        // 시스템 콜: write() to stderr
        write(2, "Error opening file\n", 19);
        return 1;
    }

    // 시스템 콜 2: write()
    ssize_t bytes_written = write(fd, msg, strlen(msg));

    // 시스템 콜 3: close()
    close(fd);

    return 0;
}

/*
strace로 시스템 콜 추적:
$ strace ./a.out

openat(AT_FDCWD, "output.txt", O_WRONLY|O_CREAT|O_TRUNC, 0644) = 3
write(3, "Hello, OS!\n", 11) = 11
close(3) = 0
*/

6. 인터럽트 처리

인터럽트란?

인터럽트(Interrupt) = CPU에게 주의가 필요한 이벤트를 알리는 신호

┌────────────────────────────────────────────────┐
│                 인터럽트 종류                    │
├──────────────────┬─────────────────────────────┤
│   하드웨어 인터럽트  │   소프트웨어 인터럽트         │
├──────────────────┼─────────────────────────────┤
│ • 타이머          │ • 시스템 콜 (트랩)           │
│ • 키보드 입력     │ • 예외 (Exception)          │
│ • 마우스 이동     │   - 0으로 나누기             │
│ • 디스크 I/O 완료  │   - 잘못된 메모리 접근        │
│ • 네트워크 패킷   │   - 페이지 폴트              │
└──────────────────┴─────────────────────────────┘

인터럽트 처리 과정

┌───────────────────────────────────────────────────────┐
│                  인터럽트 처리 과정                     │
└───────────────────────────────────────────────────────┘

1. CPU가 명령어 실행 중
   │
   ▼
2. 인터럽트 발생 (예: 키보드 입력)
   │
   ▼
3. 현재 상태 저장 (PC, 레지스터)
   │
   ▼
4. 인터럽트 벡터 테이블에서 핸들러 주소 찾기
   │
   ▼
5. 인터럽트 핸들러(ISR) 실행
   │
   ▼
6. 저장된 상태 복구
   │
   ▼
7. 중단된 프로그램 재개

┌─────────────────────────────────────────────┐
│            인터럽트 벡터 테이블               │
├───────┬─────────────────────────────────────┤
│ 번호   │           핸들러 주소               │
├───────┼─────────────────────────────────────┤
│   0   │ 0x00001000 (나누기 오류)             │
│   1   │ 0x00001100 (디버그)                  │
│   2   │ 0x00001200 (NMI)                    │
│  ...  │ ...                                 │
│  32   │ 0x00002000 (타이머)                  │
│  33   │ 0x00002100 (키보드)                  │
└───────┴─────────────────────────────────────┘

인터럽트 타임라인

시간 →

프로세스 실행: ████████░░░░░░░░████████████████████
                      ↑     ↑
                   인터럽트  ISR
                    발생   완료

상세:
┌────────┬────────┬────────────────┬────────────────┐
│ 명령어  │ 상태   │  인터럽트       │    명령어       │
│ 실행   │ 저장   │  핸들러 실행    │    재개        │
└────────┴────────┴────────────────┴────────────────┘

인터럽트 우선순위

높음   ┌─────────────────────────┐
   ↑   │ 전원 이상 (Power Failure) │
   │   ├─────────────────────────┤
   │   │ 머신 체크 (Machine Check) │
   │   ├─────────────────────────┤
   │   │ 외부 인터럽트 (NMI)       │
   │   ├─────────────────────────┤
   │   │ 타이머 인터럽트           │
   │   ├─────────────────────────┤
   │   │ I/O 인터럽트              │
   │   ├─────────────────────────┤
   │   │ 소프트웨어 인터럽트        │
   ↓   └─────────────────────────┘
낮음

인터럽트 핸들러 예제 (개념)

// 인터럽트 핸들러 (Interrupt Service Routine)
// 실제로는 어셈블리와 함께 작성됨

// 타이머 인터럽트 핸들러 (개념적 코드)
void timer_interrupt_handler(void) {
    // 1. 현재 프로세스의 타임 슬라이스 감소
    current_process->time_slice--;

    // 2. 시스템 시간 업데이트
    system_time++;

    // 3. 타임 슬라이스가 0이면 스케줄링
    if (current_process->time_slice == 0) {
        schedule();  // 다음 프로세스 선택
    }

    // 4. 인터럽트 처리 완료 신호
    send_EOI();  // End Of Interrupt
}

// 키보드 인터럽트 핸들러 (개념적 코드)
void keyboard_interrupt_handler(void) {
    // 1. 키보드 컨트롤러에서 스캔 코드 읽기
    uint8_t scancode = inb(KEYBOARD_DATA_PORT);

    // 2. 스캔 코드를 ASCII로 변환
    char key = scancode_to_ascii(scancode);

    // 3. 키보드 버퍼에 저장
    keyboard_buffer_put(key);

    // 4. 대기 중인 프로세스 깨우기
    wakeup_keyboard_waiters();

    // 5. EOI 전송
    send_EOI();
}

7. 연습 문제

문제 1: 기초 개념

다음 빈칸을 채우세요.

  1. 운영체제의 두 가지 관점은 __와 서비스 제공자이다.
  2. 멀티프로그래밍의 목적은 __를 줄여 CPU 이용률을 높이는 것이다.
  3. 커널 중 모든 기능이 하나로 통합된 구조를 __이라 한다.
정답 보기 1. 자원 관리자 2. CPU 유휴 시간 3. 모놀리식 커널 (Monolithic Kernel)

문제 2: 시스템 콜 분류

다음 시스템 콜을 적절한 범주로 분류하세요.

fork(), open(), socket(), getpid(), ioctl()
프로세스 제어 파일 관리 장치 관리 정보 유지 통신
정답 보기 | 프로세스 제어 | 파일 관리 | 장치 관리 | 정보 유지 | 통신 | |-------------|----------|----------|----------|------| | fork() | open() | ioctl() | getpid() | socket() |

문제 3: 커널 구조 비교

다음 설명에 맞는 커널 구조를 고르세요.

A. 모놀리식 커널 B. 마이크로커널 C. 하이브리드 커널

  1. ( ) 커널이 최소 기능만 포함하고 나머지는 사용자 공간에서 실행
  2. ( ) Linux가 사용하는 구조
  3. ( ) Windows NT가 사용하는 구조
  4. ( ) IPC 오버헤드로 인한 성능 저하가 단점
정답 보기 1. (B) 마이크로커널 2. (A) 모놀리식 커널 3. (C) 하이브리드 커널 4. (B) 마이크로커널

문제 4: 인터럽트 처리

인터럽트 처리 과정을 올바른 순서로 나열하세요.

A. 인터럽트 핸들러 실행 B. 현재 상태 저장 C. 인터럽트 발생 D. 저장된 상태 복구 E. 인터럽트 벡터 테이블에서 핸들러 찾기

정답 보기 C → B → E → A → D 1. (C) 인터럽트 발생 2. (B) 현재 상태 저장 3. (E) 인터럽트 벡터 테이블에서 핸들러 찾기 4. (A) 인터럽트 핸들러 실행 5. (D) 저장된 상태 복구

문제 5: 시스템 콜 코드 분석

다음 코드에서 발생하는 시스템 콜을 순서대로 나열하세요.

#include <unistd.h>
#include <fcntl.h>

int main() {
    pid_t pid = fork();

    if (pid == 0) {
        int fd = open("child.txt", O_WRONLY | O_CREAT, 0644);
        write(fd, "child", 5);
        close(fd);
    } else {
        wait(NULL);
        write(1, "parent\n", 7);
    }

    return 0;
}
정답 보기 1. fork() - 자식 프로세스 생성 자식 프로세스: 2. open() - 파일 열기 3. write() - 파일에 쓰기 4. close() - 파일 닫기 5. exit() - 프로세스 종료 (암시적) 부모 프로세스: 6. wait() - 자식 프로세스 대기 7. write() - 표준 출력에 쓰기 8. exit() - 프로세스 종료 (암시적)

다음 단계


참고 자료

to navigate between lessons