As informações abaixo possuem foco exclusivo na inicialização e boot de um sistema Android, uma vez que plataformas móveis e sistemas embutidos possuem sensíveis diferenças no processo de inicialização quando comparados a sistemas Desktop.
Nossa intenção neste post é dar uma visão geral de cada um dos passos que estão envolvidos no carregamento da plataforma Android. Os detalhes de cada uma das fases foge do escopo deste. Um leitor mais interessado poderá encontrá-los facilmente no código-fonte do AOSP.
1. Inicialização e execução do código de boot presente na ROM
Até o momento anterior ao boot inicial, a CPU permanece em um estado no qual nenhum tipo de inicialização foi feita. Relógios internos não foram configurados e a única memória disponível é a RAM interna. Quando o botão de “Ligar” é pressionado, o processo de boot começa à partir do chamado código de boot da ROM. Este é um pequeno pedaço de código que pode ser acessado diretamente pela CPU. Cada arquitetura pode possuir uma maneira diferente de conectar a CPU a este pedaço de código de inicialização.
- O código de boot da ROM irá detectar o dispositivo ou região de memória utilizada para o boot do sistema através de um registrador específico da CPU (normalmente este é mapeado para controles físicos – jumpers ou dip switches). Isto irá determinar onde a plataforma encontrará o primeiro estágio de um outro componente: boot loader.
- Uma vez que a sequência de boot é estabelecida, código de boot da ROM tentará carregar o primeiro estágio da inicialização dentro da memória RAM interna. E, uma vez que isso se sucedeu corretamente, o código de boot da ROM seguirá o fluxo de carregamento e execução, agora diretamente pelo código do boot loader.
2. O boot loader
O boot loader é um programa especial, separado do restante do kernel do Linux que é utilizado para inicialmente configurar a memória, além de carregar o kernel na RAM. Em sistemas operacionais (particularmente Linux) para desktops, existem programas como o LILO e o GRUB que executam tal tarefa. Entretanto, no mundo do Linux embarcado, o mais utilizado boot loader é o conhecido Das U-Boot (http://sourceforge.net/projects/u-boot/). Entretanto, frequentemente os fabricantes de hardware fazem uso de seu próprio boot loader. Os requisitos de um boot loader para um processador ARM rodando Linux podem ser encontrados dentro da própria documentação do kernel (presente no código-fonte do mesmo), em /Documentation/arm. A execução do boot loader prossegue dentro dos seguintes passos:
- O primeiro estágio do boot loader irá detectar e configurar a memória RAM externa.
- Uma vez que a memória RAM externa está disponível e o sistema pronto para executar algo mais significante, o primeiro estágio do boot loader irá carregar o estágio principal e colocá-lo na própria RAM externa.
- O segundo estágio do boot loader é o primeiro “programa principal” a ser executado. Este pode vir a conter código para configurar: sistemas de arquivos, memória adicional, suporte a rede entre outras coisas. Em um telefone, por exemplo, este pode ser encarregado de carregar código específico do modem da CPU assim como configurar as proteções da memória de baixo nível e outras opções de segurança.
- Uma vez que o boot loader finalizou o carregamento e configuração das tarefas ditas “especiais”, ele irá procurar por um kernel Linux para iniciar sua execução. Este por sua vez será carregado da media (cartão SD, memória flash ou mesmo ponto de montagem remoto via rede) de boot e colocado diretamente na RAM externa. Além disso, alguns parâmetros de boot também serão colocados em memória já que o próprio kernel também fará uso dos mesmos quando da sua inicialização (ponto de montagem dos dispositivos seriais, por exemplo).
- Por fim, o boot loader é finalizado e passa então o fluxo de execução para o kernel do Linux (usualmente este é descomprimido). Deste ponto em diante o kernel é quem assume a responsabilidade pelo sistema.
3. O kernel do Linux
O kernel do Linux inicializa um sistema Android da mesma forma que as diversas distribuições Linux configurando tudo o que é necessário para que o sistema execute corretamente: Controladores de interrupções, proteções de memória, escalonamento e caching…:
- Uma vez que as unidades de gerenciamento de memória e cache foram inicializadas o sistema está apto a utilizar a memória virtual e carregar programas em espaço de usuário.
- O kernel buscará no sistema de arquivos raiz o processo inicial (init – encontrado em system/core/init na árvore de códigos do Android) e o inicializará como o processo inicial do espaço de usuário.
4. O processo init
O processo init é como um “pai” de todos os outros processos do sistema. Cada um dos outros processos serão iniciados à partir deste processo ou à partir de algum dos seus processos-filhos:
- O processo init no Android irá procurar pelo arquivo de nome init.rc. Este é um script que descreve os serviços de sistema, sistema de arquivos e outros parâmetros que necessitam ser configurados. O script init.rc fica localizado em system/core/rootdir na árvore de códigos-fonte do projeto Android.
- O processo init irá ler o script init.rc e inicializar os processos dos serviços do sistema Android.
5. Zygote e Dalvik
O componente Zygote é lançado pelo processo init e basicamente sua tarefa se resume a inicializar e executar a máquina virtual Dalvik.
6. O servidor do sistema (system server)
O system server é o primeiro componente Java a executar no sistema. Ele está à cargo de executar todos os serviços Android, como o gerenciador telefônico e a interface Bluetooth. A inicialização de cada um dos serviços é atualmente escrita diretamente dentro do método “run” do system server. O código-fonte do system server pode ser encontrado dentro do arquivo frameworks/base/services/java/com/android/server/SystemServer.java na árvore de códigos-fonte do projeto Android.
7. Finalização do processo de boot
Uma vez que o system server foi configurado, está executando e o boot do sistema foi completado com sucesso, existe uma ação global tomada pelo sistema que é broadcast do evento ACTION_BOOT_COMPLETED. Tal funcionalidade visa estabelecer uma ordem temporal na inicialização dos serviços do sistema. Dessa forma, um desenvolvedor que deseja iniciar seu próprio serviço precisa se registrar para receber o broadcast deste intent.
Nota: Os detalhes de cada um dos passos apresentados aqui pode variar conforme a versão do Android ou ainda de acordo com a plataforma na qual este é executado. As partes específicas aqui apresentadas se baseiam fortemente em um sistema similar àquele apresentado pela Beagle Board (http://beagleboard.org).