Inhaltsverzeichnis
Einführung
ServiceManager ist ein Daemon-Prozess in der Binder IPC-Kommunikation, der zur Realisierung der Binder IPC-Kommunikation dient. ServiceManager selbst ist also ein Dienst und ein Bindemitteldienst.
ServiceManager weist zwei Merkmale auf:
1. Anstatt das Multithread-Modell in libbinder für die Kommunikation mit dem Binder-Treiber zu verwenden, kommuniziert es direkt mit dem Binder-Treiber über die sicher geschriebene binder.c, und es gibt nur eine Schleife binder_loop zum Lesen und Verarbeiten von Transaktionen Halten Sie es einfach und effizient
2. Es gibt zwei Hauptfunktionen von ServiceManager: Registrierungsdienst und Abfragedienst.
Konzept
ServiceManager-Startvorgang (vereinfachte Version):
1) Öffnen Sie den Binder-Treiber
2) Registrieren Sie sich als Kontextmanager
3) Geben Sie den Schleifenzyklus ein und verarbeiten Sie Transaktionen
Die beiden Kernfunktionen von ServiceManager:
Dienst registrieren: Dienstnamen und Handler aufzeichnen, in der svclist-Liste speichern
Abfragedienst: Fragen Sie die entsprechenden Handlerinformationen gemäß dem Dienstnamen ab
Start-up
Flussdiagramm
Framework/native/cmds/servicemanager/
- service_manager.c
- Bindemittel.c
kernel/drivers/ (verschiedene Linux-Zweigpfade unterscheiden sich geringfügig)
- android/binder.c
ServiceManager wird vom Init-Prozess erstellt
service servicemanager /system/bin/servicemanager
class core animation
user system
group system readproc
critical
onrestart restart healthd
onrestart restart zygote
onrestart restart audioserver
onrestart restart media
onrestart restart surfaceflinger
onrestart restart inputflinger
onrestart restart drm
onrestart restart cameraserver
onrestart restart keystore
onrestart restart gatekeeperd
onrestart restart thermalservice
writepid /dev/cpuset/system-background/tasks
shutdown critical
hauptsächlich
int main(int argc, char** argv)
{
struct binder_state *bs;
union selinux_callback cb;
char *driver;
if (argc > 1) {
driver = argv[1];
} else {
driver = "/dev/binder";
}
bs = binder_open(driver, 128*1024);
if (!bs) {
#ifdef VENDORSERVICEMANAGER
ALOGW("failed to open binder driver %s\n", driver);
while (true) {
sleep(UINT_MAX);
}
#else
ALOGE("failed to open binder driver %s\n", driver);
#endif
return -1;
}
if (binder_become_context_manager(bs)) {
ALOGE("cannot become context manager (%s)\n", strerror(errno));
return -1;
}
cb.func_audit = audit_callback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
#ifdef VENDORSERVICEMANAGER
cb.func_log = selinux_vendor_log_callback;
#else
cb.func_log = selinux_log_callback;
#endif
selinux_set_callback(SELINUX_CB_LOG, cb);
#ifdef VENDORSERVICEMANAGER
sehandle = selinux_android_vendor_service_context_handle();
#else
sehandle = selinux_android_service_context_handle();
#endif
selinux_status_open(true);
if (sehandle == NULL) {
ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");
abort();
}
if (getcon(&service_manager_context) != 0) {
ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
abort();
}
binder_loop(bs, svcmgr_handler);
return 0;
}
ServiceManager-Startvorgang (allgemeine Version):
1) Öffnen Sie den Binder-Treiber und rufen Sie die mmap-Methode auf, um 128 KB Speicherzuordnungsraum zuzuweisen: binder_open
2) Registrieren Sie sich als Kontextmanager: binder_become_context_manager
3) Überprüfen Sie die Selinux-Berechtigungen, um festzustellen, ob der Prozess die Berechtigung hat, bestimmte Dienste zu registrieren oder anzuzeigen
4) Betreten Sie die Schleife und warten Sie auf die Anfrage des Clients: bind_loop
binder_open
struct binder_state
{
int fd;
void *mapped;
size_t mapsize;
};
struct binder_state *binder_open(const char* driver, size_t mapsize)
{
struct binder_state *bs;
struct binder_version vers;
bs = malloc(sizeof(*bs));
if (!bs) {
errno = ENOMEM;
return NULL;
}
bs->fd = open(driver, O_RDWR | O_CLOEXEC);
if (bs->fd < 0) {
fprintf(stderr,"binder: cannot open %s (%s)\n",
driver, strerror(errno));
goto fail_open;
}
if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
(vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
fprintf(stderr,
"binder: kernel driver version (%d) differs from user space version (%d)\n",
vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION);
goto fail_open;
}
bs->mapsize = mapsize;
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
if (bs->mapped == MAP_FAILED) {
fprintf(stderr,"binder: cannot map device (%s)\n",
strerror(errno));
goto fail_map;
}
return bs;
fail_map:
close(bs->fd);
fail_open:
free(bs);
return NULL;
}
void binder_close(struct binder_state *bs)
{
munmap(bs->mapped, bs->mapsize);
close(bs->fd);
free(bs);
}
Der Benutzerbereich ruft die offene Methode des Treibers über die offene Methode auf, erstellt ein binder_proc-Strukturobjekt, weist dann das binder_proc-Objekt fd->private_data zu und fügt es in die globale verknüpfte Liste binder_procs ein
Die Binder-Version des Treibers kann über ioctl(bs->fd, BINDER_VERSION, &vers) abgerufen werden.
Rufen Sie mmap() für die Speicherzuordnung auf. In ähnlicher Weise wird die mmap()-Methode vom System aufgerufen und entspricht der binder_mmap()-Methode der Binder-Treiberebene. Diese Methode erstellt ein Binder_buffer-Objekt in der Binder-Treiberebene und fügt es ein die proc->buffers der aktuellen verknüpften Liste binder_proc.
int fd; // Dateideskriptor von dev/binder
void *mapped; //Zeigen Sie auf die Speicheradresse von mmap
size_t Mapsize; // Die zugewiesene Speichergröße, der Standardwert ist 128 KB
binder_become_context_manager
Seien Sie der Manager des Kontexts, und im gesamten System gibt es nur einen solchen Manager. Durch den Systemaufruf über die Methode ioctl () entspricht es der Methode binder_ioctl () der Binder-Treiberschicht.
binder_ioctl
Gemäß dem Parameter BINDER_SET_CONTEXT_MGR wird schließlich die Methode binder_ioctl_set_ctx_mgr () aufgerufen, und dieser Prozess hält binder_main_lock.
binder_ioctl_set_ctx_mgr
Das globale binder_node-Objekt binder_context_mgr_node wird erstellt und die starken und schwachen Referenzen von binder_context_mgr_node werden um 1 addiert.
binder_new_node
Erstellen Sie ein binder_node-Strukturobjekt in der Binder-Treiberebene und fügen Sie den aktuellen binder_proc zum Knoten->proc von binder_node hinzu. Und erstellen Sie zwei Warteschlangen mit binder_node async_todo und binder_work.
binder_loop
Geben Sie den Lese- und Schreibvorgang der Schleife ein. Der von der Methode main () übergebene Parameter func zeigt auf svcmgr_handler.
binder_write sendet den Befehl BC_ENTER_LOOPER über ioctl () an den Binder-Treiber. Zu diesem Zeitpunkt hat bwr nur Daten im write_buffer und ruft die Methode binder_thread_write () auf. Geben Sie als Nächstes die for-Schleife ein und führen Sie ioctl () aus. Zu diesem Zeitpunkt enthält nur read_buffer Daten im BWR, und geben Sie dann die Methode binder_thread_read () ein.
binder_write
Initialisieren Sie gemäß den übergebenen Parametern bwr, wobei write_size 4 ist, write_buffer auf die Startadresse des Puffers zeigt und sein Inhalt die BC_ENTER_LOOPER-Anforderungsprotokollnummer ist. Senden Sie die BWR-Daten über ioctl an den Binder-Treiber und rufen Sie dann dessen Methode binder_ioctl wie folgt auf:
binder_ioctl
binder_ioctl_write_read
binder_thread_write
Nehmen Sie die cmd-Daten aus bwr.write_buffer heraus, hier ist BC_ENTER_LOOPER. Es ist ersichtlich, dass die obere Ebene dieses Mal die Methode binder_write() aufruft, hauptsächlich um die Einstellung des Looper-Status des aktuellen Threads als BINDER_LOOPER_STATE_ENTERED abzuschließen.
binder_parse
Analysieren Sie die Binderinformationen, wobei der Parameter ptr auf BC_ENTER_LOOPER und func auf svcmgr_handler zeigt. Wenn daher eine Anfrage kommt, wird svcmgr_handler aufgerufen.
bio_init
bio_init_from_txn
svcmgr_handler
Funktionen dieser Methode: Dienste abfragen, Dienste registrieren und alle Dienste auflisten
svcinfo
Jeder Dienst wird durch die svcinfo-Struktur dargestellt, und der Handle-Wert wird durch das Ende des Prozesses bestimmt, in dem sich der Dienst während des Registrierungsprozesses des Dienstes befindet.
Abfragedienst
do_find_service
Fragen Sie den Zieldienst ab und geben Sie das dem Dienst entsprechende Handle zurück
find_svc
Überprüfen Sie anhand der svclist-Dienstliste, ob er entsprechend dem Dienstnamen registriert wurde. Wenn der Dienst bereits in svclist vorhanden ist, wird der entsprechende Dienstname zurückgegeben, andernfalls wird NULL zurückgegeben.
Wenn das Handle des Dienstes gefunden wird, rufen Sie bio_put_ref(reply, handle) auf, um das Handle in die Antwort einzukapseln.
bio_put_ref
bio_alloc_obj
bio_alloc
do_add_service
Registrierungsservice
Der Registrierungsservice gliedert sich in die folgenden 3 Teile:
- svc_can_register: Überprüfen Sie die Berechtigungen und prüfen Sie, ob die Selinux-Berechtigungen erfüllt sind.
- find_svc: Dienstabruf, Abfrage des passenden Dienstes anhand des Dienstnamens;
- svcinfo_death: Geben Sie den Dienst frei. Wenn ein Dienst mit demselben Namen gefunden wird, werden zuerst die Dienstinformationen gelöscht und dann wird der aktuelle Dienst zur Dienstliste svclist hinzugefügt.
svc_can_register
svcinfo_death
bio_get_ref
binder_link_to_death
binder_write: Rufen Sie nach Eingabe des Binder-Treibers direkt binder_thread_write auf, geben Sie ihn ein und verarbeiten Sie den Befehl BC_REQUEST_DEATH_NOTIFICATION
binder_ioctl_write_read
binder_thread_write
Der Prozess und der Thread in dieser Methode beziehen sich auf die Informationen des aktuellen Servicemanager-Prozesses. Wenn sich zu diesem Zeitpunkt Daten in der TODO-Warteschlange befinden, geben Sie binder_thread_read ein.
In welchen Szenarien werden also BINDER_WORK_DEAD_BINDER-Transaktionen zur Warteschlange hinzugefügt? Das heißt, wenn der Prozess, in dem sich der Binder befindet, stirbt, wird die Methode binder_release aufgerufen, und dann wird binder_node_release aufgerufen. Dieser Prozess sendet einen Rückruf für die Todesbenachrichtigung.
binder_thread_read
Schreiben Sie den Befehl BR_DEAD_BINDER in den Benutzerbereich, wo das Cookie der zuvor übergebene svcinfo_death ist. Die Nachricht wird verarbeitet, wenn binder_loop das nächste Mal binder_parse ausführt.