Extended Berkeley Packet Filter (eBPF) هي آلة افتراضية داخل النواة تُشغّل برامج eBPF التي يقدّمها المستخدم لتوسيع وظائف النواة. يمكن ربط هذه البرامج بأجهزة التحقيق أو الأحداث في النواة واستخدامها لجمع إحصاءات النواة المفيدة ومراقبتها وتصحيح أخطائها. يتم تحميل برنامج في النواة باستخدام استدعاء النظام bpf(2)، ويقدّمه المستخدم على شكل كائن ثنائي كبير الحجم من تعليمات آلة eBPF. يتوافق نظام إصدار Android مع تجميع برامج C إلى eBPF باستخدام بنية ملف الإصدار البسيطة الموضّحة في هذا المستند.
يمكنك العثور على مزيد من المعلومات حول العناصر الداخلية لبنية eBPF في صفحة Brendan Gregg حول eBPF.
يتضمّن Android أداة تحميل ومكتبة eBPF تحمّلان برامج eBPF في وقت التشغيل.
أداة تحميل BPF في Android
أثناء تشغيل Android، يتم تحميل جميع برامج eBPF الموجودة في /system/etc/bpf/. هذه البرامج هي كائنات ثنائية أنشأها نظام إصدار Android من برامج C، وتكون مصحوبة بملفات Android.bp في شجرة مصدر Android. يخزّن نظام الإصدار الكائنات التي تم إنشاؤها في /system/etc/bpf، وتصبح هذه الكائنات جزءًا من صورة النظام.
تنسيق برنامج C لـ eBPF في Android
يجب أن يكون لبرنامج C لـ eBPF التنسيق التالي:
#include <bpf_helpers.h>
/* Define one or more maps in the maps section, for example
* define a map of type array int -> uint32_t, with 10 entries
*/
DEFINE_BPF_MAP(name_of_my_map, ARRAY, int, uint32_t, 10);
/* this also defines type-safe accessors:
* value * bpf_name_of_my_map_lookup_elem(&key);
* int bpf_name_of_my_map_update_elem(&key, &value, flags);
* int bpf_name_of_my_map_delete_elem(&key);
* as such it is heavily suggested to use lowercase *_map names.
* Also note that due to compiler deficiencies you cannot use a type
* of 'struct foo' but must instead use just 'foo'. As such structs
* must not be defined as 'struct foo {}' and must instead be
* 'typedef struct {} foo'.
*/
DEFINE_BPF_PROG("PROGTYPE/PROGNAME", AID_*, AID_*, PROGFUNC)(..args..) {
<body-of-code
... read or write to MY_MAPNAME
... do other things
>
}
LICENSE("GPL"); // or other licenseالمكان:
name_of_my_mapهو اسم متغيّر الخريطة. يُعلم هذا الاسم أداة تحميل BPF بنوع الخريطة التي سيتم إنشاؤها والمعلّمات التي سيتم استخدامها. يتم توفير تعريف البنية هذا من خلال عنوانbpf_helpers.hالمضمّن.PROGTYPE/PROGNAMEيمثّل نوع البرنامج واسمه. يمكن أن يكون نوع البرنامج أيًا من الأنواع المدرَجة في الجدول التالي. عندما لا يتم إدراج نوع برنامج، لا توجد اصطلاحات تسمية صارمة للبرنامج، بل يجب أن يكون الاسم معروفًا للعملية التي تربط البرنامج.PROGFUNCهي دالة يتم وضعها، عند تجميعها، في قسم من الملف الناتج.
| kprobe | يربط PROGFUNC بتعليمات النواة باستخدام البنية الأساسية لـ kprobe. يجب أن يكون PROGNAME هو اسم دالة النواة التي يتم استخدام kprobe لها. يُرجى الرجوع إلى مستندات النواة الخاصة بـ kprobe لمزيد من المعلومات حول
kprobe.
|
|---|---|
| tracepoint | يربط PROGFUNC بنقطة تتبُّع. يجب أن يكون PROGNAME بالتنسيق SUBSYSTEM/EVENT على سبيل المثال، سيكون قسم نقطة التتبُّع
لربط الدوال بأحداث تبديل سياق المجدول هو
SEC("tracepoint/sched/sched_switch")، حيث sched هو
اسم نظام التتبُّع الفرعي، و sched_switch هو اسم
حدث التتبُّع. يُرجى الاطّلاع على
مستندات النواة الخاصة بأحداث التتبُّع لمزيد من المعلومات حول نقاط التتبُّع.
|
| skfilter | تعمل دالات البرنامج كفلتر لمقبس الشبكة. |
| schedcls | تعمل دالات البرنامج كأداة لتصنيف حركة بيانات الشبكة. |
| cgroupskb, cgroupsock | يتم تشغيل البرنامج عندما تنشئ العمليات في مجموعة تحكّم (CGroup) مقبس AF_INET أو AF_INET6. |
يمكن العثور على أنواع إضافية في رمز مصدر أداة التحميل.
على سبيل المثال، يضيف برنامج myschedtp.c التالي معلومات حول آخر رقم تعريف عملية (PID) تم تشغيلها على وحدة معالجة مركزية معيّنة. يحقّق هذا البرنامج هدفه من خلال إنشاء خريطة وتحديد دالة tp_sched_switch يمكن ربطها بحدث التتبُّع sched:sched_switch. لمزيد من المعلومات، يُرجى الاطّلاع على مقالة
ربط البرامج بنقاط التتبُّع.
#include <linux/bpf.h> #include <stdbool.h> #include <stdint.h> #include <bpf_helpers.h> DEFINE_BPF_MAP(cpu_pid_map, ARRAY, int, uint32_t, 1024); struct switch_args { unsigned long long ignore; char prev_comm[16]; int prev_pid; int prev_prio; long long prev_state; char next_comm[16]; int next_pid; int next_prio; }; DEFINE_BPF_PROG("tracepoint/sched/sched_switch", AID_ROOT, AID_SYSTEM, tp_sched_switch) (struct switch_args *args) { int key; uint32_t val; key = bpf_get_smp_processor_id(); val = args->next_pid; bpf_cpu_pid_map_update_elem(&key, &val, BPF_ANY); return 1; // return 1 to avoid blocking simpleperf from receiving events } LICENSE("GPL");
يتم استخدام ماكرو LICENSE للتحقّق مما إذا كان البرنامج متوافقًا مع ترخيص النواة عندما يستخدم البرنامج دوال المساعد BPF التي توفّرها النواة. يُرجى تحديد اسم ترخيص برنامجك في شكل سلسلة، مثل
LICENSE("GPL") أو LICENSE("Apache 2.0").
تنسيق ملف Android.bp
لكي يتمكّن نظام إصدار Android من إنشاء برنامج eBPF .c، عليك إنشاء إدخال في ملف Android.bp الخاص بالمشروع. على سبيل المثال، لإنشاء برنامج C لـ eBPF باسم bpf_test.c، عليك إجراء الإدخال التالي في ملف Android.bp الخاص بمشروعك:
bpf {
name: "bpf_test.o",
srcs: ["bpf_test.c"],
cflags: [
"-Wall",
"-Werror",
],
}يؤدي هذا الإدخال إلى تجميع برنامج C الذي ينتج عنه الكائن /system/etc/bpf/bpf_test.o. أثناء التشغيل، يحمّل نظام Android تلقائيًا البرنامج bpf_test.o في النواة.
الملفات المتاحة في sysfs
أثناء التشغيل، يحمّل نظام Android تلقائيًا جميع كائنات eBPF من /system/etc/bpf/، وينشئ الخرائط التي يحتاجها البرنامج، ويثبّت البرنامج الذي تم تحميله مع خرائطه في نظام ملفات BPF. يمكن بعد ذلك استخدام هذه الملفات للتفاعل بشكل أكبر مع برنامج eBPF أو قراءة الخرائط. يوضّح هذا القسم الاصطلاحات المستخدَمة لتسمية هذه الملفات ومواقعها في sysfs.
يتم إنشاء الملفات التالية وتثبيتها:
بالنسبة إلى أي برامج تم تحميلها، بافتراض أنّ
PROGNAMEهو اسم البرنامج وFILENAMEهو اسم ملف C لـ eBPF، تنشئ أداة التحميل في Android كل برنامج وتثبّته في/sys/fs/bpf/prog_FILENAME_PROGTYPE_PROGNAME.على سبيل المثال، بالنسبة إلى مثال نقطة التتبُّع
sched_switchالسابق فيmyschedtp.c، يتم إنشاء ملف برنامج وتثبيته في/sys/fs/bpf/prog_myschedtp_tracepoint_sched_sched_switch.بالنسبة إلى أي خرائط تم إنشاؤها، بافتراض أنّ
MAPNAMEهو اسم الخريطة وFILENAMEهو اسم ملف C لـ eBPF، تنشئ أداة التحميل في Android كل خريطة وتثبّتها في/sys/fs/bpf/map_FILENAME_MAPNAME.على سبيل المثال، بالنسبة إلى مثال نقطة التتبُّع
sched_switchالسابق فيmyschedtp.c، يتم إنشاء ملف خريطة وتثبيته في/sys/fs/bpf/map_myschedtp_cpu_pid_map.تعرض الدالة
bpf_obj_get()في مكتبة Android BPF واصف ملف من الملف المثبّت/sys/fs/bpf. يمكن استخدام واصف الملف هذا لإجراء عمليات إضافية، مثل قراءة الخرائط أو ربط برنامج بنقطة تتبُّع.
مكتبة Android BPF
اسم مكتبة Android BPF هو libbpf_android.so وهي جزء من صورة النظام. توفّر هذه المكتبة للمستخدم إمكانات eBPF من المستوى المنخفض اللازمة لإنشاء الخرائط وقراءتها، وإنشاء أجهزة التحقيق ونقاط التتبُّع ومخازن الأداء المؤقتة.
ربط البرامج بنقاط التتبُّع
يتم تحميل برامج نقاط التتبُّع تلقائيًا أثناء التشغيل. بعد التحميل، يجب تفعيل برنامج نقطة التتبُّع باستخدام الخطوات التالية:
- يجب استدعاء
bpf_obj_get()للحصول علىfdالبرنامج من موقع الملف المثبّت. لمزيد من المعلومات، يُرجى الرجوع إلى الـ ملفات المتاحة في sysfs. - يجب استدعاء
bpf_attach_tracepoint()في مكتبة BPF، مع تمريرfdالبرنامج واسم نقطة التتبُّع.
يوضّح نموذج الرمز التالي كيفية ربط نقطة التتبُّع sched_switch المحدّدة في ملف المصدر myschedtp.c السابق (لا يتم عرض عملية التحقّق من الأخطاء):
char *tp_prog_path = "/sys/fs/bpf/prog_myschedtp_tracepoint_sched_sched_switch"; char *tp_map_path = "/sys/fs/bpf/map_myschedtp_cpu_pid"; // Attach tracepoint and wait for 4 seconds int mProgFd = bpf_obj_get(tp_prog_path); int mMapFd = bpf_obj_get(tp_map_path); int ret = bpf_attach_tracepoint(mProgFd, "sched", "sched_switch"); sleep(4); // Read the map to find the last PID that ran on CPU 0 android::bpf::BpfMap<int, int> myMap(mMapFd); printf("last PID running on CPU %d is %d\n", 0, myMap.readValue(0));
القراءة من الخرائط
تتوافق خرائط BPF مع بُنى أو أنواع المفاتيح والقيم المعقّدة العشوائية. تتضمّن مكتبة Android BPF فئة android::BpfMap تستخدم نماذج C++ لإنشاء مثيل BpfMap استنادًا إلى نوع المفتاح والقيمة للخريطة المعنيّة. يوضّح عينة تعليمات برمجية السابق استخدام BpfMap مع المفتاح والقيمة كأعداد صحيحة. يمكن أن تكون الأعداد الصحيحة أيضًا بُنى عشوائية.
وبالتالي، تتيح لك فئة BpfMap المستندة إلى النماذج تحديد كائن BpfMap مخصّص مناسب للخريطة المعيّنة. يمكن بعد ذلك الوصول إلى الخريطة باستخدام الدوال التي تم إنشاؤها بشكل مخصّص، والتي تكون على دراية بالنوع، ما يؤدي إلى الحصول على رمز أكثر وضوحًا.
لمزيد من المعلومات حول BpfMap، يُرجى الرجوع إلى
مصادر Android.
تصحيح الأخطاء
أثناء وقت التشغيل، يتم تسجيل عدة رسائل ذات صلة بتحميل BPF. إذا تعذّر إكمال عملية التحميل لأي سبب، يتم تقديم رسالة سجلّ مفصّلة في logcat. يؤدي فلترة سجلّات logcat حسب bpf إلى طباعة جميع الرسائل وأي أخطاء مفصّلة أثناء وقت التحميل، مثل أخطاء أداة التحقّق من eBPF.
أمثلة على eBPF في Android
تقدّم البرامج التالية في AOSP أمثلة إضافية على استخدام eBPF:
يستخدم برنامج
netdeBPF C برنامج خدمة الشبكة (netd) في Android لأغراض مختلفة، مثل فلترة المقابس وجمع الإحصاءات. لمعرفة كيفية استخدام هذا البرنامج، يُرجى الاطّلاع على مصادر أداة مراقبة حركة بيانات eBPF.يحسب برنامج C لـ
time_in_stateeBPF مقدار الوقت الذي يقضيه تطبيق Android بترددات مختلفة لوحدة المعالجة المركزية، ويُستخدم ذلك لحساب الطاقة.في Android 12، يتتبّع برنامج C لـ eBPF باسم
gpu_memeBPF C إجمالي استخدام ذاكرة وحدة معالجة الرسومات لكل عملية وللنظام بأكمله. يُستخدم هذا البرنامج لإنشاء ملفات تعريف استخدام ذاكرة وحدة معالجة الرسومات.
لمزيد من المعلومات حول استخدام eBPF لجمع بيانات تتبُّع النواة ذات الحمل المنخفض وإدارة الذاكرة
ومخططات توزيع وقت استجابة الطاقة لـ statsd، يُرجى الاطّلاع على مقالة جمع بيانات تتبُّع النواة
باستخدام eBPF.