Setiap program yang kita jalankan di terminal — mulai dari cat, ls, sampai server database — hidup di user space dan tidak boleh menyentuh hardware secara langsung. Satu-satunya jalan meminta layanan kernel adalah melewati gerbang resmi bernama system call. Chapter Kernel dan Kernel Modules sudah memperkenalkan pembagian wilayah kernel space dan user space; sekarang kita bedah pintu penghubungnya. Pada chapter ini kita pelajari mekanisme system call, peran libc sebagai pembungkus, pelaporan galat lewat errno, dan cara strace membongkar percakapan program dengan kernel.

Mekanisme System Call sebagai Gerbang Resmi ke Kernel #

Prosesor modern menjalankan kode pada beberapa tingkat hak akses, dan Linux memakai dua di antaranya: kernel berjalan di mode istimewa (ring 0) yang bebas menyentuh hardware, sedangkan aplikasi berjalan di mode terbatas (ring 3). Ketika program membutuhkan layanan — membaca file, membuat proses baru — program tidak bisa memanggil fungsi kernel sembarangan. Program memakai instruksi khusus CPU yang memicu perpindahan mode: pada x86-64 instruksinya bernama syscall. Nomor layanan dibawa di register (misalnya write bernomor 1), argumen di register lain, lalu kernel memeriksa keabsahan setiap argumen sebelum bekerja dan mengembalikan hasilnya ke register. Daftar lengkap mekanisme ini terdokumentasi di halaman manual syscall(2) .

Berikut syscall yang paling sering dipakai program sehari-hari:

SyscallPeran
openatmembuka file, menghasilkan file descriptor (fd)
readmenyalin data dari fd ke buffer program
writemenyalin data dari buffer program ke fd
closemenutup fd dan melepas resource kernel
clonemembuat proses atau thread baru (fondasi fork())
execvemengganti isi proses dengan program baru
mmapmemetakan file atau memori ke ruang alamat proses
exit_groupmengakhiri proses dan melaporkan exit status

Prinsip “everything is a file” membuat jangkauan tabel ini sangat luas: terminal, pipe, hingga koneksi jaringan semuanya diakses lewat fd sehingga cukup memakai read dan write.

libc sebagai Pembungkus Syscall #

Mengatur nomor syscall dan register satu per satu rawan salah, jadi hampir semua program memakai pustaka C (libc, misalnya glibc) sebagai pembungkus: fopen() membungkus openat, printf() pada akhirnya memanggil write, dan malloc() mengambil memori lewat brk atau mmap. Program berikut membaca /proc/self/status — jendela informasi kernel tentang proses itu sendiri — hanya dengan empat fungsi stdio:

c
#include <stdio.h>
#include <string.h>

int main(void) {
    FILE *f;
    char baris[256];

    f = fopen("/proc/self/status", "r");
    if (f == NULL) {
        perror("fopen");
        return 1;
    }
    while (fgets(baris, sizeof baris, f) != NULL) {
        if (strncmp(baris, "Name:", 5) == 0 ||
            strncmp(baris, "Threads:", 8) == 0) {
            fputs(baris, stdout);
        }
    }
    fclose(f);
    return 0;
}

Program di atas tersimpan sebagai baca-proc.c pada bundle chapter ini. Kompilasi dan jalankan:

bash
gcc -o baca-proc baca-proc.c && ./baca-proc

Output yang Dihasilkan:

text
Name:	baca-proc
Threads:	1

Bedah program di atas baris-per-baris, lengkap dengan syscall di baliknya:

  1. Baris 8 (fopen(...)): libc menerjemahkan fopen() menjadi syscall openat; kernel mengembalikan fd yang dibungkus libc menjadi struktur FILE *.
  2. Baris 9–12 (if (f == NULL)): bila openat gagal, fopen() mengembalikan NULL dan perror() mencetak pesan berdasarkan errno.
  3. Baris 13 (while (fgets(...))): tiap fgets() memicu syscall read untuk menarik potongan isi file ke buffer libc.
  4. Baris 14–17 (strncmp/fputs): hanya baris berawalan Name: atau Threads: yang dicetak; fputs() memicu syscall write ke fd 1 (stdout).
  5. Baris 19 (fclose(f)): menutup FILE * memicu syscall close sehingga kernel melepas fd.

Dua baris pada output dihasilkan kernel saat file dibaca — tidak ada file fisik di disk yang menyimpannya.

Kegagalan Syscall dan Pelaporan errno #

Setiap syscall mentaati kontrak seragam: bila berhasil, nilai kembalinya nol atau positif; bila gagal, kernel mengembalikan -1 dan menyetel variabel global errno dengan kode galat spesifik — antara lain ENOENT (berkas tidak ada), EACCES (izin ditolak), dan ENOSPC (penyimpanan penuh); daftar lengkapnya ada di errno(3) . Perintah berikut memicu jalur gagal karena berkasnya tidak pernah dibuat:

bash
cat berkas-hilang.txt 2>&1

Output yang Dihasilkan:

text
cat: berkas-hilang.txt: No such file or directory

Urutan kejadiannya: cat memanggil openat untuk berkas-hilang.txt, kernel menolak karena path tidak ada, openat mengembalikan -1 dengan errno = ENOENT, lalu libc menerjemahkan kode itu menjadi kalimat No such file or directory. Terakhir cat keluar dengan exit status 1 — nilai yang pada chapter exit codes kita kenal sebagai tanda kegagalan.

Melacak Syscall dengan strace #

Ketika program berperilaku aneh — file konfigurasi tidak terbaca, koneksi macet tanpa pesan — strace adalah alat diagnosis utama. strace memakai syscall ptrace untuk menghentikan target di setiap batas syscall, mencatat nama, argumen, dan nilai kembalinya. File strace-cat.txt pada bundle chapter ini berisi ringkasan keluaran strace cat catatan.txt:

bash
cat strace-cat.txt

Output yang Dihasilkan:

text
execve("/usr/bin/cat", ["cat", "catatan.txt"], 0x7ffc7e9d0050 /* 24 vars */) = 0
brk(NULL)                                 = 0x5613e21f0000
openat(AT_FDCWD, "catatan.txt", O_RDONLY) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=23}, 0) = 0
read(3, "halo dari file catatan\n", 23)    = 23
write(1, "halo dari file catatan\n", 23)    = 23
close(3)                                  = 0
close(1)                                  = 0
exit_group(0)                             = ?
+++ exited with 0 +++

Arti tiap baris pentingnya:

BarisPenjelasan
execve("/usr/bin/cat", ...)kernel mengganti isi proses shell dengan program cat; semua program dimulai dari sini
brk(NULL)cat menanyakan batas heap untuk menyiapkan buffer
openat(... "catatan.txt" ...) = 3file dibuka; kernel menugaskan fd 3
newfstatat(3, ...)kernel melaporkan metadata file (ukuran, permission)
read(3, ...) = 2323 byte disalin dari file ke buffer cat
write(1, ...) = 23isi buffer ditulis ke fd 1 (stdout) — momen teks muncul di layar
close(3), close(1)fd dilepas setelah selesai dipakai
exit_group(0)proses berakhir dengan status sukses

Keluaran strace sesungguhnya dari cat memuat puluhan baris tambahan (mmap, access, pemeriksaan pustaka), jadi fixture ini sengaja diringkas ke jalur inti. Skrip kecil berikut menghitung frekuensi syscall utama dari fixture tersebut:

bash
for s in execve openat read write close; do
    printf '%-8s %d\n' "$s" "$(grep -c "^$s(" strace-cat.txt)"
done

Output yang Dihasilkan:

text
execve   1
openat   1
read     1
write    1
close    2

Nilai close yang mencapai dua sesuai penjelasan tabel di atas: satu untuk fd 3 dan satu untuk fd 1.

Dengan system call, kita memahami bagaimana program berbicara dengan kernel. Pertanyaan berikutnya: bagaimana mengintip keadaan kernel dan proses lain tanpa alat khusus? Kernel menyediakan jawabannya dalam bentuk dua pseudo filesystem, /proc dan /sys, yang kita bedah pada chapter berikutnya.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

baca-proc.c

Unduh berkas
c
/* baca-proc.c — membaca /proc/self/status lewat stdio (libc). */
#include <stdio.h>
#include <string.h>

int main(void) {
    FILE *f;
    char baris[256];

    f = fopen("/proc/self/status", "r");
    if (f == NULL) {
        perror("fopen");
        return 1;
    }
    while (fgets(baris, sizeof baris, f) != NULL) {
        if (strncmp(baris, "Name:", 5) == 0 ||
            strncmp(baris, "Threads:", 8) == 0) {
            fputs(baris, stdout);
        }
    }
    fclose(f);
    return 0;
}