โปรเซสใน Linux: ps, top, htop, kill, สัญญาณ และ nice
ดูโปรเซสบน Linux ด้วย ps aux (ภาพรวม ณ ขณะหนึ่ง) top และ htop (รายการที่อัปเดตต่อเนื่องพร้อมโหลด) จบโปรเซสด้วย kill PID ซึ่งส่ง SIGTERM แบบสุภาพเป็นค่าเริ่มต้น ใช้ kill -9 (SIGKILL) เฉพาะเมื่อโปรเซสไม่ตอบสนอง ลำดับความสำคัญกำหนดด้วย nice ตอนเริ่มโปรเซส และ renice สำหรับโปรเซสที่รันอยู่ ช่วงค่าอยู่ระหว่าง -20 ถึง 19
โปรแกรมที่รันอยู่ใน Linux ทุกตัวคือโปรเซสหนึ่งตัวที่มีหมายเลข PID เจ้าของ สถานะ และลำดับความสำคัญของตัวเอง ดูโปรเซสได้ด้วยคำสั่ง ps, top และ htop จบโปรเซสด้วย kill และเปลี่ยนลำดับความสำคัญด้วย nice และ renice ด้านล่างจะอธิบายวิธีอ่านผลลัพธ์ของคำสั่งเหล่านี้ ควรส่งสัญญาณใดเมื่อไร และเหตุใด kill -9 จึงไม่ควรเป็นสิ่งแรกที่ทำ
โปรเซสและ PID คืออะไร
โปรเซสคือโปรแกรมในขณะที่กำลังทำงาน ประกอบด้วยโค้ด หน่วยความจำ ไฟล์ที่เปิดอยู่ และชุดคุณสมบัติต่าง ๆ เคอร์เนลกำหนด PID ที่ไม่ซ้ำกันให้แต่ละโปรเซส และจดจำ PPID ซึ่งเป็นหมายเลขของโปรเซสแม่ที่เริ่มมัน โปรเซสแรกในระบบที่มี PID 1 มักเป็น systemd และเป็นบรรพบุรุษของโปรเซสอื่นทั้งหมด
โปรเซสใหม่เกิดขึ้นในสองขั้นตอน ขั้นแรกโปรเซสแม่เรียก fork() เพื่อได้สำเนาของตัวเอง จากนั้นสำเนาเรียก execve() เพื่อแทนที่โค้ดของตัวเองด้วยโค้ดของโปรแกรมใหม่ เมื่อคุณพิมพ์ ls ในเทอร์มินัล bash ทำแบบนี้และรอให้โปรเซสลูกจบด้วย wait()
$ echo $$ # PID ของ shell ปัจจุบัน
4182
$ ps -o pid,ppid,cmd -p $$
PID PPID CMD
4182 4170 -bash
ดูโปรเซสด้วยคำสั่ง ps
ps ให้ภาพรวม ณ ขณะที่รัน แสดงโปรเซสตามสถานะตอนนั้นแล้วจบการทำงาน หากไม่ระบุอาร์กิวเมนต์จะแสดงเฉพาะโปรเซสของเทอร์มินัลปัจจุบัน ในทางปฏิบัติจึงมักใช้หนึ่งในสองรูปแบบนี้
$ ps aux | head -5
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.3 168540 12780 ? Ss 09:12 0:03 /sbin/init
root 2 0.0 0.0 0 0 ? S 09:12 0:00 [kthreadd]
www-data 1532 1.2 2.1 412308 86420 ? S 09:15 1:47 php-fpm: pool www
postgres 1610 0.4 3.8 221004 154312 ? Ss 09:15 0:31 postgres: checkpointer
ที่นี่ a คือโปรเซสของผู้ใช้ทุกคน u คือรูปแบบละเอียดพร้อมเจ้าของ และ x รวมโปรเซสที่ไม่มีเทอร์มินัล (เดมอน) ไวยากรณ์แบบ System V อย่าง ps -ef ให้ PPID ซึ่งสะดวกเมื่อต้องการดูว่าใครเป็นผู้เริ่มโปรเซสใด
| คอลัมน์ | ความหมาย |
|---|---|
PID | หมายเลขโปรเซส |
%CPU, %MEM | สัดส่วนของ CPU และหน่วยความจำจริง |
VSZ | หน่วยความจำเสมือนเป็นกิโลไบต์ (รวมส่วนที่ไม่ได้โหลดเข้า RAM) |
RSS | หน่วยความจำจริงที่ใช้อยู่เป็นกิโลไบต์ |
STAT | สถานะของโปรเซส (R, S, D, Z, T) และแฟล็ก |
TIME | เวลา CPU รวมที่ใช้ ไม่ใช่เวลาที่โปรเซสทำงานมา |
เทคนิคที่มีประโยชน์กับ ps
# 10 อันดับแรกตามหน่วยความจำ
ps aux --sort=-%mem | head -11
# 10 อันดับแรกตาม CPU
ps aux --sort=-%cpu | head -11
# กำหนดคอลัมน์เอง
ps -eo pid,ppid,user,ni,stat,etime,cmd
# แสดงโปรเซสเป็นต้นไม้
ps -ef --forest
pstree -p
# หา PID จากชื่อ
pgrep -a nginx
pidof nginx
ข้อผิดพลาดที่พบบ่อยคือค้นหาโปรเซสด้วย ps aux | grep nginx แล้วเห็น grep เองในผลลัพธ์ ให้ใช้ pgrep หรือเทคนิค grep [n]ginx ซึ่งจะไม่ตรงกับบรรทัดคำสั่งของ grep เอง
สถานะของโปรเซส: R, S, D, Z, T
อักษรในคอลัมน์ STAT ไม่ใช่สิ่งที่ไร้ความหมาย แต่คือสถานะจริงของโปรเซส สถานะนี้กำหนดวิธีตีความโหลด และบอกว่า kill จะได้ผลหรือไม่
| รหัส | สถานะ | ความหมายในทางปฏิบัติ |
|---|---|---|
R | Running | กำลังทำงานหรือรอคิวบน CPU |
S | Interruptible sleep | รอเหตุการณ์ (เครือข่าย การป้อนข้อมูล ตัวจับเวลา) เป็นสถานะปกติของโปรเซสส่วนใหญ่ |
D | Uninterruptible sleep | รอ I/O อยู่ในเคอร์เนล สัญญาณจะไม่ถูกส่งถึง |
Z | Zombie | จบแล้ว แต่โปรเซสแม่ยังไม่ได้รับรหัสออก |
T | Stopped | ถูกหยุดชั่วคราวด้วยสัญญาณ SIGSTOP หรือ Ctrl+Z |
อักขระเพิ่มเติมหลังตัวอักษรก็มีประโยชน์เช่นกัน: s คือผู้นำของเซสชัน + คือโปรเซสหน้าจอหลักของเทอร์มินัล < คือลำดับความสำคัญสูง N คือลำดับความสำคัญต่ำ l คือมีหลายเธรด
โปรเซสในสถานะ D
โปรเซสในสถานะ D อยู่ภายในการเรียกระบบและกำลังรอ เช่น การตอบกลับจากดิสก์ หรือ NFS ที่ค้างอยู่ โปรเซสนี้ไม่รับสัญญาณใดเลย แม้แต่ SIGKILL ดังนั้น kill -9 จึงไม่มีประโยชน์ ควรหาสาเหตุของการรอ โดยดู iostat, dmesg และสถานะของที่เก็บข้อมูลเครือข่าย
โปรเซสซอมบี้
ซอมบี้ตายไปแล้ว ไม่ใช้ CPU หรือหน่วยความจำ แต่ยังกินแถวหนึ่งในตารางโปรเซส จะฆ่ามันไม่ได้เพราะมันจบไปแล้ว มันจะหายไปเมื่อโปรเซสแม่เรียก wait() หรือเมื่อโปรเซสแม่จบและ PID 1 รับซอมบี้นั้นไปเป็นลูกบุญธรรม
# หาซอมบี้และโปรเซสแม่ของมัน
ps -eo pid,ppid,stat,cmd | awk '$3 ~ /Z/'
ซอมบี้ตัวเดียวไม่เป็นอันตราย แต่หากมีหลายร้อยตัวและจำนวนเพิ่มขึ้น แสดงว่าโปรแกรมแม่มีข้อผิดพลาด ต้องแก้ไขหรือรีสตาร์ตโปรแกรมนั้น
top: โปรเซสและโหลดแบบเรียลไทม์
top แสดงรายการโปรเซสเดียวกัน แต่อัปเดตทุกไม่กี่วินาที พร้อมสรุปภาพรวมของระบบ เป็นคำสั่งแรกที่คนมักรันเมื่อ "เซิร์ฟเวอร์ช้า"
top - 14:21:07 up 12 days, 3:02, 2 users, load average: 1.84, 1.20, 0.95
Tasks: 213 total, 2 running, 211 sleeping, 0 stopped, 0 zombie
%Cpu(s): 23.1 us, 4.2 sy, 0.0 ni, 70.3 id, 1.9 wa, 0.0 hi, 0.5 si, 0.0 st
MiB Mem : 7821.4 total, 412.6 free, 3105.8 used, 4303.0 buff/cache
MiB Swap: 2048.0 total, 2048.0 free, 0.0 used. 4398.2 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1532 www-data 20 0 412308 86420 14212 R 38.2 1.1 1:47.31 php-fpm
1610 postgres 20 0 221004 154312 148900 S 6.0 1.9 0:31.08 postgres
ในบรรทัด %Cpu(s) สำคัญที่สุดคือ: us คือโค้ดของผู้ใช้ sy คือเคอร์เนล id คือว่าง wa คือการรอดิสก์ ค่า wa ที่สูงในขณะที่ CPU ว่าง เป็นสัญญาณที่แน่นอนว่าคอขวดอยู่ที่ I/O
ปุ่มลัดของ top
| ปุ่ม | การทำงาน |
|---|---|
P / M | เรียงตาม CPU / ตามหน่วยความจำ |
1 | แสดงแต่ละคอร์แยกกัน |
c | แสดงบรรทัดคำสั่งเต็ม |
u | กรองตามผู้ใช้ |
k | ส่งสัญญาณให้โปรเซส |
r | เปลี่ยนค่า nice ของโปรเซส |
H | แสดงเธรด |
q | ออก |
สำหรับสคริปต์และล็อก โหมดแบตช์มีประโยชน์: top -b -n 1 | head -20 แสดงภาพรวมหนึ่งครั้งโดยไม่ต้องโต้ตอบ ติดตามโปรเซสเฉพาะตัวด้วย top -p 1532
Load Average คือคิว ไม่ใช่เปอร์เซ็นต์
ตัวเลขสามค่าของ load average คือความยาวเฉลี่ยของคิวในช่วง 1, 5 และ 15 นาทีที่ผ่านมา ใน Linux โปรเซสในสถานะ R และ D จะถูกนับ ส่วนการหลับปกติ S ไม่นับ ต้องเทียบ LA กับจำนวนคอร์ (nproc): บนระบบ 4 คอร์ ค่า 4 คือโหลดเต็มพอดี ส่วนค่า 12 หมายถึงคิวที่ยาวเป็นสามเท่าของความสามารถที่มี
หาก LA สูงแต่ CPU ว่าง ให้หาโปรเซสในสถานะ D: ps -eo stat,pid,cmd | grep '^D' เกือบทุกครั้งสาเหตุคือดิสก์ช้าหรือที่เก็บข้อมูลผ่านเครือข่าย
htop: ทางเลือกที่สะดวกกว่า top
htop แสดงข้อมูลเดียวกัน แต่มีตัวบ่งชี้สีตามคอร์ การเลื่อนหน้า รองรับเมาส์ และแสดงต้นไม้โปรเซส โดยทั่วไปต้องติดตั้งก่อน: sudo apt install htop หรือ sudo dnf install htop
F3หรือ/ค้นหาโปรเซสด้วยชื่อF4ตัวกรองที่แสดงเฉพาะรายการที่ตรงF5หรือtแสดงต้นไม้ "แม่ — ลูก"F6เลือกคอลัมน์สำหรับการเรียงF7/F8ลดหรือเพิ่มค่า niceF9เมนูสัญญาณสำหรับโปรเซสที่เลือกSpaceทำเครื่องหมายหลายโปรเซสเพื่อสั่งการพร้อมกัน
บนเซิร์ฟเวอร์ที่ติดตั้งอะไรเพิ่มไม่ได้ top ก็เพียงพอ เพราะการทำงานหลักทั้งหมดมีอยู่ใน top แล้ว
วิธีจบโปรเซส: kill และสัญญาณ
คำสั่ง kill ไม่ได้ "ฆ่า" โดยตรง แต่ส่งสัญญาณให้โปรเซส สิ่งที่จะเกิดขึ้นต่อไปขึ้นอยู่กับสัญญาณและวิธีที่โปรแกรมจัดการกับมัน
| สัญญาณ | หมายเลข | การใช้งาน |
|---|---|---|
SIGHUP | 1 | เทอร์มินัลขาด หลายเดมอนอ่านคอนฟิกใหม่เมื่อได้รับสัญญาณนี้ |
SIGINT | 2 | ขัดจังหวะ เหมือน Ctrl+C |
SIGKILL | 9 | จบโดยบังคับจากเคอร์เนล ดักจับไม่ได้ |
SIGTERM | 15 | ขอให้จบอย่างเรียบร้อย เป็นค่าเริ่มต้นของ kill |
SIGSTOP | 19 | หยุดชั่วคราว ดักจับไม่ได้ |
SIGCONT | 18 | ทำงานต่อจากโปรเซสที่ถูกหยุดไว้ |
SIGTSTP | 20 | หยุดชั่วคราวจากเทอร์มินัล Ctrl+Z |
หมายเลขนี้ใช้กับ x86 และ ARM ดูรายการเต็มได้ด้วย kill -l การระบุสัญญาณด้วยชื่อปลอดภัยกว่าการใช้หมายเลข
kill 1532 # SIGTERM
kill -TERM 1532 # เหมือนกัน แต่ระบุชัดเจน
kill -HUP $(pidof nginx) # อ่านคอนฟิกใหม่
kill -STOP 1532; kill -CONT 1532
kill -0 1532 && echo "โปรเซสยังทำงานอยู่"
สัญญาณ 0 ไม่ส่งอะไรเลย เพียงตรวจว่าโปรเซสมีอยู่จริงและคุณมีสิทธิ์ส่งสัญญาณถึงมันหรือไม่ เหมาะกับสคริปต์
ทำไมไม่ควรเริ่มด้วย kill -9
SIGTERM ให้โอกาสโปรแกรมปิดไฟล์ เขียนบัฟเฟอร์ลงดิสก์ ลบไฟล์ล็อก และจบธุรกรรมอย่างถูกต้อง ส่วน SIGKILL หยุดทุกอย่างทันที ข้อมูลที่เขียนไม่เสร็จจะสูญหาย ไฟล์ชั่วคราวยังค้างอยู่ และฐานข้อมูลอาจต้องกู้คืนตอนเริ่มระบบครั้งถัดไป
ลำดับที่ถูกต้องคือ:
- ส่ง
kill PID(SIGTERM) - รอ 5–30 วินาที โดยตรวจด้วย
kill -0 PID - หากโปรเซสยังอยู่และไม่ได้อยู่ในสถานะ
Dค่อยใช้kill -9 PID
สำหรับบริการใน systemd ไม่ควรจัดการ PID ด้วยมือ: systemctl stop จะส่ง SIGTERM รอจนหมดเวลา แล้วจึงส่ง SIGKILL ให้เอง
pkill และ killall: ค้นหาด้วยชื่อ
pkill -f "python worker.py" # ตามบรรทัดคำสั่งเต็ม
pkill -u alice # โปรเซสทั้งหมดของผู้ใช้
killall -TERM node # โปรเซสทั้งหมดที่ชื่อ node
pgrep -l -f worker # ตรวจก่อนว่าอะไรจะถูกจับคู่
ก่อนรัน pkill แบบกว้าง ให้รัน pgrep ด้วยแพตเทิร์นเดียวกันก่อนเสมอ เพราะ -f จับคู่กับส่วนใดก็ได้ของบรรทัดคำสั่ง และอาจจับโปรเซสที่ไม่ต้องการได้ง่าย
คุณส่งสัญญาณได้เฉพาะโปรเซสของตัวเอง โปรเซสของผู้อื่นต้องใช้ sudo ข้อความ Operation not permitted หมายถึงข้อจำกัดนี้
ลำดับความสำคัญ: nice และ renice
ค่า nice บอกตัวจัดตารางงาน (scheduler) ว่าโปรเซสใดควรยอม CPU ช่วงค่าอยู่ระหว่าง -20 (ลำดับความสำคัญสูงสุด) ถึง 19 (ยอมให้ทุกโปรเซส) ค่าเริ่มต้นคือ 0 ใน top ค่านี้แสดงในคอลัมน์ NI ส่วน PR คือลำดับความสำคัญภายในที่คำนวณจากค่านี้
# รันงานหนักด้วยลำดับความสำคัญต่ำ
nice -n 10 tar czf backup.tar.gz /var/www
# ลดลำดับความสำคัญของโปรเซสที่รันอยู่
renice -n 15 -p 1532
# เพิ่มลำดับความสำคัญ (เฉพาะ root)
sudo renice -n -5 -p 1610
# โปรเซสทั้งหมดของผู้ใช้
sudo renice -n 10 -u alice
ผู้ใช้ทั่วไปทำได้เพียงลดลำดับความสำคัญของโปรเซสของตัวเอง ค่าติดลบใช้ได้เฉพาะ root ต้องเข้าใจขีดจำกัดของ nice ด้วย: มันมีผลเฉพาะ CPU หากงานทำให้ระบบช้าเพราะดิสก์ ให้ใช้ ionice -c3 คำสั่ง ซึ่งอยู่ในคลาส idle และได้รับดิสก์ก็ต่อเมื่อไม่มีใครใช้
โปรเซสเบื้องหลังและงานของ shell
คำสั่งสามารถรันเบื้องหลังได้โดยเพิ่ม & หรือหยุดชั่วคราวด้วย Ctrl+Z shell จะเก็บรายการงานไว้ และควบคุมด้วยคำสั่งในตัว
$ long_task &
[1] 5120
$ jobs
[1]+ Running long_task &
$ fg %1 # นำกลับมาไว้หน้าจอหลัก
^Z # หยุดชั่วคราว (SIGTSTP)
$ bg %1 # ทำงานต่อเบื้องหลัง
เมื่อปิดเทอร์มินัล shell จะส่ง SIGHUP ให้งานที่ยังทำงานอยู่ และงานเหล่านั้นจะจบ หากต้องการให้โปรเซสอยู่รอดหลังปิดเทอร์มินัล ให้รันด้วย nohup คำสั่ง & แยกออกด้วย disown หรือใช้ tmux สำหรับงานที่ต้องทำงานต่อเนื่อง ควรตั้งเป็นบริการ systemd
สถานการณ์การวินิจฉัยที่พบบ่อย
ใครกิน CPU
top -o %CPU # หรือกด P ภายใน top
ps -eo pid,user,%cpu,etime,cmd --sort=-%cpu | head
หากโปรเซสกิน CPU โดยไม่มีประโยชน์ ให้ลดลำดับความสำคัญด้วย renice ก่อน วิธีนี้ปลอดภัยและให้เวลาในการหาสาเหตุ
ใครยึดพอร์ต
sudo ss -ltnp | grep :8080
sudo lsof -i :8080
ผลลัพธ์จะมี PID และจาก PID นั้นคุณสามารถดูไฟล์ที่รัน (exe) ไดเรกทอรีทำงาน (cwd) และอาร์กิวเมนต์ (cmdline) ได้ใน /proc/PID/
โปรเซสหายไปโดยไม่คาดคิด
สาเหตุที่พบบ่อยคือ OOM Killer ซึ่งฆ่าโปรเซสเมื่อหน่วยความจำไม่พอ ตรวจสอบได้ด้วย dmesg -T | grep -i oom หรือ journalctl -k | grep -i "killed process"
สรุป
การทำงานกับโปรเซสใน Linux ใช้คำสั่งไม่มาก: ps aux สำหรับภาพรวม top และ htop สำหรับการเฝ้าดู kill พร้อมสัญญาณที่ถูกต้องสำหรับการจบ และ nice/renice สำหรับลำดับความสำคัญ อ่านสถานะของโปรเซสก่อนลงมือทุกครั้ง เพราะซอมบี้และโปรเซสในสถานะ D ไม่หายด้วยสัญญาณ และเริ่มด้วย SIGTERM เสมอ โดยเก็บ kill -9 ไว้เป็นทางเลือกสุดท้าย โปรเซส หน่วยความจำ Load Average และการเรียกระบบถูกฝึกใช้จริงในหลักสูตร Linux
คำถามที่พบบ่อย
ps aux ต่างจาก ps -ef อย่างไร?
เป็นไวยากรณ์สองแบบของคำสั่งเดียวกัน aux เป็นแบบ BSD ที่มีคอลัมน์ %CPU, %MEM และ STAT ส่วน -ef เป็นแบบ System V ที่มีคอลัมน์ PPID ทั้งสองแสดงชุดโปรเซสเดียวกัน คือโปรเซสทั้งหมดของระบบ
จะรู้ได้อย่างไรว่ามีโปรเซสทำงานอยู่กี่ตัว?
ดูบรรทัด Tasks ใน top หรือรัน ps -e --no-headers | wc -l หากต้องการนับเธรด ใช้ ps -eLf --no-headers | wc -l
ทำไมโปรเซสยังไม่จบแม้ใช้ kill -9?
อาจอยู่ในสถานะ D และรอ I/O ในเคอร์เนล หรือเป็นซอมบี้ที่จบไปแล้ว กรณีแรกให้ตรวจดิสก์หรือที่เก็บข้อมูลเครือข่าย กรณีหลังให้แก้ไขหรือรีสตาร์ตโปรเซสแม่
จะเกิดอะไรขึ้นถ้าจบโปรเซส PID 1?
เคอร์เนลป้องกัน init: สัญญาณปกติ รวมถึง SIGKILL จากผู้ใช้ จะไม่ถูกส่งถึงมัน หาก init จบจริง ระบบจะเกิด kernel panic
จะจำกัดหน่วยความจำของโปรเซสได้อย่างไร?
สำหรับบริการ systemd ตั้ง MemoryMax= ในไฟล์ unit สำหรับการรันครั้งเดียว ใช้ systemd-run --scope -p MemoryMax=500M คำสั่ง วิธีนี้ทำงานผ่าน cgroups และเชื่อถือได้กว่า ulimit -v แบบเดิม
หัวข้อนี้สอนอย่างละเอียดพร้อมฝึกปฏิบัติในคอร์ส “Linux: с нуля до уверенного администрирования”
ไปที่คอร์ส