O processo init pode ser um script de shell no Linux?

14

Eu estava passando por um tutorial sobre como configurar um initramfs personalizado, onde afirma:

A única coisa que está faltando é / init, o executável na raiz do initramfs que é executado pelo kernel após o carregamento. Como o sys-apps / busybox inclui um shell totalmente funcional, isso significa que você pode escrever seu binário / init como um simples script de shell (em vez de torná-lo um aplicativo complicado escrito em Assembler ou C que você precisa compilar).

e fornece um exemplo de init como um script de shell que começa com #!/bin/busybox sh

Até o momento, tive a impressão de que o init é o processo principal iniciado e que todos os outros processos de espaço do usuário são eventualmente filhos do init. No entanto, no exemplo dado, o primeiro processo é realmente bin/busybox/ shdo qual o init mais tarde é gerado.

Essa é uma interpretação correta? Se eu tivesse, por exemplo, um intérprete disponível naquele momento, eu poderia escrever init como um script Python, etc.?

TheMeaningfulEngineer
fonte

Respostas:

12

O init não é "gerado" (como um processo filho), mas sim execcomo este:

# Boot the real thing.
exec switch_root /mnt/root /sbin/init

execsubstitui todo o processo no local. O init final ainda é o primeiro processo (pid 1), embora tenha sido precedido pelos do Initramfs.

O Initramfs /init, que é um script de shell do Busybox com pid 1, execs para o Busybox switch_root(agora switch_rooté o pid 1); este programa altera seus pontos de montagem, assim /mnt/rootserá o novo /.

switch_rootentão novamente execpara o /sbin/initseu sistema de arquivos raiz real; desse modo, torna seu sistema init real o primeiro processo com o pid 1, que por sua vez pode gerar qualquer número de processos filhos.

Certamente, isso também poderia ser feito com um script Python, se você conseguisse inserir o Python no seu Initramfs. Embora se você não planeja incluir o busybox de qualquer maneira, seria necessário reimplementar meticulosamente algumas de suas funcionalidades (como switch_roote tudo o que você normalmente faria com um simples comando).

No entanto, ele não funciona em kernels que não permitem binários de script ( CONFIG_BINFMT_SCRIPT=y) ou, nesse caso, você teria que iniciar o interpretador diretamente e fazê-lo carregar o seu script de alguma forma.

frostschutz
fonte
/não desaparece no ar - é montado (embora geralmente seu conteúdo seja excluído antes de economizar memória) . Ainda está . switch_rootfaz o syscall switchroot- que é o que os desenvolvedores do kernel forneceram quando mudaram o processo de inicialização no kernel 2.6. algo para exigir o initramfs. É o núcleo que faz a mágica.
mikeserv
1
Um switchrootsyscall seria de fato uma novidade para mim. Você tem uma fonte para isso? Se você olhar o código-fonte switch_root.c, parece ser um processo manual, e o mesmo descrito em Documentation / filesystems / ramfs-rootfs-initramfs.txt. Além disso, se você excluir tudo e montá-lo, praticamente desaparecerá neste momento, não acha?
Frostschutz
pivot_root, por outro lado, é um syscall. No switch_rootentanto, não é usado para isso e não pode ser usado sem pular alguns aros, e de qualquer maneira não importa nada para esta resposta, então eu a removi completamente. Muito ruim, eu pensei que a magia e desaparecer no ar funcionou muito bem ... :-P
frostschutz
Bem, talvez eu tenha entendido a idéia errada switch_root- pela qual sinto muito e agradeço por me mostrar -, mas isso não desaparece de qualquer maneira. A raiz do initramfs persiste e está sempre lá para todos - é a raiz.
mikeserv
1
Como os documentos aos quais você vinculou : Mas o initramfs é rootfs: você não pode pivot_root rootfs nem desmontá-lo. Em vez disso, exclua tudo do rootfs para liberar espaço ( find -xdev / -exec rm '{}' ';'), substitua o rootfs pela nova raiz ( cd /newmount; mount --move . /; chroot .), anexe stdin / stdout / stderr ao novo / dev / console e execute o novo init.
mikeserv
4

O syscall exec do kernel Linux compreende shebangs nativamente

Quando o arquivo executado começa com os bytes mágicos #!, eles dizem ao kernel para usar #!/bin/shcomo:

  • fazer e execchamar sistema
  • com executável /bin/sh
  • e com argumento da CLI: caminho para o script atual

É exatamente o mesmo que acontece quando você executa um script de shell do usuário comum com:

./myscript.sh

Se o arquivo tivesse iniciado com os bytes mágicos em .ELFvez de #!, o kernel escolheria o carregador ELF para executá-lo.

Mais detalhes em: Por que as pessoas escrevem o #! / Usr / bin / env python shebang na primeira linha de um script Python? | Estouro de pilha

Depois de ter isso em mente, fica fácil aceitar que /initpode ser qualquer coisa que o kernel possa executar, incluindo um script de shell, e também por /bin/shque será o primeiro executável nesse caso.

Aqui está um exemplo executável mínimo para aqueles que desejam experimentá-lo: https://github.com/cirosantilli/linux-kernel-module-cheat/tree/cbea7cc02c868711109ae1a261d01fd0473eea0b#custom-init

Ciro Santilli adicionou uma nova foto
fonte