PintOS Project와 함께 알아보는 C언어로 객체 지향의 '상속' 개념 구현하기 💡

@developer/takealittle.time·2024년 12월 3일
1

KAIST PintOS Project

목록 보기
9/9
post-thumbnail

"한 페이지는 VM_UNINIT , VM_ANON , VM_FILE 라는 세 가지 종류 중 하나입니다. 하나의 페이지는 swap in, swap out, 그리고 페이지 삭제같은 여러 동작을 수행하게 됩니다.
페이지의 타입별로 이러한 동작을 하기 위해 요구되는 단계와 작업들이 다릅니다. 즉, VM_ANON 페이지와 VM_FILE 페이지는 서로 다른 destroy 함수를 호출합니다. 이를 위해 switch-case 구문을 활용해서 각 케이스에 맞는 함수를 사용하는 방법이 하나 있습니다.
우리는 이 문제를 해결하는 방법으로 객체 지향 프로그래밍의 컨셉인 “클래스 상속”을 소개하려고 합니다.
실제로 C 언어에서는 클래스와 상속이라는 개념은 없지만, 우리는 이 개념을 이해하기 위해 함수 포인터를 사용할 것입니다. 이 방법은 리눅스같은 실제 운영체제 코드에서 사용되는 방법과 유사합니다."


00. 들어가며..

  • PintOS Project 3주차 과정 중 재미있는 내용이 있어서 간단하게 글을 작성해보려고 한다. 💡

  • 요지는 pintOS의 VM 부분에서 page 객체를 사용할 때 절차 지향 언어인 C언어를 사용해서 객체 지향 언어의 상속(inheritance) 개념을 사용하는 것이다.


01. 절차 지향 프로그래밍 vs 객체 지향 프로그래밍

1. 절차 지향 프로그래밍 (Procedural Programming)

  • 프로그램을 '프로시저(Procedure)', 즉, 함수의 집합으로 서술하는 방식.
    • 여기저기 흩뿌려진 변수와 함수로 데이터들을 핸들링 해서 프로그래밍!
  • 예제 언어: C, Pascal, Fortran

2. 객체 지향 프로그래밍 (Object-Oriented Programming, OOP)

  • 프로그램을 수많은 '객체(Object)'라는 기본 단위로 나누고 이들의 상호 작용으로 서술하는 방식.
    • 변수(속성)와 함수(메서드)를 캡슐화 하여 '객체' 단위로 데이터들을 핸들링 해서 프로그래밍!
  • 예제 언어: Java, C++, Python, C#

정리

구분 절차 지향 프로그래밍 (Procedural Programming) 객체 지향 프로그래밍 (Object-Oriented Programming)
중심 요소 함수와 명령어 객체와 클래스
데이터 접근 변수와 함수로 접근 캡슐화를 통해 데이터 접근 제어
재사용성 함수 단위로 재사용 클래스와 객체 단위로 재사용
유지보수성 코드가 복잡해지면 유지 보수의 어려움 클래스 구조를 통해 유지보수 용이
모델링 절차적 문제 해결 방식 현실 세계의 객체를 모델링 하기 적합
절차 지향 언어와 객체 지향 언어의 비교

02. pintOS Project에서 C언어를 이용한 상속 개념의 구현

02-1. 클래스와 인스턴스, 상속의 개념 간단하게 살펴보기

1. 클래스 (Class)

객체를 정의하기 위한 청사진(Template) 또는 설계도.

  • 그림에서 onAnimal이 최상위 클래스
  • onAnimal 클래스는 모든 동물들이 공통으로 가지는 속성 (brain = true, legs=0)과 동작(메서드가 있다면)을 정의.

2. 인스턴스 (Instance)

클래스를 기반으로 생성된 실제 객체

  • ex) oHuman, oDog, oCat 등은 각각 oAnimal 클래스로부터 생성된 인스턴스들
  • 각 인스턴스는 기본적으로 클래스의 속성과 메서드를 공유하지만, 고유한 값을 가질 수도 있음.
    • ex) oHumanlegs=2, oDogsfleas = 8

3. 상속 (Inheritance)

기존 클래스의 속성과 메서드를 다른 클래스가 물려받는 OOP의 핵심 기능.

  • 그림에서 oHumanoPetoAnimal을 상속받아 brainlegs 속성을 공유.
  • oDogoCatoPet을 상속 받아, legs=4를 공유하지만 각자의 고유 속성 (ex. fleas)을 추가적으로 정의

02-2. PintOS 내에서 구현된 상속 개념 살펴보기

/* vm/vm.h */

struct page {
    const struct page_operations *operations;
    void *va;            /* Address in terms of user space */
    struct frame *frame; /* Back reference for frame */

    /* Per-type data are binded into the union.
     * Each function automatically detects the current union */
    union {
        struct uninit_page uninit;
        struct anon_page anon;
        struct file_page file;
#ifdef EFILESYS
        struct page_cache page_cache;
#endif
    };
};
  • vm/vm.h에 위와 같이 struct page가 정의되어 있다.

  • 재미있는 점은, union 구조체를 사용해서 해당 page의 상태에 따라 요소를 달리 갖는데, 이 때 해당 상태에 따라 사용하는 '함수'까지 바꿀 수 있다는 것이다.

/* vm/vm.h */

struct page_operations {
    bool (*swap_in)(struct page *, void *);
    bool (*swap_out)(struct page *);
    void (*destroy)(struct page *);
    enum vm_type type;
};
  • struct page 구조체 안에는 위와 같이 struct page_operations라는 구조체가 정의되어 있다.

  • 안에는 swap_in(), swap_out(), destroy() 각 함수에 대한 포인터가 정의되어 있다.

  • uninit.cuninit_ops

/* vm/uninit.c */
static const struct page_operations uninit_ops = {
	.swap_in = uninit_initialize,
	.swap_out = NULL,
	.destroy = uninit_destroy,
	.type = VM_UNINIT,
};
  • anon.canon_ops
/* vm/anon.c */
static const struct page_operations anon_ops = {
	.swap_in = anon_swap_in,
	.swap_out = anon_swap_out,
	.destroy = anon_destroy,
	.type = VM_ANON,
};
  • file.cfile_ops
/* vm/file.c */
static const struct page_operations file_ops = {
	.swap_in = file_backed_swap_in,
	.swap_out = file_backed_swap_out,
	.destroy = file_backed_destroy,
	.type = VM_FILE,
};

[ FigJam 보드로 보기🌱 ]

추천글: https://velog.io/@gomjellie/C%EC%9D%98-%EA%B0%9D%EC%B2%B4

It Seems Like... 🤔

02-3. → *** JAVA 코드로 변환한다면?

  1. Page 클래스
// Page.java
public class Page {
    private PageOperations operations;
    private Object va; // Address in terms of user space
    private Frame frame; // Back reference for frame

    // Union-like structure for different page types
    private UninitPage uninit;
    private AnonPage anon;
    private FilePage file;
    private PageCache pageCache; // Optional, for EFILESYS

    public Page(PageOperations operations, Object va, Frame frame) {
        this.operations = operations;
        this.va = va;
        this.frame = frame;
    }

    // Getters and setters
    public PageOperations getOperations() {
        return operations;
    }

    public void setOperations(PageOperations operations) {
        this.operations = operations;
    }

    // Other getters and setters for uninit, anon, file, and pageCache
}
  1. PageOperations 인터페이스
// PageOperations.java
public interface PageOperations {
    boolean swapIn(Page page, Object aux); // Corresponds to swap_in
    boolean swapOut(Page page);           // Corresponds to swap_out
    void destroy(Page page);              // Corresponds to destroy
    VMType getType();                     // Corresponds to type
}
  1. UninitOps 클래스
// UninitOps.java
public class UninitOps implements PageOperations {
    @Override
    public boolean swapIn(Page page, Object aux) {
        return Uninit.initialize(page, aux); // Assuming Uninit.initialize is a method
    }

    @Override
    public boolean swapOut(Page page) {
        return false; // NULL equivalent in C
    }

    @Override
    public void destroy(Page page) {
        Uninit.destroy(page); // Assuming Uninit.destroy is a method
    }

    @Override
    public VMType getType() {
        return VMType.VM_UNINIT;
    }
}
  1. AnonOps클래스
// AnonOps.java
public class AnonOps implements PageOperations {
    @Override
    public boolean swapIn(Page page, Object aux) {
        return Anon.swapIn(page, aux); // Assuming Anon.swapIn is a method
    }

    @Override
    public boolean swapOut(Page page) {
        return Anon.swapOut(page); // Assuming Anon.swapOut is a method
    }

    @Override
    public void destroy(Page page) {
        Anon.destroy(page); // Assuming Anon.destroy is a method
    }

    @Override
    public VMType getType() {
        return VMType.VM_ANON;
    }
}
  1. FileOps 클래스
// FileOps.java
public class FileOps implements PageOperations {
    @Override
    public boolean swapIn(Page page, Object aux) {
        return FileBacked.swapIn(page, aux); // Assuming FileBacked.swapIn is a method
    }

    @Override
    public boolean swapOut(Page page) {
        return FileBacked.swapOut(page); // Assuming FileBacked.swapOut is a method
    }

    @Override
    public void destroy(Page page) {
        FileBacked.destroy(page); // Assuming FileBacked.destroy is a method
    }

    @Override
    public VMType getType() {
        return VMType.VM_FILE;
    }
}

02-4. Linux에서도 위와 같은 구조를 내부적으로 활용하고 있다.

주요 사용 사례:

  1. 파일 시스템 (VFS: Virtual File System)
    https://www.kernel.org/doc/html/latest/filesystems/vfs.html
    https://www.kernel.org/doc/html/latest/filesystems/api-summary.html#c.file
  2. 장치 드라이버 모델
  3. 네트워크 프로토콜 스택

예시. VFS(Virtual File System)의 사례

struct file_operations {
    ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);
    int (*open)(struct inode *, struct file *);
    int (*release)(struct inode *, struct file *);
    ...
};

struct file {
    const struct file_operations *f_op;
    void *private_data;
    ...
};
  • 우리가 pintOS에서 VM File System을 구현할 때 위에서 살펴본 구조와 동일하다!
    • file_operations 구조체는 File System 관리를 위한 함수들의 테이블로 역할하고, 이를 상속 받아 하위 함수들로 동작한다.

3. 재미있었던 점 / 배운 점 / 느낀 점

  1. union 자료형에 대해서
    학과 시절
    'H/W가 지금처럼 발전하기 이전에, 메모리 용량 문제를 해결하기 위한 방법 중 하나로 주로 사용되었고, 태생적으로 단편화 문제를 갖고있기 때문에 H/W가 고도로 발전한 현재에는 굳이 잘 사용하지 않는 자료형이다.'
    라고 배웠었다.

    → 'C언어에서 구조체 안에 union변수를 정의 해 다형성을 흉내내는 설계를 할 수 있구나'
    → 학과에서도 안가르쳐주는, 하위 자료형을 가지고 또 새로운 고차원적인 구조를 만드는 방법에 대해 학습할 수 있었다.

  2. JAVA와 같은 객체 지향 프로그래밍 언어도 '문법적으로는 다르지만, 내부적으로 상속이나 다형성 등 개념을 구현하는데에 있어서는 우리가 위에서 작성한 C코드처럼 결국 메모리 참조를 기반으로 이루어지겠구나!' 라는 것을 몸으로 체감할 수 있었다.

    → 단, C언어에서는 우리가 직접 주소를 다루었다면, JAVA에서는 JVM(JAVA Virtual Machine)이 메모리 관리를 자동화 해주는 등의 차이는 있다.

  3. Linux와 같이 실제 상용화 되고 있는 운영체제 같은 경우에도 위와 같은 구조를 사용하고 있다!

    → '현실 세계에서 기능하는 어떤 서비스의 일부 형식을 내가 배우고 있구나.' 하는 자각에서 오는 즐거움을 느낄 수 있었다.


++시리즈의 이전 글


* 이미지 / 참고 자료 출처

profile
능동적으로 사고하고, 성장하기 위한. 🌱

0개의 댓글

관련 채용 정보