C++로 시스템 프로그래밍 시작하기: 운영체제 핵심 개념 구현 🖥️💻
시스템 프로그래밍은 컴퓨터 과학의 핵심 분야 중 하나로, 운영체제와 하드웨어 간의 상호작용을 다루는 중요한 영역입니다. C++는 이러한 시스템 프로그래밍에 적합한 언어로 널리 사용되고 있죠. 오늘은 C++를 활용하여 운영체제의 핵심 개념들을 직접 구현해보면서, 시스템 프로그래밍의 세계로 깊이 들어가 보겠습니다. 🚀
이 글을 통해 여러분은 단순히 이론적인 지식을 넘어, 실제로 동작하는 코드를 작성하며 운영체제의 핵심 메커니즘을 이해할 수 있을 것입니다. 마치 재능넷에서 다양한 재능을 거래하듯, 우리는 여기서 C++와 시스템 프로그래밍의 재능을 습득하고 공유하게 될 것입니다. 자, 그럼 시작해볼까요? 🎨✨
1. 프로세스 관리: 멀티태스킹의 기초 🔄
운영체제의 가장 기본적인 기능 중 하나는 프로세스 관리입니다. 여러 프로그램을 동시에 실행하는 것처럼 보이게 하는 멀티태스킹의 핵심이죠. C++로 간단한 프로세스 관리 시스템을 구현해보겠습니다.
#include <iostream>
#include <vector>
#include <queue>
#include <string>
class Process {
public:
Process(int id, std::string name, int priority) : id(id), name(name), priority(priority) {}
int getId() const { return id; }
std::string getName() const { return name; }
int getPriority() const { return priority; }
private:
int id;
std::string name;
int priority;
};
class ProcessManager {
public:
void addProcess(const Process& process) {
processes.push(process);
}
void runNextProcess() {
if (!processes.empty()) {
Process current = processes.top();
processes.pop();
std::cout << "Running process: " << current.getName() << " (ID: " << current.getId() << ")" << std::endl;
} else {
std::cout << "No processes to run." << std::endl;
}
}
private:
std::priority_queue<process std::vector>, std::function<bool process>> processes{
[](const Process& a, const Process& b) { return a.getPriority() < b.getPriority(); }
};
};
int main() {
ProcessManager manager;
manager.addProcess(Process(1, "Web Browser", 3));
manager.addProcess(Process(2, "Text Editor", 2));
manager.addProcess(Process(3, "File Manager", 1));
for (int i = 0; i < 3; ++i) {
manager.runNextProcess();
}
return 0;
}
</bool></process>
이 코드에서는 우선순위 큐를 사용하여 간단한 프로세스 스케줄러를 구현했습니다. 각 프로세스는 ID, 이름, 우선순위를 가지며, 우선순위가 높은 프로세스가 먼저 실행됩니다. 이는 실제 운영체제의 프로세스 스케줄링 알고리즘을 간소화한 버전이라고 볼 수 있죠.
2. 메모리 관리: 동적 메모리 할당 구현 💾
메모리 관리는 운영체제의 핵심 기능 중 하나입니다. 여기서는 간단한 메모리 할당기를 구현해보겠습니다. 이 할당기는 First-Fit 알고리즘을 사용하여 메모리 블록을 할당합니다.
#include <iostream>
#include <vector>
#include <algorithm>
struct MemoryBlock {
size_t start;
size_t size;
bool free;
MemoryBlock(size_t start, size_t size, bool free = true)
: start(start), size(size), free(free) {}
};
class MemoryAllocator {
public:
MemoryAllocator(size_t totalSize) {
blocks.push_back(MemoryBlock(0, totalSize));
}
void* allocate(size_t size) {
for (auto& block : blocks) {
if (block.free && block.size >= size) {
block.free = false;
if (block.size > size) {
blocks.push_back(MemoryBlock(block.start + size, block.size - size));
block.size = size;
}
std::cout << "Allocated " << size << " bytes at address " << block.start << std::endl;
return reinterpret_cast<void>(block.start);
}
}
std::cout << "Failed to allocate " << size << " bytes" << std::endl;
return nullptr;
}
void deallocate(void* ptr) {
auto it = std::find_if(blocks.begin(), blocks.end(),
[ptr](const MemoryBlock& block) { return reinterpret_cast<void>(block.start) == ptr; });
if (it != blocks.end()) {
it->free = true;
std::cout << "Deallocated " << it->size << " bytes at address " << it->start << std::endl;
mergeAdjacentFreeBlocks();
} else {
std::cout << "Invalid pointer for deallocation" << std::endl;
}
}
private:
std::vector<memoryblock> blocks;
void mergeAdjacentFreeBlocks() {
std::sort(blocks.begin(), blocks.end(),
[](const MemoryBlock& a, const MemoryBlock& b) { return a.start < b.start; });
for (auto it = blocks.begin(); it != blocks.end(); ) {
auto next = std::next(it);
if (next != blocks.end() && it->free && next->free) {
it->size += next->size;
blocks.erase(next);
} else {
++it;
}
}
}
};
int main() {
MemoryAllocator allocator(1024); // 1024 bytes total memory
void* ptr1 = allocator.allocate(256);
void* ptr2 = allocator.allocate(128);
void* ptr3 = allocator.allocate(512);
allocator.deallocate(ptr2);
allocator.deallocate(ptr1);
void* ptr4 = allocator.allocate(384);
allocator.deallocate(ptr3);
allocator.deallocate(ptr4);
return 0;
}
</memoryblock></void></void>
이 메모리 할당기는 First-Fit 알고리즘을 사용합니다. 즉, 요청된 크기를 수용할 수 있는 첫 번째 자유 블록을 찾아 할당합니다. 또한, 메모리 단편화를 줄이기 위해 인접한 자유 블록들을 병합하는 기능도 구현되어 있습니다.
이러한 메모리 관리 기법은 실제 운영체제에서 사용되는 방식과 유사합니다. 물론 실제 운영체제는 더 복잡하고 효율적인 알고리즘을 사용하지만, 이 예제를 통해 기본적인 개념을 이해할 수 있습니다.
3. 파일 시스템: 간단한 가상 파일 시스템 구현 📁
파일 시스템은 운영체제의 또 다른 중요한 구성 요소입니다. 여기서는 간단한 가상 파일 시스템을 C++로 구현해보겠습니다. 이 시스템은 기본적인 파일 생성, 읽기, 쓰기, 삭제 기능을 지원합니다.
#include <iostream>
#include <unordered_map>
#include <string>
#include <vector>
#include <stdexcept>
class File {
public:
File(const std::string& name) : name(name) {}
void write(const std::string& content) {
this->content = content;
}
std::string read() const {
return content;
}
std::string getName() const {
return name;
}
private:
std::string name;
std::string content;
};
class Directory {
public:
Directory(const std::string& name) : name(name) {}
void addFile(const std::string& fileName) {
files[fileName] = File(fileName);
}
File& getFile(const std::string& fileName) {
if (files.find(fileName) == files.end()) {
throw std::runtime_error("File not found");
}
return files[fileName];
}
void deleteFile(const std::string& fileName) {
if (files.find(fileName) == files.end()) {
throw std::runtime_error("File not found");
}
files.erase(fileName);
}
std::vector<:string> listFiles() const {
std::vector<:string> fileList;
for (const auto& pair : files) {
fileList.push_back(pair.first);
}
return fileList;
}
std::string getName() const {
return name;
}
private:
std::string name;
std::unordered_map<:string file> files;
};
class FileSystem {
public:
void createDirectory(const std::string& dirName) {
directories[dirName] = Directory(dirName);
}
Directory& getDirectory(const std::string& dirName) {
if (directories.find(dirName) == directories.end()) {
throw std::runtime_error("Directory not found");
}
return directories[dirName];
}
void deleteDirectory(const std::string& dirName) {
if (directories.find(dirName) == directories.end()) {
throw std::runtime_error("Directory not found");
}
directories.erase(dirName);
}
std::vector<:string> listDirectories() const {
std::vector<:string> dirList;
for (const auto& pair : directories) {
dirList.push_back(pair.first);
}
return dirList;
}
private:
std::unordered_map<:string directory> directories;
};
int main() {
FileSystem fs;
fs.createDirectory("documents");
fs.createDirectory("pictures");
Directory& docs = fs.getDirectory("documents");
docs.addFile("report.txt");
docs.getFile("report.txt").write("This is a report");
Directory& pics = fs.getDirectory("pictures");
pics.addFile("vacation.jpg");
std::cout << "Directories: ";
for (const auto& dir : fs.listDirectories()) {
std::cout << dir << " ";
}
std::cout << std::endl;
std::cout << "Files in documents: ";
for (const auto& file : docs.listFiles()) {
std::cout << file << " ";
}
std::cout << std::endl;
std::cout << "Content of report.txt: " << docs.getFile("report.txt").read() << std::endl;
docs.deleteFile("report.txt");
fs.deleteDirectory("pictures");
std::cout << "Directories after deletion: ";
for (const auto& dir : fs.listDirectories()) {
std::cout << dir << " ";
}
std::cout << std::endl;
return 0;
}
</:string></:string></:string></:string></:string></:string>
이 가상 파일 시스템은 디렉토리와 파일의 기본적인 구조를 모방합니다. Directory 클래스는 파일들을 관리하고, FileSystem 클래스는 디렉토리들을 관리합니다. 각 파일은 이름과 내용을 가지며, 읽기와 쓰기 작업을 수행할 수 있습니다.
이러한 구조는 실제 파일 시스템의 기본적인 개념을 반영하고 있습니다. 물론 실제 파일 시스템은 더 복잡하고 효율적인 구조를 가지고 있지만, 이 예제를 통해 파일 시스템의 기본 개념을 이해할 수 있습니다.
4. 프로세스 간 통신(IPC): 파이프 구현 🔗
프로세스 간 통신(Inter-Process Communication, IPC)은 운영체제에서 중요한 개념 중 하나입니다. 여기서는 가장 기본적인 IPC 메커니즘 중 하나인 파이프를 C++로 구현해보겠습니다.