שכבת הפשטה של קבוצה

ב-Android 10 ואילך נעשה שימוש בשכבת הפשטה של קבוצת בקרה (cgroup) עם פרופילי משימות, שבאמצעותם המפתחים יכולים לתאר קבוצה (או קבוצות) של הגבלות שיוחלו על חוט או על תהליך. לאחר מכן המערכת פועלת לפי הפעולות שנקבעו בפרופילים של המשימות כדי לבחור קבוצת cgroup מתאימה אחת או יותר, שבאמצעותה חלות ההגבלות. אפשר לבצע שינויים בקבוצת התכונות הבסיסית של ה-cgroup בלי להשפיע על שכבות התוכנה ברמה גבוהה יותר.

מידע על cgroups

קבוצות קבוצות מספקות מנגנון לצבירה ולחלוקה למחיצות של קבוצות של משימות (שכוללות תהליכים, שרשורים וכל הצאצאים שלהן בעתיד) לקבוצות היררכיות עם התנהגות ייחודית. מערכת Android משתמשת ב-cgroups כדי לשלוט במשאבי המערכת, כמו שימוש והקצאה של מעבד (CPU) וזיכרון, ולתעד אותם. יש תמיכה ב-cgroups v1 וב-cgroups v2 של ליבה של Linux.

Android מגרסה 9 ומטה

ב-Android 9 ובגרסאות ישנות יותר, סקריפט האיפוס init.rc הכיל את הקבוצות הזמינות של cgroups, נקודות הטעינה שלהן והגרסאות שלהן. אפשר לשנות את הפרמטרים האלה, אבל מסגרת Android מצפה לקבוצה ספציפית של קבוצות cgroups במיקומים ספציפיים, עם גרסה ספציפית והיררכיה של קבוצות משנה ספציפית, על סמך הסקריפט. המגבלה הזו הגבילה את היכולת לבחור את גרסת ה-cgroup הבאה לשימוש, או לשנות את היררכיית ה-cgroup כדי להשתמש בתכונות חדשות.

Android מגרסה 10 ואילך

ב-Android מגרסה 10 ואילך נעשה שימוש ב-cgroups עם פרופילי משימות:

  • הגדרת cgroup המפתחים מתארים את הגדרת ה-cgroups בקובץ cgroups.json כדי להגדיר קבוצות של cgroups, את המיקומים והמאפיינים שלהן לטעינה. כל ה-cgroups מותקנים בשלב ההתחלה של תהליך האינטליקציה.
  • אפשר לבצע משימות בפרופילים. התרשימים האלה מופשטים שמפרידים בין הפונקציונליות הנדרשת לפרטי ההטמעה. מסגרת Android מחילה את פרופילי המשימות כפי שמתואר בקובץ task_profiles.json על תהליך או על חוט באמצעות ממשקי ה-API SetTaskProfiles ו-SetProcessProfiles. (ממשקי ה-API האלה ייחודיים ל-Android מגרסה 11 ואילך).

כדי לספק תאימות לאחור, הפונקציות הקודמות set_cpuset_policy,‏ set_sched_policy ו-get_sched_policy מספקות את אותו API ואת אותה פונקציונליות, אבל ההטמעה שלהן שונתה כך שתשתמש בפרופילים של משימות. בתרחישי שימוש חדשים, מומלץ ב-AOSP להשתמש בממשקי API חדשים של פרופילים של משימות במקום בפונקציה set_sched_policy מהדור הקודם.

קובץ תיאור של קבוצות Google

קבוצות הקבוצות מתוארות בקובץ cgroups.json שנמצא בקטע <ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/. כל אחד מהבקרים מתואר בקטע משנה ועליהם לעמוד בדרישות הבאות לפחות:

  • שם, שמוגדר על ידי השדה Controller.
  • נתיב הטעינה, שמוגדר בשדה Path.
  • Mode,‏ UID (מזהה משתמש) ו-GID (מזהה קבוצה) שמתארים את הבעלים ואת מודעות הגישה של הקבצים בנתיב הזה (הכול אופציונלי).
  • מאפיין אופציונלי, שמוגדר כ-true כדי לאפשר למערכת להתעלם מהשגיאה בטעינה שנגרמת על ידי בקר cgroup שהטעינה שלו לא נתמכת בליבה.

דוגמה לקובץ cgroups.json

בדוגמה הבאה מוצגים תיאורים של בקרי cgroup v1‏ (Cgroups) ו-cgroup v2‏ (Cgroups2) עם הנתיבים המתאימים שלהם.

{
  "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"
     }
   ]
 }
}

קובץ הדוגמה הזה מכיל שני קטעים: Cgroups (מתאר את הפקדים של cgroup v1) ו-Cgroups2 (מתאר את הפקדים של cgroup v2). כל אמצעי הבקרה בהיררכיה של קבוצות cgroups v2 נטענים באותו מיקום. לכן, לקטע Cgroups2 יש מאפיינים משלו של נתיב, מצב, UID ו-GID, שמתארים את המיקום והמאפיינים של שורש ההיררכיה. המאפיין Path של Controllers בקטע Cgroups2 יחסי לנתיב השורש הזה. ב-Android מגרסה 12 ואילך, אפשר להגדיר בקר cgroup שמוגדר עם נתיב ומצב כ-"Optional" על ידי הגדרת true.

קובץ cgroups.json מנותח כחלק מתהליך ה-init, בשלב ההתחלה של ה-init, וה-cgroups מותקנים במיקומים שצוינו. כדי לקבל מאוחר יותר את המיקומים של ה-cgroup למאח, משתמשים בפונקציית ה-API CgroupGetControllerPath.

קובץ של פרופילים של משימות

הקובץ task_profiles.json נמצא ב-<ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/. אפשר להשתמש בו כדי לתאר קבוצה ספציפית של פעולות שיחולו על תהליך או על חוט. קבוצת פעולות משויכת לשם פרופיל, שמשמשים בקריאות ל-SetTaskProfiles ול-SetProcessProfiles כדי להפעיל פעולות בפרופיל.

דוגמה לקובץ 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" ]
     }
}

אפשר להקצות שמות לקובצי cgroup ספציפיים כרשומות ברשימת המאפיינים. כל רשומה מכילה את הפרטים הבאים:

  • השדה Name מציין את שם המאפיין.
  • השדה Controller מפנה למנהל cgroup מהקובץ cgroups.json לפי השם שלו.
  • בשדה File מצוין שם של קובץ ספציפי במנהל הזה.

מאפיינים הם הפניות בהגדרות של פרופילי המשימות. מחוץ לפרופילים של משימות, צריך להשתמש בהם רק כשהמסגרת דורשת גישה ישירה לקבצים האלה, ולא ניתן לבצע הפשטה של הגישה באמצעות פרופילים של משימות. בכל שאר המקרים, מומלץ להשתמש בפרופילים של משימות. הם מאפשרים לבצע ניתוק טוב יותר בין ההתנהגות הנדרשת לבין פרטי ההטמעה שלה.

הקטע Profiles מכיל הגדרות של פרופילי משימות עם הפרטים הבאים:

  • השדה Name מגדיר את שם הפרופיל.
  • בקטע פעולות מפורטות פעולות שמבוצעות כשהפרופיל מוחל. לכל פעולה יש את הפרטים הבאים:

    • השדה Name מציין את הפעולה.
    • הקטע Params מציין קבוצת פרמטרים לפעולה.

הפעולות הנתמכות מפורטות בטבלה:

פעולה פרמטר תיאור
SetTimerSlack Slack מרווח הזמן של הטיימר ב-ns
SetAttribute Name שם שמתייחס למאפיין מהקטע מאפיינים
Value ערך שצריך לכתוב בקובץ שמיוצג על ידי המאפיין בעל השם
WriteFileFilePathהנתיב לקובץ
Valueערך שייכתב לקובץ
JoinCgroup Controller שם של בקר ה-cgroup מ-cgroups.json
Path נתיב של קבוצת משנה בהיררכיה של בקר ה-cgroup

ב-Android 12 ואילך מופיע הקטע AggregateProfiles שמכיל פרופילים מצטברים, שכל אחד מהם הוא כינוי לקבוצה של פרופיל אחד או יותר. ההגדרות של פרופיל מצטבר כוללות את הפרטים הבאים:

  • השדה Name מציין את שם הפרופיל המצטבר.
  • בשדה Profiles מפורטים שמות הפרופילים שכלולים בפרופיל המצטבר.

כשמחילים פרופיל מצטבר, כל הפרופילים שמכילים גם מוחלים באופן אוטומטי. פרופילים מצטברים יכולים להכיל גם פרופילים ספציפיים וגם פרופילים מצטברים אחרים, כל עוד אין חזרה חוזרת על עצמה (פרופיל שכולל את עצמו).

הפקודה task_profiles init language

הפקודה task_profiles ב-Android Init Language זמינה ב-Android מגרסה 12 ואילך כדי להפעיל פרופיל משימות בתהליך ספציפי. היא מחליפה את הפקודה writepid (שהווצאה משימוש ב-Android 12) ששימשה להעברת תהליך בין קבוצות cgroups. הפקודה task_profiles מספקת גמישות לשינוי ההטמעות הבסיסיות בלי להשפיע על השכבות העליונות. בדוגמה הבאה, שתי הפקודות מבצעות את אותה פעולה:

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

    הוצא משימוש ב-Android 12, משמש לכתיבת ה-PID של המשימה הנוכחית בקובץ /dev/cpuctl/top-app/tasks.

  • task_profiles MaxPerformance

    מצטרף לתהליך הנוכחי לקבוצת האפליקציות העליונה בבקר cpu (cpuctl), וכתוצאה מכך נכתב ה-PID של התהליך אל dev/cpuctl/top-app/tasks.

תמיד צריך להשתמש בפקודה task_profiles כדי להעביר משימות בהיררכיות של cgroup ב-Android 12 ואילך. היא מקבלת פרמטר אחד או יותר, שמייצגים את שמות הפרופילים שצוינו בקובץ task_profiles.json.

לכל פרופילים של משימות ברמת ה-API

ב-Android 12 ואילך, אפשר לשנות או לבטל הגדרות בקובצי ברירת המחדל cgroups.json ו-task_profiles.json, על סמך השינוי ברמת Android API או על סמך השינוי במחיצה של הספק.

כדי לשנות את ההגדרות שמבוססות על רמת ה-API, הקבצים הבאים צריכים להיות קיימים במכשיר:

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

    שימוש באפשרות הזו כשמדובר בקבוצות ספציפיות לרמת API.

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

    משתמשים באפשרות הזו לפרופילים ספציפיים לרמת API.

כדי לשנות את ההגדרות מהמחיצה של הספק, הקבצים הבאים צריכים להיות נוכחים במכשיר:

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

אם השם של מאפיין או הגדרת פרופיל בקבצים האלה זהה לשם שלהם בקובץ ברירת המחדל, ההגדרה בקובץ (ברמת ה-API או ברמת הספק) מבטלת את ההגדרה הקודמת. שימו לב גם שהגדרות ברמת הספק מבטלות את ההגדרות ברמת ה-API. אם להגדרה החדשה יש שם חדש, קבוצת המאפיינים או הפרופילים תתוקן עם ההגדרה החדשה.

מערכת Android טוענת את הקבצים cgroup ו-task_profile בסדר הזה:

  1. קבצים שמוגדרים כברירת מחדל מסוג cgroups.json ו-task_profiles.json.
  2. קבצים ספציפיים לרמת ה-API, אם יש כאלה.
  3. קבצים של מחיצות של ספקים, אם יש כאלה.

שינויים בממשקי API קיימים

ב-Android מגרסה 10 ואילך, הפונקציות set_cpuset_policy, ‏ set_sched_policy ו-get_sched_policy נשארות ללא שינויים ב-API. עם זאת, ב-Android 10 הפונקציות האלה מועברות למרחב libprocessgroup, שכולל עכשיו את כל הפונקציונליות שקשורה ל-cgroup.

למרות שהכותרת cutils/sched_policy.h עדיין קיימת, כדי למנוע פגיעה בקוד הקיים, כדאי לוודא שהקוד החדש כולל כותרת processgroup/sched_policy.h חדשה במקום זאת.

במודולים שמשתמשים באחת מהפונקציות האלה צריך להוסיף תלות בספרייה libprocessgroup לקובץ ה-makefile שלהם. אם מודול לא משתמש בפונקציות אחרות של libcutils, צריך להסיר את התלות בספרייה libcutils מקובץ ה-makefile.

ממשקי API של פרופילי משימות

ממשקי ה-API הפרטיים ב-processgroup/processgroup.h מוגדרים בטבלה:

סוג API והגדרה
bool SetTaskProfiles(int tid, const std::vector& profiles)
המערכת מחילה את הפרופילים של המשימות שצוינו ב-profiles על השרשור שצוין על ידי מזהה השרשור (מזהה) באמצעות הפרמטר tid שלו.
bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector& profiles)
החלת פרופילי המשימות שצוינו ב-profiles על התהליך שצוין לפי מזהה המשתמש ומזהה התהליך שלו, באמצעות הפרמטרים uid ו-pid
bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path)
הפונקציה מחזירה אם קיים בקר cgroup שצוין על ידי cgroup_name. אם הערך הוא true, משתנה path מוגדר כשורש של ה-cgroup הזה.
bool CgroupGetAttributePath(const std::string& attr_name, std::string* path)
הפונקציה מחזירה אם מאפיין פרופיל שצוין על ידי attr_name קיים. אם הערך הוא true, המשתנה path מוגדר לנתיב של הקובץ שמשויך למאפיין הפרופיל הזה.
bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path)
הפונקציה מחזירה אם קיים מאפיין פרופיל שצוין על ידי attr_name. אם true, המשתנה מוגדר לנתיב של הקובץ שמשויך לאותו מאפיין פרופיל, ולשרשור שצוין במזהה השרשור שלו באמצעות הפרמטר tid.path
bool UsePerAppMemcg()
חוזרת אם המערכת מוגדרת לשימוש בקבוצות זיכרון לכל אפליקציה.