Extended Berkeley Packet Filter (eBPF) هي آلة افتراضية داخل النواة
تشغّل برامج eBPF يوفّرها المستخدم لتوسيع وظائف النواة. يمكن ربط هذه البرامج
بأدوات التحقيق أو الأحداث في النواة واستخدامها لجمع إحصاءات
النواة المفيدة ومراقبتها وتصحيح أخطائها. يتم تحميل برنامج في النواة باستخدام استدعاء النظام bpf(2)
، ويوفّره المستخدم على شكل كائن ثنائي كبير الحجم يتضمّن تعليمات الجهاز eBPF. يتوافق نظام الإصدار في Android مع تجميع برامج C إلى eBPF باستخدام صيغة بسيطة لملف الإصدار موضّحة في هذا المستند.
يمكنك الاطّلاع على مزيد من المعلومات حول التفاصيل الداخلية وبنية eBPF على صفحة eBPF الخاصة بـ "بريندان غريغ".
يتضمّن نظام التشغيل Android أداة تحميل ومكتبة eBPF لتحميل برامج eBPF عند بدء التشغيل.
Android BPF loader
أثناء عملية تشغيل 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 للحصول على مزيد من المعلومات حول kprobes.
|
---|---|
tracepoint | يربط PROGFUNC بنقطة تتبُّع. يجب أن يكون PROGNAME بالتنسيق SUBSYSTEM/EVENT . على سبيل المثال، سيكون قسم نقطة التتبُّع
لربط الدوال بأحداث تبديل سياق المجدول هو
SEC("tracepoint/sched/sched_switch") ، حيث sched هو
اسم نظام التتبُّع الفرعي، وsched_switch هو اسم
حدث التتبُّع. يمكنك الاطّلاع على مستندات أحداث التتبُّع kernel
لمزيد من المعلومات عن نقاط التتبُّع.
|
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 من إنشاء برنامج .c
eBPF، عليك إنشاء إدخال في ملف 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
المثبَّت. يمكن استخدام واصف الملف هذا لإجراء عمليات أخرى، مثل قراءة الخرائط أو ربط برنامج بنقطة تتبُّع.
مكتبة BPF لنظام التشغيل Android
اسم مكتبة Android BPF هو libbpf_android.so
وهي جزء من صورة النظام. توفّر هذه المكتبة للمستخدم إمكانات eBPF منخفضة المستوى اللازمة لإنشاء الخرائط وقراءتها، وإنشاء أدوات الفحص ونقاط التتبُّع ومخازن perf.
ربط البرامج بنقاط التتبُّع
يتم تحميل برامج نقاط التتبُّع تلقائيًا عند بدء التشغيل. بعد التحميل، يجب تفعيل برنامج نقاط التتبُّع باتّباع الخطوات التالية:
- اتّصِل بالرقم
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
توفّر البرامج التالية في "مشروع Android مفتوح المصدر" أمثلة إضافية على استخدام eBPF:
يستخدم برنامج
netd
eBPF برنامج netd الخفي للشبكات في Android لأغراض مختلفة، مثل فلترة المقابس وجمع الإحصاءات. للاطّلاع على كيفية استخدام هذا البرنامج، راجِع مصادر أداة مراقبة الزيارات المستندة إلى eBPF.يحسب
time_in_state
برنامج eBPF مقدار الوقت الذي يقضيه تطبيق Android في استخدام ترددات مختلفة لوحدة المعالجة المركزية، ويتم استخدام ذلك لحساب استهلاك الطاقة.في نظام التشغيل Android 12، يتتبّع
gpu_mem
برنامج eBPF C إجمالي استخدام ذاكرة وحدة معالجة الرسومات لكل عملية وللنظام بأكمله. يُستخدَم هذا البرنامج لإنشاء ملفات تعريف لذاكرة وحدة معالجة الرسومات.