Lớp trừu tượng Cgroup

Android 10 trở lên sử dụng lớp trừu tượng nhóm điều khiển (cgroup) với hồ sơ tác vụ. Nhà phát triển có thể dùng lớp này để mô tả một (hoặc nhiều) tập hợp các quy định hạn chế cần áp dụng cho một luồng hoặc một quy trình. Sau đó, hệ thống sẽ tuân theo các hành động được quy định của hồ sơ tác vụ để chọn một hoặc nhiều cgroup thích hợp, thông qua đó các hạn chế được áp dụng và có thể thực hiện các thay đổi đối với bộ 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ề cgroup

Cgroup 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 quy trình con trong tương lai) thành các nhóm phân cấp có hành vi chuyên biệt. Android sử dụng cgroup để kiểm soát và tính toán các tài nguyên hệ thống như mức sử dụng và mức phân bổ CPU và bộ nhớ, có hỗ trợ cgroup phiên bản 1cgroup phiên bản 2 của nhân Linux.

Android 9 trở xuống

Trong Android 9 trở xuống, tập lệnh khởi tạo init.rc chứa tập hợp các cgroup có sẵn, điểm gắn kết và phiên bản của chúng. Mặc dù có thể thay đổi các nhóm này, nhưng khung Android dự kiến sẽ có một nhóm cgroup cụ thể tồn tại ở các vị trí cụ thể với một phiên bản và hệ thống 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ệ thống 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 cgroup với hồ sơ tác vụ:

  • Thiết lập cgroup. Nhà phát triển mô tả chế độ thiết lập cgroup trong tệp cgroups.json để xác định các nhóm cgroup, cũng như vị trí và thuộc tính gắn kết của các nhóm đó. Tất cả các cgroup đều được gắn trong giai đoạn khởi động ban đầu của quy trình khởi tạo.
  • Hồ sơ công việc. Các lớp này cung cấp một lớp trừu tượng tách rời chức năng bắt buộc khỏi thông tin chi tiết về việc triển khai. Ứng dụng khung Android áp dụng các 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 các API SetTaskProfilesSetProcessProfiles. (Các API này chỉ có trên 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 quá trình 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

Cgroup được mô tả trong tệp cgroups.json nằm trong <ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/. Mỗi bộ điều khiển được mô tả trong một phần phụ và phải có tối thiểu 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 kết, được xác định bằng trường Đường dẫn.
  • Mode, 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 (tất cả đều không bắt buộc).
  • Thuộc tính không bắt buộc, đặt thành true để hệ thống bỏ qua lỗi gắn do bộ điều khiển cgroup mà hạt nhân không hỗ trợ được gắn.

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

Ví dụ bên dưới cho thấy nội dung mô tả về bộ điều khiển cgroup phiên bản 1 (Cgroups) và cgroup phiên bản 2 (Cgroups2) cùng 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 ví dụ này có 2 phần: Cgroups (mô tả bộ điều khiển cgroup phiên bản 1) và Cgroups2 (mô tả bộ điều khiển cgroup phiên bản 2). Tất cả các bộ điều khiển trong hệ thống phân cấp cgroups phiên bản 2 đều được gắn ở cùng một vị trí. Do đó, phần Cgroups2 có các thuộc tính Path, Mode, UIDGID riêng để mô tả vị trí và các thuộc tính cho gốc của hệ phân cấp. Thuộc tính Đường dẫn cho Bộ điều khiển trong Cgroups2 tương ứng với đường dẫn gốc đó. Trong Android 12 trở lên, bạn có thể xác định một bộ điều khiển cgroup được chỉ định bằng đường dẫn và chế độ dưới dạng "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 ban đầu và các cgroup được gắn tại các vị trí đã chỉ định. Để sau này lấy vị trí gắn kết cgroup, hãy dùng hàm API CgroupGetControllerPath.

Tệp hồ sơ công việc

Tệp task_profiles.json nằm trong <ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/. Sử dụng nó để mô tả một tập hợp các hành động cụ thể sẽ được áp dụng cho một quy trình hoặc một luồng. Một tập hợp các thao tác đượ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 thao tác trên 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 các mục trong danh sách Thuộc tính. Mỗi mục nhập chứa những thông tin sau:

  • Trường Name (Tên) chỉ định tên của Thuộc tính.
  • Trường Controller tham chiếu đến một trình kiểm soát cgroup từ tệp cgroups.json theo tên của trình kiểm soát đó.
  • Trường File đặ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 thông tin tham chiếu trong định nghĩa hồ sơ nhiệm vụ. 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 những tệp đó và không thể trừu tượng hoá 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ụ; chúng cung cấp khả năng tách rời tốt hơn giữa hành vi bắt buộc và các chi tiết triển khai của hành vi đó.

Mục Hồ sơ chứa các định nghĩa về hồ sơ công việc với những thông tin sau:

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

    • Trường Name (Tên) chỉ định thao tác.
    • Mục Params chỉ định một nhóm 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 Tham số Mô tả
SetTimerSlack Slack Độ trễ của bộ tính giờ tính bằng nano giây
SetAttribute Name Tên tham chiếu đến một thuộc tính trong mục 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
Valuegiá trị cần được ghi vào tệp
JoinCgroup Controller Tên của bộ đ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ó một phần AggregateProfiles chứa các hồ sơ tổng hợp, mỗi hồ sơ là một biệt hiệu cho 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 những nội dung sau:

  • 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ả các hồ sơ chứa trong đó cũng sẽ tự động được áp dụng. Hồ sơ tổng hợp có thể chứa cả hồ sơ riêng lẻ hoặc hồ sơ tổng hợp khác, miễn là không có đệ quy (hồ sơ tự bao gồm chính nó).

lệnh task_profiles init language

Lệnh task_profiles trong Ngôn ngữ khởi động Android có sẵn cho Android 12 trở lên để tạo điều kiện thuận lợi cho việc 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) được dùng để di chuyển một quy trình giữa các nhóm điều khiển. Lệnh task_profiles mang lại sự linh hoạt khi thay đổi các hoạt động 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. API 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

    Kết hợp 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ệ thống 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 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 thông tin này cho các cgroup dành riêng cho một cấp độ API.

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

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

Để ghi đè các định nghĩa từ 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 như 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 đó. 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. Các 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 có thay đổi nào đối với API. Tuy nhiên, Android 10 di chuyển các chức năng này vào libprocessgroup, hiện 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 rằng mã mới có 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 đều phải thêm phần phụ thuộc vào thư viện libprocessgroup vào makefile của chúng. 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 xoá phần phụ thuộc thư viện libcutils khỏi tệp makefile.

API hồ sơ công việc

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 các hồ sơ tác vụ được chỉ định trong profiles cho luồng được chỉ định theo mã nhận dạng 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 các hồ sơ tác vụ được chỉ định trong profiles cho quy trình do người dùng và mã nhận dạng quy trình chỉ định bằng cách sử dụng các tham số uidpid
bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path)
Trả về liệu có tồn tại 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 có thuộc tính hồ sơ do attr_name chỉ định hay không; nếu có true, hãy đặt biến path thành đường dẫn của tệp được 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 có thuộc tính hồ sơ do attr_name chỉ định hay không; nếu true, hãy đặt biến path thành đường dẫn của tệp được liên kết với thuộc tính hồ sơ đó và thành luồng do mã nhận dạng luồng của tệp 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 cgroup bộ nhớ cho mỗi ứng dụng hay không.