Server yang lengang tiba-tiba berat: terminal patah-patah, halaman lambat, kipas meraung. Dua tersangka terbesar selalu sama — CPU yang diperebutkan atau memori yang menipis — dan keduanya menuntut reaksi berbeda. CPU penuh hanya membuat proses mengantri, tetapi memori habis memaksa kernel membunuh proses lewat OOM killer tanpa mempersoalkan mana yang paling penting. Chapter ini melatih urutannya: menemukan proses rakus dari satu layar ps, memahami apa yang sedang ia lakukan, menghentikannya dengan tingkatan yang sopan, lalu membaca jejak yang ditinggalkan OOM killer dan swap.
Menemukan Proses Rakus dari Snapshot #
Perintah klasiknya singkat: ps aux --sort=-%cpu | head mengurutkan seluruh proses berdasarkan kolom %CPU — materi lengkapnya di ps, top, dan htop
. Namun saat insiden berlalu, yang tersisa biasanya hanya snapshot yang sempat disimpan — file teks berisi output ps aux pada menit kejadian. Keterampilan mengurutkannya kembali dengan awk membuat snapshot mati tetap bisa diinterogasi:
cat > ps-snapshot.txt <<'AKHIR'
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 169400 12280 ? Ss 08:02 0:01 /sbin/init
budi 4712 91.3 2.1 517233 87600 ? Sl 09:14 12:03 /usr/bin/python3 /opt/analisis.py
mysql 3310 4.2 18.7 1620300 762300 ? Sl 08:02 3:41 /usr/sbin/mysqld
www-d 5200 3.1 2.4 312450 98210 ? S 08:03 2:12 /usr/sbin/apache2 -k start
siti 6100 0.6 12.9 421900 524800 ? Sl 09:31 0:22 /usr/bin/firefox
budi 5120 0.2 0.6 74310 24800 ? Ss 09:20 0:05 sshd: budi@pts/0
AKHIR
echo "== tiga teratas versi CPU =="
awk 'NR>1 {print $3, $2, $1, $11}' ps-snapshot.txt | sort -rn | head -3
echo "== tiga teratas versi memori =="
awk 'NR>1 {print $4, $2, $1, $11}' ps-snapshot.txt | sort -rn | head -3Output yang Dihasilkan:
== tiga teratas versi CPU ==
91.3 4712 budi /usr/bin/python3
4.2 3310 mysql /usr/sbin/mysqld
3.1 5200 www-d /usr/sbin/apache2
== tiga teratas versi memori ==
18.7 3310 mysql /usr/sbin/mysqld
12.9 6100 siti /usr/bin/firefox
2.4 5200 www-d /usr/sbin/apache2Mari kita bedah perintah-perintah kuncinya:
- Baris 9 (
awk 'NR>1 {print $3, $2, $1, $11}' ...):NR>1melewati baris judul; tiga kolom yang dicetak sengaja dimulai dari angka —%CPUdi depan membuatsort -rnbisa mengurutkan seluruh baris secara numerik tanpa opsi kolom. - Baris 10 (
sort -rn | head -3):-rmengurutkan terbesar dulu,-nmemaksa perbandingan angka — tanpa opsi ini,9.5akan menempati urutan setelah91.3karena keduanya diawali digit9. $11(kolom COMMAND): karena command bisa mengandung spasi,$11hanya mengambil kata pertamanya — cukup untuk mengenali program; versi lengkapnya tetap ada di file asli.
Hasilnya menunjuk satu tersangka jauh di atas yang lain: python3 milik budi menggerus 91.3% CPU. Sebelum menghakimi, kita perlu tahu apa yang sedang ia kerjakan.
Membedah Perilaku Tersangka #
Angka %CPU memberi tahu siapa, bukan mengapa. Dua alat menjawab pertanyaan kedua. strace -p PID memperlihatkan system call yang dipanggil proses per detiknya — polanya sangat bicara. Berikut contoh tampilan belasan detik menelusuri proses analisis tadi:
read(3, "baris data ke-8124\n", 4096) = 19
write(1, "interim 41%\n", 12) = 12
read(3, "baris data ke-8125\n", 4096) = 19
write(1, "interim 41%\n", 12) = 12
read(3, "baris data ke-8126\n", 4096) = 19Himpunan read-write yang berulang dengan cepat menandakan pekerjaan sungguhan — proses sedang membaca dan memproses data, bukan macet. Bandingkan dengan baris yang sama terus-menerus menampilkan futex(...) (proses mati-matian menunggu lock) atau read( yang tidak pernah kembali (menunggu I/O): itu bukan proses rakus, melainkan proses yang terjebak — dan pembunuhannya tidak akan menambah CPU sedikit pun. Alat kedua, lsof -p PID, mendaftar file yang sedang dibukanya: berguna untuk memastikan proses membaca file yang benar dan tidak sedang menggerus partition backup yang salah sasaran. Konsep system call sebagai gerbang user space ke kernel dibedah di Process Monitoring
.
Menghentikan dengan Tingkatan Sinyal #
Setelah yakin proses memang bermasalah, cara menghentikannya pun bertingkat — seperti mengetuk pintu, meminta dengan sopan, baru mendobrak:
| Sinyal | Perintah | Perilaku proses | Kapan dipakai |
|---|---|---|---|
| SIGHUP (1) | kill -HUP PID | umumnya: baca ulang konfigurasi | meminta daemon reload tanpa putus layanan |
| SIGTERM (15) | kill PID | berhenti rapi: menyimpan data, menutup file | pilihan pertama menghentikan |
| SIGKILL (9) | kill -9 PID | mati seketika, tak bisa ditangkap | pilihan terakhir saja |
SIGTERM adalah permintaan yang bisa ditangkap program — program diberi kesempatan menyelesaikan pekerjaan: menyiram data ke disk, menutup koneksi, menghapus file kuncinya. SIGKILL dikerjakan langsung oleh kernel tanpa sepengetahuan proses, sehingga file setengah tulis dan kunci yang tertinggal menjadi risiko nyata. Materi lengkap sinyal beserta konvensi exit status 128 + nomor sinyal sudah dibahas di Signals dan Process Termination
.
Untuk runaway process yang masih sehat — misalnya skrip analisis budi yang memang sedang memproses file besar — opsi paling sopan bukan membunuh sama sekali: turunkan prioritasnya dengan renice agar proses lain tetap lincah, materi nice dan renice
. Membunuh hanya jika prosesnya memang salah jalan.
Jejak OOM Killer di Log #
Skenario memori berbeda sama sekali: kita tidak selalu sempat melihat proses rakus sebelum ia memakan semuanya. Yang tersisa adalah korban — proses yang dibunuh kernel untuk menyelamatkan sistem. Jejaknya selalu ada di log kernel, polanya Out of memory: Killed process:
cat > dmesg-oom.log <<'AKHIR'
Jan 15 09:31:44 server-kantor kernel: memori tersisa 3% dari 16361276kB, aktif mencari korban
Jan 15 09:31:45 server-kantor kernel: Out of memory: Killed process 4712 (analisis.py) total-vm:517233kB, anon-rss:87600kB
Jan 15 09:32:40 server-kantor kernel: Out of memory: Killed process 3310 (mysqld) total-vm:1620300kB, anon-rss:762300kB
AKHIR
grep 'Out of memory' dmesg-oom.log | awk '{print "korban :", $12, "— PID", $11}' | tr -d '()'Output yang Dihasilkan:
korban : analisis.py — PID 4712
korban : mysqld — PID 3310Urutan korban itu justru membingungkan pembaca pemula: korban kedua adalah mysqld yang RSS-nya 762 MB — lebih besar dari analisis.py yang 87 MB. OOM killer tidak membunuh pemakan memori terbesar, melainkan proses dengan skor “kejahatan” tertinggi — perhitungan yang mempertimbangkan total memori, waktu hidup, dan prioritas. Jadi jangan menyalahkan korban: mysqld yang mati adalah gejala, dan pemakan memori sesungguhnya harus dicari dari snapshot ps sebelum insiden. Mekanisme skor ini dibahas dari sisi pemantauan di Memory Monitoring
; pada kasus di atas, analisis.py yang menggelembung adalah tersangka utamanya.
Swap Thrashing: Lambat Meski CPU Santai #
Gejala satu lagi sering menipu: sistem merangkak, tetapi %CPU rendah dan tidak ada proses yang menonjol. Periksa swap. Saat RAM penuh, kernel mulai memindahkan halaman memori bolak-balik ke disk — di vmstat, kolom si/so yang terus berganti angka bukan nol adalah tanda thrashing: mesin menghabiskan waktunya memindahkan memori, bukan menghitung. Solusinya dua arah: hentikan proses pemakan memori terbesar (kembali ke ps aux --sort=-%mem), lalu tinjau ukuran swap dan memori fisiknya di Swap Space
. Swap memang menyelamatkan sistem dari OOM, tetapi swap yang sibuk terus-menerus berarti RAM sudah jauh ketinggalan kebutuhan.
Pola Umum dan Skrip Deteksi #
Snapshot sekali pandang bisa ditangkap otomatis dengan ambang sederhana. Skrip berikut menandai proses yang melewati batas CPU atau memori:
cat > deteksi-rakus.sh <<'AKHIR'
#!/bin/bash
# Tandai proses di atas ambang dari snapshot ps.
cpu_min=50
mem_min=15
awk -v c="$cpu_min" -v m="$mem_min" \
'NR>1 && ($3+0 >= c || $4+0 >= m) {
printf "PERINGATAN %s (PID %s): CPU %s%%, MEM %s%%\n", $11, $2, $3, $4
}' ps-snapshot.txt
echo "diperiksa: $(($(wc -l < ps-snapshot.txt) - 1)) proses"
AKHIR
cat > ps-snapshot.txt <<'AKHIR'
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 169400 12280 ? Ss 08:02 0:01 /sbin/init
budi 4712 91.3 2.1 517233 87600 ? Sl 09:14 12:03 /usr/bin/python3 /opt/analisis.py
mysql 3310 4.2 18.7 1620300 762300 ? Sl 08:02 3:41 /usr/sbin/mysqld
www-d 5200 3.1 2.4 312450 98210 ? S 08:03 2:12 /usr/sbin/apache2 -k start
siti 6100 0.6 12.9 421900 524800 ? Sl 09:31 0:22 /usr/bin/firefox
budi 5120 0.2 0.6 74310 24800 ? Ss 09:20 0:05 sshd: budi@pts/0
AKHIR
bash deteksi-rakus.shOutput yang Dihasilkan:
PERINGATAN /usr/bin/python3 (PID 4712): CPU 91.3%, MEM 2.1%
PERINGATAN /usr/sbin/mysqld (PID 3310): CPU 4.2%, MEM 18.7%
diperiksa: 6 prosesBedah baris-per-barisnya:
- Baris 4-5 (
cpu_min=50danmem_min=15): ambang dipasang sebagai variabel di puncak skrip — mudah diubah tanpa menyentuh logika;awkmenerimanya lewat opsi-v. - Baris 6 (
NR>1 && ($3+0 >= c || $4+0 >= m)):+0memaksa perbandingan numerik — kolom tetap teks diawk, dan teks"91.3"dibandingkan dengan angka bisa menipu; syaratnya di-OR agar satu layar menangkap dua jenis kerakusan sekaligus. - Baris 13 (
$(($(wc -l < ps-snapshot.txt) - 1))): jumlah proses = jumlah baris dikurangi baris judul; tanda$(( ))mengevaluasi aritmetika di dalamecho.
Tabel berikut merangkum pola yang paling sering ditemui beserta langkah pertamanya:
| Gejala | Penyebab paling umum | Tindakan pertama |
|---|---|---|
satu proses %CPU jauh di atas lain | runaway process: loop atau pekerjaan sungguhan | strace -p PID; renice bila pekerjaan sah |
load tinggi, %CPU rendah, merangkak | swap thrashing | cek si/so vmstat; hentikan pemakan memori |
Out of memory: Killed process di log | RAM habis, OOM killer bertindak | cari pemakan memori dari snapshot ps |
| service mati berulang tiap kali sibuk | OOM memilih service itu sebagai korban | kurangi kebutuhan, tambah RAM/swap |
%CPU tinggi hanya di %sy (kernel) | pekerjaan sistem: I/O berat atau banyak syscall | periksa iowait di Disk Monitoring |
Proses rakus biasanya menyelesaikan ceritanya di sini: ditemukan, dipahami, dihentikan dengan sopan. Namun tidak semua masalah tinggal di user space — kadang CPU dan memori yang sehat dikalahkan oleh perangkat keras yang tidak dikenali sistemnya. Cara mengidentifikasi perangkat tanpa driver itulah materi chapter berikutnya.
Referensi lengkapnya: kolom-kolom ps di ps(1)
, pengiriman sinyal di kill(1)
, dan daftar sinyal beserta perilaku bawaannya di signal(7)
.