تعامل kswapd و lmkd

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

پس‌زمینه

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

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

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

  • قابل‌بازیابی درمقابل مخرب: kswapd صفحه‌های حافظه‌ای را که می‌توانند از فضای ذخیره‌سازی بازخوانی شوند رها می‌کند، یا صفحه‌های حافظه را به پارتیشن‌های حافظه دیگر یا فضای ذخیره‌سازی که می‌تواند درصورت درخواست بازخوانی شود جابه‌جا می‌کند. lmkd پردازش‌ها را متوقف می‌کند، که وضعیت غیرفرار را ازبین می‌برد.
  • Reclaim Bandwidth: kswapd spends more CPU to reclaim a given amount of free memory than lmkd, and takes longer to reclaim said memory. 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 فرایندی را که بیشترین حافظه را در میان نامزدها در همان سطل کمبود حافظه استفاده می‌کند ازبین می‌برد.

مقادیر تنظیم مربوطه در دستگاه آزمایشی (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: پیش‌زمینه (امتیاز کمبود حافظه ۰)
  • prcp: قابل‌درک (سرویس پیش‌زمینه‌ای، امتیاز کمبود حافظه ۲۰۰)
  • ‫svc: سرویس شروع‌شده (امتیاز OOM‏ ۵۰۰)
  • cch: پردازش ذخیره‌شده در حافظه نهان (امتیاز کمبود حافظه ۹۰۰)

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

برنامه به‌طور خودکار شروع به اختصاص حافظه در فرایند اصلی می‌کند. برای اینکه آزمایش در دستگاه‌های دارای 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: وقتی PSI به سطوح بحرانی می‌رسد (مثلاً، از آستانه ro.lmk.psi_complete_stall_ms به‌طور مفهومی فراتر می‌رود) و حافظه آزاد بسیار کم است، lmkd با کشتن فرایندهای با اولویت پایین (تکانه‌های نارنجی در زمان ۳۰ و ۴۵) مداخله می‌کند تا بخش‌های بزرگی از حافظه را آزاد کند. توجه داشته باشید که بلافاصله پس‌از توقف، حافظه آزاد افزایش می‌یابد و PSI کاهش می‌یابد، که نشان‌دهنده اثربخشی توقف در کاهش فشار است. ________________________________________________________________________________

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