Introdução ao Ambiente UNIX I
Objectivo
- Introdução ao desenvolvimento de aplicações em linguagem C no ambiente UNIX.
- Familiarização com as ferramentas necessárias: gcc, gdb.
Material de Apoio
- Manuais: emacs, vim, gcc, gdb, make.
- Matéria relevante de IAED para rever: ponteiros I, ponteiros II e scope.
- Cartões de referência: gdb, comandos UNIX.
Introdução
Estude o ciclo de desenvolvimento de aplicações em
linguagem C no ambiente UNIX. Descarregue e copie o ficheiro
aula1.zip
para a sua área de trabalho. Descomprima e extraia o seu
conteúdo com o comando unzip:
$ unzip aula1.zip
1. Geração do executável
- Visualize e compreenda o conteúdo dos ficheiros.
- Compile os ficheiros utilizando o comando gcc (documentação
completa). Verifique depois que os ficheiros objecto foram
criados usando o comando ls através da interface operacional
(shell/console).
$ gcc -g -c list.c main.c
- Faça a ligação dos ficheiros objecto, de novo com o gcc, de modo a
produzir o executável
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
- Utilize o comando
break
(abreviadob
) para colocar um breakpoint na primeira instrução da funçãoinsert_new_account
(ficheirolist.c
e linha 37). Um breakpoint pode ser colocado numa linha de um ficheiro, ou numa função:(gdb) b list.c:37 (gdb) b insert_new_account
- Execute a aplicação usando o comando
run
(abreviador
):(gdb) r
- A aplicação é executada normalmente. Quando chega ao breakpoint é
interrompida pelo gdb. Pode ver onde o código parou de executar
utilizando o comando
list
:(gdb) list
- Pode agora ver o valor das variáveis que estão no scope da função usando o
comando
print
(abreviadop
):(gdb) p item (gdb) p *item
- Qual a diferença entre os dois comandos anteriores?
- Pode agora executar a aplicação utilizando o comando
step
(abreviados
), que entra nas funções por onde passa, ounext
(abreviadon
) que salta as funções por onde passa. Pode ainda utilizar o comandocontinue
(abreviadoc
) para resumir a execução do programa, parando no próximo breakpoint (ou quando o programa rebentar).(gdb) n (gdb) s
- Enquanto executa os comandos
next
oustep
pode executar o comandoprint
para mostrar o valor das variáveis ou executar apenas uma vez o comandodisplay
:(gdb) display *item
- Saia do gdb com
quit
ou premindoCtrl-D
:(gdb) q
- Execute o seguinte comando para colocar o limite do tamanho do ficheiro
core a 10MB:
$ ulimit -c 10000000
- Execute de novo o programa. O erro segmentation fault (core dumped) irá ocorrer.
- O gdb pode ser utilizado para analisar o programa após este terminar,
como se fosse uma autópsia. Para isso, comece por listar os ficheiros que
estão na directoria (ver nota no fim):
$ ls
- Que observa?
- Use em seguida o gdb para saber onde ocorreu o erro executando:
$ gdb main ovossoficheiro.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
(abreviadobt
):(gdb) bt
- O comando
backtrace
mostra, de baixo para cima, a lista de funções que foram executadas até ao ponto em que o programa terminou. Por exemplo:
Algumas das funções pertencem ao sistema. As que nos interessam são as nossas,#0 0x00007fc8b3912998 in __tz_convert () from /usr/lib/libc.so.6 #1 0x00007fc8b3910ce9 in ctime () from /usr/lib/libc.so.6 #2 0x0000000000400985 in lst_print (list=0x164e420) at list.c:55 #3 0x0000000000400797 in main (argc=1, argv=0x7ffeea9883c8) at main.c:34
lst_print
emain
. O primeiro número em cada linha indica o nível em que essa função está, começando pela função onde o programa rebentou. Para observar as variáveis que estão no nível da funçãolst_print
deve mudar para esse nível executando o comandoframe
seguido do nível. Por exemplo:(gdb) frame 2
- Pode agora ver o conteúdo das variáveis que estão no scope da função
lst_print
:(gdb) p item
- Pode assim ver que o erro ocorreu porque a variável
item
éNULL
. A partir daqui pode colocar um breakpoint no ciclo while, executar o programa de novo, e seguir passo a passo enquanto observa os valores das variáveis, até descobrir o que gerou o segmentation fault. - Corrija o programa.
3. Memória dinâmica
- Implemente a função
update_account_balance
. A funçãoupdate_account_balance
recebe uma lista, um número de conta, e um saldo inicial, e atualiza o saldo da conta especificada. - Adicionar um campo
owner
do tipochar *
. Modifique a funçãomain
para exemplificar o uso dessa funcionalidade. Deve certificar-se que o programa liberta (utilizando a funçãofree
) toda a memória que alocou. - Implemente a função
delete_accounts_by_owner
. A função recebe uma lista de contas e um nome, e remove da lista as contas cujo owner tenha esse nome.
Nota sobre o ponto 2.12
Em certos sistemas o ficheiro core não é gerado na directoria actual, sendo os coredumps geridos por um programa chamado systemd. O acesso aos coredumps é feito com coredumpctl:
$ coredumpctl gdb