0
0
Lập trình
Flame Kris
Flame Krisbacodekiller

Chạy lệnh với giới hạn thời gian trên Linux hiệu quả

Đăng vào 3 tuần trước

• 5 phút đọc

Hướng dẫn sử dụng lệnh timeout trên Linux

Chào mừng bạn đến với pikoTutorial tiếp theo! Trong bài viết này, chúng ta sẽ khám phá một lệnh rất hữu ích trong Bash - lệnh timeout. Lệnh này cho phép bạn thực thi một lệnh khác với giới hạn thời gian. Cú pháp của lệnh rất đơn giản:

Copy
timeout [thời gian] [lệnh_mục_tiêu]

Để tránh việc phải nhập các số lớn cho các giới hạn thời gian lâu, tham số thời gian chấp nhận các hậu tố để đánh dấu khoảng thời gian cụ thể:

  • s - giây
  • m - phút
  • h - giờ
  • d - ngày

Ví dụ đơn giản

Để minh họa, chúng ta sẽ sử dụng một đoạn mã Python đơn giản chạy vô hạn:

python Copy
# some_job.py
import time

while True:
    time.sleep(1)

Chúng ta có thể chạy nó với giới hạn thời gian 3 giây bằng lệnh sau:

Copy
timeout 3s python3 some_job.py

Sau 3 giây chờ đợi, some_job.py sẽ bị kết thúc.

Chỉ định tín hiệu kết thúc

Khi thời gian cho phép hết, lệnh timeout sẽ gửi tín hiệu SIGTERM đến lệnh đã cho. Trong triển khai của bạn, có thể bạn có một số trình xử lý tín hiệu tùy chỉnh để thực hiện việc dọn dẹp trước khi thoát. Chẳng hạn, nếu bạn mong đợi rằng tập lệnh của bạn sẽ bị gián đoạn chủ yếu bởi CTRL + C, bạn có thể có một trình xử lý tín hiệu SIGINT. Trong trường hợp này, bạn có thể muốn giữ tín hiệu này trong trường hợp lệnh timeout để vẫn có thể thực hiện việc dọn dẹp khi thời gian hết:

python Copy
# some_job.py
import sys
import signal
import time

def handle_sigint(signum, frame):
    print("Nhận được SIGINT, dọn dẹp và thoát...")
    sys.exit(0)

signal.signal(signal.SIGINT, handle_sigint)

print("Bắt đầu chạy tập lệnh...")
time.sleep(10)

Nếu bạn gọi:

Copy
timeout 3s python3 some_job.py

Bạn sẽ thấy rằng trình xử lý tín hiệu của bạn không được gọi trước khi tập lệnh thoát. Để thay đổi điều này, hãy chỉ định tín hiệu mong đợi bằng tùy chọn --signal:

Copy
timeout --signal=SIGINT 3s python3 some_job.py

Sau đó, bạn sẽ thấy thông tin từ trình xử lý handle_sigint khi thời gian hết.

Cung cấp thời gian để thoát êm dịu

Trong ví dụ trước, tôi đã giả định rằng việc dọn dẹp là nhanh và luôn thành công, nhưng trên thực tế, việc tắt ứng dụng có thể cần thời gian:

python Copy
# some_job.py
import sys
import signal
import time

def handle_sigterm(signum, frame):
    print("Nhận được SIGTERM, dọn dẹp và thoát...")
    time.sleep(5)

signal.signal(signal.SIGTERM, handle_sigterm)

print("Bắt đầu chạy tập lệnh...")
time.sleep(10)

Trong trường hợp này, có thể cần một thời gian giới hạn riêng để hoàn tất mọi thứ - nếu vi phạm thời gian giới hạn cuối cùng đó, ứng dụng sẽ bị giết. Điều này có thể đạt được bằng cách sử dụng --kill-after. Lệnh sau cho phép tập lệnh chạy trong 5 giây, gửi SIGTERM (tín hiệu mặc định) và sau đó cho phép tập lệnh có thêm 2 giây để thoát. Nếu tập lệnh không thoát trong 2 giây sau khi nhận SIGTERM, thì SIGKILL sẽ được gửi:

Copy
timeout --kill-after=2s 5s python3 some_job.py

Bảo tồn mã trả về

Theo mặc định, nếu lệnh được cung cấp cho timeout hết thời gian, mã trả về của nó sẽ bằng 124. Bạn có thể kiểm tra điều này bằng cách chạy tập lệnh Python ví dụ và sau đó kiểm tra mã trả về của nó:

Copy
timeout 3s python3 some_job.py
echo $?

Ghi chú cho người mới bắt đầu: ? trong phần trên là một biến Shell đặc biệt lưu trữ mã thoát của lệnh được thực thi gần nhất. Ký hiệu $, như thường lệ trong môi trường Shell, lấy giá trị cơ bản của biến đó, vì vậy echo $? in ra trạng thái thoát của lệnh gần đây nhất.

Tuy nhiên, đây có thể là điều mà hệ thống của bạn không mong đợi, đặc biệt nếu tập lệnh của bạn có một số logic mà cuối cùng trả về các mã thoát cụ thể. Chẳng hạn, tập lệnh sau mô phỏng một quá trình tiêu tốn thời gian của 3 giai đoạn khác nhau. Trong trường hợp bị gián đoạn, tập lệnh trả về số giai đoạn đang được xử lý trong thời điểm gián đoạn:

python Copy
# some_job.py
import sys
import signal
import time

current_phase: int = 0

def handle_sigterm(signum, frame):
    sys.exit(current_phase)

signal.signal(signal.SIGTERM, handle_sigterm)

for i in range(3):
    current_phase += 1
    time.sleep(2)

Để bảo tồn mã trả về đó sau khi hết thời gian, hãy sử dụng cờ --preserve-status:

Copy
timeout --preserve-status 3s python3 some_job.py

Bây giờ, khi bạn kiểm tra trạng thái thoát của thao tác như vậy, bạn sẽ thấy 2 vì timeout đã kết thúc tập lệnh trong khi giai đoạn 2 đang được xử lý.

Thực tiễn tốt nhất khi sử dụng lệnh timeout

1. Đặt thời gian hợp lý

  • Cần cân nhắc kỹ lưỡng thời gian giới hạn để tránh việc vô tình kết thúc quá trình cần thiết.

2. Kiểm tra lỗi

  • Luôn kiểm tra mã thoát của lệnh để đảm bảo rằng không có lỗi xảy ra.

3. Sử dụng các tín hiệu phù hợp

  • Đảm bảo rằng các tín hiệu được chỉ định là phù hợp với logic của ứng dụng.

4. Ghi log đầy đủ

  • Ghi lại các hoạt động trước và sau khi sử dụng timeout để theo dõi hành vi của ứng dụng.

5. Thực hiện dọn dẹp đúng cách

  • Đảm bảo rằng mọi tài nguyên đều được giải phóng đúng cách khi ứng dụng kết thúc.

Những cạm bẫy thường gặp

  • Thời gian không đủ: Đặt thời gian quá ngắn có thể dẫn đến việc dừng quá trình không cần thiết.
  • Không xử lý tín hiệu: Bỏ qua việc xử lý các tín hiệu có thể dẫn đến tình trạng tài nguyên không được giải phóng.

Mẹo hiệu suất

  • Tối ưu hóa mã: Đảm bảo mã của bạn chạy hiệu quả để giảm thời gian thực thi.
  • Giám sát hiệu suất: Sử dụng các công cụ giám sát để theo dõi hiệu suất ứng dụng.

Giải quyết sự cố

  • Lệnh không phản hồi: Kiểm tra xem lệnh có đang chạy không. Có thể lệnh cần thời gian hơn mong đợi.
  • Mã trả về không như mong đợi: Kiểm tra logic mã trong ứng dụng để đảm bảo điều đó không ảnh hưởng đến mã trả về.

Kết luận

Lệnh timeout là một công cụ mạnh mẽ trong Bash giúp bạn kiểm soát thời gian thực thi của các lệnh. Bằng cách sử dụng đúng cách, bạn có thể cải thiện hiệu suất ứng dụng và đảm bảo rằng các quy trình không kéo dài vô hạn. Hãy thử nghiệm với các tùy chọn khác nhau và áp dụng những mẹo thực hành tốt nhất để đạt được hiệu suất tối ưu.

Tham khảo thêm: Nếu bạn muốn tìm hiểu thêm về Bash và các lệnh hữu ích khác, hãy theo dõi các bài viết tiếp theo trong chuỗi pikoTutorial!

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào