Extended Berkeley Packet Filter (eBPF), çekirdek işlevlerini genişletmek için kullanıcı tarafından sağlanan eBPF programlarını çalıştıran bir çekirdek içi sanal makinedir. Bu programlar, çekirdekteki sondalara veya etkinliklere bağlanabilir ve yararlı çekirdek istatistiklerini toplamak, izlemek ve hata ayıklamak için kullanılabilir. Program, bpf(2) sistem çağrısı kullanılarak çekirdeğe yüklenir ve kullanıcı tarafından eBPF makine talimatlarının ikili blob'u olarak sağlanır. Android derleme sistemi, bu belgede açıklanan basit derleme dosyası söz dizimini kullanarak C programlarını eBPF'ye derlemeyi destekler.
eBPF'nin dahili özellikleri ve mimarisi hakkında daha fazla bilgiyi Brendan Gregg'in eBPF sayfasında bulabilirsiniz.
Android, önyükleme sırasında eBPF programlarını yükleyen bir eBPF yükleyici ve kitaplık içerir.
Android BPF yükleyici
Android başlatılırken /system/etc/bpf/ konumundaki tüm eBPF programları yüklenir. Bu programlar, Android derleme sistemi tarafından C programlarından oluşturulan ikili nesnelerdir ve Android kaynak ağacında Android.bp dosyalarıyla birlikte bulunur. Derleme sistemi, oluşturulan nesneleri /system/etc/bpf konumunda depolar ve bu nesneler sistem görüntüsünün bir parçası olur.
Android eBPF C programının biçimi
Bir eBPF C programı şu biçimde olmalıdır:
#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 licenseNerede:
name_of_my_map, harita değişkeninizin adıdır. Bu ad, BPF yükleyiciye hangi tür haritanın hangi parametrelerle oluşturulacağını bildirir. Bu yapı tanımı, dahil edilenbpf_helpers.hbaşlığı tarafından sağlanır.PROGTYPE/PROGNAME, programın türünü ve adını temsil eder. Program türü, aşağıdaki tabloda listelenenlerden herhangi biri olabilir. Bir program türü listelenmediğinde program için katı bir adlandırma kuralı yoktur. Adın, programı ekleyen işlem tarafından bilinmesi yeterlidir.PROGFUNC, derlendiğinde sonuç dosyasının bir bölümüne yerleştirilen bir işlevdir.
| kprobe | kprobe altyapısını kullanarak bir çekirdek talimatına PROGFUNC bağlanır. PROGNAME, kprobe uygulanan çekirdek işlevinin adı olmalıdır. kprobe'lar hakkında daha fazla bilgi için kprobe çekirdek belgelerine bakın.
|
|---|---|
| izleme noktası | Bir izleme noktasına PROGFUNC bağlanır. PROGNAME, SUBSYSTEM/EVENT biçiminde olmalıdır. Örneğin, işlevleri planlayıcı bağlam değiştirme etkinliklerine eklemek için bir izleme noktası bölümü SEC("tracepoint/sched/sched_switch") olur. Burada sched, izleme alt sisteminin adı, sched_switch ise izleme etkinliğinin adıdır. İzleme noktaları hakkında daha fazla bilgi için izleme etkinlikleri çekirdek belgelerine bakın.
|
| skfilter | Program, ağ soketi filtresi olarak çalışır. |
| schedcls | Program, ağ trafiği sınıflandırıcı olarak işlev görür. |
| cgroupskb, cgroupsock | Program, bir CGroup'taki işlemler AF_INET veya AF_INET6 soketi oluşturduğunda çalışır. |
Diğer türleri Loader kaynak kodunda bulabilirsiniz.
Örneğin, aşağıdaki myschedtp.c programı, belirli bir CPU'da çalışan en son görev PID'si hakkında bilgi ekler. Bu program, bir harita oluşturarak ve sched:sched_switch izleme etkinliğine eklenebilecek bir tp_sched_switch işlevi tanımlayarak amacına ulaşır. Daha fazla bilgi için Programları izleme noktalarına ekleme başlıklı makaleyi inceleyin.
#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 makrosu, programın çekirdek tarafından sağlanan BPF yardımcı işlevlerini kullandığı durumlarda programın çekirdek lisansıyla uyumlu olup olmadığını doğrulamak için kullanılır. Programınızın lisansının adını dize biçiminde belirtin (ör. LICENSE("GPL") veya LICENSE("Apache 2.0")).
Android.bp dosyasının biçimi
Android derleme sisteminin bir eBPF .c programı oluşturması için projenin Android.bp dosyasına bir giriş eklemeniz gerekir. Örneğin, bpf_test.c adlı bir eBPF C programı oluşturmak için projenizin Android.bp dosyasına aşağıdaki girişi ekleyin:
bpf {
name: "bpf_test.o",
srcs: ["bpf_test.c"],
cflags: [
"-Wall",
"-Werror",
],
}Bu giriş, nesneyle sonuçlanan C programını derler
/system/etc/bpf/bpf_test.o. Android sistemi, başlatma sırasında bpf_test.o programını otomatik olarak çekirdeğe yükler.
sysfs'de bulunan dosyalar
Android sistemi, başlatma sırasında /system/etc/bpf/ konumundaki tüm eBPF nesnelerini otomatik olarak yükler, programın ihtiyaç duyduğu haritaları oluşturur ve yüklenen programı haritalarıyla birlikte BPF dosya sistemine sabitler. Bu dosyalar daha sonra eBPF programıyla daha fazla etkileşim kurmak veya haritaları okumak için kullanılabilir. Bu bölümde, bu dosyaları adlandırmak için kullanılan kurallar ve sysfs'deki konumları açıklanmaktadır.
Aşağıdaki dosyalar oluşturulur ve sabitlenir:
Yüklenen tüm programlar için (
PROGNAMEprogramın adı veFILENAMEeBPF C dosyasının adı olduğu varsayıldığında) Android yükleyici, her programı/sys/fs/bpf/prog_FILENAME_PROGTYPE_PROGNAMEkonumunda oluşturup sabitler.Örneğin,
myschedtp.ciçindeki öncekisched_switchizleme noktası örneği için bir program dosyası oluşturulur ve/sys/fs/bpf/prog_myschedtp_tracepoint_sched_sched_switchdizinine sabitlenir.Oluşturulan tüm haritalar için
MAPNAMEharitanın adı veFILENAME, eBPF C dosyasının adıysa Android yükleyici her haritayı oluşturup/sys/fs/bpf/map_FILENAME_MAPNAMEkonumuna sabitler.Örneğin,
myschedtp.ciçindeki öncekisched_switchizleme noktası örneği için bir eşleme dosyası oluşturulur ve/sys/fs/bpf/map_myschedtp_cpu_pid_mapdizinine sabitlenir.Android BPF kitaplığındaki
bpf_obj_get(), sabitlenmiş/sys/fs/bpfdosyasından bir dosya tanımlayıcı döndürür. Bu dosya tanımlayıcı, haritaları okuma veya bir programı izleme noktasına ekleme gibi diğer işlemler için kullanılabilir.
Android BPF kitaplığı
Android BPF kitaplığının adı libbpf_android.so olup sistem görüntüsünün bir parçasıdır. Bu kitaplık, kullanıcıya harita oluşturma ve okuma, problar, izleme noktaları ve performans arabellekleri oluşturma için gereken alt düzey eBPF özelliklerini sağlar.
İzleme noktalarına program ekleme
Tracepoint programları, başlatma sırasında otomatik olarak yüklenir. Yükleme işleminden sonra, izleme noktası programı aşağıdaki adımlar kullanılarak etkinleştirilmelidir:
- Sabitlenen dosyanın konumundan
fdprogramını almak içinbpf_obj_get()işlevini çağırın. Daha fazla bilgi için sysfs'de bulunan dosyalar başlıklı makaleyi inceleyin. - BPF kitaplığında
bpf_attach_tracepoint()işlevini çağırın ve programafdile izleme noktası adını iletin.
Aşağıdaki kod örneğinde, önceki myschedtp.c kaynak dosyasında tanımlanan sched_switch izleme noktasının nasıl ekleneceği gösterilmektedir (hata kontrolü gösterilmemiştir):
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));
Haritalardan okuma
BPF haritaları, rastgele karmaşık anahtar ve değer yapılarını veya türlerini destekler. Android BPF kitaplığı, söz konusu eşlemenin anahtar ve değer türüne göre BpfMap örneklendirmek için C++ şablonlarını kullanan bir android::BpfMap sınıfı içerir. Önceki kod örneğinde, anahtar ve değerin tam sayı olarak kullanıldığı bir BpfMap gösterilmektedir. Tam sayılar rastgele yapılar da olabilir.
Bu nedenle, şablonlaştırılmış BpfMap sınıfı, belirli bir haritaya uygun özel bir BpfMap nesnesi tanımlamanıza olanak tanır. Ardından, türe duyarlı olan özel olarak oluşturulmuş işlevler kullanılarak haritaya erişilebilir. Bu da daha temiz bir kod sağlar.
BpfMap hakkında daha fazla bilgi için Android kaynaklarına bakın.
Hataları ayıklama
Başlatma sırasında, BPF yüklemeyle ilgili birkaç mesaj günlüğe kaydedilir. Yükleme işlemi herhangi bir nedenle başarısız olursa logcat'te ayrıntılı bir günlük mesajı sağlanır. Logcat günlüklerini bpf ile filtrelemek, tüm mesajları ve yükleme süresindeki ayrıntılı hataları (ör. eBPF doğrulayıcı hataları) yazdırır.
Android'de eBPF örnekleri
AOSP'deki aşağıdaki programlar, eBPF kullanımıyla ilgili ek örnekler sunar:
eBPF C programı, Android'deki ağ arka plan programı (netd) tarafından soket filtreleme ve istatistik toplama gibi çeşitli amaçlarla kullanılır.
netdBu programın nasıl kullanıldığını görmek için eBPF trafik monitörü kaynaklarına bakın.time_in_stateeBPF C programı, bir Android uygulamasının farklı CPU frekanslarında geçirdiği süreyi hesaplar. Bu süre, güç hesaplamasında kullanılır.Android 12'de
gpu_memeBPF C programı her işlem ve tüm sistem için toplam GPU bellek kullanımını izler. Bu program, GPU belleği profil oluşturma için kullanılır.
statsd için düşük ek yük içeren çekirdek bellek yönetimi ve güç gecikmesi histogramları toplamak üzere eBPF'yi kullanma hakkında daha fazla bilgi edinmek istiyorsanız eBPF ile çekirdek telemetrisi toplama başlıklı makaleyi inceleyin.