ב-Android 10 ואילך נעשה שימוש בשכבת הפשטה של קבוצת בקרה (cgroup) עם פרופילי משימות, שבאמצעותם המפתחים יכולים לתאר קבוצה (או קבוצות) של הגבלות שיחולו על חוט או על תהליך. לאחר מכן המערכת פועלת לפי הפעולות שנקבעו בפרופילים של המשימות כדי לבחור קבוצת cgroup מתאימה אחת או יותר, שבאמצעותה חלות ההגבלות. אפשר לבצע שינויים בקבוצת התכונות הבסיסית של ה-cgroup בלי להשפיע על שכבות התוכנה ברמה גבוהה יותר.
מידע על cgroups
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
על תהליך או על חוט באמצעות ממשקי ה-APISetTaskProfiles
ו-SetProcessProfiles
. (ממשקי ה-API האלה ייחודיים ל-Android מגרסה 11 ואילך).
כדי לספק תאימות לאחור, הפונקציות הקודמות set_cpuset_policy
, set_sched_policy
ו-get_sched_policy
מספקות את אותו API ואת אותה פונקציונליות, אבל ההטמעה שלהן שונתה כך שתשתמש בפרופילים של משימות. בתרחישי שימוש חדשים, מומלץ ב-AOSP להשתמש בממשקי API חדשים של פרופילים של משימות במקום בפונקציה set_sched_policy
מהדור הקודם.
קובץ תיאור של קבוצות C
ה-cgroups מתוארים בקובץ 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 יש מאפייני Path, Mode, UID ו-GID משלו, שמתארים את המיקום והמאפיינים של שורש ההיררכיה. המאפיין Path של Controllers בקטע Cgroups2 הוא יחסי לנתיב הבסיס הזה. ב-Android מגרסה 12 ואילך, אפשר להגדיר בקר cgroup שמוגדר עם נתיב ומצב כ-"Optional"
על ידי הגדרת true
.
קובץ cgroups.json
מנותח כחלק מתהליך ה-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 כרשומים ברשימה Attributes. כל רשומה מכילה את הפרטים הבאים:
- השדה Name מציין את שם המאפיין.
- השדה Controller מפנה למנהל cgroup מהקובץ
cgroups.json
לפי השם שלו. - בשדה File מצוין שם של קובץ ספציפי במנהל הזה.
מאפיינים הם הפניות בהגדרות של פרופילי המשימות. מחוץ לפרופילים של משימות, צריך להשתמש בהם רק כשהמסגרת דורשת גישה ישירה לקבצים האלה, ולא ניתן לבצע הפשטה של הגישה באמצעות פרופילים של משימות. בכל שאר המקרים, מומלץ להשתמש בפרופילים של משימות. הם מאפשרים לבצע ניתוק טוב יותר בין ההתנהגות הנדרשת לבין פרטי ההטמעה שלה.
הקטע Profiles מכיל הגדרות של פרופילי משימות עם הפרטים הבאים:
- השדה Name מגדיר את שם הפרופיל.
בקטע פעולות מפורטות פעולות שמבוצעות כשהפרופיל מוחל. לכל פעולה יש את הפרטים הבאים:
- השדה Name מציין את הפעולה.
- הקטע Params מציין קבוצת פרמטרים של הפעולה.
הפעולות הנתמכות מפורטות בטבלה:
פעולה | פרמטר | תיאור |
---|---|---|
SetTimerSlack |
Slack |
מרווח הזמן של הטיימר ב-ns |
SetAttribute |
Name |
שם שמפנה למאפיין מהקטע מאפיינים |
Value |
ערך שצריך לכתוב בקובץ שמיוצג על ידי המאפיין בעל השם | |
WriteFile | FilePath | הנתיב לקובץ |
Value | ערך שרוצים לכתוב לקובץ | |
JoinCgroup |
Controller |
שם של בקר ה-cgroup מ-cgroups.json |
Path |
נתיב של קבוצת משנה בהיררכיה של בקר ה-cgroup |
ב-Android 12 ואילך מופיע הקטע AggregateProfiles שמכיל פרופילים מצטברים, שכל אחד מהם הוא כינוי לקבוצה של פרופיל אחד או יותר. הגדרות של פרופילים מצטברים מורכבות מהרכיבים הבאים:
- השדה Name מציין את שם הפרופיל המצטבר.
- בשדה Profiles מפורטים שמות הפרופילים שכלולים בפרופיל המצטבר.
כשמחילים פרופיל מצטבר, כל הפרופילים שהוא מכיל מופעלים גם כן באופן אוטומטי. פרופילים מצטברים יכולים להכיל גם פרופילים ספציפיים וגם פרופילים מצטברים אחרים, כל עוד אין חזרה חוזרת על עצמה (פרופיל שכולל את עצמו).
הפקודה task_profiles init language
ב-Android מגרסה 12 ואילך, זמינה הפקודה task_profiles
בשפת Android Init כדי להפעיל פרופיל משימות בתהליך ספציפי. היא מחליפה את הפקודה 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
משתמשים באפשרות הזו לקבוצות cgroups ספציפיות לרמת ה-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
בסדר הזה:
- קבצים שמוגדרים כברירת מחדל מסוג
cgroups.json
ו-task_profiles.json
. - קבצים ספציפיים לרמת ה-API, אם יש כאלה.
- קבצים של מחיצות של ספקים, אם יש כאלה.
שינויים בממשקי 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 על השרשור שצוין באמצעות מזהה השרשור (tid) באמצעות הפרמטר tid שלו. |
bool |
SetProcessProfiles(uid_t uid, pid_t pid, const std::vector
החלת פרופילי המשימות שצוינו ב- profiles על התהליך שצוין באמצעות מזהי המשתמש והתהליך שלו, באמצעות הפרמטרים uid ו-pid |
bool |
CgroupGetControllerPath(const std::string& cgroup_name, std::string* path)
החזרת הערך 'true' אם קיים בקר cgroup שצוין על ידי cgroup_name ,
והחזרת הערך 'false' אם לא. אם הערך הוא 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 קיים. אם הוא קיים, הפונקציה מגדירה את המשתנה path לנתיב של הקובץ שמשויך למאפיין הפרופיל הזה, ולשרשור שצוין לפי מזהה השרשור שלו באמצעות הפרמטר tid .true |
bool |
UsePerAppMemcg()
הפונקציה מחזירה אם המערכת מוגדרת לשימוש ב-cgroups של זיכרון לכל אפליקציה. |