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:
| Syscall | Peran |
|---|---|
openat | membuka file, menghasilkan file descriptor (fd) |
read | menyalin data dari fd ke buffer program |
write | menyalin data dari buffer program ke fd |
close | menutup fd dan melepas resource kernel |
clone | membuat proses atau thread baru (fondasi fork()) |
execve | mengganti isi proses dengan program baru |
mmap | memetakan file atau memori ke ruang alamat proses |
exit_group | mengakhiri 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:
#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:
gcc -o baca-proc baca-proc.c && ./baca-procOutput yang Dihasilkan:
Name: baca-proc
Threads: 1Bedah program di atas baris-per-baris, lengkap dengan syscall di baliknya:
- Baris 8 (
fopen(...)): libc menerjemahkanfopen()menjadi syscallopenat; kernel mengembalikan fd yang dibungkus libc menjadi strukturFILE *. - Baris 9–12 (
if (f == NULL)): bilaopenatgagal,fopen()mengembalikanNULLdanperror()mencetak pesan berdasarkanerrno. - Baris 13 (
while (fgets(...))): tiapfgets()memicu syscallreaduntuk menarik potongan isi file ke buffer libc. - Baris 14–17 (
strncmp/fputs): hanya baris berawalanName:atauThreads:yang dicetak;fputs()memicu syscallwriteke fd 1 (stdout). - Baris 19 (
fclose(f)): menutupFILE *memicu syscallclosesehingga 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:
cat berkas-hilang.txt 2>&1Output yang Dihasilkan:
cat: berkas-hilang.txt: No such file or directoryUrutan 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:
cat strace-cat.txtOutput yang Dihasilkan:
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:
| Baris | Penjelasan |
|---|---|
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" ...) = 3 | file dibuka; kernel menugaskan fd 3 |
newfstatat(3, ...) | kernel melaporkan metadata file (ukuran, permission) |
read(3, ...) = 23 | 23 byte disalin dari file ke buffer cat |
write(1, ...) = 23 | isi 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:
for s in execve openat read write close; do
printf '%-8s %d\n' "$s" "$(grep -c "^$s(" strace-cat.txt)"
doneOutput yang Dihasilkan:
execve 1
openat 1
read 1
write 1
close 2Nilai 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/* 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;
}