julho 21, 2013

Tutorias | Entendendo o processo de boot do Android


VISÃO GERAL
O ker­nel do Linux chama o processo init, que faz a con­fig­u­ração básica do sis­tema e ini­cia proces­sos e serviços, incluindo o zygote, respon­sável por ini­cializar a máquina vir­tual Dalvik, e todos os proces­sos e serviços Java.
Satizfeito com a ver­são resum­ida? Aposto que não! Então vamos para uma ver­são um pouco mais longa…
OS PRIMEIROS ELÉTRONS DE VIDA
Os primeiros elétrons a cir­cu­larem nas veias do roboz­inho são total­mente depen­dentes do hard­ware. Nor­mal­mente a CPU pos­sui um código de boot res­i­dente em ROM (boot­loader de primeiro nível), respon­sável por car­regar um  boot­loader de segundo nível para a memória. Este boot­loader pode ainda car­regar um outro boot­loader de ter­ceiro nível antes de ini­ciar o ker­nel do Linux. Na Bea­gle­board, é isso que acontece:
bootloader Entendendo o processo de boot do Android
1. O boot­loader de primeiro está­gio, res­i­dente em ROM, procura por ima­gens de boot em diver­sos dis­pos­i­tivos de armazena­mento e car­rega para a SRAM (lim­ite de 64KB). No nosso caso, ele irá encon­trar o X-Loader na flash NAND.
2. O X-Loader, boot­loader de segundo está­gio, é car­regado para a SRAM. Ele ini­cial­iza a con­tro­ladora da DRAM, flash NAND e leitora MMC, e car­rega o boot­loader de ter­ceiro está­gio, que no nosso caso é o U-Boot.
3. O U-Boot, boot­loader de ter­ceiro está­gio, roda da RAM. Ele ini­cial­iza alguns dis­pos­i­tivos de hard­ware (rede, USB, etc.), car­rega a imagem do ker­nel do Linux na RAM e passa o con­t­role para ele.
4. O ker­nel roda da RAM e assume o con­t­role do sis­tema. A par­tir deste ponto, os boot­load­ers viraram passado.
Sob o ponto de vista do sis­tema opera­cional, o que importa é saber que um boot­loader em algum momento será car­regado, irá ini­cializar o hard­ware, car­regar o ker­nel para a memória e executá-lo.
DENTRO DO KERNEL
O processo de boot do ker­nel do Linux é idên­tico, inde­pen­den­te­mente da plataforma ser Android, Meego ou Ubuntu. O impor­tante aqui é saber que o ker­nel fará seu tra­balho nor­mal­mente, ini­cial­izando proces­sos, memória vir­tual, dri­vers, dis­pos­i­tivos, sis­temas de arquivo, rootfs, etc. Para quem quiser uma descrição do que acon­tece “under the hood” no ker­nel, pode dar uma lida no artigo do Gus­tavo Duarte, da IBM Devel­op­er­Works e do Lin­uxJour­nal.
O que importa é que o ker­nel, após mon­tar o rootfs, irá chamar um processo de ini­cial­iza­ção, que será o processo-pai de todos os proces­sos rodando em user­space no Android. E este processo é o “init”, como podemos ver na linha de coman­dos do ker­nel con­fig­u­rada no U-Boot da Beagleboard:
# setenv bootargs 'mem=256M androidboot.console=ttyS2 console=tty0 console=ttyS2,115200n8 root=/dev/mmcblk0p2 rw init=/init rootwait omapfb.video_mode=640x480MR-16@60'
INIT
Aqui o Android começa a se difer­en­ciar de out­ros sis­temas GNU/Linux. Nada de SysV ou sys­temd. O Android imple­menta seu próprio mecan­ismo de ini­cial­iza­ção dos proces­sos bási­cos do sistema.
A imple­men­tação do init encontra-se nos fontes do Android em “system/core/init/init.c”.
Obs: Os fontes do Android estão disponíveis no site do pro­jeto para quem quiser se aventurar.
Segue um tre­cho do init.c para os mais curiosos:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
int main(int argc, char **argv)
{
    int fd_count = 0;
    struct pollfd ufds[4];
    char *tmpdev;
    char* debuggable;
    char tmp[32];
    int property_set_fd_init = 0;
    int signal_fd_init = 0;
    int keychord_fd_init = 0;
 
    printf("bk %s %d\n",__FILE__,__LINE__);
 
    if (!strcmp(basename(argv[0]), "ueventd"))
        return ueventd_main(argc, argv);
 
    printf("bk %s %d\n",__FILE__,__LINE__);
 
    /* clear the umask */
    umask(0);
 
    /* Get the basic filesystem setup we need put
     * together in the initramdisk on / and then we'll
     * let the rc file figure out the rest.
     */
 
    printf("bk %s %d\n",__FILE__,__LINE__);
 
    mkdir("/dev", 0755);
    mkdir("/proc", 0755);
    mkdir("/sys", 0755);
 
    mount("tmpfs", "/dev", "tmpfs", 0, "mode=0755");
    mkdir("/dev/pts", 0755);
    mkdir("/dev/socket", 0755);
    mount("devpts", "/dev/pts", "devpts", 0, NULL);
    mount("proc", "/proc", "proc", 0, NULL);
    mount("sysfs", "/sys", "sysfs", 0, NULL);
 
    printf("bk pre devnull %s %d\n",__FILE__,__LINE__);
 
    /* We must have some place other than / to create the
     * device nodes for kmsg and null, otherwise we won't
     * be able to remount / read-only later on.
     * Now that tmpfs is mounted on /dev, we can actually
     * talk to the outside world.
     */
    open_devnull_stdio();
 
    ERROR("bk pos devnull, pre log init %s %d\n",__FILE__,__LINE__);
 
    log_init();
 
    ERROR("bk pos log init %s %d\n",__FILE__,__LINE__);
 
    INFO("reading config file\n");
    init_parse_config_file("/init.rc");
 
    ....
Este processo faz algu­mas ini­cial­iza­ções de sis­tema, criando diretórios e pon­tos de mon­tagem bási­cos do Linux como o /proc, o /sys e o /dev, ini­cial­izando o sis­tema de log e abrindo a console.
Uma tarefa impor­tante deste processo é o trata­mento de um arquivo de con­fig­u­ração chamado init.rc (veja a linha 57 do código acima). Este arquivo tem obje­tivos pare­ci­dos com o/etc/inittab para quem esta acos­tu­mado com o mecan­ismo de ini­cial­iza­ção SysV.
É no init.rc que esta con­fig­u­rada boa parte do restante da ini­cial­iza­ção do sis­tema, incluindo a exe­cução dos serviços bási­cos do Android, den­tre eles:
  • con­sole: ini­cia o shell ash.
  • ser­vice­m­an­ager: ini­cia o binder (respon­sável pela comu­ni­cação entre os processos).
  • vold: vol­ume dae­mon — con­trola a mon­tagem de vol­umes de mídia no sis­tema de arquivos.
  • adbd: android debug­ger bridge dae­mon — servi­dor para comu­ni­cação com o cliente adb. 
  • media: ini­cia os servi­dores mul­ti­me­dia (áudio, vídeo, etc).
  • boot­sound: toca um som no boot, lendo um arquivo em /system/media/audio/ui/boot.mp3.
  • installd: servi­dor de insta­lação de pacotes/aplicações (*.apk).
Depois de inter­pre­tar este arquivo, o init entra em um loop infinito mon­i­torando a ocor­rên­cia de even­tos e a exe­cução de processos.
Você con­segue por exem­plo con­fig­u­rar o init.rc para que um processo seja ini­ci­ado quando você conec­tar um pen­drive na porta USB, (o init mon­i­tora a cri­ação do device node em /devquando um dis­pos­i­tivo USB for conectado).
Você con­segue tam­bém con­fig­u­rar um processo para ficar sem­pre em exe­cução. Se este processo cair (encer­rar), o init reini­cia ele automaticamente.
Por­tanto, se você quiser inserir um serviço para ser exe­cu­tado no boot, basta inserir as lin­has abaixo no init.rc:
service meu_servico /system/bin/meu_servico
    user meu_servico
    group meu_servico
    oneshot
Neste exem­plo, con­fig­u­ramos o serviço “meu_servico” para ser exe­cu­tado ape­nas uma vez (oneshot) com as per­mis­sões de usuário e grupo “meu_servico”.
Para saber mais sobre o init.rc, você pode dar uma olhada na especi­fi­cação do Android Init Lan­guage.
Bom, sabe­mos então que todos os proces­sos bási­cos do Android são ini­ci­a­dos através doinit.rc. Mas um destes proces­sos é o coração do Android merece uma atenção especial.
ZYGOTE
zygote é o pai dos proces­sos Java. Tudo que roda em Java é cri­ado por este processo, que instan­cia uma máquina vir­tual Dalvik para exe­cu­tar um processo ou serviço Java. No Gin­ger­bread (Android 2.3), ele é ini­ci­ado por estas lin­has no init.rc:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    socket zygote stream 666
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media
Ele esta imple­men­tado em C++, e seu código-fonte encontra-se emframeworks/base/cmds/app_process/app_main.cpp.
zygote tem basi­ca­mente dois obje­tivos principais:
  1. Prover uma infraestru­tura para a exe­cução de apli­cações Java. Primeira­mente, ele ini­cia a máquina vir­tual Dalvik. Depois, ele inicia um servi­dor que abre um socket e fica aguardando req­ui­sições para exe­cução de apli­cações Java. Qual­quer req­ui­sição de exe­cução de apli­cações Java passa por esse servi­dor, que faz um forkpara exe­cu­tar a apli­cação em uma outra instan­cia da VM. O código-fonte deste servi­dor esta disponível em 
    frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java.
  2. Ini­ciar o Sys­tem Server, que geren­cia a base dos serviços do sis­tema opera­cional Android.
SYSTEM SERVER
A imple­men­tação do sys­tem server encontra-se emframeworks/base/services/java/com/android/server/SystemServer.java. Ele ini­cia todos os serviços Java bási­cos do Android, e são muitos! Den­tre eles:
  • Power Man­ager
  • Activ­ity Manager
  • Tele­phony Registry
  • Pack­age Manager
  • Con­text Manager
  • Sys­tem Con­text Providers
  • Bat­tery Service
  • Alarm Man­ager
  • Sen­sor Service
  • Win­dow Manager
  • Blue­tooth Service
  • Mount Ser­vice
  • Sta­tus Bar Service
  • Hard­ware Service
  • Net­Stat Service
  • Con­nec­tiv­ity Service
  • Noti­fi­ca­tion Manager
  • DeviceS­tor­age­Mon­i­tor Service
  • Loca­tion Manager
  • Search Ser­vice
  • Clip­board Service
  • Checkin Ser­vice
  • Wall­pa­per Service
  • Audio Ser­vice
  • Head­se­tO­b­server
  • Adb­Set­ting­sOb­server
Todos os serviços tem sua importân­cia den­tro do ambi­ente e das apli­cações Android. Poderíamos levar um artigo com­pleto para descr­ever em detal­hes cada um deles. O mais impor­tante aqui é enten­der que cada fun­cional­i­dade esta abstraída através de um serviço, e a API do Android disponi­bi­liza funções para as apli­cações poderem se comu­nicar com estes serviços.
Quase no final do boot, o Activ­ity Man­ager ini­cia alguns proces­sos bási­cos, den­tre eles o com.android.launcher, que é a apli­cação respon­sável pela inter­face grá­fica do Android.
BOOTCHART
Para quem tra­balha por­tando ou inte­grando o Android em difer­entes hard­wares, uma fer­ra­menta inter­es­sante é o bootchart, que pos­si­bilita gerar um grá­fico com­pleto com estatís­ti­cas dos proces­sos em exe­cução durante o boot do Android. Quem estiver inter­es­sado nesta fer­ra­menta pode dar uma lida neste tuto­r­ial do elinux.org.

O grá­fico ger­ado é pare­cido com este abaixo (clique na imagem abaixo para vê-la em tamanho maior).
bootchart Entendendo o processo de boot do Android










Nenhum comentário:

Postar um comentário