คู่มือ

โปรเซสใน Linux: ps, top, htop, kill, สัญญาณ และ nice

อัปเดต: · Skillaria.Top

ดูโปรเซสบน 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 จะได้ผลหรือไม่

รหัสสถานะความหมายในทางปฏิบัติ
RRunningกำลังทำงานหรือรอคิวบน CPU
SInterruptible sleepรอเหตุการณ์ (เครือข่าย การป้อนข้อมูล ตัวจับเวลา) เป็นสถานะปกติของโปรเซสส่วนใหญ่
DUninterruptible sleepรอ I/O อยู่ในเคอร์เนล สัญญาณจะไม่ถูกส่งถึง
ZZombieจบแล้ว แต่โปรเซสแม่ยังไม่ได้รับรหัสออก
TStoppedถูกหยุดชั่วคราวด้วยสัญญาณ 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 ลดหรือเพิ่มค่า nice
  • F9 เมนูสัญญาณสำหรับโปรเซสที่เลือก
  • Space ทำเครื่องหมายหลายโปรเซสเพื่อสั่งการพร้อมกัน

บนเซิร์ฟเวอร์ที่ติดตั้งอะไรเพิ่มไม่ได้ top ก็เพียงพอ เพราะการทำงานหลักทั้งหมดมีอยู่ใน top แล้ว

วิธีจบโปรเซส: kill และสัญญาณ

คำสั่ง kill ไม่ได้ "ฆ่า" โดยตรง แต่ส่งสัญญาณให้โปรเซส สิ่งที่จะเกิดขึ้นต่อไปขึ้นอยู่กับสัญญาณและวิธีที่โปรแกรมจัดการกับมัน

สัญญาณหมายเลขการใช้งาน
SIGHUP1เทอร์มินัลขาด หลายเดมอนอ่านคอนฟิกใหม่เมื่อได้รับสัญญาณนี้
SIGINT2ขัดจังหวะ เหมือน Ctrl+C
SIGKILL9จบโดยบังคับจากเคอร์เนล ดักจับไม่ได้
SIGTERM15ขอให้จบอย่างเรียบร้อย เป็นค่าเริ่มต้นของ kill
SIGSTOP19หยุดชั่วคราว ดักจับไม่ได้
SIGCONT18ทำงานต่อจากโปรเซสที่ถูกหยุดไว้
SIGTSTP20หยุดชั่วคราวจากเทอร์มินัล 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 หยุดทุกอย่างทันที ข้อมูลที่เขียนไม่เสร็จจะสูญหาย ไฟล์ชั่วคราวยังค้างอยู่ และฐานข้อมูลอาจต้องกู้คืนตอนเริ่มระบบครั้งถัดไป

ลำดับที่ถูกต้องคือ:

  1. ส่ง kill PID (SIGTERM)
  2. รอ 5–30 วินาที โดยตรวจด้วย kill -0 PID
  3. หากโปรเซสยังอยู่และไม่ได้อยู่ในสถานะ 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: с нуля до уверенного администрирования”

ไปที่คอร์ส