ב-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
על תהליך או על חוט באמצעות ממשקי ה-APISetTaskProfiles
ו-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 |
ערך שצריך לכתוב בקובץ שמיוצג על ידי המאפיין בעל השם | |
WriteFile | FilePath | הנתיב לקובץ |
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
בסדר הזה:
- קבצים שמוגדרים כברירת מחדל מסוג
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 שלו. |
bool |
SetProcessProfiles(uid_t uid, pid_t pid, const std::vector
החלת פרופילי המשימות שצוינו ב- 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()
חוזרת אם המערכת מוגדרת לשימוש בקבוצות זיכרון לכל אפליקציה. |