terça-feira, 12 de junho de 2012

Chamadas de sistema


Chamadas em C... cadê o C?

A postagem de hoje é um pouco técnica mas bem interessante. O objetivo é dar uma espiada no problema do "ovo ou a galinha". Bem, o kernel do Unix é escrito em C e, consequentemente, alguns símbolos das bibliotecas teriam que ser resolvidos nas libs em estão em /usr/lib ou /lib. Acontece que, quando o kernel está carregando, o sistema de arquivos ainda não está disponível. Então... o que vem primeiro? O ovo ou a galinha?

Como já vimos anteriormente em outras postagens deste blog, o kernel (ou núcleo) do sistema operacional (SO) é o responsável por grande parte do gerenciamento do sistema. Assim, ele fica responsável pelo gerenciamento da memória, gerenciamento das interfaces de entrada e saída (I/O), controle das interrupções etc., além das operações de iniciar o próprio sistema [1].

Em alguns kernels (microkernels  e exokernels ) estes serviços são executados no user space (userland) mas, mesmo assim, eles ficam disponíveis por meio das chamadas de sistema (system calls). Qualquer programa que rode no userland ou no kernel space pode disparar estas chamadas de sistemas que são as interfaces entre um processo e o SO.

As chamadas de sistema são feitas por meio de funções em C do tipo read, write, open, exit, por exemplo. O FreeBSD tem cerca de 500 chamadas de sistema, enquanto o Linux tem cerca 300. Algumas delas usam bibliotecas do GCC mas existe um detalhe importante: quando o kernel está carregando o SO, ele não tem acesso, ainda, às bibliotecas e cabeçalhos do C ou do GCC porque o sistema de arquivos ainda não está montado. Isto significa que o kernel tem que ter suas próprias bibliotecas e cabeçalhos.

Então, porque não implementar a biblioteca C dentro do código do kernel? Bem, basicamente é o que se faz, mas com muitas restrições.

  1. A libc tem outras funcionalidades não necessárias ao kernel e que pode apenas servir para aumentar o tamanho do kernel depois de compilado, por exemplo, ao inserir o string.h do gcc no kernel, as referências a memcpy(), memset(), memcmp() etc. estariam no código mas não seriam usadas no kernel space.
  2. A implementação, no kernel, requer que ela tenha como chamar o sistema (system call) de dentro do kernel [3].
  3. Outros símbolos e formatos são necessários para funções implementadas no kernel como, por exemplo, a função printf().


O exemplo a saída de mensagens do sistema durante o boot que em ANSI C pode ser implementada com a função printf(). apresenta o problema de que o stdio.h e a libc ainda não estão disponíveis. Assim, a função tem que estar definida no próprio código do kernel. No Linux. por exemplo, a função é chamada de printk() que está em .../linux/kernel/printk.c [4].

Exemplos:

printk("The address of my_var is %p\n", &my_var);

printk(KERN_DEBUG "*** This is a debug message. ***\n");

Os formatos mais usados na função printk() são o KERN_INFO e KERN_DEBUG. Estes formatos KERN_* são definidos no .../include/linux/kernel.h.


Referências:

[1] http://www.makelinux.net/kernel_map/

[2] Bach, M. http://www.vivaolinux.com.br/artigo/Novidades-do-Kernel-2.6.35J., The Design of the UNIX Operating System, Prentice Hall, 1986.

[3] Tanenbaum, A. S., Modern Operating Systems, Prentice Hall, 1076 pages, 2008.

[4] Love, R. Linux Kernel Development, Novell Press, 2005.

Crédito da imagem: http://www.vivaolinux.com.br/artigo/Novidades-do-Kernel-2.6.35

Nenhum comentário:

Postar um comentário