Lớp trừu tượng Cgroup

Android 10 trở lên sử dụng lớp trừu tượng nhóm đối chứng (cgroup) với hồ sơ tác vụ mà nhà phát triển có thể sử dụng để mô tả một tập hợp (hoặc nhiều tập hợp) hạn chế cần áp dụng cho một luồng hoặc quy trình. Sau đó, hệ thống sẽ thực hiện các hành động theo quy định của hồ sơ tác vụ để chọn một hoặc nhiều nhóm cgroup thích hợp, thông qua đó áp dụng các quy tắc hạn chế và có thể thực hiện các thay đổi đối với nhóm tính năng cgroup cơ bản mà không ảnh hưởng đến các lớp phần mềm cao hơn.

Giới thiệu về cgroups

Cgroups cung cấp một cơ chế để tổng hợp và phân vùng các nhóm tác vụ (bao gồm các quy trình, luồng và tất cả các tác vụ con trong tương lai) thành các nhóm phân cấp với hành vi chuyên biệt. Android sử dụng các cgroups để kiểm soát và tính đến tài nguyên hệ thống (chẳng hạn như phân bổ và sử dụng CPU và bộ nhớ), đồng thời hỗ trợ cho cgroups v1 nhân Linux và cgroups v2.

Android 9 trở xuống

Trong Android 9 trở xuống, tập lệnh khởi chạy init.rc chứa tập hợp các nhóm tài nguyên có sẵn, điểm gắn và phiên bản của các nhóm tài nguyên đó. Mặc dù có thể thay đổi những thành phần này, nhưng khung Android dự kiến một tập hợp các nhóm cụ thể sẽ tồn tại tại những vị trí cụ thể có phiên bản và hệ phân cấp nhóm con cụ thể, dựa trên tập lệnh. Điều này hạn chế khả năng chọn phiên bản cgroup tiếp theo để sử dụng hoặc thay đổi hệ phân cấp cgroup để sử dụng các tính năng mới.

Android 10 trở lên

Android 10 trở lên sử dụng cgroups với hồ sơ tác vụ:

  • Thiết lập nhóm. Nhà phát triển mô tả chế độ thiết lập cgroups trong tệp cgroups.json để xác định các nhóm cgroups, cũng như vị trí và thuộc tính của các nhóm cgroups đó. Tất cả các cgroups đều được gắn trong giai đoạn khởi động sớm của quá trình khởi động.
  • Hồ sơ công việc. Các lớp này cung cấp một bản tóm tắt tách biệt chức năng bắt buộc khỏi thông tin chi tiết về cách triển khai chức năng đó. Khung Android áp dụng hồ sơ tác vụ như mô tả trong tệp task_profiles.json cho một quy trình hoặc luồng bằng cách sử dụng API SetTaskProfilesSetProcessProfiles. (Các API này chỉ dành riêng cho Android 11 trở lên.)

Để cung cấp khả năng tương thích ngược, các hàm cũ set_cpuset_policy, set_sched_policyget_sched_policy cung cấp cùng một API và chức năng, nhưng cách triển khai của các hàm này đã được sửa đổi để sử dụng hồ sơ tác vụ. Đối với các trường hợp sử dụng mới, AOSP đề xuất sử dụng API hồ sơ tác vụ mới thay vì hàm set_sched_policy cũ.

Tệp mô tả Cgroups

Cgroups được mô tả trong tệp cgroups.json nằm trong <ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/. Mỗi tay điều khiển được mô tả trong một tiểu mục và phải có ít nhất những thông tin sau:

  • Tên, do trường Controller (Bộ điều khiển) xác định.
  • Đường dẫn gắn, do trường Path (Đường dẫn) xác định.
  • Mode (Chế độ), UID (mã nhận dạng người dùng) và GID (mã nhận dạng nhóm) mô tả chủ sở hữu và chế độ truy cập cho các tệp trong đường dẫn này (không bắt buộc).
  • Thuộc tính không bắt buộc, được đặt thành true để cho phép hệ thống bỏ qua lỗi gắn kết do bộ điều khiển cgroup gây ra mà hạt nhân không hỗ trợ việc gắn kết.

Ví dụ về tệp cgroups.json

Ví dụ bên dưới cho thấy nội dung mô tả cho các bộ điều khiển cgroup v1 (Cgroups) và cgroup v2 (Cgroups2) với các đường dẫn tương ứng.

{
  "Cgroups": [
    {
      "Controller": "cpu",
      "Path": "/dev/cpuctl",
      "Mode": "0755",
      "UID": "system",
      "GID": "system"
    },
    {
      "Controller": "memory",
      "Path": "/dev/memcg",
      "Mode": "0700",
      "Optional": true
    }
  ],
 "Cgroups2": {
   "Path": "/sys/fs/cgroup",
   "Mode": "0755",
   "UID": "system",
   "GID": "system",
   "Controllers": [
     {
       "Controller": "freezer",
       "Path": ".",
       "Mode": "0755",
       "UID": "system",
       "GID": "system"
     }
   ]
 }
}

Tệp mẫu này chứa hai phần, Cgroups (mô tả trình điều khiển cgroup v1) và Cgroups2 (mô tả trình điều khiển cgroup v2). Tất cả trình điều khiển trong hệ phân cấp cgroups v2 đều được gắn ở cùng một vị trí. Do đó, phần Cgroups2 có các thuộc tính Path (Đường dẫn), Mode (Chế độ), UID (Mã nhận dạng duy nhất) và GID (Mã nhận dạng nhóm) riêng để mô tả vị trí và các thuộc tính cho thư mục gốc của hệ phân cấp. Thuộc tính Path (Đường dẫn) cho Controllers (Bộ điều khiển) trong Cgroups2 tương ứng với đường dẫn gốc đó. Trên Android 12 trở lên, bạn có thể xác định trình điều khiển cgroup được chỉ định bằng đường dẫn và chế độ là "Optional" bằng cách đặt thành true.

Tệp cgroups.json được phân tích cú pháp trong quá trình khởi động, trong giai đoạn khởi động sớm và các nhóm tài nguyên được gắn ở các vị trí được chỉ định. Để sau này lấy được vị trí gắn cgroup, hãy sử dụng hàm API CgroupGetControllerPath.

Tệp hồ sơ tác vụ

Tệp task_profiles.json nằm trong <ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/. Sử dụng lớp này để mô tả một tập hợp hành động cụ thể sẽ được áp dụng cho một quy trình hoặc luồng. Một tập hợp các hành động được liên kết với tên hồ sơ, được dùng trong các lệnh gọi SetTaskProfilesSetProcessProfiles để gọi các hành động trong hồ sơ.

Ví dụ về tệp task_profiles.json

{
  "Attributes": [
    {
      "Name": "MemSoftLimit",
      "Controller": "memory",
      "File": "memory.soft_limit_in_bytes"
    },
    {
      "Name": "MemSwappiness",
      "Controller": "memory",
      "File": "memory.swappiness"
    }
  ],
  "Profiles": [
    {
      "Name": "MaxPerformance",
      "Actions" : [
        {
          "Name" : "JoinCgroup",
          "Params" :
          {
            "Controller": "schedtune",
            "Path": "top-app"
          }
        }
      ]
    },
    {
      "Name": "TimerSlackHigh",
      "Actions" : [
        {
          "Name" : "SetTimerSlack",
          "Params" :
          {
            "Slack": "40000000"
          }
        }
      ]
    },
    {
      "Name": "LowMemoryUsage",
      "Actions" : [
        {
          "Name" : "SetAttribute",
          "Params" :
          {
            "Name" : "MemSoftLimit",
            "Value" : "16MB"
          }
        },
        {
          "Name" : "SetAttribute",
          "Params" :
          {
            "Name" : "MemSwappiness",
            "Value" : "150"

          }
        }
      ]
    }
  ]
  "AggregateProfiles": [
     {
       "Name": "SCHED_SP_DEFAULT",
       "Profiles": [ "TimerSlackHigh", "MaxPerformance" ]
     },
     {
       "Name": "SCHED_SP_BACKGROUND",
       "Profiles": [ "LowMemoryUsage" ]
     }
}

Chỉ định tên cho các tệp cgroup cụ thể dưới dạng mục nhập trong danh sách Attributes (Thuộc tính). Mỗi mục nhập chứa các thông tin sau:

  • Trường Name (Tên) chỉ định tên của Thuộc tính.
  • Trường Controller (Trình điều khiển) tham chiếu đến trình điều khiển cgroup từ tệp cgroups.json theo tên của trình điều khiển đó.
  • Trường Tệp đặt tên cho một tệp cụ thể trong bộ điều khiển này.

Thuộc tính là các tham chiếu trong định nghĩa hồ sơ công việc. Ngoài hồ sơ tác vụ, bạn chỉ sử dụng các hồ sơ này khi khung yêu cầu quyền truy cập trực tiếp vào các tệp đó, và không thể tóm tắt quyền truy cập bằng hồ sơ tác vụ. Trong mọi trường hợp khác, hãy sử dụng hồ sơ tác vụ; các hồ sơ này giúp tách biệt tốt hơn giữa hành vi bắt buộc và thông tin chi tiết về việc triển khai hành vi đó.

Mục Hồ sơ chứa các định nghĩa về hồ sơ tác vụ với những nội dung sau:

  • Trường Name (Tên) xác định tên hồ sơ.
  • Mục Hành động liệt kê một nhóm hành động được thực hiện khi áp dụng hồ sơ. Mỗi hành động có các thông tin sau:

    • Trường Name (Tên) chỉ định hành động.
    • Mục Params (Thông số) chỉ định một tập hợp thông số cho thao tác.

Các thao tác được hỗ trợ được liệt kê trong bảng:

Thao tác Thông số Mô tả
SetTimerSlack Slack Độ trễ của bộ tính giờ tính bằng ns
SetAttribute Name Tên tham chiếu đến thuộc tính trong phần Attributes (Thuộc tính)
Value Giá trị sẽ được ghi vào tệp do thuộc tính được đặt tên đại diện
WriteFileFilePathđường dẫn đến tệp
Valuemột giá trị cần ghi vào tệp
JoinCgroup Controller Tên của trình điều khiển cgroup từ cgroups.json
Path Đường dẫn nhóm con trong hệ phân cấp của bộ điều khiển cgroup

Android 12 trở lên có phần AggregateProfiles chứa các hồ sơ tổng hợp, mỗi hồ sơ là đại diện của một tập hợp gồm một hoặc nhiều hồ sơ. Định nghĩa hồ sơ tổng hợp bao gồm:

  • Trường Tên chỉ định tên của hồ sơ tổng hợp.
  • Trường Hồ sơ liệt kê tên của các hồ sơ có trong hồ sơ tổng hợp.

Khi một hồ sơ tổng hợp được áp dụng, tất cả hồ sơ chứa cũng sẽ được áp dụng tự động. Hồ sơ tổng hợp có thể chứa cả hồ sơ cá nhân hoặc các hồ sơ tổng hợp khác, miễn là không có hồi quy (hồ sơ bao gồm chính nó).

lệnh ngôn ngữ khởi tạo task_profiles

Lệnh task_profiles trong Ngôn ngữ khởi động Android có sẵn cho Android 12 trở lên để hỗ trợ kích hoạt hồ sơ tác vụ cho một quy trình cụ thể. Lệnh này thay thế lệnh writepid (không dùng nữa trong Android 12) dùng để di chuyển một quy trình giữa các nhóm tài nguyên. Lệnh task_profiles mang lại tính linh hoạt cho việc thay đổi cách triển khai cơ bản mà không ảnh hưởng đến các lớp trên. Trong ví dụ dưới đây, hai lệnh này thực hiện cùng một thao tác một cách hiệu quả:

  • writepid /dev/cpuctl/top-app/tasks

    Không dùng nữa trong Android 12, phương thức này được dùng để ghi PID của tác vụ hiện tại vào tệp /dev/cpuctl/top-app/tasks.

  • task_profiles MaxPerformance

    Tham gia quy trình hiện tại vào nhóm ứng dụng hàng đầu trong bộ điều khiển "cpu" (cpuctl), dẫn đến việc ghi PID của quy trình vào dev/cpuctl/top-app/tasks.

Luôn sử dụng lệnh task_profiles để di chuyển các tác vụ trong hệ phân cấp cgroup trên Android 12 trở lên. Phương thức này chấp nhận một hoặc nhiều tham số, đại diện cho tên của các hồ sơ được chỉ định trong tệp task_profiles.json.

Hồ sơ tác vụ theo cấp độ API

Trong Android 12 trở lên, bạn có thể sửa đổi hoặc ghi đè các định nghĩa trong tệp cgroups.jsontask_profiles.json mặc định, dựa trên thay đổi của bạn trên cấp độ API Android hoặc thực hiện thay đổi từ phân vùng của nhà cung cấp.

Để ghi đè các định nghĩa dựa trên cấp độ API, các tệp sau phải có trên thiết bị:

  • /system/etc/task_profiles/cgroups_<API level>.json

    Sử dụng thuộc tính này cho các nhóm dành riêng cho một cấp độ API.

  • /system/etc/task_profiles/task_profiles_<API level>.json

    Sử dụng thuộc tính này cho các hồ sơ dành riêng cho một cấp độ API.

Để ghi đè các định nghĩa trong phân vùng của nhà cung cấp, các tệp sau phải có trên thiết bị:

  • /vendor/etc/cgroups.json
  • /vendor/etc/task_profiles.json

Nếu một thuộc tính hoặc định nghĩa hồ sơ trong các tệp này sử dụng cùng tên với tên trong tệp mặc định, thì định nghĩa tệp (cấp độ API hoặc cấp nhà cung cấp) sẽ ghi đè định nghĩa trước đó. Ngoài ra, xin lưu ý rằng các định nghĩa ở cấp nhà cung cấp sẽ ghi đè các định nghĩa ở cấp API. Nếu định nghĩa mới có tên mới, thì tập hợp các thuộc tính hoặc hồ sơ sẽ được sửa đổi bằng định nghĩa mới.

Hệ thống Android tải các tệp cgrouptask_profile theo thứ tự sau:

  1. Tệp cgroups.jsontask_profiles.json mặc định.
  2. Các tệp dành riêng cho cấp độ API, nếu có.
  3. Tệp phân vùng của nhà cung cấp, nếu có.

Thay đổi đối với API hiện có

Android 10 trở lên giữ lại các hàm set_cpuset_policy, set_sched_policyget_sched_policy mà không thay đổi API. Tuy nhiên, Android 10 sẽ di chuyển các hàm này vào libprocessgroup. Giờ đây, libprocessgroup chứa tất cả chức năng liên quan đến cgroup.

Mặc dù tiêu đề cutils/sched_policy.h vẫn tồn tại, nhưng để tránh làm hỏng mã hiện có, hãy đảm bảo mã mới bao gồm một tiêu đề processgroup/sched_policy.h mới.

Các mô-đun sử dụng bất kỳ hàm nào trong số này phải thêm phần phụ thuộc trên thư viện libprocessgroup vào tệp makefile. Nếu một mô-đun không sử dụng bất kỳ chức năng libcutils nào khác, hãy loại bỏ phần phụ thuộc thư viện libcutils khỏi tệp makefile.

API hồ sơ tác vụ

Các API riêng tư trong processgroup/processgroup.h được xác định trong bảng:

Loại API và định nghĩa
bool SetTaskProfiles(int tid, const std::vector& profiles)
Áp dụng hồ sơ tác vụ được chỉ định trong profiles cho luồng được chỉ định bằng mã luồng (tid) bằng cách sử dụng tham số tid.
bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector& profiles)
Áp dụng hồ sơ tác vụ được chỉ định trong profiles cho quy trình được chỉ định theo mã nhận dạng người dùng và quy trình bằng cách sử dụng tham số uidpid
bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path)
Trả về liệu có bộ điều khiển cgroup do cgroup_name chỉ định hay không; nếu true, hãy đặt biến path thành gốc của cgroup đó
bool CgroupGetAttributePath(const std::string& attr_name, std::string* path)
Trả về liệu thuộc tính hồ sơ do attr_name chỉ định có tồn tại hay không; nếu true, hãy đặt biến path thành đường dẫn của tệp liên kết với thuộc tính hồ sơ đó.
bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path)
Trả về liệu thuộc tính hồ sơ do attr_name chỉ định có tồn tại hay không; nếu true, hãy đặt biến path thành đường dẫn của tệp liên kết với thuộc tính hồ sơ đó và với luồng do mã luồng chỉ định bằng cách sử dụng tham số tid.
bool UsePerAppMemcg()
Trả về liệu hệ thống có được định cấu hình để sử dụng cgroups bộ nhớ cho mỗi ứng dụng hay không.