implementação de system call no linux
DESCRIPTION
Esse documento faz referência a implementação de uma system call especifica no Linux. Nele há passo a passo do modo de implementação, além da explicação de sua função, e pode servir de guia para implementações de system call no sistema operacional Linux.TRANSCRIPT
UNIVERSIDADE FEDERAL DE UBERLÂNDIA
FACULDADE DE ENGENHARIA MECÂNICA
Curso de Graduação em Engenharia Mecatrônica
Trabalho de Conclusão de Disciplina
Grupo 2
Professor Rivalino Matias Júnior
Guilherme Ramalho – 11311EMT008
Rafael Froede – 11311EMT016
Taynara Brito – 11311EMT027
Uberlândia, dezembro de 2015
2
SUMÁRIO
Introdução ......................................................................................................................... 3
1 Objetivo ..................................................................................................................... 4
2 Problema proposto..................................................................................................... 5
3 Fundamentação Teórica ............................................................................................ 6
3.1 System Call ........................................................................................................ 6
4 Implementação da System Call ................................................................................. 7
4.1 Compilação do Kernel ....................................................................................... 7
4.2 System Call ........................................................................................................ 8
4.3 A implementação da System Call .................................................................... 12
5 Conclusões .............................................................................................................. 13
6 Bibliografia.............................................................................................................. 14
3
INTRODUÇÃO
Este relatório visa detalhar o trabalho realizado pelos alunos referentes ao Grupo
2 como parte integrante da disciplina de Sistemas Operacionais do curso de Engenharia
Mecatrônica da Universidade Federal de Uberlândia.
O tema é decorrente da discussão de gerenciamento de processos, que é um dos
tópicos abordados na teoria, sendo o presente trabalho uma das técnicas adotadas para
melhor abordagem do assunto, bem como tornar um pouco mais prático os conhecimentos
adquiridos em sala de aula.
Desta forma, este relatório irá abordar assuntos referentes a como implementar
uma system call no sistema operacional Linux que efetue análises a nível de processos
correntes no sistema.
Inicialmente, o grupo optou por uma abordagem teórica, embasada por fontes
eletrônicas e livros. Em sequência, encontra-se um detalhamento das práticas adotadas
para a implementação da mesma. E, finalmente, conclusões apresentado resultados e
dificuldades encontradas na execução do trabalho.
4
1 OBJETIVO
Este trabalho tem como objetivo a análise, o projeto, bem como a implementação
de uma system call para o sistema operacional Linux (x86) por parte dos alunos que estão
cursando a presente disciplina.
Tal system call tem como função a análise de características típicas de processos
existentes em sistemas operacionais, como PID do processo pai (PPID – parent process
identify), UID (user identify) e tempo de existência.
5
2 PROBLEMA PROPOSTO
Recebendo como parâmetro um valor do PID a ser pesquisado nos processos
correntes do sistema operacional, a system call implementada deve retornar os seguintes
dados pertencentes ao processo identificado pelo valor de PID escolhido:
UID – identificador de usuário;
PPID – identificador do processo pai; e
Tempo de existência.
Caso o valor entrado seja de um processo que não possui existência, a system
call deverá retornar -1 e encerrar a execução.
6
3 FUNDAMENTAÇÃO TEÓRICA
3.1 System Call
System Call, ou em português, chamada de sistema, pode ser definida como uma
função responsável por chamar o kernel do sistema para que este faça uma determinada
tarefa. Como o usuário não pode, diretamente, realizar operações com o kernel, as
chamadas de sistemas foram criadas.
A Figura 1 trata-se de uma ilustração de como funciona as system calls. Como
pode-se notar, uma determinada aplicação, ou mesmo o usuário, quando deseja solicitar
determinado serviço do kernel, faz uma system call, sendo que a mesma irá se ocupar do
papel de passar todas as informações para que ele execute a ação desejada.
Figura 1 - Ilustração da ação de uma system call no sistema operacional.
Para uma melhor análise, as system calls foram divididas em quatro grandes
grupos, sendo estes:
Chamadas de sistema para gerenciamento de processos;
Chamadas de sistema para gerenciamento de diretórios;
Chamadas de sistemas para gerenciamento de arquivo;
Chamadas de sistemas restantes.
Dado o fato de que o foco deste trabalho se dá sobre gerenciamento de processos,
este tópico será abordado mais detalhadamente.
7
4 IMPLEMENTAÇÃO DA SYSTEM CALL
4.1 Compilação do Kernel
A compilação do kernel se deu de forma apresentada pelo professor em sala de
aula. Ela será descrita brevemente pois não possui grande ênfase neste projeto.
Os arquivos do kernel utilizados e apresentados neste documento são
pertencentes a versão 3.17.1 do kernel do Linux – Ubuntu e foram obtidos pelo site
kernel.org.
Com os arquivos previamente descompactados em um novo diretório (mas não
necessariamente, podendo utilizar um diretório existente), pode-se inicializar as etapas de
compilação e instalação de um kernel modificado. Entretanto, neste primeiro momento,
o kernel está exatamente como quando foi baixado.
Para a execução do processo de compilação e instalação de um novo kernel, é
necessário que algumas funções estejam presentes no sistema operacional. Ativar estas
funções é o primeiro passo para a compilação e instalação.
Com tais funções já ativadas, deve-se estar no diretório em que os arquivos do
kernel, já baixados e descompactados. E então inicia-se as configurações do kernel para
a compilação e instalação. Embora existam diversas configurações que poderiam
aperfeiçoar a compilação, não se tratava do foco deste projeto então as únicas
configurações feitas foram a mudança do nome do novo kernel e a alteração da família
de processadores utilizado (quando necessário).
Assim, os arquivos estão prontos para a compilação. O comando para tal ação é
o make. Este comando é responsável por organizar um número mínimo de compilações
necessárias para que se possa atualizar todo o kernel. Ele foi utilizado com a sequência
–j4 para que todos os 4 processadores lógicos pudessem ser utilizados em sua máxima
capacidade a fim de minimizar o tempo de compilação.
Após o kernel compilado, compilou-se os módulos pertencentes e então os
instalou-se.
E, finalmente, o kernel pode ser instalado e, para que ele fosse inicializado com
o sistema operacional, uma reinicialização é necessária e a escolha do kernel se dá pelo
gerenciador de boot do Linux.
8
4.2 System Call
#include <linux/sched.h>
#include <linux/pid.h>
#include <linux/kernel.h>
#include <linux/cred.h>
#include <linux/ktime.h>
asmlinkage long sys_tcdso(int p){
pid_t pid = p;
kuid_t credd_uid;
long int tempost;
struct pid *pid_struct = find_get_pid(pid);
struct task_struct *task =
pid_task(pid_struct,PIDTYPE_PID);
struct task_struct *taskp =
pid_task(pid_struct,PIDTYPE_PID);
struct timespec tmp;
if (task == NULL){
return (-1);
}
else {
tempost = task -> start_time;
tmp = ktime_to_timespec(ktime_get_boottime());
credd_uid = task->cred->uid;
taskp = taskp -> parent;
printk("UID do processo: %ld.\n",credd_uid);
printk("PID do PAI: %ld.\n",(long int)taskp ->
pid);
printk("Tempo de vida [s]: %ld.\n", tmp.tv_sec
- tempost/1000000000);
return (0);
}
}
9
O código que apresentado é o utilizado para executar os requisitos do problema
proposto para o grupo. Nos parágrafos seguintes ele está explicado detalhadamente para
um melhor entendimento do leitor, tal explicação será dada por partes.
#include <linux/sched.h>
#include <linux/pid.h>
#include <linux/kernel.h>
#include <linux/cred.h>
#include <linux/ktime.h>
Este cabeçalho presente na system call mostra as bibliotecas utilizadas para o
funcionamento de todas as funções que foram necessárias para o desenvolvimento da
system call. A biblioteca <linux/sched.h> contém a estrutura necessária para
encontrar os parâmetros de identificação do processo. Por sua vez, <linux/pid.h>
possui parâmetros necessários para a localização de estruturas relacionadas ao PID do
processo procurado. Estas estruturas serão abordadas a seguir. A utilização da biblioteca
<linux/kernel.h> é necessária de modo a ser permitida a utilização da função de
impressão no buffer do kernel (função printk()). Para que o UID seja encontrado, a
biblioteca <linux/cred.h> é necessária. E, por fim mas não menos importante, a
biblioteca referente a análise do tempo de vida do processo, <linux/ktime.h>.
O trecho do código a seguir possui todas as declarações necessárias para o
funcionamento da system call que fora implementada.
asmlinkage long sys_tcdso(int p){
pid_t pid = p;
kuid_t credd_uid;
long int tempost;
struct pid *pid_struct = find_get_pid(pid);
struct task_struct *task =
pid_task(pid_struct,PIDTYPE_PID);
struct task_struct *taskp =
pid_task(pid_struct,PIDTYPE_PID);
struct timespec tmp;
Na primeira linha, está representado a declaração da system call nomeada por
sys_tcdso. Nota-se que esta função retornará um tipo long quando for chamada pelo
10
usuário ou por alguma aplicação. O marcador asmlinkage representa um macro que
instrui ao compilador GCC de que esta função necessita que seus argumentos sejam
colocados na pilha, ao invés de serem alocados em registradores. O argumento, como já
mencionado anteriormente, será um inteiro representando o PID que se deseja analisar.
A linha consequente é responsável de alocar o argumento passado pelo usuário
em uma variável tipo pid_t para que esta possa ser utilizadas em funções para análises
de PID.
Seguindo, assim como a linha anterior, as duas linhas seguintes se comprometem
a determinar um tipo kuid_t para um campo onde irá receber o UID requisitado pelo
projeto e um tipo long int para armazenar o tempo de início do processo.
A primeira estrutura será utilizada para encontrar um ponteiro para outra
estrutura. Esta estrutura é conhecida como task_struct. A task_struct se trata
de uma estrutura de dados que contém todas as informações que o kernel possui sobre um
processo e, para ela, criou-se um ponteiro. Que será inicializada nas duas linhas seguintes.
O fato de existir duas estruturas idênticas, apenas com nomes diferentes, se dá ao fato da
necessidade de encontrar o PID do processo pai do processo identificado pelo PID
passado como argumento. Logo, deve-se encontrar a estrutura para o processo pai
também.
E, por fim, uma última estrutura foi declarada. Este tipo de estrutura,
timespec, fora necessário para que o acesso ao tempo, em segundos desde o último
boot, fosse possível.
Assim, a seção de declarações é finalizada. O próximo trecho mostra o retorno
da system call caso o PID recebido não exista no momento da chamada. Como solicitado
pelo projeto, esta situação deve retornar o valor –1 para o usuário.
if (task == NULL){
return (-1);
}
Caso a comparação feita no trecho acima seja inválida, a chamada de sistema
continuará para o trecho mostrado a seguir:
else {
tempost = task -> start_time;
tmp = ktime_to_timespec(ktime_get_boottime());
credd_uid = task->cred->uid;
11
taskp = taskp -> parent;
printk("UID do processo: %ld.\n",credd_uid);
printk("PID do PAI: %ld.\n",(long int)taskp ->
pid);
printk("Tempo de vida [s]: %ld.\n", tmp.tv_sec -
tempost/1000000000);
return (0);
}
De forma a atender os dados solicitados pelo projeto, foi necessário acessar
alguns campos das estruturas.
O primeiro campo mostrado é um campo referente ao tempo em que o processo
foi inicializado no sistema, o start_time. Este campo é dado em nano segundos,
contados desde o boot do sistema até a inicialização do sistema.
Em sequência, deseja-se obter o tempo desde o boot até o momento de chamada
da função. Para isso, foi necessário a utilização de duas funções. A primeira,
ktime_get_boottime(), faz com que o tempo desde o boot seja acessado num tipo
de tempo chamado de ktime, o que inicialmente não é utilizável para o objetivo final.
Com este valor obtido, a segunda função, ktime_to_timespec(), transforma este
tempo de forma a preencher a estrutura timespec apresentada anteriormente. Nesta
estrutura encontra-se um campo para segundos e nano segundos.
Seguindo, encontramos a sequência de ponteiros que são utilizados para que o
valor do UID do processo identificado pelo PID passado pelo argumento seja encontrado.
Este valor se encontra dentro de uma estrutura diferente, a estrutura cred, mas que possui
um campo na estrutura task_struct. Com isso, apenas é necessário apontar para o
campo cred da task_struct e então apontar para o campo que possui o UID do
processo.
Assim como para o UID, o PPID não se encontra dentro da task_struct do
processo procurado. Entretanto, existe um campo que possui um ponteiro apontando para
a task_struct do processo pai. Assim, para que o PPID seja encontrado, é necessário
apontar para o processo pai, e então apontar para o PID. Esta última ação só é executada
na linha onde imprime o valor do PPID no buffer do kernel.
Por fim, os comandos de impressão são executados. Note que a conversão do
tempo de início do processo é feita diretamente.
12
4.3 A implementação da System Call
Após a definição do código, deve-se implementar a system call no kernel e então
compila-lo.
Os passos a seguir foram seguidos para que esta implementação fosse possível,
e assim, fosse utilizada após a compilação e instalação do novo kernel.
Ao iniciar, deve-se adicionar o código da system call no diretório /kernel nos
arquivos descompactados do kernel do Linux citados acima. Em seguida, as
configurações dos arquivos base para o reconhecimento da nova system call adicionada.
A ordem destes comandos não altera o resultado final, mas todos eles devem ser
feitos para a obtenção de sucesso na implementação.
Deve-se associar um número para a nova chamada de sistema. A tabela de
número das chamadas de sistema existentes no kernel pode ser encontrada em
arch/x86/syscalls/syscall_64.tbl e o formato para adicionar ela na lista é
[número] [ABI] [nome] [entry_point]. O número deve seguir a sequência
da tabela, o ABI deve ser preenchido com common para que a system call funcione em
diferentes ABIs, o nome da chamada de sistema, neste caso tcdso e o nome da função
sys_tcdso, que chamará a system call adicionada.
Deve-se então fornecer um protótipo para a system call e adicioná-lo em
include/linux/syscalls.h. Este protótipo será a declaração da system call
contida no código, asmlinkage long sys_tcdso(int p);.
Por fim, edita-se o arquivo /kernel/Makefile com a extensão da system
call, tcdso.o, para que a system call seja compilada quando acontecer a compilação do
kernel.
Após todas estas mudanças, o kernel pode ser compilado e a system call poderá
ser utilizada normalmente pelo usuário.
13
5 CONCLUSÕES
Com a modernização desenfreada do mundo atual, constantes atualizações
devem ser necessárias em sistemas regidos sistemas computacionais. Portanto, não se
existe o espaço para a criação diária de novos sistemas operacionais a fim de suprir as
necessidades de todos os consumidores.
As chamadas de sistemas analisadas durante o desenvolvimento desde trabalho
são um grande exemplo destas atualizações de forma a modificar um sistema já existente
e operando, a fim de acrescentar e este apenas novas funções.
Embora as funções implementadas neste projeto tenham sido bastantes
simplificadas, não foram triviais na implementação. A programação neste nível não era
familiar a nenhum dos integrantes. Houve grandes dificuldades relacionadas desde a
compilação e instalação do kernel, à implementação do código da system call. Outro fator
para grandes buscas fora o fato de estar programando em um ambiente diferente do
acostumado, dada a impossibilidade de utilização das funções de user level em kernel
level.
Contudo, concluímos que o presente trabalho foi de suma importância e de
grande incremento na vida acadêmica e profissional, uma vez que possibilitou através de
uma experiência prática, se adquirir um conhecimento mais aprofundado de como se
alterar um sistema operacional de forma a realizar funções impostas por condições de
projeto.
14
6 BIBLIOGRAFIA
[1] “Chamadas de Sistema - O que são, Para que servem e Exemplos de System Call,”
Programação Progressiva.net, [Online]. Available:
http://www.programacaoprogressiva.net/2014/09/O-que-sao-Chamadas-de-
Sistema-para-que-servem-Exemplos-de-System-Calss.html. [Acesso em 19
Novembro 2015].
[2] R. M. JUNIOR, “Sistemas Operacionais - Gerência de processador,” [Online].
Available:
http://www.facom.ufu.br/~rivalino/facom49060/%5b1%5d%20Slides%20(Lecture
%20Notes)/UNIDADE%20IV/FACOM49060%20-%20Unidade%20IV.pdf.
[Acesso em 19 Novembro 2015].
[3] “Tutorial - Compilação de um novo Kernel Linux,” [Online]. Available:
http://www.facom.ufu.br/~rivalino/facom49060/%5B2%5D%20Papers%20&%20
TRs/tutorial-kernel-generico.pdf. [Acesso em 19 Novembro 2015].
[4] “Tutorial - Adicionando novas chamadas de sistema ao kernel Linux,” [Online].
Available:
http://www.facom.ufu.br/~rivalino/facom49060/%5B2%5D%20Papers%20&%20
TRs/tutorial-syscall-linux.pdf. [Acesso em 22 Novembro 2015].
[5] A. S. TANENBAUM, Sistemas Operacionais Modernos, 3ª ed., vol. I, São Paulo:
Pearson Education do Brasil Ltda., 2010.
[6] R. LOVE, Linux Kernel Development - A thorough guide to the design and
implementation of the Linux kernel, 3ª ed., Crawfordsville: Pearson Education, Inc.,
2010.