ב-Android מגרסה 10 ואילך נעשה שימוש בשכבת הפשטה של קבוצת בקרה (cgroup) עם פרופילי משימות, שמפתחים יכולים להשתמש בהם כדי לתאר קבוצה (או קבוצות) של הגבלות שיוחלו על שרשור או על תהליך. לאחר מכן, המערכת פועלת לפי הפעולות שנקבעו בפרופילי המשימות כדי לבחור קבוצת cgroup אחת או יותר שמתאימות, שדרכן מוחלות ההגבלות, ואפשר לבצע שינויים בקבוצת התכונות הבסיסית של cgroup בלי להשפיע על שכבות תוכנה גבוהות יותר.
מידע על cgroups
Cgroups מספק מנגנון לצבירה ולחלוקה של קבוצות משימות (שכוללות תהליכים, שרשורים וכל הצאצאים העתידיים שלהם) לקבוצות היררכיות עם התנהגות מיוחדת. Android משתמשת ב-cgroups כדי לשלוט במשאבי המערכת ולתת דיווח עליהם, כמו שימוש במעבד (CPU) ובזיכרון והקצאה שלהם, עם תמיכה ב-cgroups v1 וב-cgroups v2 של ליבת Linux.
Android מגרסה 9 ומטה
ב-Android 9 ובגרסאות מוקדמות יותר, סקריפט האתחול init.rc
הכיל את קבוצת ה-cgroup הזמינות, נקודות ההרכבה והגרסאות שלהן. אפשר לשנות את ההגדרות האלה, אבל מסגרת Android מצפה שקבוצה ספציפית של cgroups תהיה קיימת במיקומים ספציפיים עם גרסה ספציפית והיררכיית קבוצות משנה, על סמך הסקריפט. ההגבלה הזו מנעה את האפשרות לבחור את הגרסה הבאה של cgroup לשימוש, או לשנות את ההיררכיה של cgroup כדי להשתמש בתכונות חדשות.
Android מגרסה 10 ואילך
Android מגרסה 10 ואילך משתמש ב-cgroups עם פרופילי משימות:
- הגדרת cgroup. מפתחים מתארים את הגדרת ה-cgroups בקובץ
cgroups.json
כדי להגדיר קבוצות של cgroups, את מיקומי ההרכבה והמאפיינים שלהן. כל קבוצות הבקרה מותקנות במהלך השלב המוקדם של תהליך האתחול. - פרופילים של משימות. הם מספקים הפשטה שמפרידה בין הפונקציונליות הנדרשת לבין פרטי ההטמעה שלה. אפליקציית Android framework מחילה את פרופילי המשימות כפי שמתואר בקובץ
task_profiles.json
על תהליך או על שרשור באמצעות ממשקי ה-APISetTaskProfiles
ו-SetProcessProfiles
. (ממשקי ה-API האלה ייחודיים ל-Android מגרסה 11 ואילך).
כדי לספק תאימות לאחור, הפונקציות הקודמות set_cpuset_policy
,
set_sched_policy
ו-get_sched_policy
מספקות את אותו API ואותה פונקציונליות,
אבל ההטמעה שלהן שונתה כך שהן משתמשות בפרופילים של משימות. במקרים חדשים של שימוש, מומלץ ב-AOSP להשתמש בממשקי API חדשים של פרופילי משימות במקום בפונקציה set_sched_policy
מהדור הקודם.
קובץ התיאור של cgroups
קבוצות C מתוארות בקובץ cgroups.json
שנמצא בתיקייה <ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/
.
כל בקר מתואר בקטע משנה, והוא חייב לכלול לפחות את הפרטים הבאים:
- השם, שמוגדר בשדה Controller.
- נתיב ההרכבה, שמוגדר בשדה נתיב.
- 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
עובר ניתוח כחלק מתהליך האתחול, בשלב האתחול המוקדם, וה-cgroup מותקנים במיקומים שצוינו. כדי לקבל בהמשך את מיקומי ההרכבה של 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 (שם) מגדיר את שם הפרופיל.
בקטע Actions (פעולות) מפורטת קבוצה של פעולות שמתבצעות כשמחילים את הפרופיל. לכל פעולה יש את הפרטים הבאים:
- בשדה Name מציינים את הפעולה.
- בקטע Params מציינים קבוצה של פרמטרים לפעולה.
הפעולות הנתמכות מפורטות בטבלה:
פעולה | פרמטר | תיאור |
---|---|---|
SetTimerSlack |
Slack |
הפרש הזמן בטיימר בננו-שניות |
SetAttribute |
Name |
שם שמפנה למאפיין מהקטע Attributes (מאפיינים) |
Value |
ערך שייכתב לקובץ שמיוצג על ידי המאפיין עם השם | |
WriteFile | FilePath | הנתיב לקובץ |
Value | ערך שייכתב לקובץ | |
JoinCgroup |
Controller |
שם של בקר cgroup מ-cgroups.json |
Path |
נתיב של קבוצת משנה בהיררכיה של בקר cgroup |
ב-Android 12 ואילך יש קטע AggregateProfiles שמכיל פרופילים מצטברים, שכל אחד מהם הוא כינוי לקבוצה של פרופיל אחד או יותר. הגדרות פרופיל מצטברות כוללות את הרכיבים הבאים:
- בשדה Name (שם) מציינים את השם של הפרופיל המצטבר.
- בשדה Profiles מופיעים שמות הפרופילים שנכללים בפרופיל המצטבר.
כשמחילים פרופיל מצטבר, כל הפרופילים שהוא מכיל מוחלים גם הם באופן אוטומטי. פרופילים מצטברים יכולים להכיל פרופילים אישיים או פרופילים מצטברים אחרים, כל עוד אין בהם חזרות (פרופיל שכולל את עצמו).
פקודת השפה task_profiles init
פקודת task_profiles
בשפת ההפעלה של Android זמינה ב-Android מגרסה 12 ואילך כדי להקל על הפעלת פרופיל משימה לתהליך ספציפי. היא מחליפה את הפקודה writepid
(שלא בשימוש יותר ב-Android 12) ששימשה להעברת תהליך בין קבוצות cgroup. הפקודה task_profiles
מאפשרת גמישות בשינוי של יישומי הבסיס בלי להשפיע על השכבות העליונות. בדוגמה שלמטה, שתי הפקודות האלה מבצעות למעשה את אותה פעולה:
writepid /dev/cpuctl/top-app/tasks
הפונקציה הזו הוצאה משימוש ב-Android 12. היא שימשה לכתיבת ה-PID של המשימה הנוכחית בקובץ
/dev/cpuctl/top-app/tasks
.task_profiles MaxPerformance
הפקודה מצטרפת לתהליך הנוכחי בקבוצת האפליקציות המובילות בקטע cpu controller (
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.
ממשקי Task profiles APIs
ממשקי ה-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)
מחזירה אם קיים בקר cgroup שצוין על ידי cgroup_name ;
אם true , מגדירה את המשתנה path לשורש של ה-cgroup הזה |
bool |
CgroupGetAttributePath(const std::string& attr_name, std::string* path)
הפונקציה מחזירה את הערך true אם מאפיין הפרופיל שצוין על ידי attr_name קיים, ואם
הערך הוא true , היא מגדירה את המשתנה path לנתיב של הקובץ שמשויך
למאפיין הפרופיל הזה. |
bool |
CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path)
הפונקציה מחזירה את הערך true אם מאפיין הפרופיל שצוין על ידי attr_name קיים, ואת הערך false אם הוא לא קיים. אם הוא קיים, היא מגדירה את המשתנה path לנתיב של הקובץ שמשויך למאפיין הפרופיל הזה, ולשרשור שצוין על ידי מזהה השרשור שלו באמצעות הפרמטר tid .true |
bool |
UsePerAppMemcg()
הפונקציה מחזירה אם המערכת מוגדרת להשתמש ב-cgroups של זיכרון לכל אפליקציה. |