Understanding The Linux Kernel

Capítulo 8

System Calls

System calls

Um system call é uma função do Kernel que pode ser usada pelas aplicações a correr em User-Space.

Quando escrevemos um programa em C usamos funções de bibliotecas que, por vezes, fazem pedidos ao sistema operativo. Por exemplo o printf, exit, operações de Input/Output, fork, etc…

As system calls do Kernel não podem ser chamadas directamente como as funções normais.

System calls

No caso do Linux, a interface para as chamadas ao sistema está definida seguindo a especificação do POSIX.1
(Portable Operating System Interface)

System calls

Se um processo a correr uma aplicação de um utilizador deseja um serviço do sistema operativo, deve invocar a função apropriada do sistema através da interface POSIX.

Antes da chamada, o processo corre em “user-mode”. Depois da chamada, e antes do return, o código executa em “supervisor mode”.

Isto significa que quando um processo invoca uma chamada ao sistema, o CPU deverá passar do “user mode” para o “supervisor mode” e que, quando a função do sistema terminar, o CPU deverá regressar ao “user mode”.

System calls

Qual a forma mais segura para fazer a mudança do “user” para “supervisor mode” ?

Poderia haver uma instrução simples para mudar de “user” para “supervisor mode”, mas neste caso não haveria nada que impedisse qualquer programa de aceder às instruções com privilégios.

A mudança deve ser feita a nível do Kernel. Mas, como é que podemos garantir que um processo a correr em “user mode” muda para “supervisor mode” e que depois dessa mudança, o CPU vai executar código do Kernel e não código do utilizador ?

System calls

Uma solução simples, seria a de ter todas as funções do Kernel a executar a instrução para mudar para “supervisor mode” antes de fazer qualquer outra tarefa.

Porém desta forma o código do utilizador teria de ficar ligado (linked) com o kernel. Isto representa que, qualquer programa conseguiria saber qual o endereço das funções do kernel, e desta forma poderia copiar o código que permite alterar do “user” para “supervisor mode”.

System calls

Uma outra solução, consiste em usar a instrução “trap” do CPU.

A instrução “trap” permite que o CPU “salte” para um determinado endereço e que altere o modo de execução para “supervisor mode”.

A instrução “trap” pode ser executada por qualquer programa, contudo, o endereço destino do salto é predefinido num conjunto de endereços mantido em memória protegida e que está configurado para apontar para o código do Kernel.

System calls

System calls

Nos CPUS da família i386, a instrução “trap” está associada ao interrupt 0x80.

Cada system call é identificado por um número.

Para fazer uma chamada ao sistema, é necessário atribuir o número do system call ao registo EAX, e gerar um “trap” (int 0x80).

Os argumentos são passados para a system call nos registos.

My first System Call

Kernel Projects for Linux (Gary Nutt)

Capítulo 5

1º Passo: definir o número da system call

Editar o ficheiro
.../linux-x.y.z/include/asm/unistd.h

...
#define __NR_restart_syscall 0

#define __NR_exit 1
#define __NR_fork 2
...
/* no fim da lista definir a nossa syscall */
#define __NR_my_system_call 284

#define NR_syscalls 285 /* alterar valor para +1 */
...

2º Passo: inserir uma nova entrada na tabela dos system calls

Editar o ficheiro .../linux-x.y.z/arch/i386/kernel/entry.S

...
.data
ENTRY(sys_call_table)
.long SYMBOL_NAME(sys_ni_syscall) /* 0 */
.long SYMBOL_NAME(sys_exit)
.long SYMBOL_NAME(sys_fork)
...
/* no fim da lista adicionar uma entrada */
.long SYMBOL_NAME(sys_my_system_call)

3º Passo: adicionar a system call ao código do kernel

Nesta fase é necessário decidir em que directório a system call deverá ficar.

Se for uma system call relacionada com sincronização, deve ficar em linux-x.y.z/ipc/

Se está relacionado com sistema de ficheiros, deve ficar em linux-x.y.z/fs/

etc…

Neste exemplo, optou-se por guardar o código no directório linux-x.y.z/kernel, no ficheiro my_system_call.c

3º Passo: adicionar a system call ao código do kernel

A definição da system call deve obedecer às seguintes regras:

deve ser precedida pela palavra "asmlinkage";

deve ter "sys_" no inicio do nome da função.

Exemplo: ficheiro my_system_call.c
#include <linux/linkage.h> /* para poder usar o asmlinkage */
#include <asm/current.h> /* para poder usar o current */
asmlinkage int sys_my_system_call(void) {
printk("<1> uid%d -euid%d\n",current->uid, current->euid);
return (current->pid);
}

4º Passo:
alterar a Makefile

Na Makefile no directório onde ficou guardado o ficheiro com a system call é necessário criar uma entrada para que durante a compilação a nova system call seja compilada e fique ligada (linked) ao Kernel correctamente.

...
obj-y = ... my_system_call.o
...
/* nota: dependendo das versões da Makefile, a definição do O_OBJS pode ser diferente … */

5º Passo: criar um cabeçalho (.h) para as aplicações

Criar um ficheiro my_system_call.h que deverá ser guardado no directório com os cabeçalhos (linux-x.y.z/include) e que deverá ser usado por todas as aplicações que pretendem usar a system call que foi definida.

Neste ficheiro deverá ser definido the system call "stub".

Já existem algumas macros definidas em <linux/unistd.h> que permitem gerar automaticamente o "stub" para a system call.

O seu formato geral é:
_syscallN(return type, function name, arg1 type, arg1 name...)
onde N é o número de argumentos da system call.

Exemplo: ficheiro my_system_call.h
#include <linux/unistd.h> /* para poder usar as macros syscall */
_syscall0(int, my_system_call);

6º Passo: compilar e iniciar o Linux com o novo Kernel

make bzImage

…

7º Passo:
usar a system call

A system call criada, my_system_call pode agora ser usada nas aplicações sendo para isso apenas necessário fazer o include do ficheiro criado no passo 5.

Exemplo:

#include <my_system_call.h>

int main() {
…

printf(“%d\n”,my_system_call());

…

}

My System call
My first Virus … 

Porque razão devemos ter cuidado com a origem do kernel que pretendemos usar no nosso sistema ?

...Ver exemplo no ficheiro HTML…

System calls
Algumas considerações…

Por vezes a system call pode receber como argumento um endereço para onde precisa de escrever ou ler. Para tal é necessário usar as funções:

put_user(x, addr) / get_user(x,addr)

Porém, antes de fazer qualquer uma destas operações, o sistema deve confirmar se o endereço pertence à zona de memória associada ao processo do utilizador que invocou a system call.

System calls
Algumas considerações…

Para fazer essa verificação existe a função:

verify_area(int type, const void *addr, unsigned long size);

Esta função analisa a validade da operação de leitura (se type = VERIFY_READ) ou da operação de escrita (type = VERIFY_WRITE) de “size” bytes no endereço addr.

Retorna zero se a operação é válida ou um valor diferente de zero caso contrário.

Inicialização de uma variável global

Por vezes pode ser necessário definir uma variável global que a system call irá usar para manter um determinado estado.

Pode ser necessário inicializar essa variável durante o boot do sistema.

Para tal é necessário incluir o seguinte código junto com o código da system call:

#include <linux/init.h>

…

int my_global_var;

void __init myinit(void) {

my_global_var=0;

}

Inicialização de uma variável global

É também necessário adicionar no ficheiro linux-x.y.z/init/main.c

/* no inicio do ficheiro, junto das declarações de funções */

extern void myinit(void);

/* dentro da função start_kernel (ou init ou do_basic_setup) */

myinit();

Devo implementar uma
syscall ?

Vantagens de syscalls

fáceis de implementar e simples de usar.

o código executado em syscalls tem performance óptima.

Desvantagens de syscalls

é necessário ter um número atribuído para a chamada ao sistema.

A estrutura da função (argumentos, condições, etc) depois de definida, não pode ser alterado em futuras versões do kernel.

para simples troca de informação entre funções, uma chamada ao sistema é demasiado pesada.

Alternativa às chamadas de sistema

Implementar um “driver” com funções ioctl especificas para o tipo de tarefa que se quer ver realizado...

2001/2002 Aplicações Distribuídas e Internet

Ricardo Lopes

2001-09-26T18:23:03