Reading Time: 9 minutes

Một máy chủ Linux, giống như bất kỳ máy tính hiện đại nào, có thể chạy nhiều ứng dụng cùng lúc. Những ứng dụng này được gọi là các tiến trình (processes) và được quản lý riêng lẻ.

Quản lý tiến trình trong Linux

Mặc dù Linux sẽ tự động xử lý các tác vụ cấp thấp trong vòng đời của một tiến trình như khởi động, tắt, cấp phát bộ nhớ, v.v. bạn vẫn cần một cách để tương tác với hệ điều hành nhằm quản lý các tiến trình ở cấp độ cao hơn.

Trong hướng dẫn này, bạn sẽ tìm hiểu những khái niệm cơ bản về quản lý tiến trình trong Linux. Linux cung cấp một số công cụ tiêu chuẩn, được tích hợp sẵn để phục vụ cho mục đích này.

Bạn sẽ thực hành các khái niệm này trong môi trường Ubuntu 20.04, tuy nhiên hầu hết các bản phân phối Linux hiện đại khác đều hoạt động theo cách tương tự.

Bước 1: Cách xem các tiến trình đang chạy trong Linux

Bạn có thể xem tất cả các tiến trình đang chạy trên máy chủ của mình bằng cách sử dụng lệnh top:

top
top
Output
top - 15:14:40 up 46 min,  1 user,  load average: 0.00, 0.01, 0.05
Tasks:  56 total,   1 running,  55 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   1019600k total,   316576k used,   703024k free,     7652k buffers
Swap:        0k total,        0k used,        0k free,   258976k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND           
    1 root      20   0 24188 2120 1300 S  0.0  0.2   0:00.56 init               
    2 root      20   0     0    0    0 S  0.0  0.0   0:00.00 kthreadd           
    3 root      20   0     0    0    0 S  0.0  0.0   0:00.07 ksoftirqd/0        
    6 root      RT   0     0    0    0 S  0.0  0.0   0:00.00 migration/0        
    7 root      RT   0     0    0    0 S  0.0  0.0   0:00.03 watchdog/0         
    8 root       0 -20     0    0    0 S  0.0  0.0   0:00.00 cpuset             
    9 root       0 -20     0    0    0 S  0.0  0.0   0:00.00 khelper            
   10 root      20   0     0    0    0 S  0.0  0.0   0:00.00 kdevtmpfs

Những dòng đầu tiên trong kết quả của lệnh top hiển thị thống kê hệ thống, chẳng hạn như mức sử dụng CPU/bộ nhớ và tổng số tiến trình đang chạy.

Bạn có thể thấy rằng hiện có 1 tiến trình đang chạy, và 55 tiến trình đang ở trạng thái “ngủ” (sleeping), tức là chúng không sử dụng chu kỳ CPU tại thời điểm đó.

Phần còn lại của bảng hiển thị sẽ liệt kê các tiến trình đang hoạt động cùng với các thông số sử dụng tài nguyên tương ứng. Theo mặc định, top sẽ tự động sắp xếp các tiến trình theo mức sử dụng CPU, do đó bạn có thể nhanh chóng xác định các tiến trình đang chiếm nhiều tài nguyên nhất.

Lệnh top sẽ tiếp tục chạy trong terminal của bạn cho đến khi bạn tự dừng nó bằng tổ hợp phím Ctrl+C. Tổ hợp này gửi một tín hiệu **kill**, yêu cầu tiến trình đang chạy thoát ra một cách an toàn (nếu tiến trình có khả năng xử lý tín hiệu đó).

Một phiên bản nâng cấp của top có tên là htop cũng có sẵn trong hầu hết các kho phần mềm. Trên Ubuntu 20.04, bạn có thể cài đặt htop bằng lệnh apt như sau:

sudo apt install htop

Sau khi cài đặt xong, bạn có thể sử dụng lệnh htop:

htop
Output
  Mem[|||||||||||           49/995MB]     Load average: 0.00 0.03 0.05 
  CPU[                          0.0%]     Tasks: 21, 3 thr; 1 running
  Swp[                         0/0MB]     Uptime: 00:58:11

  PID USER      PRI  NI  VIRT   RES   SHR S CPU% MEM%   TIME+  Command
 1259 root       20   0 25660  1880  1368 R  0.0  0.2  0:00.06 htop
    1 root       20   0 24188  2120  1300 S  0.0  0.2  0:00.56 /sbin/init
  311 root       20   0 17224   636   440 S  0.0  0.1  0:00.07 upstart-udev-brid
  314 root       20   0 21592  1280   760 S  0.0  0.1  0:00.06 /sbin/udevd --dae
  389 messagebu  20   0 23808   688   444 S  0.0  0.1  0:00.01 dbus-daemon --sys
  407 syslog     20   0  243M  1404  1080 S  0.0  0.1  0:00.02 rsyslogd -c5
  408 syslog     20   0  243M  1404  1080 S  0.0  0.1  0:00.00 rsyslogd -c5
  409 syslog     20   0  243M  1404  1080 S  0.0  0.1  0:00.00 rsyslogd -c5
  406 syslog     20   0  243M  1404  1080 S  0.0  0.1  0:00.04 rsyslogd -c5
  553 root       20   0 15180   400   204 S  0.0  0.0  0:00.01 upstart-socket-br

htop cung cấp khả năng hiển thị tốt hơn đối với các luồng CPU đa nhân, hỗ trợ màu sắc tốt hơn trong các terminal hiện đại, và có nhiều tuỳ chọn sắp xếp hơn so với top, cùng với một số tính năng khác. Không giống như top, htop không phải lúc nào cũng được cài sẵn theo mặc định, nhưng bạn có thể coi nó như một công cụ thay thế tương đương và dễ sử dụng hơn.

Bạn có thể thoát khỏi htop bằng cách nhấn Ctrl+C, tương tự như với top.

Bước 2: Cách sử dụng ps để liệt kê các tiến trình

tophtop cung cấp giao diện dạng dashboard để xem các tiến trình đang chạy, tương tự như trình quản lý tác vụ đồ họa. Giao diện dashboard giúp bạn có cái nhìn tổng quan, nhưng thường không cho kết quả đầu ra có thể sử dụng trực tiếp trong các script hay thao tác tự động.

Để phục vụ mục đích đó, Linux cung cấp một công cụ dòng lệnh tiêu chuẩn khác có tên là ps, cho phép truy vấn các tiến trình đang chạy.

Khi chạy ps mà không truyền vào bất kỳ đối số nào, lệnh này chỉ hiển thị thông tin rất cơ bản:

ps
Output
  PID TTY          TIME CMD
 1017 pts/0    00:00:00 bash
 1262 pts/0    00:00:00 ps

Kết quả này hiển thị tất cả các tiến trình liên quan đến người dùng hiện tại và phiên làm việc trong terminal hiện tại. Điều này hoàn toàn hợp lý nếu bạn chỉ đang chạy shell bash và lệnh ps trong cùng một cửa sổ terminal vào thời điểm đó.

Để có cái nhìn đầy đủ hơn về tất cả các tiến trình đang chạy trên hệ thống, bạn có thể sử dụng lệnh sau:

ps aux
Output
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.2  24188  2120 ?        Ss   14:28   0:00 /sbin/init
root         2  0.0  0.0      0     0 ?        S    14:28   0:00 [kthreadd]
root         3  0.0  0.0      0     0 ?        S    14:28   0:00 [ksoftirqd/0]
root         6  0.0  0.0      0     0 ?        S    14:28   0:00 [migration/0]
root         7  0.0  0.0      0     0 ?        S    14:28   0:00 [watchdog/0]
root         8  0.0  0.0      0     0 ?        S<   14:28   0:00 [cpuset]
root         9  0.0  0.0      0     0 ?        S<   14:28   0:00 [khelper]
…

Các tùy chọn trong lệnh ps aux yêu cầu ps hiển thị các tiến trình của tất cả người dùng, bất kể chúng có liên kết với terminal nào hay không, và trình bày kết quả ở dạng dễ đọc hơn cho con người.

Bằng cách kết hợp với toán tử pipes, bạn có thể tìm kiếm trong kết quả của ps aux bằng lệnh grep, để lọc ra một tiến trình cụ thể theo tên. Cách làm này rất hữu ích trong trường hợp bạn nghi ngờ tiến trình đó đã bị treo hoặc cần dừng nó vì một lý do nào đó.

ps aux | grep bash
Output
sammy         41664   0.7  0.0 34162880   2528 s000  S     1:35pm   0:00.04 -bash
sammy         41748   0.0  0.0 34122844    828 s000  S+    1:35pm   0:00.00 grep bash

Kết quả trả về sẽ bao gồm cả tiến trình grep mà bạn vừa chạy, và cả shell bash hiện đang hoạt động. Ngoài ra, nó cũng hiển thị mức sử dụng CPU và bộ nhớ, thời gian tiến trình đã chạy, và quan trọng nhất trong phần kết quả được đánh dấu là mã định danh tiến trình, còn gọi là PID (Process ID).

Trong Linux và các hệ điều hành giống Unix, mỗi tiến trình đều được gán một PID duy nhất. Đây là cách mà hệ điều hành nhận diện và theo dõi các tiến trình đang chạy.

Một cách nhanh hơn để lấy PID của một tiến trình cụ thể là sử dụng lệnh pgrep:

pgrep bash
Output
1017

Tiến trình đầu tiên được khởi chạy khi hệ thống boot, được gọi là init , sẽ được gán PID“1”.

pgrep init
Output
1

Tiến trình init chịu trách nhiệm khởi chạy tất cả các tiến trình khác trên hệ thống. Các tiến trình được tạo ra sau sẽ được gán các PID lớn hơn.

Parent process là tiến trình chịu trách nhiệm tạo ra một tiến trình khác. Mỗi tiến trình đều có một PPID, tức là ID của tiến trình cha. Bạn có thể thấy cột PPID trong các công cụ quản lý tiến trình như top, htop, và ps.

Mọi sự tương tác giữa người dùng và hệ điều hành liên quan đến tiến trình đều phải dịch qua lại giữa tên tiến trình và PID tại một thời điểm nào đó trong quá trình thao tác. Chính vì vậy, các công cụ quản lý tiến trình luôn hiển thị PID trong kết quả của chúng.

Bước 3: Cách gửi tín hiệu đến tiến trình trong Linux

Tất cả các tiến trình trong Linux đều có thể phản hồi tín hiệu. Tín hiệu là một cơ chế ở cấp độ hệ điều hành dùng để yêu cầu chương trình dừng hoặc thay đổi hành vi của nó.

Cách phổ biến nhất để gửi tín hiệu đến một tiến trình là sử dụng lệnh kill. Như tên gọi gợi ý, chức năng mặc định của lệnh này là cố gắng kết thúc một tiến trình:

kill PID_of_target_process

Lệnh kill sẽ gửi tín hiệu TERM đến tiến trình. Tín hiệu TERM yêu cầu tiến trình kết thúc một cách lịch sự. Điều này cho phép chương trình thực hiện các thao tác dọn dẹp cần thiết trước khi thoát ra một cách an toàn.

Tuy nhiên, nếu chương trình không phản hồi hoặc hoạt động không đúng và không thoát khi nhận được tín hiệu TERM, bạn có thể nâng cấp mức độ tín hiệu bằng cách gửi tín hiệu KILL mạnh hơn:

kill -KILL PID_of_target_process

Đây là một tín hiệu đặc biệt, không được gửi trực tiếp đến chương trình.

Thay vào đó, tín hiệu được gửi đến kernel, và kernel sẽ ép tiến trình phải dừng ngay lập tức. Cách này thường được sử dụng khi chương trình bỏ qua các tín hiệu bình thường mà bạn đã gửi cho nó.

Mỗi tín hiệu đều có một số định danh tương ứng, bạn có thể sử dụng số thay vì tên khi gửi tín hiệu. Ví dụ:

  • 15 tương đương với TERM
  • 9 tương đương với KILL

Tín hiệu không chỉ được dùng để tắt chương trình, mà còn có thể kích hoạt các hành vi khác.

Ví dụ, nhiều tiến trình được thiết kế để chạy liên tục dưới nền (thường được gọi là “daemon”) sẽ tự động khởi động lại khi nhận được tín hiệu HUP , hoặc tiến hiệu hang-up. Máy chủ web Apache là một ví dụ điển hình hoạt động theo cách này.

sudo kill -HUP pid_of_apache

Lệnh trên sẽ khiến Apache tải lại tập tin cấu hình và tiếp tục phục vụ nội dung như bình thường.

Lưu ý: Nhiều tiến trình chạy nền như vậy thường được quản lý thông qua các dịch vụ hệ thống, vốn cung cấp một lớp tương tác bổ sung để kiểm soát chúng. Vì vậy, việc khởi động lại toàn bộ dịch vụ thường là lựa chọn tốt hơn thay vì gửi trực tiếp tín hiệu HUP đến một tiến trình đang chạy.

Nếu bạn xem xét các tập tin cấu hình của các dịch vụ khác nhau, bạn có thể thấy rằng các cơ chế restart trong dịch vụ thực chất cũng đang gửi tín hiệu đến tiến trình, nhưng đồng thời còn ghi lại nhật ký và cung cấp thêm báo cáo chi tiết.

Bạn có thể liệt kê tất cả các tín hiệu mà lệnh kill có thể gửi bằng cách sử dụng tùy chọn -l:

kill -l
Output
1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM

Mặc dù cách truyền thống để gửi tín hiệu là thông qua PID, nhưng vẫn có những phương pháp khác cho phép bạn làm điều đó bằng cách sử dụng tên tiến trình.

Lệnh pkill hoạt động gần như giống hệt với kill, nhưng thay vì sử dụng PID, nó dựa vào tên tiến trình:

pkill -9 ping

Lệnh trên tương đương với:

kill -9 `pgrep ping`

Nếu bạn muốn gửi tín hiệu đến tất cả các phiên bản của một tiến trình cụ thể, bạn có thể sử dụng lệnh killall:

killall firefox

Lệnh trên sẽ gửi tín hiệu TERM đến tất cả các phiên bản của firefox đang chạy trên máy tính.

Bước 4: Cách điều chỉnh mức độ ưu tiên của tiến trình

Trong môi trường máy chủ, đôi khi bạn sẽ muốn điều chỉnh mức độ ưu tiên của các tiến trình.

Một số tiến trình có thể được xem là rất quan trọng đối với hệ thống, trong khi các tiến trình khác có thể được thực thi khi hệ thống còn dư tài nguyên.

Linux quản lý mức độ ưu tiên này thông qua một giá trị gọi là niceness.

  • Tiến trình có độ ưu tiên cao sẽ được coi là ít “nice” hơn, vì chúng không chia sẻ tài nguyên một cách “lịch sự”.
  • Ngược lại, tiến trình ưu tiên thấp được gọi là “nice” hơn, vì chúng sẵn sàng chỉ sử dụng tài nguyên khi hệ thống còn dư.

Khi bạn chạy lệnh top ở phần đầu bài viết, có một cột được đánh dấu là “NI”, đó chính là giá trị nice của tiến trình.

top
[secondary_label Output] 
Tasks:  56 total,   1 running,  55 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.0%us,  0.3%sy,  0.0%ni, 99.7%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   1019600k total,   324496k used,   695104k free,     8512k buffers
Swap:        0k total,        0k used,        0k free,   264812k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND           
 1635 root      20   0 17300 1200  920 R  0.3  0.1   0:00.01 top                
    1 root      20   0 24188 2120 1300 S  0.0  0.2   0:00.56 init               
    2 root      20   0     0    0    0 S  0.0  0.0   0:00.00 kthreadd           
    3 root      20   0     0    0    0 S  0.0  0.0   0:00.11 ksoftirqd/0

Giá trị nice có thể nằm trong khoảng từ -19 hoặc -20 (mức ưu tiên cao nhất) đến 19 hoặc 20 (mức ưu tiên thấp nhất).

Để chạy một chương trình với một giá trị nice cụ thể, bạn có thể sử dụng lệnh nice như sau:

nice -n 15 command_to_execute

Cách sử dụng chỉ áp dụng khi bạn khởi chạy một chương trình mới.

Nếu bạn muốn thay đổi giá trị nice của một chương trình đang chạy, bạn sẽ cần dùng một công cụ có tên là renice:

renice 0 PID_to_prioritize

Kết luận

Quản lý tiến trình trong Linux là một phần cốt lõi và có ích trong hầu hết mọi tình huống. Ngay cả khi bạn không trực tiếp quản trị hệ thống, thì việc xác định và xử lý các tiến trình bị treo hoặc hoạt động không đúng cũng là một kỹ năng rất hữu ích và thực tế.

0 Bình luận

Đăng nhập để thảo luận

Chuyên mục Hướng dẫn

Tổng hợp các bài viết hướng dẫn, nghiên cứu và phân tích chi tiết về kỹ thuật, các xu hướng công nghệ mới nhất dành cho lập trình viên.

Đăng ký nhận bản tin của chúng tôi

Hãy trở thành người nhận được các nội dung hữu ích của CyStack sớm nhất

Xem chính sách của chúng tôi Chính sách bảo mật.

Đăng ký nhận Newsletter

Nhận các nội dung hữu ích mới nhất