Sistemas Operacionais-Chamadas de sistemas

|

Para quem desenvolve em plataforma baixa saber e utilizar chamadas de sistemas é fundamental, conheça algumas delas.


Um programa comunica-se com o sistema operacional e requisita serviços através das chamadas de sistemas.

Exitem dois mecanismos no Linux para implementar chamadas de sistemas.

lcall7/lcall27 call gates;
int 0x80 software interrupt.

Programas nativos do Linux usam int 0x80, enquanto que binários vindo de outros sabores de UNIX (Solaris, UnixWare 7, etc.) usam o mecanismo lcall7.

Neste texto vamos usar o int 0x80. No linux as system calls são chamadas executando a instrução em assembler int $0x80, a qual é uma execução programada (interrupção de software).

Como o kernel implementa muitas system calls, o processo deve passar um parâmetro chamado “system call number” para identificar a desejada. O registrador %eax é usado para este propósito. Assim o kernel usa um número para identificar cada chamada de sistema. A fim de associar cada número com cada system call o kernel usa uma “system call dispatch table”, que é armazenada no vetor “sys_call_table” e tem “NR_syscalls” entradas (normalmente 256). NR_syscalls é o limite estático do número máximo de chamadas de sistema implemetáveis. Isto não indica que o número atual de system calls implementada seja este. As entradas na “sys_call_table” que não contém uma chamada de sistema associada possuem “sys_ni_syscall”, que é uma função que retorna o erro “-ENOSYS” Deste modo a entrada número n na “sys_call_table” contém o endereço da rotina da chamada de sistema tendo como número n. A lista completa das chamadas de sistema e seus respectivos números podem ser encontradas em /usr/src/linux/include/asm-i386/unistd.h

Quando o sistema inicia (boot), a função trap_init em arch/i386/kernel/traps.c é chamada. Ela seta a entrada na IDT correspondente ao vetor 0x80 para apontar para a função system_call, que pode ser encontrada em arch/i386/kernel/entry.S

IDT (Interrupt Descriptor Table) é uma tabela do sistema que associa cada interrupção ou vetor de exceção com o endereço correspondente ao handler da interrupção ou exceção. A IDT deve ser inicializada antes do kernel habilitar interrupções.

#define SYSCALL_VECTOR 0x80
set_system_gate(SYSCALL_VECTOR,&system_call);

A função system_call é que implementa o handler para as chamada de sistemas. Quando uma aplicação faz uma chamada de sistema, os argumentos são passados via registrador, e a intrução int 0x80 é executada. Assim, entrando em modo kernel e o processador executando a função system_call.

Como system_call é a única entrada para todas as chamadas de sistemas, cada uma tem pelo menos um parâmetro que é o seu número, passado através do registrador %eax. O número de parâmetros para uma chamada de sistema não pode ser maior que sete. Os sete registradores para isto são: %eax, %ebx, %ecx, %edx, %esi e %edi, sendo que o registrador %ebp pode ser usado temporariamente. Quando uma chamada de sistema tem mais do que sete parâmetros, ela utiliza o segundo registrador (%ebx) para passar um endereço de memória do processo que contém os parâmetros restantes. As chamadas de sistema freqüentemente tem que ler e escrever dados no espaço endereçamento do processo, para isto elas utilizam as macros get_user() e put_user().

Vamos olhar para a função system_call.

ENTRY(system_call)
pushl %eax # save orig_eax
SAVE_ALL
GET_CURRENT(%ebx)
testb $0x02,tsk_ptrace(%ebx) # PT_TRACESYS
jne tracesys
cmpl $(NR_syscalls),%eax
jae badsys
call *SYMBOL_NAME(sys_call_table)(,%eax,4)
movl %eax,EAX(%esp) # save the return value
ENTRY(ret_from_sys_call)
RESTORE_ALL

Alguns dos passos do código acima são: a macro SAVE_ALL copia todos os registradores para a pilha da CPU, assim as funções das chamadas de sistema encontrarão seus argumentos na pilha e não nos registradores; o conteúdo de %eax é comparado com NR_syscalls (256), se for maior falha com o erro ENOSYS; depois disso é chamada a função que executará a rotina desejada.

Normalmente estas funções começam com o prefixo sys_, exemplo sys_open, sys_chmod, etc…

Cada entrada na sys_call_table tem o tamanho de 4 bytes. O kernel acha o endereço da rotina a ser invocada multiplicando o número da chamada de sistema por 4, depois adicionando o endereço inicial da sys_call_table e extraindo o ponteiro para a rotina da entrada x da tabela.

Quando a rotina chamada terminar, a função ret_from_sys_call termina a chamada de sistema.

Quando uma aplicação faz uma chamada de sistema, ela não se preocupa com o número da mesma ou como passar os seus argumentos. Quem faz este trabalho por exemplo é a libc.

Ela utiliza as chamadas macros _syscallX, onde X é o número de argumentos passados. X vai de 0 à 6. Estas macros podem ser encontradas em /usr/src/linux/include/asm-i386/unistd.h



Navegue:

Comente

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *