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
psmemberi potret sekali jalan,topmemberi tampilan yang berulang.- Kolom
STATpunya satu huruf utama plus beberapa penanda tambahan. WCHANmenampilkan fungsi kernel tempat proses menunggu.Zberarti proses selesai tapi belum di-reap, sehingga masih memblokir slot PID.PPIDbernilai 1 menandai proses tanpa induk yang sudah di-reparent ke init.SIGKILLdanSIGSTOPtidak bisa ditangkap, diblokir, atau diabaikan.SIGTERMmemungkinkan program membersihkan diri,SIGKILLtidak.kill -0memeriksa keberadaan tanpa mengirim sinyal apa pun.
Lingkungan Pengujian
Seluruh output di bawah berasal dari mesin uji, bukan asumsi.
| Komponen | Nilai Teruji | Keterangan |
|---|---|---|
| Distribusi | Ubuntu 26.04.1 LTS | Kernel 7.0.0-31-generic |
| Arsitektur | x86_64 | 12 thread CPU, pid_max 4194304 |
| procps | procps-ng 4.0.4 | Menyediakan ps dan top |
| PID 1 | systemd | Penerima proses tanpa induk yang di-reparent |
| Beban saat uji | 375 proses | 45 ber-PPID 1, 2 ber-PPID 0 |
Nama kolom dan arti statusnya mengikuti paket procps, jadi distribusi lain bisa punya versi berbeda. Verifikasi cepat:
ps --version
top -h | head -3
uname -srmoSatu 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:
$ 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-terminalMembaca 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).
| State | Arti | Tindakan yang Wajar |
|---|---|---|
R | Running atau runnable, ada di run queue | Normal, tidak perlu tindakan |
S | Interruptible sleep, menunggu event selesai | Normal, cek WCHAN bila mencurigakan |
D | Uninterruptible sleep, biasanya menunggu I/O | Tunggu atau periksa storage |
Z | Zombie, selesai tapi belum di-reap | Periksa program induknya |
T | Berhenti karena sinyal job control | SIGCONT untuk melanjutkan |
I | Idle kernel thread | Normal 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.
| Karakter | Arti |
|---|---|
l | Multithread, memakai CLONE_THREAD seperti pthread NPTL |
s | Session leader |
+ | Berada di foreground process group |
< | Prioritas tinggi, kurang ramah ke user lain |
N | Prioritas rendah, ramah ke user lain |
L | Halaman terkunci di memori |
Karakter l bisa dikonfirmasi dengan kolom nlwp, yaitu jumlah thread dalam proses:
$ ps -o pid,nlwp,comm -p 45797 -p 7530
PID NLWP COMMAND
45797 122 firefox
7530 41 opencodeDua 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:
$ ps -eo pid,nlwp,comm -p 1,7530
... (seluruh proses tercetak, bukan hanya 2 baris)Untuk mengisolasi satu PID, jangan pakai -e:
ps -o pid,ppid,stat,wchan:20,etime,comm -p 7530
ps -o pid,ppid,stat,comm --ppid 7530Opsi --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:
$ 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_nanosleepProses 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:
$ grep -E '^(Name|State|Threads|SigBlk|SigIgn|SigCgt):' /proc/48218/status
Name: sleep
State: S (sleeping)
Threads: 1
SigBlk: 0000000000000000
SigIgn: 0000000000000006
SigCgt: 0000000000000000Tiga 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.
$ 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+ COMMANDDua 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():
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'
1Zombie 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”.
PID 1 di mesin ini = 1 (systemd)
proses tanpa induk pid = 48496
PID PPID STAT COMMAND
48496 1 S python3Statusnya 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:
| Sinyal | Default | Nomor di x86-64 | Peran Praktis |
|---|---|---|---|
SIGHUP | Term | 1 | Minta program memuat ulang konfigurasi |
SIGINT | Term | 2 | Interup dari keyboard, Ctrl+C |
SIGKILL | Term | 9 | Hentikan paksa, tidak bisa ditangkap |
SIGUSR1 | Term | 10 | Milik aplikasi, sering dipakai untuk membuka ulang berkas log |
SIGTERM | Term | 15 | Minta program berhenti dengan rapi |
SIGSTOP | Stop | 19 | Bekukan 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:
SIGTERM, tanpa handler -> exit 143
SIGHUP, tanpa handler -> exit 129
SIGUSR1, tanpa handler -> exit 138
SIGKILL, tanpa handler -> exit 137
SIGPIPE, tanpa handler -> exit 141Pola 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:
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 0Perbedaannya tidak hanya pada exit code, tapi pada kesempatan untuk beres-beres. Program uji yang menangkap SIGTERM sempat menulis berkas penanda sebelum keluar:
$ kill -TERM <pid>; wait
exit=0 | handler: terima 15, bersihkan resource, exit 0
$ ls cleaned-*
cleaned-15Berkas 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:
python3 sigtrap.py ignore # signal.signal(SIGTERM, SIG_IGN)
setelah SIGTERM: masih hidup, state=S
setelah SIGKILL: exit 137Kedua, 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:
kill -TERM "$PID"
for _ in $(seq 1 30); do
kill -0 "$PID" 2>/dev/null || break
sleep 1
done
kill -9 "$PID" 2>/dev/nullkill -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:
pid hidup -> exit 0
pid tidak ada -> exit 1Jadi 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:
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
WCHANsebelum memutuskan prosesnya macet;SdenganWCHANyang sudah terisi masih menunjukkan apa yang ditunggu. - Zombie yang menumpuk adalah bug di program induk; perbaiki induknya, jangan
kill -9zombie. - Kirim
SIGTERM, tunggu proses punya waktu beres, baruSIGKILLsebagai upaya terakhir. - Pakai
kill -0untuk memeriksa keberadaan proses, bukanps | grepyang bisa salah cocokkan nama. - Jangan pernah memakai nomor sinyal keras; nama sinyal portabel lintas arsitektur.