تعامل kswapd و lmkd

این صفحه تعامل بین kswapd و lmkd را در Android پوشش می‌دهد، و نشان می‌دهد که چگونه آن‌ها باهم کار می‌کنند تا فشار حافظه را مدیریت کنند.

پس‌زمینه

Android از دو سازوکار اصلی برای بازیابی حافظه درصورت کمبود حافظه سیستم استفاده می‌کند:

  1. kswapd: یک دیمون هسته استاندارد Linux که با بیرون کردن صفحه‌های تمیز با پشتیبانی فایل یا جابه‌جا کردن صفحه‌های ناشناس به ZRAM، صفحه‌ها را بازیابی می‌کند.
  2. lmkd: دیمن Low Memory Killer فضای کاربر Android. وقتی kswapd نمی‌تواند با درخواست‌های حافظه همگام شود و فشار سیستم افزایش می‌یابد (همان‌طور که توسط PSI نشان داده می‌شود)، lmkd وارد عمل می‌شود تا فرایندها را متوقف کند و بخش‌های بزرگ‌تری از حافظه را آزاد کند.

‫kswapd و lmkd دو سیستم مکمل برای بازیابی حافظه هستند. آن‌ها برای رسیدن به یک هدف (ایجاد حافظه آزاد) اما با روش‌های مختلف کار می‌کنند. برای مقایسه و هم‌سنجی:

  • قابل‌بازیابی درمقابل مخرب: kswapd صفحه‌های حافظه را که می‌توانند از فضای ذخیره‌سازی بازخوانی شوند رها می‌کند، یا صفحه‌های حافظه را به پارتیشن‌های حافظه دیگر یا فضای ذخیره‌سازی که می‌تواند درصورت درخواست بازخوانی شود جابه‌جا می‌کند. lmkd پردازش‌ها را متوقف می‌کند، که وضعیت غیرفرار را ازبین می‌برد.
  • پهنای باند بازیابی: kswapd نسبت‌به lmkd واحد پردازش مرکزی بیشتری برای بازیابی مقدار مشخصی از حافظه آزاد صرف می‌کند و بازیابی حافظه مذکور بیشتر طول می‌کشد. lmkd می‌تواند مقدار زیادی از حافظه را در مدت کوتاهی با تلاش نسبتاً کمی بازیابی کند. درنظر داشته باشید که در دستگاه‌های همراه، تلاش CPU نه تنها به تأخیر، بلکه به مصرف باتری نیز ترجمه می‌شود.

باکت‌های امتیاز کمبود حافظه

Android براساس اهمیت فرایندها برای کاربر، تنظیم امتیاز «کمبود حافظه» (OOM) (oom_score_adj) را به آن‌ها اختصاص می‌دهد. پردازش‌های با امتیاز بالاتر زودتر متوقف می‌شوند.

دسته‌های رایج عبارت‌اند از:

  • ذخیره‌شده در حافظه نهان (امتیاز >= ۹۰۰): پردازش‌هایی که درحال‌حاضر نیاز نیستند و برای بازراه‌اندازی سریع‌تر نگهداری می‌شوند.
  • سرویس (امتیاز ۵۰۰): پردازش‌های میزبانی سرویس‌های شروع‌شده.
  • سرویس پیش‌زمینه‌ای (امتیاز ۲۰۰): پردازش‌های میزبان سرویس‌های پیش‌زمینه‌ای.
  • پیش‌زمینه (امتیاز ۰): برنامه‌ای که کاربر درحال‌حاضر با آن تعامل دارد.

ژرف‌کاوی: راه‌اندازی PSI و LMKD

PSI چیست؟

اطلاعات توقف فشار (PSI) یکی از ویژگی‌های هسته Linux است که روشی استاندارد برای اندازه‌گیری کمبود منابع ارائه می‌دهد. این معیار زمان تأخیر تکالیف را به‌دلیل کمبود CPU، حافظه، و I/O ردیابی می‌کند.

PSI حافظه: «برخی» درمقابل «کامل»

شاخص PSI حافظه به‌طور خاص تأخیرهای ناشی از کمبود حافظه را ردیابی می‌کند (مثلاً انتظار برای بازیابی صفحه یا خطاهای مجدد). این بخش دو سنجه ارائه می‌دهد:

  • some: درصد زمانی که طی آن حداقل یک تکلیف به‌دلیل حافظه متوقف شده است. این نشان می‌دهد که کمبود حافظه بر عملکرد تأثیر می‌گذارد اما برخی‌از کارها همچنان درحال پیشرفت است.
  • full: درصد زمانی که طی آن همه تکالیف غیربیکار به‌طور هم‌زمان متوقف شده‌اند. این نشان‌دهنده وضعیت بحرانی است که در آن سیستم کاملاً مسدود شده و منتظر حافظه است.

نحوه راه‌اندازی LMKD توسط PSI

پس‌زمینه lmkd فضای کاربر در هسته ثبت می‌شود تا وقتی از آستانه‌های PSI فراتر رفت (با نظارت بر /proc/pressure/memory)، اعلان دریافت کند. وقتی سیستم فشار حافظه بالایی را تجربه می‌کند (مثلاً توقف full از مدت پیکربندی‌شده فراتر می‌رود)، هسته lmkd را بیدار می‌کند. سپس lmkd وضعیت سیستم را ارزیابی می‌کند و براساس امتیاز کمبود حافظه و اندازه فرایندها تصمیم می‌گیرد که کدام فرایندها را متوقف کند.

تنظیم LMKD

رفتار LMKD را می‌توان بااستفاده از ویژگی‌های سیستم تنظیم کرد.

یافتن مقادیر تنظیم در دستگاه

با اجرای دستور زیر می‌توانید مقادیر تنظیم فعلی LMKD را در دستگاه متصل پیدا کنید:

adb shell getprop | grep lmk

ویژگی‌های رایج LMKD

در اینجا برخی‌از ویژگی‌های کلیدی مورداستفاده برای تنظیم LMKD آورده شده است (برای فهرست کامل به اسناد Android مراجعه کنید):

  • ‫ro.lmk.use_psi: روی true تنظیم کنید تا از نمایشگرهای PSI برای تشخیص فشار حافظه استفاده شود (به‌طور پیش‌فرض در Android نسخه ۱۰ و بالاتر).
  • ro.lmk.psi_partial_stall_ms: آستانه مدت توقف (جزئی) some برحسب میلی‌ثانیه برای راه‌اندازی توقف اجباری (به‌طور پیش‌فرض ۷۰ میلی‌ثانیه در دستگاه‌های معمولی، ۲۰۰ میلی‌ثانیه در دستگاه‌های با حافظه دسترسی تصادفی کم).
  • ro.lmk.psi_complete_stall_ms: آستانه مدت توقف full برحسب میلی‌ثانیه برای راه‌اندازی توقف اجباری (پیش‌فرض ۷۰۰ میلی‌ثانیه).
  • ‫ro.lmk.kill_heaviest_task: اگر true،‏ LMKD فرایندی را که بیشترین حافظه را در بین نامزدها در همان سطل OOM استفاده می‌کند ازبین می‌برد.

مقادیر تنظیم مربوطه در دستگاه آزمایشی (Google Pixel 8 Pro)

(توجه: پیش‌فرض‌های معمول برای دستگاه‌های پیشرفته دارای Android 15)

  • ro.lmk.use_psi: true
  • ro.lmk.psi_partial_stall_ms: 70 (معمول)
  • ro.lmk.psi_complete_stall_ms: 700 (معمول)
  • ro.lmk.kill_heaviest_task: true

درستی‌سنجی ZRAM و swap

ظرفیت ZRAM تأثیر زیادی بر سرعت پر شدن فضای مبادله و فعال شدن توقف‌های LMKD دارد. بااستفاده از این فرمان‌ها می‌توانید وضعیت ZRAM و swap را در دستگاه بررسی کنید:

# Check ZRAM sizing and usage
adb shell free -m

# Check swap partition details
adb shell cat /proc/swaps

تمرین عملی: راه‌اندازی kswapd و lmkd

در این تمرین، از برنامه نمونه KswapdLmkdLab برای ایجاد کردن فرایندها در دسته‌های مختلف «کمبود حافظه» استفاده خواهید کرد و سپس حافظه را در فرایند اصلی مصرف خواهید کرد تا kswapd و lmkd را راه‌اندازی کنید.

برنامه به‌گونه‌ای پیکربندی شده است که به‌طور خودکار زیرفرایند ایجاد کند و حافظه را به آن‌ها اختصاص دهد تا به اهداف جذابی برای LMKD تبدیل شوند.

۱. ایجاد زیرفرایندها

برنامه به‌طور خودکار زیرفرایندهای زیر را هنگام راه‌اندازی ایجاد می‌کند:

  • فرایند ذخیره‌شده در حافظه نهان (oom_score_adj >= 900)
  • فرایند سرویس (oom_score_adj 500)
  • فرایند سرویس پیش‌زمینه‌ای (oom_score_adj 200)

فرایندها و امتیازهای OOM آن‌ها را بااستفاده از ADB درستی‌سنجی کنید:

adb shell dumpsys activity processes | grep com.android.kswapdlmkdlab

نمونه تکه‌کد خروجی:

    Proc #  0: fg       T/A/TOP  LCMNFUATI  t: 0 18959:com.android.kswapdlmkdlab/u0a358 (top-activity)
    Proc #  1: prcp     b/ /FGS  -------TI  t: 0 19050:com.android.kswapdlmkdlab:foreground/u0a358 (fg-service)
    Proc #  2: svc      b/ /SVC  -------TI  t: 0 19077:com.android.kswapdlmkdlab:service/u0a358 (started-services)
    Proc #  3: cch      b/ /CACC -------TI  t: 0 19014:com.android.kswapdlmkdlab:cached/u0a358 (cch-client-act)

اختصارهای وضعیت فرایند مربوط به موارد زیر است:

  • ‫fg: پیش‌زمینه (امتیاز OOM‏ ۰)
  • prcp: قابل‌درک (سرویس پیش‌زمینه‌ای، امتیاز کمبود حافظه ۲۰۰)
  • ‫svc: سرویس شروع‌شده (امتیاز OOM‏ ۵۰۰)
  • cch: پردازش ذخیره‌شده در حافظه نهان (امتیاز OOM‏ ۹۰۰)

۲. راه‌اندازی فشار حافظه

برنامه به‌طور خودکار شروع به اختصاص حافظه در فرایند اصلی می‌کند. برای اینکه آزمایش در دستگاه‌های دارای RAM بالا مؤثر باشد، برنامه به زیرفرایندها نیز دستور می‌دهد حافظه اختصاص دهند (مثلاً هرکدام ۵۰۰ مگابایت) تا هدف‌های بهتری برای LMKD باشند.

نظارت بر مرگ زیرپردازش

برنامه بااستفاده از سازوکار IBinder.linkToDeath بر مرگ این زیرفرایندها نظارت می‌کند:

  1. فرایند اصلی بااستفاده از bindService به سرویسی در فرایند هدف متصل می‌شود.
  2. پس‌از اتصال، شیء IBinder را که نشان‌دهنده سرویس است دریافت می‌کند.
  3. این روش با فراخوانی linkToDeath در binder، پیاده‌سازی IBinder.DeathRecipient را ثبت می‌کند.
  4. وقتی فرایند هدف ازبین می‌رود (برای مثال، وقتی توسط LMKD ازبین می‌رود)، سیستم روش binderDied() را فراخوانی می‌کند و به برنامه اجازه می‌دهد رویداد ازبین رفتن را به‌صورت ناهمگام ثبت کند.

برای جلوگیری از اینکه این اتصال اولویت فرایندهای هدف را بالا ببرد (که هدف تمرین را ازبین می‌برد)، برنامه از پرچم Context.BIND_WAIVE_PRIORITY استفاده می‌کند.

برنامه نمونه در عمل

در اینجا نماگرفتی از برنامه پس‌از تکمیل اجرای آزمایشی در Google Pixel 8 Pro (که درمجموع حدود ۱۲ گیگابایت حافظه دسترسی تصادفی دارد) ارائه شده است.

نماگرفت برنامه نمونه پس‌از تکمیل اجرای آزمایشی.
شکل ۱. برنامه نمونه پس‌از تکمیل اجرای آزمایشی در Pixel 8 Pro.

تحلیل ردیابی Perfetto

برای ضبط تعامل، از پیکربندی Perfetto زیر برای ضبط زمان‌بندی، شمارنده‌های حافظه، توقف‌های LMK، و اطلاعات توقف فشار (PSI) استفاده کنید.

پیکربندی Perfetto

تعامل را با اجرای Perfetto با زمان‌بندی، شمارنده‌های حافظه، کشتن‌های LMK، و اطلاعات توقف فشار (PSI) ضبط کنید:

adb shell perfetto -c - --txt -o /data/misc/perfetto-traces/kswapd_lmkd.perfetto-trace <<EOF
buffers: { size_kb: 131072 }
data_sources: {
    config {
        name: "linux.ftrace"
        ftrace_config {
            ftrace_events: "lowmemorykiller/lowmemory_kill"
            ftrace_events: "vmscan/mm_vmscan_kswapd_wake"
            ftrace_events: "vmscan/mm_vmscan_kswapd_sleep"
            ftrace_events: "psi/psi_event"
        }
    }
}
duration_ms: 180000
EOF

ردپا را مرور کنید

نماگرفت زیر از ردیابی‌ای گرفته شده است که دوره زمانی کمی قبل‌از توقف فرایند :cached (با بیشترین احتمال توقف) تا توقف فرایند :foreground (با کمترین احتمال توقف) را نشان می‌دهد.

نماگرفت واسط کاربر Perfetto که فعالیت kswapd و lmkd را نشان می‌دهد.
شکل ۲. ردیابی Perfetto که بازیابی kswapd و کشتن lmkd را نشان می‌دهد.

می‌توانید ببینید که MemFree درحال‌حاضر نسبتاً پایین است و تحت فشار مداوم قرار دارد. kswapd به‌طور مداوم درحال کار است و با حذف کردن صفحه‌های mem.rss.file و انتقال mem.rss.anon به mem.swap از همه پردازش‌های kswapdlmkdlab (پردازش اصلی و پردازش‌های فرعی) و احتمالاً از پردازش‌های دیگری که در این نماگرفت نشان داده نشده‌اند، فشار را کاهش می‌دهد. هم‌زمان، برنامه kswapdlmkdlab به اختصاص حافظه ادامه می‌دهد و فشار را سریع‌تر از آنچه kswapd بتواند بازیابی کند افزایش می‌دهد.

وقتی فشار خیلی زیاد می‌شود، همان‌طور که در بالا بودن psi.mem برای مدت زمان کافی دیده می‌شود، lmkd بیدار می‌شود و برخی‌از فرایندها را متوقف می‌کند. می‌توانید ببینید که lmkd به امتیازهای OOM احترام می‌گذارد و ابتدا kswapdlmkdlab:cached، سپس :service، و بعد :foreground را می‌کشد.

خط زمان رویدادها را مرور کنید و درباره موارد زیر فکر کنید:

  • چگونه kswapd و lmkd مکمل یکدیگر هستند: kswapd اولین موردی است که به فشار حافظه پاسخ می‌دهد، اما lmkd زمانی کمک می‌کند که پهنای باند بازیابی kswapd تقاضای تخصیص را برآورده نمی‌کند.
  • نرخ آزاد شدن حافظه: زمان واحد پردازش مرکزی صرف‌شده در kswapd و حافظه بازیابی‌شده با حذف mem.rss.file یا جابه‌جایی mem.rss.anon به mem.swap را مشاهده کنید. این را با فوران‌های کوتاه فعالیت در lmkd مقایسه کنید که منجر به آزاد شدن مقادیر زیادی از حافظه خصوصی پردازش می‌شود.

عبارت‌های جستجو

این پُرسمان‌ها را در میانای کاربر Perfetto (پُرسمان SQL) اجرا کنید تا داده‌ها را از ردگیری‌تان استخراج کنید.

‫۱. فعالیت kswapd

برای پیدا کردن زمان فعال بودن و اجرای دیمون مبادله هسته، این دستور را اجرا کنید:

SELECT ts, dur, state
FROM thread_state
JOIN thread USING (utid)
WHERE thread.name LIKE 'kswapd%'
  AND state = 'Running'
LIMIT 5;

خروجی نمونه:

ts dur ایالت
30317633052969 159180 درحال اجرا
30317633398347 3999186 درحال اجرا
30317638448111 1847941 درحال اجرا
30317641455354 1031006 درحال اجرا
30317643272615 888387 درحال اجرا

۲. کشته‌های LMKD

LMKD بستن‌های ناشی از کمبود حافظه را در جدول instant می‌نویسد. برای پرسمان کردن همه کشتن‌های LMKD:

SELECT ts, name
FROM instant
WHERE name LIKE 'lmk,%'
ORDER BY ts ASC;

قالب نام رویداد به‌صورت زیر است: lmk,<pid>,<kill_reason>,<oom_score_adj>,<min_oom_score>,<max_thrashing>.

  • kill_reason: عدد صحیحی که نشان می‌دهد چرا lmkd تصمیم گرفت فرایند را متوقف کند (تعریف‌شده در system/memory/lmkd/statslog.h):
    • 0: PRESSURE_AFTER_KILL
    • 1: NOT_RESPONDING
    • 2: LOW_SWAP_AND_THRASHING (فضای مبادله ZRAM بسیار کم است و کوبش رخ داده است)
    • 3: LOW_MEM_AND_SWAP (حافظه آزاد و فضای مبادله ZRAM سیستم کم است)
    • 4: LOW_MEM_AND_THRASHING
    • 5: DIRECT_RECL_AND_THRASHING
    • 6: LOW_MEM_AND_SWAP_UTIL
    • 7: LOW_FILECACHE_AFTER_THRASHING
    • 8: LOW_MEM
    • 9: DIRECT_RECL_STUCK
  • max_thrashing: حداکثر درصد کوبش صفحه در زمان توقف اجباری.

خروجی نمونه:

ts نام
30326148679967 lmk,2308,3,900,201,0
30330439763710 lmk,2309,3,500,201,0
30334985999837 lmk,2310,2,200,0,307

دیداری‌سازی

برای تجسم داده‌ها، می‌توانید از اسکریپت gnuplot زیر برای رسم حافظه آزاد سیستم، فعالیت kswapd، کشتن‌های lmkd، و PSI در یک سری زمانی واحد استفاده کنید. فرض بر این است که اسکریپت از دایرکتوری بالای ساخت Android اجرا می‌شود.

نمونه نمودار برونداد

نمودار زیر بااستفاده از اسکریپت قبلی با داده‌های نمونه معرف اجرای آزمایشی تولید شده است.

نمودار حافظه آزاد، فعالیت kswapd،‏ PSI، و lmkd kills.
شکل ۳. نمودار فشار حافظه که حافظه آزاد، kswapd،‏ PSI، و کشتن‌های lmkd را نشان می‌دهد.

توضیح طرح داستان:

این نمودار تعامل پویا بین مؤلفه‌های مختلف مدیریت حافظه را درطول آزمون تنش نشان می‌دهد:

  1. کاهش حافظه آزاد: با اختصاص حافظه توسط برنامه اصلی، حافظه آزاد سیستم (خط بنفش) به‌طور پیوسته کاهش می‌یابد.
  2. فعال‌سازی kswapd: وقتی حافظه آزاد به زیر حد مشخصی می‌رسد، kswapd بیدار می‌شود (پالس‌های آبی) تا با جابه‌جایی به ZRAM حافظه را بازیابی کند. این به‌صورت جهش‌های متناوب قابل‌مشاهده است.
  3. جهش‌های PSI: وقتی kswapd برای همگام شدن با نرخ بالای تخصیص تلاش می‌کند، رشته‌ها در تخصیص حافظه دچار وقفه می‌شوند و این امر باعث جهش PSI (اطلاعات وقفه فشار) (خط سبز) می‌شود.
  4. LMKD Kills: وقتی PSI به سطوح بحرانی می‌رسد (مثلاً، از آستانه ro.lmk.psi_complete_stall_ms فراتر می‌رود) و حافظه آزاد بسیار کم است، lmkd با ازبین بردن فرایندهای با اولویت پایین (تکانه‌های نارنجی در زمان ۳۰ و ۴۵) مداخله می‌کند تا بخش‌های بزرگی از حافظه را آزاد کند. توجه داشته باشید که بلافاصله پس‌از توقف اجباری، حافظه آزاد افزایش می‌یابد و PSI کاهش می‌یابد، که نشان‌دهنده اثربخشی توقف اجباری در کاهش فشار است. ________________________________________________________________________________

← بازپس‌گیری حافظه: اخراج و مبادله | ↑ بالا | برگرداندن صفحه‌های حافظه به سیستم‌عامل →