New FAMILUG

The PyMiers

Thursday 28 November 2019

zombie - những xác chết biết đi mà không thể kill

top 

top là câu lệnh để hiển thị các process đang chạy trên một máy tính *NIX.
Trên Windows sử dụng chương trình "Task Manager" để làm nhiệm vụ tương tự.

top rất dễ dùng, gõ `top` rồi ngồi nhìn màn hình update thông tin mỗi 3s, và bấm q để kết thúc.



Thế nhưng top cũng là một chương trình chứa rất nhiều thông tin, mà hỏi ra mỗi dòng có nghĩa là gì, cũng đủ để viết vài bài thật dài...

htop là một lệnh tương tự top, nhưng có giao diện dòng lệnh thân thiện hơn, màu mè hơn. htop không được cài sẵn trên mọi máy tính giống như top, người dùng phải tự cài thêm.

$ whatis top
top (1)              - display Linux processes



Một cách khác dùng lệnh top ít phổ biến, đó là dùng ở "batch mode" với option -b, khi thêm -n1 , top sẽ có tác dụng tương tự như lệnh ps, chỉ in ra 1 lần.

 $ top -bn1 | head
top - 23:16:05 up  1:41,  1 user,  load average: 2,91, 2,60, 1,66
Tasks: 284 total,   4 running, 212 sleeping,   0 stopped,   0 zombie
%Cpu(s):  3,9 us,  2,9 sy,  0,0 ni, 93,0 id,  0,1 wa,  0,0 hi,  0,2 si,  0,0 st
KiB Mem :  7975128 total,  4841968 free,  1716268 used,  1416892 buff/cache
KiB Swap:  2097148 total,  2097148 free,        0 used.  5578912 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 5564 hvn       20   0   31452   5560   3776 R 100,0  0,1  12:02.90 bash
 2781 hvn       20   0  721456  43840  28704 R  87,5  0,5  10:28.96 /usr/lib/gnome-t+
 5790 root      20   0       0      0      0 I  31,2  0,0   1:19.12 [kworker/u16:2-e]

Bỏ qua 5 dòng đầu đầy thông tin tổng quát về OS,
tập trung vào output từ chỗ:

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
  5564 hvn       20   0   31452   5560   3776 R 100,0  0,1  12:02.90 bash

Các tên cột PID USER .... COMMAND
có giải thích cụ thể trong man 1 top , phần 3. FIELDS / Columns

Bài này sẽ không giải thích các cột khác mà chỉ nói tới cột có chữ S - Status

Trích man 1 ps
               D    uninterruptible sleep (usually IO)
               R    running or runnable (on run queue)
               S    interruptible sleep (waiting for an event to complete)
               T    stopped by job control signal
               t    stopped by debugger during the tracing
               W    paging (not valid since the 2.6.xx kernel)
               X    dead (should never be seen)
               Z    defunct ("zombie") process, terminated but not reaped by its
                    parent

Thuật ngữ sử dụng ở đây có khác một chút, mỗi dòng trong output này được gọi là 1 task (ở trên ta dùng từ process). Cột S hiển thị status (trạng thái) của task.

Trên máy thực hiện bài viết này, các task chỉ ở 1 trong 3 trạng thái:

$ top -bn1 | tail +10 | awk '{print $8}'| sort -nr | uniq -c
    215 S
      1 R
     66 I

S == sleeping
R == running
I == idle

D == uninterruptible sleep (usually IO) - thường là đang đọc ghi đĩa cứng. Ví dụ: khi chạy lệnh cp copy file sẽ thấy process cp này có state D

Running nên được hiểu là sẵn sàng để chạy, task đang nằm trong Linux kernel run-queue.

Z - zombie là một status đáng chú ý hơn cả ở đây, dù nó không xuất hiện trong ví dụ. Khi một task có status là Z, ta gọi process này là zombie process.

zombie process là gì

Trích man 2 wait

A child that terminates, but has not been waited for becomes a zombie. The kernel maintains a minimal set of information about the zombie process (PID, termination status, resource usage information) in order to allow the parent to later perform a wait to obtain information about the child. As long as a zombie is not removed from the system via a wait, it will consume a slot in the kernel process table, and if this table fills, it will not be possible to create further processes. If a parent process terminates, then its zombie children (if any) are adopted by init(1), (or by the nearest subreaper process as defined through the use of the prctl(2) PR_SET_CHILD_SUBREAPER operation); init(1) automatically performs a wait to remove the zombies.
zombie process (cũng gọi là defunct process) là một process đã kết thúc, đã xong (có thể hiểu là đã chết), nhưng vẫn xuất hiện trong process table (xem bằng lệnh top hay ps). Thuật ngữ zombie ở đây ám chỉ việc process đã chết, nhưng ta vẫn thấy nó tồn tại trong output các câu lệnh top, ps.
Zombie process tồn tại trong một khoảng thời gian ngắn (vài giây) là chuyện hoàn toàn bình thường.

Vậy làm sao để xóa nó khỏi process table?

hay hỏi theo ngôn ngữ thông dụng:

làm sao "kill" zombie process?


Ta không thể "kill" zombie process, theo nghĩa bắt buộc 1 chương trình đang chạy phải dừng lại một cách lịch sự (SIGTERM) hay ép buộc (SIGKILL). Bởi zombie process đã dừng lại rồi, đã chết rồi, không thể giết (kill) được nữa.

Tại sao lại phải xóa zombie process?

Mặc dù đã kết thúc và không còn chiếm bộ nhớ hay CPU nữa, nhưng zombie process chiếm 1 chỗ (entry) trong process table - một số PID. Đây là một tài nguyên hữu hạn, và khi hết số, hệ điều hành sẽ không thể cấp được cho các chương trình cần chạy.

Giới hạn số PID là bao nhiêu?

Giá trị này có thể thay đổi được, mặc định trên Ubuntu 1804 x64 có giá trị mặc định là:
$ cat /proc/sys/kernel/pid_max
32768

Số này hoàn toàn không phải là lớn, trên những server phục vụ hàng trăm ngàn người dùng cùng lúc.

Làm thế nào để xóa zombie process?

Trước tiên cần hiểu tại sao lại có zombie process: trong một chương trình chạy multiprocess, process chính (parent) sẽ tạo ra các process khác (children).
Để quản lý các children, parent sẽ phải theo dõi Status của chúng. Việc theo dõi này được thực hiện bằng cách theo dõi các thay đổi của Status của children thông qua systemcall `wait`. Nếu trong code của parent "quên" không wait children, thì khi children chạy xong (terminated), nó sẽ ở trạng thái Z. Chỉ khi parent gọi wait để kiểm tra Status của children, lúc đó kernel mới reap (gặt hái) zombie process, xóa khỏi kernel process table.
Một chương trình để lại zombie process được xem là chương trình có bug cần fix.

Để dọn dẹp zombie process, ta cần đi tìm parent của nó để xử lý:
- Gửi SIGCHLD đến parent process để nó hiểu cần đi tìm children và wait
- Nếu không có tác dụng, có thể xem xét kill parent process (gửi SIGTERM hay SIGKILL). Sau khi parent process đã kết thúc, các children process trở thành các "orphan" (mồ côi) process, và được system process manager (init - PID 1) nhận làm con nuôi. System manager là một parent có trách nhiệm, nó sẽ gọi "wait" để kernel xóa các zombie process khỏi kernel process table.
- Nếu zombie có parent process ID (PPID) là 1 - tức init, mà không được dọn dẹp thì đây là bug của hệ điều hành (cụ thể là kernel, ví dụ Linux).

Dùng lệnh ps xau cũng cho ra output tương tự như top -bn1:
$ ps xau | head -n2
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.1  78052  9268 ?        Ss   21:34   0:05 /lib/systemd/systemd --system --deserialize 20

Thực hiện trên:
$ lsb_release -a ; uname -a
No LSB modules are available.
Distributor ID:    Ubuntu
Description:    Ubuntu 18.04.3 LTS
Release:    18.04
Codename:    bionic
Linux hvnzen 5.0.0-32-generic #34~18.04.2-Ubuntu SMP Thu Oct 10 10:36:02 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

Hết.
HVN at https://pymi.vn and http://www.familug.org.


Còn đây là ca khúc Zombie:

No comments:

Post a Comment