Home / Linux, Systems, and Infrastructure / Proses Linux: …

Proses Linux: Membaca ps, top, dan Mengendalikan dengan Signal

Saat program nge-hang, pertanyaan pertama biasanya “prosesnya masih hidup atau sudah mati?” Satu huruf di kolom STAT dan satu angka di /proc sudah bisa menjawabnya. Artikel ini menjalankan proses uji sungguhan di Ubuntu 26.04.1 dengan kernel 7.0 dan procps-ng 4.0.4, lalu menunjukkan output aslinya.

Ringkasan

  • ps memberi potret sekali jalan, top memberi tampilan yang berulang.
  • Kolom STAT punya satu huruf utama plus beberapa penanda tambahan.
  • WCHAN menampilkan fungsi kernel tempat proses menunggu.
  • Z berarti proses selesai tapi belum di-reap, sehingga masih memblokir slot PID.
  • PPID bernilai 1 menandai proses tanpa induk yang sudah di-reparent ke init.
  • SIGKILL dan SIGSTOP tidak bisa ditangkap, diblokir, atau diabaikan.
  • SIGTERM memungkinkan program membersihkan diri, SIGKILL tidak.
  • kill -0 memeriksa keberadaan tanpa mengirim sinyal apa pun.

Lingkungan Pengujian

Seluruh output di bawah berasal dari mesin uji, bukan asumsi.

KomponenNilai TerujiKeterangan
DistribusiUbuntu 26.04.1 LTSKernel 7.0.0-31-generic
Arsitekturx86_6412 thread CPU, pid_max 4194304
procpsprocps-ng 4.0.4Menyediakan ps dan top
PID 1systemdPenerima proses tanpa induk yang di-reparent
Beban saat uji375 proses45 ber-PPID 1, 2 ber-PPID 0

Nama kolom dan arti statusnya mengikuti paket procps, jadi distribusi lain bisa punya versi berbeda. Verifikasi cepat:

bash
ps --version
top -h | head -3
uname -srmo

Satu jebakan kecil: top dari procps-ng 4.0.4 tidak punya opsi -v. Memanggilnya menghasilkan top: invalid option -- 'v'. Gunakan -V atau -h.

Snapshot Sekali Jalan dengan ps

ps(1) menjelaskan batasannya sendiri di bagian DESCRIPTION: “ps displays information about a selection of the active processes. If you want a repetitive update of the selection and the displayed information, use top instead.”

Artinya pemilihan alatnya mengikuti kebutuhan. Kalau butuh satu potret untuk di-pipe ke awk atau disimpan ke file, ps cukup. Kalau butuh melihat proses yang berganti status, top yang tepat.

Kolom STAT di format BSD perlu dibaca sebagai satu rangkaian karakter, bukan satu huruf:

text
$ ps -eo pid,ppid,pgid,sid,user,stat,wchan:22,etime,time,comm --sort=-%cpu | head -5
    PID    PPID    PGID     SID USER     STAT WCHAN                      ELAPSED     TIME COMMAND
   7530    7262    7530    7262 su       Rl+  -                            56:25 00:48:38 opencode
  45797       1    3226    3226 su       Sl   poll_schedule_timeout.       03:07 00:01:12 firefox
  46461   46021    3226    3226 su       Sl   poll_schedule_timeout.       03:04 00:00:46 Isolated Web Co
   2102    2016    2102    2102 root     Rsl+ -                         01:12:47 00:09:36 Xorg
   7236       1    3226    3226 su       Sl   poll_schedule_timeout.    56:38 00:06:30 xfce4-terminal

Membaca Huruf Utama

Huruf pertama berasal dari tabel PROCESS STATE CODES di ps(1), dan daftar yang sama bisa ditemukan di kolom ketiga /proc/<pid>/stat menurut proc_pid_stat(5).

StateArtiTindakan yang Wajar
RRunning atau runnable, ada di run queueNormal, tidak perlu tindakan
SInterruptible sleep, menunggu event selesaiNormal, cek WCHAN bila mencurigakan
DUninterruptible sleep, biasanya menunggu I/OTunggu atau periksa storage
ZZombie, selesai tapi belum di-reapPeriksa program induknya
TBerhenti karena sinyal job controlSIGCONT untuk melanjutkan
IIdle kernel threadNormal untuk thread kernel

Penjelasan D di ps(1) adalah “uninterruptible sleep (usually IO)”. Ini state yang sering membuat orang salah diagnosa, karena proses di D tidak bisa dihentikan dengan SIGKILL sampai I/O-nya selesai. Pada mesin uji ini tidak ada disk lambat atau mount jaringan yang bisa menahan proses, jadi state D tidak berhasil direproduksi; pada mesin dengan NVMe dan I/O sehat, D memang jarang muncul dan tidak lama.

Membaca Karakter Tambahan

Huruf kedua dan seterusnya adalah penanda sifat proses, juga dari tabel yang sama.

KarakterArti
lMultithread, memakai CLONE_THREAD seperti pthread NPTL
sSession leader
+Berada di foreground process group
<Prioritas tinggi, kurang ramah ke user lain
NPrioritas rendah, ramah ke user lain
LHalaman terkunci di memori

Karakter l bisa dikonfirmasi dengan kolom nlwp, yaitu jumlah thread dalam proses:

text
$ ps -o pid,nlwp,comm -p 45797 -p 7530
    PID NLWP COMMAND
  45797  122 firefox
   7530   41 opencode

Dua proses di tabel sebelumnya punya l, dan NLWP mengonfirmasi keduanya memang multithread. Karakter + pada opencode dan Xorg menandai keduanya berada di foreground process group, sedangkan firefox tidak.

Jebakan Opsi Seleksi ps

Opsi seleksi pada ps bersifat aditif, dan -e berarti “pilih semua proses”. Menggabungkannya dengan -p tidak menyaring:

text
$ ps -eo pid,nlwp,comm -p 1,7530
... (seluruh proses tercetak, bukan hanya 2 baris)

Untuk mengisolasi satu PID, jangan pakai -e:

bash
ps -o pid,ppid,stat,wchan:20,etime,comm -p 7530
ps -o pid,ppid,stat,comm --ppid 7530

Opsi --ppid berguna untuk melihat anak langsung sebuah proses tanpa harus membaca seluruh tabel.

Menelusuri Proses yang Ngelant

Dua hal yang paling sering dicari saat program menggantung: apa yang ditunggu kernel, dan berapa sumber daya yang terpakai.

WCHAN dan /proc

Kolom wchan berisi nama fungsi kernel tempat proses menunggu, sedangkan /proc/<pid>/wchan memberi sumber yang sama tanpa memotong nama:

text
$ ps -o pid,stat,pcpu,wchan:20,comm -p 48217 -p 48218
    PID STAT %CPU WCHAN                COMMAND
  48217 R     100 -                    python3
  48218 S     0.0 hrtimer_nanosleep    sleep

$ cat /proc/48218/wchan
hrtimer_nanosleep

Proses yang sedang berjalan menampilkan - karena tidak sedang menunggu apa pun. Pada mesin uji, proses di state S menampilkan nama fungsi kernel tempat ia tertidur, dari hrtimer_nanosleep untuk sleep sampai poll_schedule_timeout. untuk aplikasi berbasis event loop.

Berkas /proc/<pid>/status memberi versi yang lebih mudah dibaca manusia:

text
$ grep -E '^(Name|State|Threads|SigBlk|SigIgn|SigCgt):' /proc/48218/status
Name:	sleep
State:	S (sleeping)
Threads:	1
SigBlk:	0000000000000000
SigIgn:	0000000000000006
SigCgt:	0000000000000000

Tiga baris terakhir adalah bitmask dari 64 sinyal. SigIgn bernilai 0x6, yaitu biner 110, sehingga bit 2 dan bit 3 menyala: sinyal nomor 2 dan 3 diabaikan. Ini bukan keanehan, melainkan aturan POSIX bahwa bash non-interaktif membuat job latar mengabaikan SIGINT dan SIGQUIT.

Konsekuensi praktisnya sering membuat bingung: kill -INT pada proses latar yang dimulai dari skrip bash tidak berefek, sementara kill -TERM selalu berefek. Kalau sinyal percobaan tidak melakukan apa-apa, periksa SigIgn di /proc/<pid>/status sebelum menyimpulkan programnya yang salah.

Ringkasan Real-time dengan top

top(1) menyediakan “a dynamic real-time view of a running system”, termasuk daftar proses maupun thread. Untuk penggunaan noninteraktif, -b membuat output plain text dan -n1 berhenti setelah satu iterasi, yang berguna untuk snapshot yang bisa disimpan.

text
$ top -b -n1 | head -3
top - 13:16:26 up  1:12,  1 user,  load average: 3.35, 3.03, 3.26
Tasks: 369 total,   2 running, 367 sleeping,   0 stopped,   0 zombie
%Cpu(s): 22.1 us,  4.4 sy,  0.0 ni, 72.1 id,  0.7 wa,  0.0 hi,  0.7 si,  0.0 st

$ top -b -n1 -H -p 1 | head -4
top - 13:33:13 up  1:29,  1 user,  load average: 3.64, 3.67, 3.48
Threads:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s): 16.5 us,  2.5 sy,  0.0 ni, 80.2 id,  0.7 wa,  0.0 hi,  0.8 si,  0.0 st
    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND

Dua detail yang sering luput. Tanpa -H, ringkasan memakai kata “Tasks”, dan dengan -H kata itu berubah jadi “Threads”. Dan opsi -p 1 membatasi tampilan ke satu proses saja, agar tidak kewalahan saat menelusuri satu PID yang bermasalah. Urutan kolom yang dipakai bisa diubah tanpa perlu menebak, karena top -O mencetak 78 nama field yang tersedia.

Zombie dan Proses Tanpa Induk

Dua kondisi yang sering tertukar harus dibedakan karena penyebabnya berbeda.

Zombie: Sudah Berakhir, Belum Di-reap

Menurut definisi di ps(1), Z adalah “defunct (zombie) process, terminated but not reaped by its parent”. Skrip uji membuat anak yang langsung keluar sementara induknya tidak pernah memanggil wait():

text
parent pid = 48191, anak = 48193
    PID    PPID STAT NLWP COMMAND
  48193   48191 Z       1 python3

$ awk '{print $3}' /proc/48193/stat
Z

$ ps -eo stat= | grep -c '^Z'
1

Zombie tidak memakai CPU dan hampir tidak memakai memori, tetapi tetap menempati satu entri tabel proses dan memblokir nomor PID. Setelah induknya dihentikan, kernel memindahkan anaknya ke PID 1, yang langsung me-reap, dan hitungan kembali nol. Jadi zombie yang menumpuk hampir selalu menandakan bug di program induknya, bukan masalah di kernel. Memperbaiki program induknya adalah langkah yang benar; kill -9 pada zombie tidak bekerja karena prosesnya sudah tidak ada.

Tanpa Induk: Masih Hidup, Induknya yang Berakhir

Kondisi berbeda terjadi ketika proses induknya berakhir lebih dulu. pid_namespaces(7) menyatakan: “When a child process becomes orphaned, it is reparented to the ‘init’ process in the PID namespace of its parent”.

text
PID 1 di mesin ini = 1 (systemd)
proses tanpa induk pid = 48496
    PID    PPID STAT COMMAND
  48496       1 S    python3

Statusnya S, bukan Z. Prosesnya masih hidup dan masih memakai sumber daya, hanya induknya yang sudah tidak ada. Di mesin uji, 45 dari 375 proses ber-PPID 1, sebagian besar service yang dijalankan systemd, dan dua proses ber-PPID 0: PID 1 itu sendiri dan kthreadd yang memang tidak punya induk di user space.

Menghentikan Proses dengan Signal

signal(7) mencatat bahwa setiap sinyal punya disposition default, yaitu Term (akhiri), Ign (abaikan), Core (akhiri plus core dump), Stop, atau Cont. Proses bisa mengubahnya lewat sigaction(2) atau signal(2), sehingga hasil dari sinyal yang sama bisa sangat berbeda antarprogram.

Sinyal yang dipakai di artikel ini, beserta disposition default-nya:

SinyalDefaultNomor di x86-64Peran Praktis
SIGHUPTerm1Minta program memuat ulang konfigurasi
SIGINTTerm2Interup dari keyboard, Ctrl+C
SIGKILLTerm9Hentikan paksa, tidak bisa ditangkap
SIGUSR1Term10Milik aplikasi, sering dipakai untuk membuka ulang berkas log
SIGTERMTerm15Minta program berhenti dengan rapi
SIGSTOPStop19Bekukan proses, tidak bisa ditangkap

Nomor sinyal di signal(7) berbeda antar arsitektur, misalnya SIGSTOP bernomor 19 di x86-64 tapi 17 di i386. Karena itu lebih aman memakai nama sinyal daripada angka keras di dalam skrip.

Default Action dan Exit Code

Proses uji berikut tidak memasang handler sama sekali, jadi semua sinyal memakai disposition default:

text
SIGTERM, tanpa handler  -> exit 143
SIGHUP,  tanpa handler  -> exit 129
SIGUSR1, tanpa handler  -> exit 138
SIGKILL, tanpa handler  -> exit 137
SIGPIPE, tanpa handler  -> exit 141

Pola exit code selalu 128 ditambah nomor sinyal: 15 jadi 143, 1 jadi 129, 10 jadi 138, 9 jadi 137, 13 jadi 141. Ini konvensi shell untuk melaporkan proses yang berakhir akibat sinyal, bukan angka yang dikembalikan kernel. Dengan begitu exit code 143 berarti proses berhenti karena SIGTERM, sedangkan 137 berarti berhenti karena SIGKILL.

Handler Mengubah Segalanya

Begitu program memasang handler, default action tidak berlaku lagi. Ketiga sinyal berikut tertangani dan program keluar dengan status 0:

text
SIGTERM, ada handler   -> exit 0   | handler: terima 15, keluar bersih exit 0
SIGHUP,  ada handler   -> exit 0   | handler: terima 1, keluar bersih exit 0
SIGUSR1, ada handler   -> exit 0   | handler: terima 10, keluar bersih exit 0

Perbedaannya tidak hanya pada exit code, tapi pada kesempatan untuk beres-beres. Program uji yang menangkap SIGTERM sempat menulis berkas penanda sebelum keluar:

text
$ kill -TERM <pid>; wait
exit=0 | handler: terima 15, bersihkan resource, exit 0
$ ls cleaned-*
cleaned-15

Berkas itu ada. Pada percobaan dengan SIGKILL terhadap proses yang memasang handler serupa, exit code tetap 137 dan handler tidak pernah dipanggil, jadi tidak ada berkas apa pun yang ditulis. Inilah alasan SIGTERM selalu dikirim lebih dulu dan SIGKILL hanya jadi pilihan terakhir.

Dua Sinyal yang Tidak Bisa Ditolak

signal(7) menyatakan langsung: “The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored.” Karena itu ada dua skenario yang perlu diletakkan di kepala.

Pertama, ada program yang sengaja mengabaikan SIGTERM:

text
python3 sigtrap.py ignore   # signal.signal(SIGTERM, SIG_IGN)
setelah SIGTERM: masih hidup, state=S
setelah SIGKILL: exit 137

Kedua, berlaku umum pada program yang handlernya memanggil operasi blocking, misalnya menunggu job asynchronous selesai. Sinyal tertangkap, tapi prosesnya tidak berhenti sampai pekerjaan di dalam handler itu beres. Ini konsekuensi dari semantik handler di signal(7), bukan hasil pengukuran di mesin uji; yang terukur di sini adalah kasus SIG_IGN di atas.

Kedua skenario berakhir sama: hanya SIGKILL yang berhasil. Makna praktisnya, systemctl stop yang mengirim SIGTERM bisa menggantung, dan systemctl kill -s SIGKILL adalah jalan keluar yang disengaja.

Pola Pengiriman yang Aman

Urutan yang benar adalah meminta berhenti dengan baik, lalu memaksa kalau perlu:

bash
kill -TERM "$PID"
for _ in $(seq 1 30); do
  kill -0 "$PID" 2>/dev/null || break
  sleep 1
done
kill -9 "$PID" 2>/dev/null

kill -0 di sini dipakai sebagai pemeriksaan keberadaan. Menurut kill(2): “If sig is 0, then no signal is sent, but existence and permission checks are still performed”. Hasil di mesin uji:

text
pid hidup     -> exit 0
pid tidak ada -> exit 1

Jadi kill -0 tidak mengirim apa pun, dan exit code 0 berarti prosesnya ada. Perhatikan juga argumen negatif: kill -1 berarti “semua proses yang boleh saya kirim sinyal”, bukan “semua proses”, karena kill(2) mengecualikan PID 1 pada kasus tersebut. Dalam script, kill -1 hampir selalu bug.

Batas Izin

Mengirim sinyal ke proses milik user lain akan gagal, dan kegagalan itu perlu dibaca sebagai informasi, bukan error yang perlu disembunyikan:

text
uid=1000 user=su
$ kill -TERM 1
exit 1 (EPERM, bukan root)

Untuk service yang berjalan sebagai root dari user biasa, sudo diperlukan, dan sudo harus dipakai secara selektif. Sinyal yang salah pada proses yang salah adalah salah satu cara paling cepat merusak sistem yang sedang berjalan.

Rekomendasi Praktis

  • Mulai dari ps -o pid,ppid,stat,wchan:20,etime,comm -p <pid> saat menelusuri satu proses.
  • Pakai top -b -n1 -H -p <pid> untuk snapshot thread yang bisa disimpan atau ditempel ke laporan.
  • Baca WCHAN sebelum memutuskan prosesnya macet; S dengan WCHAN yang sudah terisi masih menunjukkan apa yang ditunggu.
  • Zombie yang menumpuk adalah bug di program induk; perbaiki induknya, jangan kill -9 zombie.
  • Kirim SIGTERM, tunggu proses punya waktu beres, baru SIGKILL sebagai upaya terakhir.
  • Pakai kill -0 untuk memeriksa keberadaan proses, bukan ps | grep yang bisa salah cocokkan nama.
  • Jangan pernah memakai nomor sinyal keras; nama sinyal portabel lintas arsitektur.