Belajar C++ - System Programming & Low-level Integration
Series/Belajar C++/Episode 18
Episode 18 of 24

Belajar C++ - System Programming & Low-level Integration

Episode ini membahas system programming: interaksi dengan system call dan API sistem operasi, pembuatan proses dengan fork dan pipes untuk inter-process communication, memory mapped files dan low-level I/O, serta dasar embedded systems dan pertimbangan bare-metal.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

C++ tidak hanya untuk aplikasi berlevel tinggi. Ketika aplikasi harus berinteraksi dengan kernel, mengelola proses, atau berjalan di perangkat tanpa sistem operasi penuh, C++ adalah pilihan utama. Inilah system programming.

Episode 18 membawa kalian ke lapisan bawah: system call dan API POSIX, pembuatan proses dengan fork dan komunikasi lewat pipes, inter-process communication, memory mapped files dan low-level I/O, serta konsep embedded dan bare-metal. Semua contoh memakai Linux dengan POSIX API.

System Call dan API Sistem Operasi

Antarmuka antara Aplikasi dan Kernel

System call adalah pintu masuk aplikasi ke kernel: membuka file, membaca, mengirim data, dan memulai proses. Bahasa C menyediakan pembungkus langsung seperti open, read, write, dan fork. Di C++, kalian bisa memakainya langsung atau membungkusnya dalam class RAII:

Menulis file dengan write
cat > syscall.cpp <<'EOF'
#include <iostream>
#include <fcntl.h>
#include <unistd.h>
 
int main() {
    int fd = open("keluaran.txt",
                  O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd < 0) {
        std::cerr << "gagal membuka file\n";
        return 1;
    }
 
    const char* teks = "tulisan dari system call\n";
    write(fd, teks, 24);
    close(fd);
    std::cout << "selesai\n";
}
EOF
g++ -std=c++20 syscall.cpp -o syscall
./syscall

open("keluaran.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644) membuat file untuk ditulis. write(fd, teks, 24) menulis byte ke file, dan close(fd) menutupnya. Header <fcntl.h> dan <unistd.h> menyediakan deklarasi POSIX.

Membungkus dengan RAII

Pola aman: bungkus file descriptor dalam class yang destructornya memanggil close. File ditutup otomatis saat objek keluar dari scope — persis prinsip RAII dari episode 7. Setiap open berpasangan dengan close, dan RAII menjaminnya meski ada exception.

Membuat Proses dan Pipe

fork dan wait

fork() menduplikasi proses berjalan menjadi dua: parent dan child. waitpid membuat parent menunggu child selesai:

Fork dan wait
cat > fork.cpp <<'EOF'
#include <iostream>
#include <sys/wait.h>
#include <unistd.h>
 
int main() {
    pid_t pid = fork();
 
    if (pid == 0) {
        std::cout << "Child: proses anak\n";
        return 0;
    } else {
        int status;
        waitpid(pid, &status, 0);
        std::cout << "Parent: child selesai\n";
    }
}
EOF
g++ -std=c++20 fork.cpp -o fork
./fork

fork() mengembalikan 0 di proses anak dan PID anak di proses induk. waitpid(pid, &status, 0) membuat parent menunggu. Setelah fork, kedua proses melanjutkan dari baris yang sama — perbedaan hanya pada nilai kembalian.

Pipe untuk Komunikasi

Pipe adalah saluran satu arah antara dua proses. pipe(fds) membuat dua deskriptor: fds[1] untuk menulis, fds[0] untuk membaca. Pola umumnya: fork, tutup ujung yang tidak dipakai di masing-masing proses, lalu kirim:

Pipe antar proses
cat > pipe.cpp <<'EOF'
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
 
int main() {
    int fds[2];
    pipe(fds);
 
    pid_t pid = fork();
    if (pid == 0) {
        close(fds[0]);
        write(fds[1], "pesan dari child", 16);
        close(fds[1]);
    } else {
        close(fds[1]);
        char buf[64];
        int n = read(fds[0], buf, sizeof(buf));
        buf[n] = '\0';
        std::cout << "Parent terima: " << buf << "\n";
        close(fds[0]);
        waitpid(pid, nullptr, 0);
    }
}
EOF
g++ -std=c++20 pipe.cpp -o pipe
./pipe

pipe(fds) membuat pipe, dan setelah fork, child menulis ke fds[1] sementara parent membaca dari fds[0]. Ujung yang tidak dipakai ditutup di masing-masing proses — penting agar read tahu kapan data berakhir.

Inter-Process Communication

Pilihan IPC

Selain pipe, Unix menyediakan berbagai mekanisme IPC: message queues untuk pesan terstruktur, shared memory untuk data bersama dengan kecepatan tertinggi, dan Unix domain sockets untuk komunikasi mirip jaringan antar proses. Pilihan bergantung pada kebutuhan:

  • Pipe: sederhana, satu arah, untuk parent-child.
  • Message queue: berurutan dan terstruktur, tanpa hubungan proses.
  • Shared memory: tercepat, perlu sinkronisasi manual.
  • Unix socket: fleksibel, dua arah, mudah diperluas ke jaringan.

Pipe cukup untuk komunikasi sederhana. Untuk pola yang lebih kaya, pertimbangkan message queue atau socket — keduanya memakai konsep dari episode 13. Shared memory menuntut mutex atau semaphore untuk mencegah race, seperti di episode 12.

Memory Mapped Files dan Low-level I/O

mmap: File sebagai Memori

Memory mapped files memetakan isi file langsung ke ruang alamat proses dengan mmap. Panggilan mmap(nullptr, len, PROT_READ, MAP_PRIVATE, fd, 0) memetakan file read-only, lalu p[i] membaca byte file seolah-olah array. munmap(p, len) melepas pemetaan. Pola ini efisien untuk file besar dan jadi dasar database serta runtime loader.

Low-level I/O versus iostream

Low-level I/O dengan read dan write bekerja pada byte mentah, sedangkan std::ifstream menambahkan pemformatan. Untuk data biner berukuran besar, low-level I/O memberi kontrol penuh. Untuk teks terformat, iostream lebih nyaman — episode 9 membahas sisi iostream lengkap.

Dasar Embedded dan Bare-metal

C++ di Dunia Tanpa OS

Di bare-metal, tidak ada sistem operasi, malloc, atau standard library penuh. Program memakai freestanding subset C++ dan menulis langsung ke register perangkat:

Gaya bare-metal
#include <cstdint>
 
volatile std::uint32_t* REG =
    reinterpret_cast<std::uint32_t*>(0x40021000);
 
void tulis() {
    *REG = 0x1;
}

volatile std::uint32_t* REG menunjuk ke alamat register perangkat keras, dan menulis ke *REG mengirim nilai ke perangkat. Kata kunci volatile mencegah compiler menghapus atau menata ulang akses.

Info

Di embedded, hindari new dan exception yang membutuhkan runtime besar. Pakai alokasi statis, std::array, dan error handling berbasis status — pola yang dibahas di episode 21.

Penutup

Inti yang harus dibawa pulang:

  • System call seperti open, write, dan close adalah pintu ke kernel.
  • Bungkus file descriptor dengan RAII agar selalu tertutup.
  • fork menduplikasi proses; waitpid menunggu child selesai.
  • Pipe menghubungkan proses induk dan anak secara satu arah.
  • mmap memetakan file sebagai memori untuk akses cepat.
  • Bare-metal memakai freestanding C++ dan akses register langsung.

Di episode 19 selanjutnya kita akan membahas tooling modern dan build automation — workflow CMake modern, continuous integration dengan GitHub Actions dan GitLab CI, static analysis dan code formatting dengan clang-format, serta reproducible builds dan dependency management.

Belajar C++ - System Programming & Low-level Integration | Belajar C++