Goal

  • Facade 패턴에대한 이해
  • SRP원칙에 대한 이해

SRP : 단일 책임 원칙(Single responsibility principle)

객체는 변화해야 할 단 한가지 이유만을 가져야 합니다.

쉽게 생각하면 변경이 있을때 파급 효과가 적다면, 즉 수정해야 할 코드가 적다면 단일 책임 원칙을 잘 따른것이라 볼 수 있습니다.

다음과 같이 SRP위반 사례를 예시로 SRP를 지켜야 하는 이유에 대해 알아보겠습니다.

Problem 1 : Accidental duplication

다음은 급여 애플리케이션의 Employee 클래스입니다.

Employ 클래스에는 calculatePay(), reportHours(), save() 메서드가 존재하는데 각각은 회계팀, 인사팀, 전산팀이 주로 사용합니다.

개발자가 이 세 메서드를 Employee 단일 클래스에 배치하여 서로 결합되었습니다. 이로인해 각 엑터의 결정이 다른 엑터가 의존하는 부분에 영향을 줄 수 있게 됩니다.

class Employee {
    int hour;

    // Actor : 회계팀
    public int calculatePay() { 
        ...
        regularHour();
        ... 
    }

    // Actor : 인사팀
    public void reportHours() { 
        ...
        regularHour();
        ... 
    }

    // Actor : 전산팀
    public void save() { ... }

    // 초과 업무 시간을 제외한 업무시간 계산
    private int regularHour() { ... }
}

calculatePay()reportHours()는 초과 업무 시간을 제외한 업무시간을 계산하는 regularHour()를 공유합니다. 회계팀에서 초과 업무 시간을 제외한 업무시간 계산 방식을 변경하고자 regularHour()를 수정하게 되면 기존의 방식을 그대로 사용하던 인사팀은 이에 영향을 받게 되고, reportHours()는 기존의 방식대로 동작하지 못하게 됩니다.

이러한 문제는 서로 다른 액터가 의존하는 코드를 너무가까이(단일 클래스)에 배치했기 때문에 발생합니다.

Problem 2 : Merge

인사팀에서는 reportHours()를 전산팀에서는 Employee의 스키마를 수정하기로 동시에 결정합니다. 각 팀의 수정사항이 반영될 때 충돌 즉, 병합이 발생하게 됩니다.

Solve

이러한 문제를 해결 하기 위해서는 데이터를 가지고 있는 EmployeeData클래스와 각 메서드를 가지고 있는 클래스로 분리하고 메서드를 가진 각 클래스들이 EmployeeData클래스를 공유하도록 하면 됩니다. 하지만 이경우 개발자가 메서드를 가진 세 클래스를 인스턴스화 하고 추적해야한다는게 단점입니다.

이경우 퍼사드 패턴(Facade Pattern)을 적용하여 클래스의 생성하여 인스턴스의 생성을 위임할 수 있습니다. 퍼사드 패턴은 서브 클래스간의 통합 인터페이스를 제공하는 역할로 클라이언트는 퍼사드 객체에서 제공하는 메서드를 호출함으로써 복잡한 서브클래스 사용을 간편하게 할 수 있습니다.

class EmployeeData {
    //data(공유자원)
    int hour;

    private EmployeeData() {
        this.hour = 0;
    }

    private static class LazyHolder {
        static final EmployeeData EMPLOYEE_DATA = new EmployeeData();
    }

    public static EmployeeData getInstance() {
        return LazyHolder.EMPLOYEE_DATA;
    }
}

class PayCalculator {
    final EmployeeData data = EmployeeData.getInstance();

    void calculatePay() {
        // 예제 코드
        data.hour += 1;
    }
}

class HourReporter {
    final EmployeeData data = EmployeeData.getInstance();

    void reportHours() {
        // 예제 코드
        data.hour += 1;
    }
}

class EmployeeSaver {
    final EmployeeData data = EmployeeData.getInstance();

    void save() {
        // 예제 코드
        data.hour += 1;
        System.out.println(data.hour);
    }
}

class EmployeeFacade {
    private final PayCalculator payCalculator;
    private final HourReporter hourReporter;
    private final EmployeeSaver employeeSaver;

    public EmployeeFacade() {
        payCalculator = new PayCalculator();
        hourReporter = new HourReporter();
        employeeSaver = new EmployeeSaver();
    }

    public void calculatePay() {
        payCalculator.calculatePay();
    }

    public void reportHour() {
        hourReporter.reportHours();
    }

    public void save() {
        employeeSaver.save();
    }
}

public class Facade {
    public static void main(String[] args) {
        EmployeeFacade employeeFacade = new EmployeeFacade();

        employeeFacade.calculatePay();
        employeeFacade.reportHour();
        employeeFacade.save();
    }
}

Singleton pattern을 적용하여 공유자원인 EmployeeData에 대한 thread-safety를 확보하였고, EmployeeFacade 클래스를 통해 객체의 생성을 위임하여 개발자가 메서드를 가진 세 클래스에 대한 인스턴스화를 하지 않아도 되도록 설계를 하였습니다.

다만 이러한 Facade 패턴을 관련 없는 메소드를 엮기 위해 사용할 경우 God-object가 될 수 있으므로 성능향상(관련없는 객체도)과 유지보수성(관련있는 객체만)의 Trade-off를 잘 고려하여 사용하여야 합니다.

Reference

'CS' 카테고리의 다른 글

[Network] 웹소켓(Websocket)  (0) 2022.10.09
[OS] 데드락의 탐지와 해결방법  (1) 2022.10.03
[Design Pattern] Singleton Pattern  (0) 2022.08.22
[OS] Scheduler  (0) 2022.08.22
[OS] RAM disk vs Disk 성능비교  (0) 2022.08.22

Definition

Singleton의 사전적 의미는 외동, 카드 패에서 한장만 있는것을 지칭하는 말입니다.
이러한 사전적 의미와 비슷하게, Singleton Pattern은 객체를 하나만 생성하도록 제한하기 위해 사용하는 디자인 패턴입니다.

생성패턴의 종류

  1. 추상 팩토리 패턴(Abstract Factory Pattern)
  2. 빌더 패턴(Builder Pattern)
  3. 팩토리 메서드 패턴(Factory Method Pattern)
  4. 프로토타입 패턴(Prototype Pattern)
  5. 싱글턴 패턴(Singleton pattern)

Problem

1. 싱글톤 패턴은 클래스가 인스턴스를 하나만 가짐을 보장합니다.

이렇게 인스턴스의 수를 통제하는 가장 보편적인 이유는 데이터 베이스나 파일과 같은 일부 공유 리소스에 대한 액세스를 제어하기 위함입니다.

생성자 호출은 항상 새로운 객체를 반환하기 때문에 생성자로는 이러한 동작을 구현할 수 없습니다.

2. 인스턴스에 대한 전역 접근을 허용합니다.

전역변수와 마찬가지로 프로그램의 모든 위치에서 인스턴스에 접근할 수 있으며, 다른 코드에 의해 인스턴스가 덮어쓰이지 않도록 보호하기도 합니다.

Solution

  • Singleton 패턴을 적용한 클래스의 생성자의 접근지정자를 private으로 둠으로써 생성자에 의한 새로운 객체 생성을 제한합니다.
  • 정적(static) 생성 메서드는 인스턴스가 호출된 적이 없다면, private 생성자를 호출하고, 생성된 인스턴스를 static field에 캐싱하여 이후 호출되더라도 같은 인스턴스를 반환하게 합니다.

요구사항

  • 개발중인 시스템의 스피커에 접근할 수 있는 클래스를 만들어라

만약 스피커의 인스턴스가 100개 존재할 경우 볼륨을 1올리기 위해서 100개의 모든 스피커 인스턴스에 접근하여
볼륨을 올리는 작업을 수행해 주어야 합니다. 이는 개발의 복잡도를 높이고, 시스템 리소스도 크게 낭비하게 합니다.

다음과 같이 싱글턴 패턴(Singleton Pattern)을 사용하여 이러한 문제를 방지할 수 있습니다.

class SystemSpeaker {
    static private SystemSpeaker speaker;
    private int volume;

    // 외부에서의 생성자 접근을 방지하기 위해 private 접근 지정자를 사용
    private SystemSpeaker() {
        this.volume = 5;
    }

    public static SystemSpeaker getInstance() {
        // speaker 가 null일 경우에만 생성하여 인스턴스가 하나만 생성됨을 보장.
        if(speaker == null) {
            speaker = new SystemSpeaker();
        }
        return speaker;
    }
}

Client Side

public class Singletone {
    public static void main(String[] args) {
        SystemSpeaker speaker1 = SystemSpeaker.getInstance();
        SystemSpeaker speaker2 = SystemSpeaker.getInstance();

        System.out.println(speaker1.equals(speaker2));
    }
}

이와같이 프로그램의 클래스가 모든 클라이언트에서 단일 인스턴스만 있어야 하는 경우 Singleton Pattern을 사용합니다.

  • Singleton Pattern은 생성자의 접근 지정자를 private으로 설정하여 getInstance()함수 이외에 클래스의 개체를 만드는 모든 생성방법을
    비활성화 합니다.

But race condition can occur

하지만 다중 스레드 환경에서 객체의 생성 여부를 확인하고 생성자를 호출하는 과정은 원자성을 보장하지 못하기 때문에 경합 상태(Race Condition)을 발생시킬 수 있습니다.

  1. Thread 1 : Speaker 인스턴스가 생성되지 않음을 확인함
  2. Thread 1 -> Thread 2 : Context Switch
  3. Thread 2 : Speaker 인스턴스가 생성되지 않음을 확인함
  4. Thread 2: Speaker Instance생성
  5. Thread 2 -> Thread1 : Context Switch
  6. Thread 1: Speaker Instance생성(정적 speaker 객체에 대한 경합 상태 발생 -> 2개의 인스턴스 생성)

Solution

thread-safe한 Singleton Pattern을 위해 다음과 같은 방법들을 고려할 수 있습니다.

1. 정적변수 초기화

class SystemSpeaker {
    // 정적 변수는 클래스 로딩시 한번만 수행되기 때문에 인스턴스 초기화는 한번만 수행됩니다.
    static private SystemSpeaker speaker = new SystemSpeaker();
    private int volume;

    private SystemSpeaker() {
        this.volume = 5;
    }

    public static SystemSpeaker getInstance() {
        return speaker;
    }
}

이경우 객체 생성을 원치 않더라도 클래스가 로딩되는 시점에 인스턴스가 초기화 되어 메모리를 점유하게 되어 비효율적입니다.

2. Synchronized 를 통한 동기화 제어

class SystemSpeaker {
    static private SystemSpeaker speaker;
    private int volume;

    private SystemSpeaker() {
        this.volume = 5;
    }

    // 인스턴스 생성 메서드를 임계구역으로 변경
    public synchronized static SystemSpeaker getInstance() {
        if(speaker == null) {
            speaker = new SystemSpeaker();
        }
        return speaker;
    }
}

Synchronize를 통한 객체 초기화는 lock, unlock 과정에서 성능을 크게 저하시킬 수 있습니다.

3. Enum

enum 클래스는 클래스처럼 보이게 하는 상수로 서로 관련 있는 상수들을 모아 심볼릭한 명칭의 집합으로 정의한 것입니다.(Jdk 1.5버전 이상에서만 가능한 방법입니다.)

enum SystemSpeaker {
    SPEAKER;

    public static SystemSpeaker getInstance() {
        return SPEAKER;
    }
}

Enum을 이용한 방식은 Thread-safe를 보장하지만 컴파일 타임에 enum의 초기화가 진행됩니다.
때문에 Android와 같이 Context에 의존성이 있는 환경에서는 초기화 과정에서 매번 Context정보를 전달해야 하는 문제가 발생합니다.

4. Lazy Holder

class SystemSpeaker{
    int volume;

    private SystemSpeaker() {
        this.volume = 5;
    }

    private static class LazyHolder {
        static final SystemSpeaker SPEAKER = new SystemSpeaker();
    }

    public static SystemSpeaker getInstance() {
        return LazyHolder.SPEAKER;
    }
}

객체가 필요할때로 초기화를 미룬다는 뜻에서 Lazy Initialization이라 부르기도 합니다.
JVM에 의해 SystemSpeaker클래스가 로드되면 클래스는 초기화를 거칩니다. 이때 LazyHolder클래스는 getInstance()메서드가 호출되기 전까지 로드되지 않습니다.

getInstance()메서드가 호출되면 LazyHolder()클래스를 로드하고 초기화를하는데 클래스의 초기화 단계는 JLS에 의해 순차적, 즉 비동시적임을 보장하므로 thread-safe합니다.

Singletone Pattern의 thread-safe를 보장하기 위한 가장 좋은 방법입니다.

Reference

'CS' 카테고리의 다른 글

[OS] 데드락의 탐지와 해결방법  (1) 2022.10.03
[Design Pattern] Facade 패턴을 통한 SRP원칙 준수  (0) 2022.08.22
[OS] Scheduler  (0) 2022.08.22
[OS] RAM disk vs Disk 성능비교  (0) 2022.08.22
[OS] Multiprocessor Scheduling  (0) 2022.08.22

CPU Scheduling

많은 프로세스가 동시에 자원을 요구할 때 어떤 프로세스가 CPU를 먼저 사용할지 결정하는 정책을 말합니다.

Workload (작업부하)

  • 프로세스가 얼마나 많은 양의 자원을 요구하느냐

Simple assumtion about porcess

  • 각 작업은 같은 시간동안 수행된다
  • 모든 작업은 같은 시간에 시작된다
  • 한번 시작되면 완료될때 까지 실행된다
  • 모든 작업은 CPU만을 사용한다.
  • 각 작업의 수행시간을 미리 알고 있다.

Job & Process

  • 동일한 의미의 단어지만 스케줄러를 연구하는 도메인 에서는 job이라는 단어를 선호합니다.

프로세스(Process)는 주소공간을 가지고 있는 실행중인 프로그램을 말합니다

작업(job)은 셸에서 사용하는 개념으로 실행중인 대화형 프로그램을 말합니다. 데몬과 반대되는 개념입니다. 대화형 프로그램을 실행 중인 경우 ctrl + z키를 눌러 프로그램을 일시 중단할 수 있습니다.

Deamon - 사용자가 직접적으로 제어하지 않고, 백그라운드에서 돌면서 여러 작업을 하는 프로그램입니다.

Metric

성능, 의존성 등과 같은 것을 측정하기위한 척도

Metric for Scheduling

  • Turnaround time(반환시간, Complete time - Arrived time)
    • 처음 실행시간부터 작업을 완료하는데 소요된 시간(CPU, waiting, I/O 등 모든 시간을 포함)
  • Response time(응답시간, First run time - Arrived time)
    • 처음 실행시간부터 작업이 응답하기까지 소요된 시간
  • Fairness(형평성)
    • 두개의 프로세스가 종료되는 시간이 얼마나 비슷한지
  • Throughput(처리율, jobs/sec)
    • 단위시간당 처리하는 작업의 수(처리량)
  • Deadline(마감시간)
    • Turnaround time < Deadline time

Response time(응답시간)

Turnaround time - Time required for a particular process to complete, from submission time to completion. It is equal to the sum total of Waiting time and Execution time.
-> batch 시스템에서 유리

Response time - The time taken in a program from the issuance of a command to the commence of a response to that command.(i.e., the time-interval between submission of a request, and the first response to that request, not the output .)
-> interactive 시스템에서 유리

FIFO(First In First Out)

처음으로 도착한 프로세스를 먼저 스케줄링하는 정책(a.k.a FCFS(First Come First Serve))

  • Pros : 1) 간단하다 2) 구현하기 쉽다
  • Cons : 1) 대기시간이 길어질 수 있다. (convoy effect)

Convoy Effect - The whole Operating System slows down due to few slow processes

SJF(Shortest Job First)

수행시간이 가장 짧은 작업에 높은 우선순위를 부여(a.k.a SPN(Shortest Process Next))

  • Pros : 최적의 알고리즘으로 증명됨
  • Cons : 짧은 수행시간의 작업이 늦게 도착했을때 어떻게 하나?

STCF(Shortest Time to Completion First)

SJF와 유사하나 선점형(Preemptive) 스케줄링 방식입니다

Non-preemptive Scheduling(비선점형 스케줄링)

  • 프로세스가 한번 스케줄 되면 그 프로세스가 끝날때까지 수행합니다.

Preemptive Scheduling(선점형 스케줄링)

  • 프로세스가 완료되지 않더라도 중간에 멈추고 다른 프로세스가 끼어들 수 있는 스케줄링 방식입니다.
  • context switch가 요구됩니다.
  • 최근 모든 스케줄러는 선점형 스케줄링 방식을 사용합니다

Response time

  • Turnaround time : batching 시스템에서 유리한 metric
  • Response time : interactive 시스템에서 중요한 metric(ex, 대화형 쉘에서 프로그램을 실행했는데 늦게 실행되면 답답)

RR(Round Robin)

Run queue의 작업이 완료될때 까지 Time quantum만큼 빠르게 돌아가며 수행
-> Response Time 단축, Turnaround time 증가

Tradeoff of time slice(time quantum)

  • Samll : 높은 응답 속도, Context switch로 인한 오버헤드 증가
  • Large : 낮은 응답 속도, Context switch로 인한 오버헤드 감소

-> time quantum의 크기가 커질수록 FCFS와 동일하게 동작하며 0에 가까울수록 switching overhead가 증가하여 비효율적입니다.

Tradeoff of between response time and turnaround time

  • Traditional isuue in CS : Interactivity(응답속도) vs Performance(성능)
  • 두마리 토끼를 동시에 잡을수는 없다

Incorporating I/O

프로세스로 부터 입출력 요청이 있을때 프로세스는 입출력이 완료될 때 까지 기다려야 합니다.

  • Busy waiting : 프로세스 A의 I/O가 수행되는동안 상태 확인, CPU자원 낭비
  • Blocked : 프로세스 A의 I/O가 수행되는동안 프로세스 B를 스케줄 하여 자원 낭비를 최소화

MLFO(Multilevel Feedback Queue)

  • 선점형 스케줄링 방식으로 여러개의 Ready Queue에 사용
  • 각 Queue마다 다른 Priority level 을 부여받음
  • 높은 우선순위의 Queue가 먼저 스케줄되며, 같은 queue내에서는 RR방식 적용합ㄴ디ㅏ
    • Rule 1 : Priority(A) > Priority(B) 일때 A가 먼저 수행합니다.
    • Rule 2 : Priority(A) = Priority(B) 일때 라운드 로빈 방식으로 수행합니다.
    • Rule 3 : Job이 들어오면 가장 상위 Ready Queue에 할당합니다.
    • Rule 4a : 할당된 Time slice(time quantum)가 끝나면 하위 Queue로 우선순위가 내려갑니다.
    • Rule 4b : Time slice가 끝나기전 스케줄링이 해제 동일한 우선순위 레벨에 남습니다.

'CS' 카테고리의 다른 글

[Design Pattern] Facade 패턴을 통한 SRP원칙 준수  (0) 2022.08.22
[Design Pattern] Singleton Pattern  (0) 2022.08.22
[OS] RAM disk vs Disk 성능비교  (0) 2022.08.22
[OS] Multiprocessor Scheduling  (0) 2022.08.22
[OS] Linux Kernel Structure  (0) 2022.08.22

RAM vs Disk

DRAM과 DISK모두 저장장치의 역할을 하지만 각각이 가지고 있는 특징이 다르기 때문에 서로 다른 용도로 사용이 됩니다.

DRAM의 경우 속도가 뛰어나지만 가격이 높고, 휘발성이 있어 데이터가 영구적으로 저장되지 못한다는 특징이 있습니다. 또 바이트 단위로 데이터에 접근합니다. 때문에 CPU가까이(데이터의 이동이 잦은곳)에서 프로세서가 필요로 하는 데이터를 저장한 후 공급하는 주기억장치의 역할을 합니다.

반면 DISK의 경우 DRAM에 비해 현저하게 느린 데이터 전송속도를 가지지만, 휘발성이 없고 섹터단위로 데이터에 접근합니다. 때문에 데이터를 저장하는 보조기억장치 역할을 합니다.

RAM Disk

RAM을 주 기억 장치가 아니라 보조 기억 장치로 사용하는 개념입니다. 메모리를 기반으로 한 파일시스템이기 때문에 SSD, HDD에 비해 속도면에서 아주 우수하며 caching또는 작업 디렉토리 용도로 사용될수 있습니다. 제 개인적인 경험으로는 DRAM의 휘발성을 생각하고 RAM disk를 윈도우 인터넷 익스플로러의 임시파일 경로로 활용했던 경험이 있습니다.

Create RAM disk

Verify Memory Size

$ free -h
              total        used        free      shared  buff/cache   available
Mem:          6.1Gi       384Mi       5.6Gi       0.0Ki       189Mi       5.5Gi
Swap:         2.0Gi          0B       2.0Gi

제경우 5.6Gi정도의 여유 메모리가 있어 이를 고려하여 램디스크로 사용할 크기를 결정 하였습니다.

ramdisk 설정

먼저 램디스크를 만들 디렉토리를 생성합니다.

$ mkdir /workspace/ramdisk

아래 명령어를 통해 램 디스크를 마운트 합니다.(저는 1G만큼 할당하였습니다.)

$ sudo mount -t tmpfs -o size=1G tmpfs /workspace/ramdisk

램디스크가 성공적으로 마운트 된것을 확인할 수 있습니다.

$ df -h
...
D:\             466G  111M  466G   1% /mnt/d
tmpfs           1.0G     0  1.0G   0% /workspace/ramdisk

램디스크를 사용하고 싶지 않다면 언마운트로 해제합니다.

$ umount /workspace/ramdisk

RAM disk, Disk Performance evaluation

간단하게 dd유틸리티를 사용하여 속도를 테스트 합니다.

on RAM disk

$ dd if=/dev/zero of=/workspace/ramdisk/perf bs=256M count=1; rm -f /workspace/ramdisk/perf

1+0 records in
1+0 records out
268435456 bytes (268 MB, 256 MiB) copied, 0.100249 s, 2.7 GB/s

on disk

$ dd if=/dev/zero of=/workspace/perf bs=256M count=1; rm -f /workspace/perf

1+0 records in
1+0 records out
268435456 bytes (268 MB, 256 MiB) copied, 0.159167 s, 1.7 GB/s

'CS' 카테고리의 다른 글

[Design Pattern] Singleton Pattern  (0) 2022.08.22
[OS] Scheduler  (0) 2022.08.22
[OS] Multiprocessor Scheduling  (0) 2022.08.22
[OS] Linux Kernel Structure  (0) 2022.08.22
[Design Pattern] Factory Pattern  (0) 2022.08.22

CPU Cache

  • 캐시는 용량이 작고, 빠른 특성때문에 자주사용되는 데이터를 주로 저장합니다
    • Temporal locality(시간 지역성)
      • 마지막에 접근한 데이터를 다시 접근하는 특성을 말합니다.
      • stack, for loop
    • Spatial locality(공간 지역성)
      • 접근한 데이터의 주변 데이터를 다시 접근하는 특성을 말합니다.
      • Array, sequential execution ...
  • Benefit
    • Cache hit : 캐시에 담아놓은 데이터를 접근하게 되면 Cache hit, 담아놓지 않은 데이터를 접근하면 Cache miss가됩니다. Cache hit하게 되면 메모리보다 훨씬 빠른속도로 데이터에 접근할 수 있습니다.
    • Delayed Write : 쓰기 작업시 바로 메모리에 접근하지 않고, 캐시에 쓰기작업을 한 후 메모리에 접근할 때 한번에 쓰기작업을 합니다.

Cache Affinity

  • CPU Cache는 job이 이전에 접근한 메모리를 저장해놓고있는데 Context Switch이후 캐시되어 있는 프로세서가 아닌 다른 프로세서에 스케줄링 되면 Cache miss로 인해 성능저하(Cache miss)가 발생할 수 있습니다.
  • 이를 방지하기 위해서 이전에 수행했던 프로세서에 스케줄링하여 Cache hit를 높이는것이 좋습니다.

SQMS(Single Queue Multiprocessor Scheduling)

여러개의 코어를 하나의 큐로 스케줄링

  • 장점 : 구현이 간단하다, job이 공평하게 분배됨
  • 단점 : 공유자원인 큐에 대한 lock오버헤드 발생

MQMS(Multi Queue Multiprocessor Scheduling)

각 코어마다 큐를 가지고 있음.

  • 장점 : Affinity가 좋다, lock으로 인한 오버헤드가 적다.
  • 단점 : load balancing을 고려해야 한다.

Load balancing(부하균등)

  • 각 CPU의 큐는 독립적이므로 서로의 상태를 알지 못한다.
  • 스케줄러는 가장 여유로운 큐를 Source큐로 선택, 임의 선택된 Target큐가 Source큐보다 Busy하면 Source큐는 Target큐의 Job을 Steal(Work stealing, Migration).

'CS' 카테고리의 다른 글

[OS] Scheduler  (0) 2022.08.22
[OS] RAM disk vs Disk 성능비교  (0) 2022.08.22
[OS] Linux Kernel Structure  (0) 2022.08.22
[Design Pattern] Factory Pattern  (0) 2022.08.22
[Design Pattern] Builder Pattern을 사용하는 이유  (0) 2022.08.22

운영체제가 관리해야 할 자원은 크게 물리적인 자원과 추상적인 자원으로 구분할 수 있습니다.

운영체제에서 커널의 역할은 CPU나 메모리와 같은 물리적인 자원을 사용자에게 태스크와 세그먼트, 페이지와 같은 추상적인 자원으로 추상화 하여 사용자에게 인터페이스를 제공하는것입니다. 덕분에 우리는 메모리 레이아웃, 스케줄러, 네트워킹 등과 같은 시스템의 세부 구현 사항에 대한 이해 없이도 프로그램을 실행시킬 수 있습니다.

리눅스 커널의 구성요소

Figure 1 : Linux Kernel

그림은 리눅스 커널 내부를 논리적인 구성 요소로 구분하여 그린 것입니다. 커널의 Manager에 해당하는 요소들은 각각 Filesystem Manger는 디스크를 파일로, Memroy Manager는 메모리를 Page또는 Segment로, Task Manger는 CPU를 Task로, Network Manager는 네트워크 장치를 소켓으로, Device Manager는 block 또는 character로 물리적인 자원을 추상적인 자원으로써 제공해 줍니다.

Figure 2 : Linux Kernel Source Organization

kerenl

kernel 디렉토리에는 Figure 1의 Task Manager에 해당하는 부분이 구현된 디렉터리입니다. Task 생성 및 소멸, 스케줄링, 시그널 처리 등의 기능을 구현하고 있습니다.

arch

arch 디렉토리에는 하드웨어에 종속적인 부분들을 구현한 디렉토리로 cpu의 타입에 따라 하위 디렉토리가 구성다. 대표적인 architecture인 x86, arm등을 여기서 확인할 수 있습니다.

x86을 기준으로 살펴보면 ./arch/x86/boot에는 부팅시 사용하는 부트 스트랩 코드, ./arch/x86/kernel에는 task manager의 문맥교환 및 스레드 관리, ./arch/x86/mm page fault관련 처리 기능 등 하드웨어 종속적인 부분들이 구현되어있음을 확인할 수 있습니다.

fs

fs 디렉토리에는 Figure 1의 Filesystem Manager에 해당하는 부분이 구현된 디렉터리 입니다. 리눅스에서 지원하는 ext2, ext3, ext4, f2fs등의 파일시스템이 구현되어 있으며 open(), read(), write()와 같은 파일관련 시스템 호출이 구현되어 있습니다

mm

Memory Manager가 구현된 디렉토리로물리메모리, 가상메모리 관리, Task마다 할당되는 메모리 객체관리 등의 기능이 구현되어 있습니다.

driver

주변 장치를 추상화 하는 디바이스 드라이버가 구현된 디렉토리로 android, bluetooth, dma등 다양한 주변장치에 대한 드라이버가 구현되어있는것을 확인할 수 있습니다.

net

Network Manager가 구현된 디렉토리로 802, ipv4,6, appletalk, bluetooth등 다양한 통신 프로토콜이 구현되어있는것을 확인할 수 있습니다.

이외에도 프로세스간 통신기능을 구현한 ipc디렉토리, 커널 초기화를 구현한 init디렉토리, 리눅스 커널의 헤더파일을 구현한 inlcude디렉토리, 이외 다양한 기능들을 구현한 others디렉토리가 있습니다.

Process manager(CPU)

커널은 System Call Interface(이하 SCI)를 통해 응용 프로그램에 인터페이스를 제공하여 프로세스 생성(fork(), exec(), ...), 프로세스 종료(kill(), exit()), 프로세스간의 통신 및 동기화(signal(),...)를 수행합니다.

실행중인 스레드들은 CPU를 공유해야 합니다. 리눅스의 경우에는 CFS(Completely Fair Scheduler) 스케줄러를 통해 이를 관리를 합니다. 시스템이 현재 프로세스를 계속 수행할 수 없는 상황이 되면 커널의 schedule()함수가 호출되고 그 내부의 __schedule()함수가 스케줄링의 핵심동작을 수행합니다.

static void __sched notrace __schedule(bool preempt) {
    struct task_struct *prev, *next; // 프로세스의 속성 정보를 표현하는 자료구조
    unsigned long *switch_count;
    struct rq_flags rf;
    struct rq *rq;
    int cpu;

    cpu = smp_processor_id();
    rq = cpu_rq(cpu);
    prev = rq->curr; // 현재 실행중인 프로세스의 태스크 디스크립터 주소를  prev로

...

    next = pick_next_task(rq, prev, &rf); // 다음 실행할 프로세스의 태스크 디스크립터 주소를 next로
    clear_tsk_need_resched(prev);
    clear_preempt_need_resched();

    if (likely(prev != next)) { // prev와 next가 다른경우
        rq->nr_switches++;
        rq->curr = next; // 런큐 curr 필드에 저장합니다.

        ++*switch_count; // context switch 횟수 정보를 담고있습니다.

        trace_sched_switch(preempt, prev, next);

        /* Also unlocks the rq: */
        rq = context_switch(rq, prev, next, &rf); // context switch를 수행합니다.
    } else {
        rq->clock_update_flags &= ~(RQCF_ACT_SKIP|RQCF_REQ_SKIP);
        rq_unlock_irq(rq, &rf);
    }

    balance_callback(rq);
}

'CS' 카테고리의 다른 글

[OS] RAM disk vs Disk 성능비교  (0) 2022.08.22
[OS] Multiprocessor Scheduling  (0) 2022.08.22
[Design Pattern] Factory Pattern  (0) 2022.08.22
[Design Pattern] Builder Pattern을 사용하는 이유  (0) 2022.08.22
[DataStructure] Bloom filter  (0) 2022.08.22

+ Recent posts