LEIC/LETI 2015/2016
Sistemas Operativos
Aula 1
Introdução ao Ambiente Unix
Objectivo
- Introdução ao desenvolvimento de aplicações em linguagem C no ambiente Unix
- Familiarização com as ferramentas necessárias: gcc, gdb, make.
Material de Apoio
Introdução
Estude o ciclo de desenvolvimento de aplicações em linguagem C no ambiente Unix (slide).
Copie o ficheiro aula1-eg1.tgz para a sua área de trabalho e descomprima o seu conteúdo com o comando:
tar -zxvf aula1-eg1.tgz
1. Geração do executável (slide)
- Visualize e compreenda o conteúdo dos ficheiros.
- Compile os ficheiros. Verifique depois que os ficheiros objecto foram criados usando o comando "ls" através da interface operacional (shell).
gcc -g -c list.c main.c
- Faça a ligação dos ficheiros objecto de modo a produzir o executável denominado main.
gcc -o main list.o main.o
- Execute a aplicação main e compreenda o resultado.
./main
2. Utilização do debugger gdb
- Execute a aplicação no debugger gdb:
gdb main
- Coloque um breakpoint na primeira instrução da função insert_new_process (ficheiro list.c e linha 36):
b list.c:36
- Execute a aplicação usando o comando run:
r
A aplicação executa-se até ao breakpoint.
- Pode agora ver o valor das variáveis que estão no scope da função usando o comando print:
p item
ou
p *item
Qual a diferença entre estes dois comandos?
- Pode agora executar a aplicação utilizando o comando de step para executar a próxima instrução entrando dentro das funções:
s
ou o comando de next para executar a próxima instrução sem entrar dentro das funções:
n
- Enquanto executa os comandos next ou step pode ir executando o comando print para mostrar o valor das variáveis ou executar apenas uma vez o comando display:
display *item
- Altere agora o ficheiro list.c:
Descomente o comentário da linha 61 e comente a linha 62.
- Execute o seguinte comando fora do gdb para colocar o limite do tamanho do ficheiro core com o valor de 10Mb:
ulimit -c 10000000
- Gere o executável e execute o programa fora do gdb. O erro de segmentation fault irá ocorrer.
- O gdb é uma muita boa ferramenta para saber o que aconteceu. Para isso, comece por listar os ficheiros que estão na diretoria:
ls
Que observa?
- Use em seguida o gdb para saber onde ocorreu o erro executando:
gdb main <ficheiro core>
- O gdb irá ficar parado na instrução onde ocorreu o erro. Para saber qual a instrução onde o erro ocorreu execute o comando backtrace:
bt
- O gdb mostra-lhe assim a função em que ocorreu o erro e todas as funções que chamaram essa até ao nível da função main. Algumas dessas funções são de sistema, mas há duas que podemos reconhecer: lst_print e main. O primeiro número em cada linha indica o nível em que essa função está. Para observar as variáveis que estão no nível da função lst_print deve mudar para esse nível executando o comando frame seguido do nível. Por exemplo:
frame 2
- Pode agora ver o conteúdo das variáveis que estão no scope da função lst_print:
p item
Pode assim ver que o erro ocorreu porque a variável item é NULL. A partir daqui poderia colocar um breakpoint no início do ciclo while, correr o programa de novo e seguir depois passo a passo, enquanto vai observando os valores das variáveis, até descobrir o que gerou o segmentation fault.
3. Utilização da ferramenta make (slide)
- Copie o ficheiro Makefile para a sua área de trabalho e execute make. O que aconteceu?
- Apague o ficheiro list.o. Re-execute make. Interprete o sucedido.
- Simule uma alteração ao ficheiro main.c com o comando seguinte e re-execute make. Compreenda o resultado.
touch main.c
- Simule a alteração do ficheiro list.h e execute make. Porque razão todos os ficheiros foram gerados?
- Simule a alteração do ficheiro list.o. O que acontece quando faz make list.o? E se agora fizer make?
- Retire a dependência do ficheiro list.h da regra list.o da Makefile. Repita procedimento da alínea d). Explique a diferença no resultado?
- Adicione a regra seguinte no fim do ficheiro. O que descreve esta regra? Identifique: o alvo, as dependências e o comando. Tenha em atenção que os espaços inicias em cada linha são tabs.
clean:
rm -f *.o main
- Execute make clean. O que aconteceu? Porque razão o comando é executado sempre que esta regra é invocada explicitamente?
Outros exercícios
- Implemente a função update_terminated_process. A função update_terminated_process recebe uma lista, um valor de pid e um tempo de fim, procura pelo elemento com esse valor de pid e atualiza esse elemento com o tempo de fim.
- Gestor de tarefas pessoais
Construa uma aplicação de organização pessoal que permite gerir as tarefas pendentes de uma pessoa.
Cada tarefa:
- é identificada por uma sequência de caracteres única que, por simplicidade, não pode conter espaços;
- tem uma prioridade, definida por um inteiro entre 0 e 5 (5 é mais prioritária, 0 é menos prioritária).
A aplicação tem os seguintes comandos:
- > new <prioridade> <id-nova-tarefa>
que insere a nova tarefa;
- > list <prioridade>
que lista todas as tarefas com tarefa da prioridade indicada ou superior; a listagem deve estar ordenada por prioridade (mais prioritárias primeiro) e, entre tarefas igualmente prioritárias, por data de criação (mais recentes primeiro);
- > complete <id-nova-tarefa>
que retira a tarefa indicada; caso a tarefa não exista, deve ser apresentada a mensagem de erro "TAREFA INEXISTENTE".
Sugestão: usar tantas listas quanto níveis de prioridade.