Giới hạn số lượng thread tối đa của một process trong hệ điều hành Linux

Phân bổ không gian địa chỉ ảo

Trong hệ điều hành Linux, không gian địa chỉ ảo được chia thành hai phần chính: Kernel Space (không gian nhân) và User Space (không gian người dùng). Tùy thuộc vào kiến trúc của CPU là 32-bit hay 64-bit mà kích thước của các vùng này sẽ khác nhau:

  • Hệ thống 32-bit: Tổng không gian địa chỉ ảo là 4 GB. Thông thường, 1 GB cao nhất được dành cho Kernel Space, 3 GB còn lại dành cho User Space.
  • Hệ thống 64-bit: Không gian địa chỉ ảo cực kỳ lớn. Cả User Space và Kernel Space đều có thể lên tới 128 TB, nằm ở hai đầu của dải địa chỉ, phần ở giữa hiện tại chưa được định nghĩa và sử dụng.

Việc một process có thể tạo ra bao nhiêu thread phụ thuộc vào hai yếu tố then chốt: giới hạn bộ nhớ ảo và các tham số cấu hình của hệ thống.

Giới hạn bởi bộ nhớ ảo trên hệ thống 32-bit

Mỗi khi một thread được khởi tạo, hệ điều hành cần cấp phát cho nó một vùng không gian ngăn xếp (stack). Nếu số lượng thread tăng lên, tổng dung lượng stack yêu cầu sẽ tăng theo, chiếm dần không gian bộ nhớ ảo của process.

Bạn có thể kiểm tra kích thước stack mặc định bằng lệnh: ulimit -a. Thông thường, giá trị này là 8 MB (8192 KB).

Xét một hệ thống Linux 32-bit với 3 GB User Space. Nếu mỗi thread tiêu tốn khoảng 10 MB bộ nhớ ảo cho stack, về mặt lý thuyết, một process chỉ có thể tạo ra tối đa khoảng 300 thread (3 GB / 10 MB). Để tăng con số này, chúng ta buộc phải giảm kích thước stack của mỗi thread xuống, ví dụ như 512 KB hoặc 256 KB.

Giới hạn trên hệ thống 64-bit

Với hệ thống 64-bit, User Space lên đến 128 TB. Nếu giữ nguyên mức 10 MB mỗi thread, con số thread lý thuyết có thể tạo ra là hàng triệu. Do đó, trên kiến trúc 64-bit, bộ nhớ ảo thường không phải là rào cản đầu tiên. Thay vào đó, giới hạn sẽ nằm ở các tham số hạt nhân (kernel parameters).

Dưới đây là đoạn mã C đơn giản để kiểm tra giới hạn tạo thread trên máy cục bộ:

#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

void* task_handler(void* arg) {
    while (1) {
        sleep(60);
    }
    return NULL;
}

int main() {
    int status;
    int total_threads = 0;
    pthread_t thread_id;

    while (1) {
        status = pthread_create(&thread_id, NULL, task_handler, NULL);
        if (status != 0) {
            printf("Khong the tao them thread. Tong so: %d\n", total_threads);
            perror("Loi");
            break;
        }
        total_threads++;
    }
    return 0;
}

Các tham số hệ thống ảnh hưởng đến số lượng thread

Khi chạy thử nghiệm trên máy ảo 64-bit, bạn có thể gặp các mức trần do hệ điều hành thiết lập sẵn nhằm bảo vệ tài nguyên hệ thống:

  • /proc/sys/kernel/threads-max: Giới hạn tổng số thread trên toàn bộ hệ thống. Nếu tổng số thread của tất cả các process chạm ngưỡng này, việc tạo thread mới sẽ thất bại.
  • /proc/sys/kernel/pid_max: Giới hạn giá trị PID tối đa. Vì mỗi thread trong Linux được quản lý như một Lightweight Process (LWP) và cần một PID riêng, nên nếu hết dải PID, bạn không thể tạo thêm thread.
  • /proc/sys/vm/max_map_count: Giới hạn số lượng vùng nhớ ảo (VMA) mà một process có thể sở hữu. Mỗi thread khi tạo ra sẽ sinh thêm các vùng ánh xạ bộ nhớ cho stack, do đó tham số này cũng gián tiếp gây ra giới hạn.

Thử nghiệm điều chỉnh giới hạn

Giả sử hệ thống của bạn dừng lại ở khoảng 14.000 thread do giới hạn threads-max, bạn có thể nâng mức này lên bằng cách ghi giá trị mới vào file cấu hình:

echo 100000 > /proc/sys/kernel/threads-max

Sau khi nâng threads-max, process có thể tiếp tục bị chặn bởi pid_max (thường mặc định là 32.768). Tiếp tục nâng pid_maxmax_map_count sẽ giúp đẩy giới hạn đi xa hơn. Tuy nhiên, khi số lượng thread quá lớn, hệ thống sẽ đối mặt với rào cản về tài nguyên phần cứng như CPU (quá tải do Context Switching) và RAM vật lý.

Mối quan hệ giữa Virtual Memory và Physical Memory

Một điểm đáng lưu ý là mặc dù process có thể chiếm hàng chục Terabyte bộ nhớ ảo (VIRT), nhưng lượng RAM vật lý thực tế sử dụng (RES) có thể rất nhỏ. Điều này là do hệ điều hành sử dụng cơ chế nạp trang theo yêu cầu (Demand Paging). Các trang bộ nhớ ảo chỉ được ánh xạ vào RAM vật lý khi chúng thực sự được truy cập để đọc hoặc ghi. Do đó, một process có thể duy trì hàng ngàn thread "ngủ" với tổng dung lượng stack ảo khổng lồ mà không làm cạn kiệt RAM ngay lập tức.

Tổng kết

Số lượng thread tối đa của một process phụ thuộc vào:

  1. Kiến trúc hệ thống: 32-bit bị giới hạn bởi không gian 3 GB User Space; 64-bit gần như không bị giới hạn bởi không gian địa chỉ ảo.
  2. Kích thước Stack: ulimit -s càng lớn thì số thread tạo được trên hệ thống 32-bit càng ít.
  3. Cấu hình Kernel: Các tham số threads-max, pid_max, và max_map_count đóng vai trò là "chốt chặn" trên hệ thống 64-bit.
  4. Phần cứng: Khả năng xử lý của CPU và dung lượng RAM vật lý sẽ quyết định hiệu suất thực tế khi chạy số lượng thread lớn.

Thẻ: linux multithreading virtual-memory kernel-parameters operating-system

Đăng vào ngày 12 tháng 6 lúc 00:22