Treinamento_C

download Treinamento_C

of 83

Transcript of Treinamento_C

Programacao em C no GNU/Linux Lucas Correia Villa Real [email protected] 29 de Fevereiro de 2004

Conteudo1 Operacao do GNU/Linux 1.1 Obtendo acesso ao GNU/Linux . . . . . . . . 1.2 Como executar comandos sobre o GNU/Linux 1.3 Obtendo ajuda sobre comandos . . . . . . . . 1.4 Consoles virtuais . . . . . . . . . . . . . . . 1.5 Processos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3 3 4 5 5 7 7 8 9 14 14 15 16 17 18 18 21 21 24 28 30 31 32 32 33 38 44 46 54 54 54

2 Ambiente de desenvolvimento 2.1 Compilador GCC . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 Arquivos headers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3 Utilizando os recursos de debug . . . . . . . . . . . . . . . . . . . . 2.4 Autoconf e Automake . . . . . . . . . . . . . . . . . . . . . . . . . . 2.4.1 Escrevendo um arquivo Makele.am . . . . . . . . . . . . . . 2.4.2 Escrevendo um arquivo congure.in . . . . . . . . . . . . . . 2.5 DDD - Data Display Debugger . . . . . . . . . . . . . . . . . . . . . 2.6 Criando aplicativos que rodem tanto no GNU/Linux como no Windows 3 Explorando os recursos b sicos do sistema operacional a 3.1 Lendo par metros curtos e longos passados pela linha de comando a 3.2 Trabalhando com arquivos . . . . . . . . . . . . . . . . . . . . . 3.3 Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.4 Descritores de Arquivos . . . . . . . . . . . . . . . . . . . . . . . 4 CGI: Common Gateway Interface 5 Bibliotecas din micas a 5.1 Criacao de bibliotecas din micas . . . . . . . . . . . . . . . . . . . . a 6 Processos e threads 6.1 Processos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.2 Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 TCP/IP 7.1 Select . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.2 Conex es n o-bloqueantes . . . . . . . . . . . . . . . . . . . . . . . o a 8 Bancos de dados 8.1 Implementacoes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.2 Programando com Bancos de Dados em C . . . . . . . . . . . . . . . 1 . . . . . . . .

CONTEUDO9 Interface Gr ca a 9.1 Servidor X . . . . . . . . 9.2 A biblioteca Xlib . . . . 9.3 Gerenciadores de Janelas 9.4 GTK+ . . . . . . . . . .

2 58 58 59 59 59 62 62 64 64 66 70 71 73 79 79 80

. . . .

. . . .

. . . .

. . . .

. . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

10 Comunicacao 10.1 Porta Paralela . . . . . . . . . . . 10.2 Porta Serial . . . . . . . . . . . . 10.2.1 A interface Termios . . . . 10.2.2 Modo can nico . . . . . . o 10.2.3 Modo n o-can nico (raw) a o 10.2.4 Modo assncrono . . . . . 10.3 USB . . . . . . . . . . . . . . . .

11 Timers 11.1 Sinais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.2 Utilizando timers . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Captulo 1

Operacao do GNU/Linux 1.1 Obtendo acesso ao GNU/LinuxDistribuicoes convencionais de GNU/Linux fornecem duas maneiras para que o usu rio a autentique-se no computador: uma delas e atrav s de login gr co, e a outra e atrav s e a e de login via console. A segunda maneira e a mais comum, onde e exigido um nome de usu rio e uma a senha. Todo o sistema GNU/Linux mant m uma conta de super-usu rio, normalmente e a associada ao nome root, e contas opcionais para cada usu rio que ir operar este host. a a Normalmente s o utilizados dois arquivos para efetuar a autenticacao de um usu rio: a a o passwd e o shadow, localizados no diret rio de conguracoes de programas da o distribuicao, freq entemente o /etc. O primeiro arquivo mant m uma listagem com u e os nomes de usu rios, seu identicador unico no sistema (um valor inteiro), o grupo a principal ao qual ele pertence, seu nome completo (al m de outros dados opcionais) e e a shell que ser utilizada em suas sess es. a o Ap s vericar a exist ncia deste usu rio a partir do passwd, o arquivo shadow o e a e processado. Este arquivo cont m uma listagem das senhas de cada usu rio, crip e a tografadas. A senha fornecida pelo usu rio e processada pelo mesmo algoritmo, e e a feito um matching para vericar se ambas senhas criptografadas s o id nticas, encera e rando o processo de autenticacao do usu rio no host e dando a ele o acesso a um console a com uma shell. Existem outras formas de autenticacao, como o Linux-PAM, que faz uso de m dulos o de autenticacao. Assim, e possvel escrever m dulos com rotinas especcas para au o tenticar o usu rio, como a validacao de algum cart o magn tico, reconhecimento facial a a e ou ainda pela voz.

1.2 Como executar comandos sobre o GNU/LinuxAs distribuicoes GNU/Linux costumam ser compostas por um conjunto de pacotes base, que capacitam a utilizacao de diferentes servicos, al m de interfaces para permitir e que o usu rio faca uso do sistema normalmente compostas por uma shell, um servidor a para a interface gr ca e um gerenciador de janelas. a Shells s o interfaces em modo texto, onde comandos digitados pelo usu rio s o a a a interpretados e executados. Existem diferentes implementacoes destas interfaces, den tre as quais podem-se destacar o ASH, BASH, KSH, CSH, TCSH e o ZSH. Estas 3

CAPITULO 1. OPERACAO DO GNU/LINUX

4

implementacoes diferenciam-se pela sintaxe na qual o usu rio utiliza para a execucao a de tarefas e pelos recursos que cada uma oferece. Esta sess o trata da operacao b sica a a sobre o BASH, um dos interpretadores mais comuns, presente em grande parte das distribuicoes atuais. Ap s realizar o login no sistema, um prompt ca disponvel ao usu rio, aguaro a dando a entrada de algum comando. A sintaxe padr o para a execucao de um programa a no BASH e: $ programa A execucao deste comando ir criar um novo processo, enquanto o processo pai (o a BASH) ir aguardar pela sua nalizacao para continuar a execucao de outros comana dos. Nota-se que essa e a mesma sem ntica obtida com comandos executados no MS a DOS, onde a shell (command.com, no caso) executa o programa de forma sncrona, ou seja: Espera que os lhos terminem para retornar o controle do terminal ao usu rio. a Em alguns casos e desej vel que o programa seja executado de forma asncrona, a ou seja, deseja-se continuar a ter acesso ao console antes mesmo que o programa tenha terminado. Nestes casos, pode-se executar o programa usando a seguinte forma: $ programa & O & indica que o processo lho relativo a execucao de programa ser criado, mas ` a que o processo pai (o BASH) ir continuar seu uxo de execucao, sem aguardar pelo a t rmino do processo lho. e Bem como a execucao de programas, freq entemente e desej vel redirecionar a u a sada do programa para algum outro dispositivo que n o a sada padr o (stdout). Da a a mesma forma, isto pode querer ser feito para redirecionar a sada de erro padr o (stderr), a ou modicar a entrada de dados para que seja feita a partir de um outro uxo que n o a a entrada padr o (stdin). a A forma que o BASH utiliza para fazer o controle de redirecionamentos e atrav s e dos operadores , processados na ordem em que aparecem, da esquerda para a direita. Caso o primeiro caracter do operador de redirecionamento seja >, o redirecionamento refere-se a sada padr o (stdout). ` a Tamb m h casos (em caso de depuracao de mensagem de erros, por exemplo) e a onde-se quer direcionar ambas sadas (stdout e stderr) para um arquivo, o que pode ser obtido com: $ programa >& arquivo_com_mensagens

1.3 Obtendo ajuda sobre comandosCada programa base do GNU/Linux costuma apresentar uma p gina de manual, cona tendo explicacoes sobre a sua operacao. Existem dois formatos predominantes como forma de documentacao de aplicativos no GNU/Linux: as man pages e as info pages. O que as diferencia e a forma na qual a informacao e apresentada ao usu rio. As a man pages apresentam basicamente: A sinopse do programa; Uma descricao do que ele faz;

CAPITULO 1. OPERACAO DO GNU/LINUX Opcoes e argumentos que s o tratados por este programa; a Refer ncias a p ginas manuais semelhantes que sejam relevantes; e ` a Problemas conhecidos; Autor(es).

5

Al m disso, uma man page e disposta na forma de uma p gina contnua de infore a macoes, podendo muitas vezes tornar difcil a localizacao de determinados assuntos. As info pages solucionam este problema, apresentando a documentacao em um for mato que permite uma navegacao mais f cil, principalmente quando existe um grande a volume de dados a ser informado. Este formato normalmente e utilizado para deta lhar a documentacao de um programa, enquanto as man pages explicam os recursos do programa de forma mais simplista.

1.4 Consoles virtuaisO kernel Linux oferece suporte a dispositivos de terminais virtuais que suportam dispositivos de vdeo e teclado. Eles s o chamados de virtuais pela possibilidade de a executar v rias inst ncias de terminais virtuais tamb m chamados de consoles vira a e tuais em um unico terminal fsico. Este recurso e bastante util, por permitir que um terminal virtual colete mensagens do sistema e warnings, enquanto outro terminal virtual seja utilizado para gerenciar uma sess o de um usu rio em modo texto, e um a a terceiro terminal rodando uma sess o X, todas em paralelo. a A maneira utilizada para alternar entre os diferentes consoles virtuais e normal mente realizada atrav s da combinacao de teclas Alt-. e Para modicar as fontes utilizadas em um terminal, utiliza-se o comando setfont(8).

1.5 ProcessosQualquer tarefa atribuda ao GNU/Linux e um processo. Todo comando executado gera um n mero de processo (PID), e atrav s deste n mero e possvel manipular este u e u processo. Alguns destes programas que permitem a manipulacao de processos s o: a top(1): lista os processos em execucao em tempo real. Ele mostra algumas informacoes sobre o sistema, bem como uma lista das tarefas sendo gerenciadas pelo kernel Linux. Algumas das informacoes incluem o nome do processo e a quantidade de mem ria e CPU consumidas por ele. o ps(1): lista os processos atuais. V rios par metros podem ser utilizados, a a como a opcao -x, que lista todos os processos da m quina, e a opcao -u, a que inclui o autor do processo. Outros par metros uteis s o o -C nome, que a a listam os processos com um nome especco, e -U usuario, que lista todos os processos pertencentes a usu rio. ` a nice(1): executa um comando com uma prioridade diferente de escalonamento. Prioridades no Linux v o de -20 (prioridade alta) a 19 (prioridade baixa). a ` A prioridade padr o para execucao de processos e 0. a

CAPITULO 1. OPERACAO DO GNU/LINUX renice(1): altera a prioridade de escalonamento de algum processo.

6

kill(1): envia um sinal para um processo. Uma lista de sinais encontra-se disponvel na man page do signal(7). killall(): envia um sinal para todos os processos especicados na linha de comando atrav s de seu nome. e nohup(): roda algum comando, ignorando sinais de hangup. Este comando e util quando deseja-se rodar algum programa e logo ap s efetuar o logoff da o m quina: o programa continuar em execucao at o t rmino de sua execucao. a a e e jobs: caso tenha algum programa (job) sendo executado em background, este comando mostra-os. Este comando e implementado pela shell (BASH e ZSH o implementam). A forma de deixar um programa em background e disparando o com o par metro &, como visto na Sess o 1.2, ou pressionando a tecla a a CTRL+Z durante a execucao do programa. fg: tamb m pertinente a controle de programas em background, este comando e ` permite trazer de volta algum job que esteja em background. Caso haja mais de um, e necess rio especicar o seu n mero, listado com o comando jobs. a u Algumas shells utilizam o nome do procsso, ao inv s do seu n mero, para tornar e u a dar o controle a ele. bg: assim como o fg, faz com que o controle seja dado a um processo. No entanto, este modo permite que o programa continue sua execucao em uma sub shell. Assim, a shell ca liberada para que o usu rio continue a execucao de a comandos nela, e o programa sai do estado de background e torna a executar. Este comando e bastante utilizado para chamar programas gr cos no ambiente a X sem bloquear o terminal.

Captulo 2

Ambiente de desenvolvimento2.1 Compilador GCCGCC [STA 2002] e uma abreviacao do termo GNU Compiler Collection. Ele leva este nome pelo fato de que v rias vers es do compilador est o integradas, com suporte a o a a linguagens como C, C++, Objective-C, Ada, Fortran, Java e Treelang. Al m disso, ` e ele representa tanto o nome geral do compilador quanto o nome do compilador de programas C, onde a abreviacao passa a ter o signicado de GNU C Compiler. Este e o compilador padr o de qualquer distribuicao GNU/Linux. a Quando o GCC e invocado, ele normalmente realiza quatro etapas para gerar o exe cut vel: pr -processamento, compilacao, montagem e ligacao, sempre nesta ordem, e a e sendo que e possvel parar o processo no nal de cada etapa. Os primeiros tr s est gios e a aplicam-se a um unico arquivo fonte, e encerram produzindo um arquivo objeto. A ligacao combina todos os arquivos objetos especicados como entrada em um arquivo execut vel. Estes passos podem ser melhor descritos como: a Pr -processamento: Esta etapa e respons vel pela resolucao de diretrizes do pr e a e processador, como #define, #if, #include. Nesta fase, o GCC utiliza o utilit rio cpp. a Compilacao: Nesta fase e produzida a linguagem de montagem dos arquivos de entrada. Montagem: Produz o arquivo objeto .o, levando em conta a linguagem de montagem dos arquivos de entrada. Nesta etapa, o GCC utiliza o utilit rio gas (GNU a Assembler), ou o montador nativo as, caso ele n o esteja disponvel. a Ligacao: Nesta fase os arquivos .o e as bibliotecas s o colocados no exe a cut vel. O utilit rio usado nessa fase e o ld (GNU Linker). a a Para parar o GCC em alguma determinada etapa, podem ser utilizados os seguintes comandos: Pr -processamento: e $ gcc -E teste.c -o teste.i Este comando redireciona a sada do pr -processador para o arquivo teste.i. e

7

CAPITULO 2. AMBIENTE DE DESENVOLVIMENTO Compilacao: $ gcc -S teste.c O arquivo teste.s ser gerado, j em linguagem assembly da arquitetura. a a Montagem: $ gcc -c teste.c Gera o arquivo objeto teste.o. Ligacao: $ gcc -o teste teste.c Gera o arquivo execut vel teste. a

8

Assim, para gerar um execut vel para um simples programa (teste.c, por exema plo), utiliza-se o seguinte comando:$ gcc -o teste teste.c No entanto, a maioria dos programas consistem em v rios arquivos de c digo. Caso hajam a o dois arquivos, arquivo1.c e arquivo2.c, a seguinte linha de comando poderia ser utilizada para compilar o programa nal: $ gcc -o teste arquivo1.c arquivo2.c O GCC interpreta os arquivos de acordo com a sua extens o. Algumas das principais podem a ser vistas na Tabela 2.1.

Tabela 2.1: Algumas extens es reconhecidas pelo GCC o Extens o a .c .C .cc .i .ii .S .s .o .a .so Interpretacao Programa em linguagem C Programa em linguagem C++ Programa em C pr -processado e Programa em C++ pr -processado e Programa em linguagem Assembly Programa objeto Bibliotecas compiladas

Alguns par metros importantes suportados pelo GCC foram vistos, como o -c e -o. No a entanto, outras opcoes podem ser importantes para compilar um projeto. Algumas destas est o a listadas na Tabela 2.2.

2.2 Arquivos headersArquivos headers cont m basicamente denicoes utilizadas por bibliotecas do sistema e prot e o tipos de funcoes, de forma que um programa sendo compilado possa realizar a vericacao de sintaxe e tipos corretamente. A localizacao dos arquivos headers em uma distribuicao GNU/Linux convencional ca no diret rio /usr/include. Alguns outros diret rios importantes s o: o o a /usr/X11R6/include/X11: cont m headers necess rios para compilar programas e a que usam o X11 Window System. /usr/include/X11: headers para compilar programas que usam o X11. Normalmente e um symlink para o diret rio /usr/X11R6/include/X11; o

CAPITULO 2. AMBIENTE DE DESENVOLVIMENTOTabela 2.2: Algumas extens es reconhecidas pelo GCC o Opcao -o FILE -c -I o -L o -lnome -static -g, -ggdb -O -On -ansi -w -Wall -Werror -v

9

Descricao Especica o nome do arquivo compilado (padr o a.out). a Gera somente o objeto (.o). Busca arquivos de headers primeiro em diretorio. Busca bibliotecas primeiro em diretorio. Liga a biblioteca libnome ao execut vel. a Executa a ligacao usando bibliotecas est ticas. a Inclui informacoes para o depurador. Otimiza o c digo. o Especica um nvel de otimizacao entre 0 e 3 (0 n o otimiza). a Desligando recursos do GCC incompatveis com o padr o ANSI. a Inibe as mensagens de warning (aviso). Emite todas as mensagens de warning. Converte todas as mensagens de warning em erro. Mostra os comandos usados em cada passo da compilacao.

/usr/include/g++: headers para compilar programas C++ que utilizam o compilador GNU C++; /usr/include/asm: Arquivos contendo as funcoes, par metros e denicoes es a peccas arquitetura na qual os bin rios gerados ser o criados; a a /usr/include/linux: link simb lico para o diret rio contendo os headers utilizao o dos na vers o do Linux usada para compilar a GNU libc. Algumas distribuicoes ainda a praticam o erro de apontar para o diret rio do kernel Linux atual sendo utilizado no host. o Estes diret rios s o uteis para localizar a denicao de uma funcao que n o apresenta uma o a a man page ou uma info page. Um comando que auxilia a localizacao de denicoes e o grep(1).

2.3 Utilizando os recursos de debugEstes recursos s o muito uteis, pois freq entemente erros acontecem. A opcao -g do GCC pera u mite que sejam gerados c digos de informacao para o depurador. Para compilar um programa o com suporte ao uso em um depurador, e necess rio utilizar a seguinte linha de comando: a $ gcc -g -o teste teste.c Para retirar os smbolos gerados pela opcao -g, pode-se utilizar o comando strip(1) com a opcao strip-debug sobre o arquivo bin rio (neste caso chamado de teste): a $ strip --strip-debug teste O depurador padr o da GNU e o GDB [GIL 2003], o GNU Debugger. Para utilizar o depua rador, chama-se o execut vel a partir da shell, passando o nome do programa a ser depurado a como argumento. Em alguns casos, quando o programa est sendo executado e realiza uma a operacao ilegal, sua execucao e abortada e um arquivo core e gerado, contendo informacoes so bre o contexto atual do processo quando o erro ocorreu. Neste caso, e possvel passar o arquivo core como par metro extra, de forma que o depurador salte imediatamente para a instrucao que a causou o erro. Vale ressaltar que esta operacao s gera algum dado relevante quando o programa o que gerou esta excess o tiver sido compilado com suporte a smbolos de depuracao. a

CAPITULO 2. AMBIENTE DE DESENVOLVIMENTO

10

O GDB pode fazer quatro tipos de operacoes (al m de outras operacoes de suporte a estas) e para ajudar a encontrar problemas no c digo: o Parar o programa em uma condicao especca; Iniciar o programa, especicando algum par metro que possa afetar seu comportamento; a

Modicar vari veis no programa, de forma a poder corrigir os efeitos causados por um a problema e avancar para poder aprender sobre um novo. A utilizacao padr o do GDB ent o e feita da seguinte maneira, tomando como exemplo um a a programa chamado teste: $ gdb teste GNU gdb 5.2 Copyright 2002 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i686-pc-linux-gnu"... (gdb) O prompt de comando agora passa a ser (gdb), onde comandos especcos do GDB poder o a ser utilizados para entender o que est acontecendo no c digo do programa. O seguinte programa a o ser utilizado como exemplo para as explicacoes de operacao do GDB: a 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

Examinar o que houve, quando o programa tiver encerrado a sua execucao;

#include #include int calcula ( int a , int b) { int resultado ; resultado = a + b; return resultado ; } int main() { int a , b , resultado ; a = 1; b = 2; resultado = calcula ( a , b ); printf ( Resultado: %d\n , resultado ); exit (0); } Para executar este programa no GDB, basta executar a operacao run: $ gdb teste ... (gdb) run

CAPITULO 2. AMBIENTE DE DESENVOLVIMENTOStarting program: /Users/lucasvr/teste Resultado: 3 Program exited normally.

11

Como n o houve nenhum problema durante a execucao deste programa, ele encerrou e rea tornou ao prompt do GDB. No entanto, podemos querer executar o programa passo a passo. Para isto, invoca-se o comando break . Para listar o c digo sendo depurado e descobrir o a linha que deseja-se utilizar como ponto de parada (breakpoint), os comandos list e list - podem ser usados para listar as pr ximas 10 linhas de c digo, ou as ultimas 10 linhas de o o c digo, de forma progressiva. Outra forma bastante utilizada e marcar o breakpoint em detero minadas funcoes. No nosso exemplo, para iniciar a execucao passo a passo a partir da funcao calcula, ao inv s de especicar a linha desejada para o comando break, pode ser informada o e nome da funcao a ser utilizada como breakpoint: (gdb) break calcula Breakpoint 1 at 0x8048446: file teste.c, line 7. (gdb) run Starting program: /Users/lucasvr/teste Breakpoint 1, calcula (a=1, b=2) at teste.c:7 7 resultado = a + b; A partir deste ponto, podemos continuar a execucao do programa at que ele encerre nor e malmente, com o uso do comando continue, ou execut -lo passo a passo. Para isto, podea mos utilizar o comando step, que continua executando o programa at encontrar uma outra e linha do c digo, retornando ent o o controle ao GDB. Uma caracterstica importante do step o a [count] e que ele n o entra em funcoes ou loops: caso haja uma chamada a uma funcao, por a exemplo, ele ir retornar o controle ao GDB apenas ap s a execucao completa dessa funcao. a o Caso deseja-se ter um controle maior sobre a execucao de funcoes, loops e switches, pode-se utilizar o comando next [count], que segue o uxo do programa. Outros comandos interessantes s o os referentes a vari veis. O GDB permite que o usu rio a ` a a modique o valor de uma vari vel com o comando set variable . a a No caso do breakpoint realizado anteriormente na funcao calcula, poderamos alterar o valor de a para 2, alterando o resultado da soma. Para isto, basta executar: (gdb) break calcula Breakpoint 1 at 0x8048446: file teste.c, line 7. (gdb) run Starting program: /Users/lucasvr/teste Breakpoint 1, calcula (a=1, b=2) at teste.c:7 7 resultado = a + b; (gdb) set variable a=2 (gdb) continue Continuing. Resultado: 4 Program exited normally. (gdb) Outro recurso bastante utilizado e o watchpoint, que interrompe a execucao do programa quando uma determinada express o for realizada. Ele pode ser usado para vericar quando uma a vari vel tem seu valor modicado, como no exemplo abaixo, onde uma mudanca no valor de b e a a condicao para o retorno do comando ao depurador:

CAPITULO 2. AMBIENTE DE DESENVOLVIMENTO(gdb) break main Breakpoint 1 at 0x804846a: file teste.c, line 14. (gdb) run Starting program: /Users/lucasvr/teste Breakpoint 1, main () at teste.c:14 14 a = 1; (gdb) watch b Hardware watchpoint 2: b (gdb) continue Continuing. Hardware watchpoint 2: b Old value = 0 New value = 2 main () at teste.c:16 16 resultado = calcula (a, b); (gdb) print b $1 = 2

12

Nota-se que o controle e retomado logo ap s a execucao da operacao que modicou b, o logo a execucao continua tendo como indicacao a pr xima linha a ser executada pelo programa. o Utilizamos ainda o comando print, que e usado para imprimir o valor de alguma vari vel a neste caso a vari vel b. a O GDB permite ainda ser anexado a um processo em execucao. Para isto, o GDB deve ser executado passando o PID do processo como segundo argumento, seguindo o nome do programa. Como o programa a ser depurado estar em um ponto desconhecido de execucao, e interessante a vericar onde ele encontra-se. O comando backtrack do GDB imprime os stack frames do processo, e assim e possvel saber onde o programa est sendo executado. Cada frame e um dado a associado a uma chamada de funcao, e cont m os argumentos enviados a ela, as vari veis locais ` e ` a a esta funcao e o endereco no qual a funcao est executando. a Para fazer um teste, modicaremos nosso programa exemplo para que ele faca a leitura de uma tecla para continuar a execucao. Assim, poderemos depur -lo enquanto estiver em a execucao, j que ele executa muito r pido para podermos anexar o GDB a ele. A funcao calcula a a dever car da seguinte forma: a int calcula ( int a , int b) { int resultado ; resultado = a + b; getchar (); return resultado ; } Ap s recompilar o programa n o esquecendo da opcao -g , iremos execut -lo: o a a $ ./teste Agora, em outro terminal, iremos anexar o GDB a sua inst ncia. Para isto, e necess rio obter ` a a o PID do processo, executando o GDB em seguida: $ ps -C teste PID TTY 15247 pts/6 TIME CMD 00:00:00 teste

CAPITULO 2. AMBIENTE DE DESENVOLVIMENTO

13

$ gdb teste 15247 -silent Attaching to program: /Users/lucasvr/teste, process 15247 Reading symbols from /System/Links/Libraries/libc.so.6...done. Loaded symbols for /System/Links/Libraries/libc.so.6 Reading symbols from /System/Links/Libraries/ld-linux.so.2...done. Loaded symbols for /System/Links/Libraries/ld-linux.so.2 0x400e76d4 in read () from /System/Links/Libraries/libc.so.6 (gdb) Neste exemplo, o processo teste encontrava-se no PID 15247, e este valor foi informado como par metro para a execucao do GDB. Nesse ponto temos controle sobre o processo, mas a n o sabemos ainda onde ele est . Para descobrir, vamos listar os stack frames do processo: a a (gdb) backtrace #0 0x400e76d4 in #1 0x40146238 in #2 0x40085b63 in #3 0x40087ffd in #4 0x40087e46 in #5 0x4008230a in #6 0x08048487 in #7 0x080484b8 in #8 0x4002cfe4 in read () from /System/Links/Libraries/libc.so.6 sys_sigabbrev () from /System/Links/Libraries/libc.so.6 _IO_file_underflow () from /System/Links/Libraries/libc.so.6 _IO_default_uflow () from /System/Links/Libraries/libc.so.6 __uflow () from /System/Links/Libraries/libc.so.6 getchar () from /System/Links/Libraries/libc.so.6 calcula (a=1, b=2) at teste.c:8 main () at teste.c:17 __libc_start_main () from /System/Links/Libraries/libc.so.6

Esta stack deve ser interpretada a partir do maior valor, que indica a base de execucao do programa neste caso a funcao libc start main(), da biblioteca padr o do C, a GNU libc. a Em seguida a funcao main() foi executada na linha 17 do programa teste.c, chamando a funcao calcula() a seguir, na linha 8, e nalmente realizando a chamada getchar(). A partir deste ponto, as funcoes referentes a implementacao desta chamada na GNU libc foram executadas, e pode ` se vericar que o processo encontra-se aguardando a leitura de algum dado com a chamada de sistema read(). Nota-se que para cada funcao chamada, um novo frame e criado, e a cada retorno de funcao, o frame para esta funcao e eliminado. Caso a funcao seja recursiva, podem existir v rios frames para a mesma funcao. a Para escolher algum frame especco, pode-se utilizar o comando frame, seguido do n mero u do frame desejado. Pode-se ainda utilizar os comandos up [n] e down [n], que avancam ou retrocedem a escolha do frame desejado na stack. Para modicarmos nosso programa agora, iremos modicar o valor do resultado a ser retornado para a funcao main(). Para isso, iremos escolher o frame referente a funcao calcula() (frame 6, como mostrou o backtrace), modicar o ` valor da vari vel resultado e continuar a execucao do programa: a (gdb) frame 6 #6 0x08048487 in calcula (a=1, b=2) at teste.c:8 8 getchar (); (gdb) set variable resultado=10 (gdb) continue Continuing. Agora, ao retornar ao terminal utilizado para disparar o processo teste e pressionar a tecla ENTER, veremos que nossa modicacao no GDB foi reetida no processo que foi modicado. A partir deste ponto pode-se retornar ao GDB e encerr -lo, com o comando quit. a (gdb) quit $

CAPITULO 2. AMBIENTE DE DESENVOLVIMENTO

14

2.4 Autoconf e AutomakeO Autoconf e Automake s o duas ferramentas que auxiliam o programador a gerar arquivos a Makele para o projeto. O Autoconf e respons vel por realizar uma s rie de checagens no host, a e como a vers o do compilador, tamanhos de tipos de vari veis, exist ncia de headers, bibliotecas a a e (bem como suas vers es), entre muitos outros. Estas vericacoes s o importantes, pois muitas o a vezes os programas rodam nas plataformas mais diversas, em arquiteturas diferentes da utilizada pelo programador. Ap s a realizacao destas vericacoes, o Automake e utilizado para gerar um o arquivo Makele a partir das exig ncias feitas pelo Autoconf. e O processo para gerar arquivos Makele utilizando estas ferramentas consiste nos seguintes passos: Escrever um arquivo congure.in; Escrever um arquivo Makele.am; Rodar aclocal para criar c pias locais de macros utilizadas pelo autoconf; o

Rodar automake -a -c para gerar um arquivo Makele.in a partir do arquivo Makele.am (o automake verica o arquivo congure.in para obter informacoes sobre o pro jeto); A estrutura utilizada em projetos GNU segue a seguinte hierarquia de arquivos e diret rios, o a partir do diret rio base dos fontes do projeto: o BUGS: listagem dos problemas conhecidos do projeto, e de formas de submeter bugs encontrados pelo usu rio; a COPYING: cont m a licenca pela qual o projeto e distribudo; e ChangeLog: cont m um hist rico com modicacoes realizadas no projeto; e o INSTALL: processo de instalacao do programa; AUTHORS: arquivo contendo os nomes dos autores do projeto; Rodar autoconf para gerar o script configure.

FAQ: perguntas freq entemente feitas a respeito de alguma caracterstica do programa; u

Makefile.am: o arquivo a ser gerado pelo programador, informando alguns dados importantes para que o arquivo Makele seja construdo; NEWS: cont m novidades sobre o programa; e README: informacoes sobre o programa, como seu prop sito e formas de contato com o o autor e/ou mailing lists; configure.in: arquivo a ser gerado pelo programador, contendo vericacoes que devem ser feitas antes de compilar o programa; include/: diret rio contendo os arquivos headers do programa; o src/: diret rio contendo os arquivos fontes do programa; o src/Makefile.am: regras de compilacao dos fontes, como nome do bin rio e ar a quivos fontes usados para constru-lo.

2.4.1 Escrevendo um arquivo Makele.amComo visto, existem dois arquivos Makele.am. O que reside no diret rio raiz do projeto ino forma os subdiret rios que cont m os fontes e algumas opcoes extras. O conte do dele normalo e u mente e: AUTOMAKE_OPTIONS = foreign gnu SUBDIRS = src EXTRA_DIST = BUGS COPYING ChangeLog INSTALL README FAQ

CAPITULO 2. AMBIENTE DE DESENVOLVIMENTOO campo AUTOMAKE OPTIONS aceita alguns nveis, dentre os quais:

15

foreign: verica se o projeto est de acordo com os padr es exigidos para distribuir o a o software; gnu: verica se o projeto inclui os arquivos exigidos pelo GNU standards para pacotes de software; no-dependencies: usado em situacoes onde n o existem informacoes sucientes para a fazer a deteccao autom tica de depend ncias no pacote. a e As opcoes suportadas s o muitas, e aconselha-se a leitura da info page do automake para a consult -las. a O campo SUBDIRS informa diret rios onde residem arquivos fontes do programa, e EXo TRA DIST informa que os arquivos ali listados devem ser ignorados pelo automake e pelo autoconf. O arquivo Makele.am interno ao diret rio src/ pode conter informacoes complexas com o regras que devem ser utilizadas para cada arquivo, mas no entanto o seguinte e suciente para gerar pacotes com estas ferramentas: bin_PROGRAMS = Programa Programa_SOURCES = arquivo1.c arquivo2.c arquivo3.c EXTRA_DIST = config.sample Ap s gerar estes arquivos, a pr xima etapa e a criacao de um arquivo congure.in. o o

2.4.2 Escrevendo um arquivo congure.inA maneira mais f cil de gerar um arquivo congure.in e atrav s do comando autoscan(1). a e Ele examina os arquivos fonte a partir do diret rio corrente, recursivamente, e gera um arquivo o congure.scan base no qual pode ser gerado o arquivo congure.in. Ap s renomeado para cono gure.in, algumas mudancas devem ser feitas, como informar o nome e vers o do projeto, e-mail a para reportar bugs, entre outros. Para uma estrutura contendo apenas um arquivo chamado teste.c no diret rio src/ com um o printf e dois includes, stdin.h e unistd.h, este e o resultado gerado pelo autoscan: # -*- Autoconf -*# Process this file with autoconf to produce a configure script. AC_PREREQ(2.57) AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS) AC_CONFIG_SRCDIR([src/teste.c]) AC_CONFIG_HEADER([config.h]) # Checks for programs. AC_PROG_CC # Checks for libraries. # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([stdlib.h]) # Checks for typedefs, structures, and compiler characteristics. # Checks for library functions.

CAPITULO 2. AMBIENTE DE DESENVOLVIMENTO

16

AC_CONFIG_FILES([Makefile src/Makefile]) AC_OUTPUT Ap s preparados estes dois arquivos, congure.in e Makele.am, o projeto pode ser gerado o executando aclocal, automake e autoconf, como explicado anteriormente.

2.5 DDD - Data Display DebuggerApesar do GDB ser um poderoso debugger, sendo at possvel com ele debuggar o pr prio e o kernel do sistema operacional, assim com aplicativos multi-threaded, a sua interface em linha de comando n o e amig vel para usu rios pouco familirizados com a ferramenta, o que frustra a a a a maioria dos usu rios iniciantes em GNU/Linux e n o permite que toda a potencialidade do a a debugger seja aproveitada em sua totalidade. Para suprir tal diculdade, foi desenvolvido o DDD [ZEL 2000], que e simplesmente uma interface gr ca para o debugger. a Por m, o DDD possui ainda extens es ao processo de debugging tradicional, permitindo que e o gr cos sejam plotados a partir do valor de vari veis, como matrizes e arrays. Um exemplo de a a operacao do DDD pode ser visto na Figura 2.5

Figura 2.1: Operando com o DDD sobre o programa exemploVale notar que mesmo com todas as extens es do DDD ao GDB, o mesmo ainda continua o sendo um mero front-end para o debugger da GNU, sendo possvel executar todos os comandos do GDB na janela no nal da janela principal do DDD, como pode ser visto na janela inferior da Figura 2.5

CAPITULO 2. AMBIENTE DE DESENVOLVIMENTO

17

2.6 Criando aplicativos que rodem tanto no GNU/Linux como no WindowsN o existe nenhuma regra especca a ser seguida para executar o mesmo c digo em sistemas a o operacionais diferentes, apenas alguns cuidados que devem ser atendidos. O mais comum e ape nas utilizar funcoes ANSI, que est o disponveis em ambos, e evitar ao m ximo o uso de funcoes a a especcas de um determinado sistema operacional. Assim, pode-se usar a ag -ansi do GCC para restringir as funcoes usadas as denidas e suportadas pelo ANSI. ` Em alguns casos, entretanto, isto n o e possvel, e ent o pode ser necess rio utilizar diretivas a a a de compilacao #ifdef para vericar o sistema em uso. E bastante comum isolar c digos o especicos de uma determinada arquitetura ou sistema operacional desta forma. Por exemplo: # if dened ( linux ) # dene DIR SEPARATOR \\ # else if dened ( WIN32) # dene DIR SEPARATOR / #endif Existe ainda uma forma de portar c digo nativo criado no GNU/Linux para o Windows, o usando um ambiente no Windows chamado Cygwin1 . Ele consiste de uma camada de bibliotecas que atuam como wrappers, de forma a mapear nomes de dispositivos existentes apenas no GNU/Linux para o Windows, como dispositivos IDE, SCSI e seriais, entre outros. Uma grande variedade de bibliotecas e interfaces e aplicativos j encontram suporte neste a ambiente, como bibliotecas de threads, a biblioteca GTK+, o servidor Apache e o servidor XFree86.

1 ver

http://www.cygwin.com

Captulo 3

Explorando os recursos b sicos a do sistema operacional3.1 Lendo par metros curtos e longos passados pela linha a de comandoEm C e possvel a um programa coletar par metros passados na linha de comando no mo a mento de sua ativacao. Assim, podemos escrever um programa chamado soma que receber os a par metros 1 e 2 que ser o passados via linha de comando: a a $ soma 1 2 Estes par metros s o passados para o programa atrav s dos argumentos da funcao main(). a a e O m dulo principal do programa (main) pode receber dois par metros externos opcionais, que o a s o o argv, um vetor de ponteiros para strings, e o argc, que indica a quantidade de strings a contidas neste array. Caso um programa n o receba nenhum par metro, existir apenas uma a a a string no array (no ndice 0), contendo o nome do programa executado, e argc ter o valor 1. a Tomando por exemplo o programa soma, poderamos escrev -lo da seguinte forma: e1 2 3 4 5 6 7 8 9 10 11 12

#include #include int main ( int argc , char argv) { int i ; printf ( Numero de argumentos: %d\n, argc ); for ( i = 0; i < argc ; i++) printf ( Argumento %d: %s\n, i, argv[ i ]); exit (0); } Este programa ir imprimir a quantidade de argumentos recebidos, e em seguida ir mostrar a a cada argumento recebido como par metro. E normal que os programas realizem a consist ncia a e dos par metros recebidos. Dessa forma, e comum encontrar o seguinte tipo de c digo: a o

1 2 3

#include int

18

CAPITULO 3. EXPLORANDO OS RECURSOS BASICOS DO SISTEMA OPERACIONAL194 5 6 7 8 9 10

main ( int argc , char argv) { if ( argc != 3) { fprintf ( stderr , Sintaxe: %s valor1 valor2 \n , argv [0]); exit (1); } } O padr o POSIX.2 prov uma funcao para tratar argumentos de entrada, o getopt(3), a e denida pelo seguinte prot tipo: o #include int getopt ( int argc , char const argv [], const char optstring ); extern char optarg ; extern int optind , opterr , optopt ; A funcao getopt(3) analisa os argumentos da linha de comando. Seus argumentos argc e argv s o, respectivamente, a contagem de argumentos e o array de strings passados para a funcao a main() na invocacao do programa. Um elemento de argv que inicia com - (e n o e exatamente - ou ) e um elemento de a opcao. Os caracteres deste elemento (fora o - inicial) s o caracteres de opcao. Se getopt(3) a e chamado repetidamente, ele retorna sucessivamente cada um dos caracteres de opcao de cada um dos elementos de opcao. Se a funcao getopt(3) encontra um outro caractere de opcao, ela retorna este caractere, atualizando a vari vel externa optind e uma vari vel est tica nextchar de forma que a pr xima a a a o chamada a getopt(3) pode continuar a busca com o caractere de opcao seguinte ou um ele mento de argv. Se n o h mais caracteres de opcao, getopt(3) retorna 1. Ent o optind e o ndice em a a a argv do primeiro elemento de argv que n o e uma opcao. a optstring e uma string contendo os caracteres de opcao legtimos. Se tal caractere e seguido por dois-pontos, a opcao requer um argumento, ent o getopt atribui um ponteiro para o texto a que segue no mesmo elemento de argv, ou para o texto do elemento seguinte de argv, em optarg. Dois dois-pontos signicam que uma opcao recebe um argumento opcional; se h texto no a elemento de argv atual, ele e retornado em optarg, caso contr rio optarg e zerado. Esta e uma ex a tens o GNU. Se optstring cont m W seguido de um ponto-e-vrgula, ent o -W foo e tratado como a e a a opcao longa foo. (A opcao -W e reservada pelo POSIX.2 para extens es de implementacao.) o Este comportamento e uma extens o GNU, n o disponvel em bibliotecas anteriores a GNU libc a a ` 2. A funcao getopt long(3) funciona como getopt(3), exceto que ele tamb m aceita e opcoes longas, iniciadas por dois tracos. Nomes de opcoes longas podem ser abreviados se a abreviacao for unica ou se iguala exatamente a alguma opcao denida. Uma opcao longa pode ` requerir um par metro da forma arg=param ou arg param. O seguinte prot tipo a dene: a o #dene GNU SOURCE #include int getopt long ( int argc , char const argv [], const char optstring , const struct option longopts , int longindex );

Neste caso, longopts e um ponteiro para o primeiro elemento de um array de struct option, declarado em como: struct option { const char name; int has arg ; int ag ; int val ; };

CAPITULO 3. EXPLORANDO OS RECURSOS BASICOS DO SISTEMA OPERACIONAL20O signicado destes campos s o: a has arg: no argument (ou 0) se a opcao n o recebe um argumento, required argument a (ou 1) se a opcao requer um argumento, ou optional argument (ou 2) se a opcao recebe um argumento opcional; ag: especica como os resultados s o retornados para uma opcao longa. Se ag e NULL, a a ent o getopt long(3) retorna val. Caso contr rio, getopt long(3) retorna 0, e a ag aponta para uma vari vel, a qual e ajustada para val se a opcao e encontrada, ou a deixada intocada se a opcao n o e encontrada; a O ultimo elemento do array deve ser preenchido com zeros. val: e o valor a retornar, ou a carregar para a vari vel apontada por ag. a nome: e o nome da opcao longa;

O seguinte exemplo ilustra o uso de getopt long(3) com a maioria de seus recursos:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39

#include #include #dene GNU SOURCE #include static struct option long options [] = { {add , 1, 0, 0}, {append, 0, 0, 0}, { delete , 1, 0, 0}, {verbose , 0, 0, 0}, { create , 1, 0, c }, { le , 1, 0, 0}, {0, 0, 0, 0} }; int main ( int argc , char argv) { int c; while (1) { int option index = 0; c = getopt long ( argc , argv , abc:d: , long options , & option index ); if ( c == 1) break; switch ( c ) { case 0: printf (opcao %s, long options [ option index ]. name); if ( optarg ) printf ( com argumento %s, optarg ); printf ( \n); break; case a : printf ( opcao a\n); break; case b :

CAPITULO 3. EXPLORANDO OS RECURSOS BASICOS DO SISTEMA OPERACIONAL2140 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61

printf break; case c : printf break; case d : printf break; case ? : break; default : printf } }

( opcao b\n); ( opcao c com valor %s\n , optarg ); ( opcao d com valor %s\n , optarg );

( erro : getopt retornou %d\n, c );

if ( optind < argc ) { printf ( parametros nao referentes a opcoes : ); while ( optind < argc ) printf ( \t%s\n, argv[ optind ++]); } exit (0); }

3.2 Trabalhando com arquivosArquivos em C podem ser manipulados com o uso de descritores de arquivos, representados por objetos do tipo int, ou streams, representados por objetos do tipo FILE *. Descritores de arquivos prov em uma interface primitiva e de baixo-nvel para operacoes de e entrada e sada. Tanto descritores de arquivos como streams podem representar uma conex o a a um dispositivo (como um terminal), ou um pipe ou socket para comunicar com outro processo, u ainda para representar um arquivo normal. Os descritores de arquivos s o usados quando e a necess rio realizar operacoes de controle sobre algum dispositivo especco, ou ainda quando a precisam realizar I/O em modos especiais, como n o-bloqueante, dado que streams n o oferecem a a recursos de baixo nvel. A maior vantagem de utilizar a interface de streams e que o conjunto de funcoes para realizar operacoes de I/O, ao oposto de operacoes de controle, e muito mais rico e que as facilidades cor respondentes para os descritores de arquivos. A interface de descritores de arquivos prov apenas e operacoes simples para transfer ncia de blocos de caracteres, enquanto a interface de streams e prov funcoes poderosas para formatacao de funcoes de I/O, bem como funcoes orientadas a e caracter e linhas. E importante ressaltar que streams s o mais port veis que descritores de arquivos, pois nem a a todas as operacoes sobre descritores de arquivos est o implementadas em outros sistemas opera a cionais sobretudo o conjunto de operacoes GNU, apesar de que grande parte delas encontram se no padr o POSIX 1.1 a

3.3 StreamsQuando a funcao main do programa e invocada, ela j disponibiliza tr s streams abertas e a e prontas para o uso. Elas representam os canais standard de entrada e sada que foram esta belecidos para o processo, e est o declaradas no arquivo header stdio.h: a FILE *stdin: e a stream de entrada padr o, que e a origem normal de entrada de dados a em um programa;

CAPITULO 3. EXPLORANDO OS RECURSOS BASICOS DO SISTEMA OPERACIONAL22 FILE *stout: e a stream padr o de sada, que e usada para imprimir os dados de sada do a programa; FILE *stderr: da mesma forma que a stream stdout, por m para imprimir mensagens de e erro e de diagn stico pelo programa. o Algumas das operacoes mais comuns de serem realizadas sobre streams s o: a FILE *fopen (const char *path, const char *mode); abre o arquivo apontado pela string path e retorna uma stream associada a ele. Os seguintes modos s o utilizados para abrir o arquivo: a r: abre um arquivo texto para leitura, posicionando a stream no incio do arquivo. r+: abre um arquivo texto para leitura e escrita, posicionando a stream no incio do arquivo. w: trunca o arquivo para zero bytes e cria o arquivo texto para escrita. A stream e posicionada no incio do arquivo. w+: abre o arquivo para leitura e escrita. O arquivo e criado caso ele n o exista, ou a truncado caso contr rio. A stream e posicionada no incio do arquivo. a a: abre o arquivo para append (escrita no nal do arquivo). Ele e criado caso ele n o exista, e a stream e posicionada no nal do arquivo. a a+: abre o arquivo para leitura e append. Ele e criado caso ele n o exista, e a stream a e posicionada no nal do arquivo. A string modo pode ainda incluir a letra b ap s indicar o modo desejado para explicitar o que o arquivo a ser trabalhado ser bin rio, e n o texto. Apesar de n o ter efeito em um a a a a programa que ir operar no GNU/Linux, ela e muito importante caso o programa ir rodar a a em algum ambiente n o-UNIX. a int fclose (FILE *stream); desassocia a stream stream do arquivo ao qual ela estava operando. Caso a stream estivesse sendo usada para dados de sada, os dados em buffer s o escritos antes, com o a uso da operacao fflush(3). int fflush (FILE *stream); forca a escrita de todos os dados em espaco de usu rio que estejam mantidos em buffer a para a sada informada ou atualizam a stream stream atrav s da funcao write associada e a ela. int feof (FILE *stream); testa o indicador de m-de-arquivo para a stream apontada por stream, retornando um valor n o-zero caso este indicador esteja setado, ou 0 caso ainda n o seja m de arquivo. a a int fileno (FILE *stream); examina o argumento stream e retorna um inteiro relativo ao seu descritor de arquivo. int fputs (const char *s, FILE *stream); escreve a string s para stream, sem o caracter 0 de nal de string. char *fgets (char *s, int size, FILE *stream); l size caracteres a partir de stream e os armazena no buffer apontado por s, que j deve e a estar pr -alocado. A leitura encerra ap s encontrar EOF ou uma nova linha. Se um e o caracter de nova linha for lida, ele e armazenada no buffer. Um caracter 0 e armazenado ap s o ultimo caracter do buffer. o size t fread(void *ptr, size t size, size t nmemb, FILE *stream); l nmemb elementos de dados, cada um tendo o tamanho de size bytes, a partir da stream e apontada por stream, e armazenando-os no local apontado por ptr. Esta operacao costuma ser utilizada para operacoes em arquivos bin rios. fread retorna o n mero de itens lidos a u com sucesso.

CAPITULO 3. EXPLORANDO OS RECURSOS BASICOS DO SISTEMA OPERACIONAL23 size t fwrite (const void *ptr, size t size, size t nmemb, FILE *stream); escreve nmemb elementos de dados, cada um tendo o tamanho de size bytes, para a stream apontada por stream, obtidos a partir do local apontado por ptr. Esta operacao costuma ser utilizada para operacoes em arquivos bin rios. fwrite retorna o n mero de itens a u escritos com sucesso. int fseek (FILE *stream, long offset, int whence); posiciona o indicador de posicao de arquivo para a stream apontada por stream. A nova posicao, medida em bytes, e obtida atrav s da soma de offset bytes para a posicao es e pecicada por whence. Caso whence seja denido como SEEK SET, SEEK CUR ou SEEK END, o offset e relativo ao incio do arquivo, a posicao corrente do indicador ou ` ao nal do arquivo, respectivamente. long ftell (FILE *stream); obt m o valor atual do indicador de posicao de arquivo para a stream apontada por stream. e void rewind (FILE *stream); aponta o indicador de posicao de arquivo para a stream apontada por stream para o incio do arquivo. Ela e equivalente a executar a funcao fseek (stream, 0L, SEEK SET). `

O programa a seguir ilustra o uso destas operacoes: ele abre um arquivo para leitura e um outro para escrita, escrevendo apenas as linhas pares para o segundo.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

#include #include int copia arquivo ( char origem , char destino ) { FILE stream origem, stream destino ; char buffer [1024]; int i ; / abre arquivos de origem e destino , criando este ultimo / stream origem = fopen ( origem , r ); if (! stream origem ) { perror ( fopen ); return 1; } stream destino = fopen ( destino , w); if (! stream destino ) { perror ( fopen ); fclose ( stream origem ); return 1; } / processa linhas do arquivo origem / i = 1; while (! feof ( stream origem )) { fgets ( buffer , sizeof ( buffer ), stream origem ); if ( i % 2 == 0) / numero par / fputs ( buffer , stream destino );

CAPITULO 3. EXPLORANDO OS RECURSOS BASICOS DO SISTEMA OPERACIONAL2432 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58

i++; } / fecha as streams e retorna / fclose ( stream origem ); fclose ( stream destino ); return 0; } int main ( int argc , char argv) { int res ; if ( argc != 3) { printf ( Sintaxe: %s \n, argv [0]); exit (1); } res = copia arquivo ( argv [1], argv [2]); if ( res < 0) { fprintf ( stderr , Um erro ocorreu durante a copia\n); exit (1); } exit (0); }

3.4 Descritores de ArquivosAssim como quando operando sobre streams, quando utilizando descritores de arquivos temos os tr s canais de entrada e sada abertos automaticamente (stdin, stdout e stderr, ou 0, 1 e 2, e respectivamente). Um conceito importante e o de que em sistemas operacionais UNIX, tudo e um arquivo. Ou seja, dispositivos de rede, de terminais, de som, vdeo, diret rios e os pr prios arquivos de dados o o do usu rio s o representados por arquivos. Desta forma e possvel manipular todos os disposia a tivos do sistema com o uso de operacoes sobre arquivos: por isto as operacoes sobre descritores de arquivos s o t o importantes. a a As operacoes b sicas sobre descritores de arquivos s o: a a int open(const char *pathname, int flags); abre o arquivo especicado por pathname, retornando um descritor de arquivo representado por um inteiro. As ags devem ser O RDONLY, O WRONLY ou O RDWR, que requisitam a abertura do arquivo no modo somente leitura, somente escrita, ou leitura e escrita, respectivamente. Estes modos podem ser combinados via bitwise OR com zero ou mais opcoes, dentre as quais est o: a O CREAT: se o arquivo n o existe, ele ser criado. Neste caso, o prot tipo da a a o funcao open listado abaixo ser utilizado. a O EXCL: quando usado em conjunto com O CREAT e o arquivo j existir, a funcao a retornar um erro. a O TRUNC: caso o arquivo esteja sendo aberto em modo de escrita em conjunto com este modo, o arquivo ser truncado para 0 bytes. a

CAPITULO 3. EXPLORANDO OS RECURSOS BASICOS DO SISTEMA OPERACIONAL25 O APPEND: abre o arquivo no modo append, para adicionar novos dados ao nal do arquivo. O NOFOLLOW: caso pathname seja um link simb lico, a chamada e interrompida o e retorna um erro. O DIRECTORY: caso pathname n o seja um diret rio, a chamada retorna um erro. a o int open(const char *pathname, int flags, mode t mode); caso o modo O CREAT seja passado como ag para a funcao open, este prot tipo dever o a ser utilizado, para explicitar o modo no qual o arquivo ser aberto. Maiores informacoes a sobre os par metros podem ser consultados na p gina manual do open(2). a a int creat(const char *pathname, mode t mode); esta funcao e equivalente a chamada open com as ags iguais a O CREAT | O WRONLY ` | O TRUNC.

int close(int fd); fecha o descritor de arquivo indicado por fd.

Algumas outras operacoes costumam ser realizadas sobre descritores de arquivos. Podemos destacar: ssize t read(int fd, void *buf, size t count); l at count bytes a partir da posicao corrente do arquivo representado pelo descritor fd e e para o buffer apontado por buf. O valor retornado informa quantos bytes foram lidos com sucesso, ou -1 em caso de erro. ssize t write(int fd, const void *buf, size t count); escreve at count bytes no arquivo representado pelo descritor fd a partir do buffer apone tado por buf. O valor retornado informa quantos bytes foram escritos com sucesso, ou -1 em caso de erro. off t lseek(int fildes, off t offset, int whence); opera da mesma forma que o fseek(3), por m sobre descritores de arquivos. ldes e refere-se ao descritor de arquivo que ser utilizado na operacao de seek, offset informa a quantos bytes ser o movimentados na operacao e whence informa a posicao relativa a a ` qual ser feito o seek. a int fstat(int filedes, struct stat *buf); retorna informacoes sobre o arquivo representado pelo descritor ledes, armazenando-as no endereco da struct stat apontada por buf. Entre as informacoes retornadas, est o a a quantidade de hard links feitas para este arquivo, o seu inode number, a data de ultima alteracao e de ultimo acesso, entre outros. O exemplo abaixo lida com um arquivo bin rio atrav s de descritores de arquivos. Neste a e exemplo, ele adiciona um novo campo do mesmo tipo de estrutura da qual ele e composto ap s o seu ultimo byte de dados, e em seguida l e imprime o seu conte do. e u1 2 3 4 5 6 7 8 9 10 11 12 13

#include #include #include < stdlib . h> #include #include #include struct informacao { int quantidade ; oat valor ; char nome[128]; };

/ / / /

atoi (3) open (2) open (2) open (2)

/ / / /

CAPITULO 3. EXPLORANDO OS RECURSOS BASICOS DO SISTEMA OPERACIONAL2614 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67

int cria arquivo ( char nome) { int fd ; fd = open (nome, O RDWR | O CREAT | O TRUNC, S IRWXU | S IRWXG | S IRWXO); if ( fd < 0) { perror ( open); exit (1); } return fd ; } void le arquivo ( int fd ) { size t n; struct informacao info ; lseek ( fd , 0, SEEK SET); while (1) { n = read ( fd, & info , sizeof ( info )); if ( n == 0) { / m de arquivo / break; } printf ( nome: %s\nvalor: %f\nquantidade: %d\n\n, info .nome, info . valor , info . quantidade ); } }

void adiciona entrada ( int fd , char numero, char arquivo ) { ssize t n; int num; struct informacao info ; num = atoi ( numero); info . quantidade = 10 num; info . valor = 123.45 ( oat ) num; snprintf ( info .nome, sizeof ( info .nome), arquivo %s , arquivo ); lseek ( fd , 0, SEEK END); n = write ( fd, & info , sizeof ( info )); if ( n < 0) perror ( write ); } int main ( int argc , char argv) {

CAPITULO 3. EXPLORANDO OS RECURSOS BASICOS DO SISTEMA OPERACIONAL2768 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87

int fd ; if ( argc != 3) { fprintf ( stderr , Sintaxe: %s \n, argv[0]); exit (1); } fd = open ( argv [1], O RDWR); if ( fd < 0) { perror ( open); printf ( tentando criar arquivo ...\ n\n); fd = cria arquivo ( argv [1]); } adiciona entrada ( fd , argv [2], argv [1]); le arquivo ( fd ); close ( fd ); exit (0); }

Captulo 4

CGI: Common Gateway InterfaceO CGI dene uma maneira de interacao entre o web browser e programas externos de geracao de conte do, normalmente referidos como programas CGI ou scripts CGI. u Existem duas principais diferencas entre criar um programa convencional e um CGI. Primeira mente, toda a sada do programa CGI deve ser precedida por um header do tipo MIME. Isto e um cabecalho HTTP que informa ao client o tipo de conte do que ele est recebendo. Na maior u a parte do tempo, o conte do ser : u a Content-type: text/html Em segundo lugar, toda a sada precisa estar formatada em HTML ou em algum outro for mato que o browser ser capaz de mostrar. Na maior parte do tempo isto e HTML, mas ocasioa nalmente poder ser necess rio escrever algum programa que gere a sada de uma imagem GIF a a ou algum outro conte do n o-HTML. u a Fora estes dois detalhes, um programa em CGI parece-se com um programa convencional qualquer. O exemplo abaixo lista o conte do de um arquivo texto: u1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

#include #include #dene ARQUIVO /tmp/teste.txt int main ( int argc , char argv) { char buffer [256]; FILE fd; printf (Contenttype: text /html\r\r\n\n); printf ( Conteudo do arquivo %s \n, ARQUIVO); fd = fopen ( ARQUIVO, r); if (! fd ) { printf ( ERRO : arquivo %s ano existe\n, ARQUIVO); printf (\n); exit (1);

28

CAPITULO 4. CGI: COMMON GATEWAY INTERFACE22 23 24 25 26 27 28 29 30 31 32 33

29

} while (! feof ( fd )) { fgets ( buffer , sizeof ( buffer ), fd ); printf ( %s
\n, buffer); } fclose ( fd ); printf (\n); exit (0); } Ap s a compilacao do programa, basta copi -lo para o diret rio cgi-bin do Apache, ou o a o do httpd em uso, e acess -lo via browser, como no exemplo abaixo: a http://localhost/cgi-bin/teste No entanto, e bastante comum o uso de alguma biblioteca para facilitar a formatacao dos dados em HTML, para evitar erros e agilizar o desenvolvimento do programa CGI. Existem v rias bibliotecas disponveis cadastradas no site da Freshmeat1 . a

1 ver

http://www.freshmeat.net

Captulo 5

Bibliotecas din micas aO objetivo de bibliotecas din micas e a centralizacao de implementacao de funcoes em bibliotea cas especcas, permitindo que o gerenciamento e atualizacao das mesmas se torne propag vel a a todos os programas que usam suas funcoes de forma trivial. No GNU/Linux, existe o conceito de shared objects, que e an logo ao conceito de DLL do a Microsoft Windows. Para utilizar bibliotecas din micas no GNU/Linux, se usam as funcoes dlopen(3) e a dlclose(3). Para carregar os smbolos das funcoes a serem utilizadas a funcao dlsym(3) deve ser utilizada. Um exemplo do uso de bibliotecas din micas pode ser visto a seguir: a1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

#include #include int main ( int argc , char argv) { void biblioteca ; double (seno )(double); char erro ; biblioteca = dlopen ( libm.so , RTLD LAZY); if (! biblioteca ) { fprintf ( stderr , %s\n, dlerror ()); exit (1); } seno = dlsym ( biblioteca , simbolo nao existe ); if (( erro = dlerror ()) != NULL) { fprintf ( stderr , %s\n, erro ); exit (1); } printf ( %f\n, (seno )(2.0)); dlclose ( biblioteca ); exit (0); } Existem basicamente 2 modos para resolucao dos smbolos disponibilizados por uma bi a blioteca: RTLD LAZY, que signica que os ponteiros para funcoes n o resolvidos em tempo de

30

CAPITULO 5. BIBLIOTECAS DINAMICAS

31

compilacao devem ser apenas resolvidos pela biblioteca din mica em tempo de execucao, ou a RTLD NOW, que signica que todos os smbolos da biblioteca din mica devem ser resolvidos na a abertura da mesma, sendo que a abertura da biblioteca dever falhar caso algum smbolo n o a a possa ser denido.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

#include #include int main ( int argc , char argv) { void biblioteca ; double (seno )(double); char erro ; biblioteca = dlopen ( libm.so , RTLD NOW); if (! biblioteca ) { fprintf ( stderr , %s\n, dlerror ()); exit (1); } seno = dlsym ( biblioteca , sin ); if (( erro = dlerror ()) != NULL) { fprintf ( stderr , %s\n, erro ); exit (1); } printf ( %f\n, (seno )(2.0)); dlclose ( biblioteca ); exit (0); } Para usar as funcoes de bibliotecas din micas a biblioteca dl deve ser especicada em tempo a linkagem, tal como: $ gcc dl-now.c -o dl-now -ldl

5.1 Criacao de bibliotecas din micas aPara criar bibliotecas din micas no GNU/Linux o processo e simples: basta compilar o c digo a o com a ag -shared, gerando a biblioteca. Um exemplo de uma biblioteca simples que exporta apenas uma funcao pode ser visto a seguir:1 2 3 4 5 6 7

#include int mundo (void) { printf ( Bemvindo ao GNU/Linux\n); } Para compilar a biblioteca, basta: $ gcc minha_biblioteca -o libminha_biblioteca.so -shared

Captulo 6

Processos e threadsExistem duas maneiras de criar novos uxos de processos no GNU/Linux. Uma delas e atrav s e da chamada de sistema fork(2), e a outra atrav s da criacao de threads, que podem ser vistas e como processos leves [STE 98]

6.1 Processos E possvel criar processos atrav s da chamada de sistema fork(2). A chamada de sistema n o e a recebe nada como par metro, retorna os seguintes valores: a >0: Sinalizando que o processo que est executando no momento e o do pai; a Um exemplo de criacao de um sub-processo pode ser visto no exemplo abaixo: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

0: Sinalizando que o processo que est executando no momento e o do lho criado; a #include < string . h> #include #include int main ( int argc , char argv) { int status ; pid t pid ; pid = fork (); if ( pid == 0) { char args ; printf ( Processo lho executando \ ls a\\n); args = ( char ) malloc ( sizeof ( char ) 3); args [0] = strdup ( ls ); args [1] = strdup ( a); args [2] = NULL;

32

CAPITULO 6. PROCESSOS E THREADS24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

33

execvp ( args [0], args ); free ( args [0]); free ( args [1]); free ( args ); } else if ( pid > 0) { printf ( Processo pai aguardando processo lho ..\ n ); waitpid ( pid, & status , WUNTRACED); if ( WIFEXITED(status)) printf ( processo lho executou normalmente\n); else printf ( processo lho nao executou normalmente\n); } else if ( pid < 0) { / algum erro ocorreu / perror ( fork ); } exit (0); } A funcao execvp(3) e utilizada para trocar a imagem do processo corrente pela imagem de um novo processo. O primeiro argumento para ela e o pathname para um programa, e o segundo argumento e um array terminado por NULL, descrevendo os argumentos que este pro grama ir receber. O conjunto do uso do fork(3) com o execvp(3) e waitpid(3) aprea senta uma sem ntica semelhante a funcao spawnl() do Microsoft Windows, com a excess o a ` a de que a spawnl() j explicita em seus argumentos se o processo pai deve aguardar pelo seu a t rmino de execucao ou n o. e a Outra funcao utilizada na listagem do programa acima e a waitpid(3), que aguarda pelo t rmino da execucao de algum processo. Os par metros enviados a ela s o o pid do processo a ser e a a aguardado, um ponteiro para um inteiro, onde ser o armazenas informacoes sobre a execucao a do processo (como sinais que foram entregues a ele para que ele fosse encerrado e valor de retorno da execucao do processo), e uma constante chamada WUNTRACED, que signica que ela deve parar de aguardar pelo lho que j teve sua execucao parada e se encontra em um status a desconhecido.

6.2 ThreadsThreads t m o mesmo prop sito de um processo: gerar um novo uxo de execucao. No entanto, e o elas s o mais adequadas para uso em programas que precisam disparar muitos processos lhos, a devido ao fato de elas implementarem o conceito de processos leves. Todas as threads em um processo compartilham o estado deste processo. Elas residem no mesmo enderecamento de mem ria, v em as mesmas funcoes e os mesmos dados. As maiores o e vantagens de utilizar threads em programas s o: a Aumento da responsividade do programa; Uso eciente dos recursos do sistema; Ganhos de performance em hardware multiprocessado;

Mudanca de m todos de comunicacao entre os processos; e Apenas um bin rio executa bem tanto em arquiteturas SMP (Symmetric Multi Processors) a quanto UP (Uni Processor); Possibilidade de criar programas bem estruturados;

CAPITULO 6. PROCESSOS E THREADS Apenas um unico arquivo fonte pode ser usado em m ltiplas plataformas. u

34

Uma thread consiste de uma stack e um stack pointer, um program counter e algumas informacoes a respeito de sua inst ncia, como prioridades de escalonamento e m scaras de sinal, a a armazenadas na sua estrutura. Al m disso, os registradores da CPU s o tamb m armazenados e a e (sendo que a stack pointer e o program counter s o alguns destes registradores). a O Linux utiliza, at a s rie 2.4, o modelo de threads POSIX, aprovado pelo padr o IEEE e e a 1003.1b-1993 [Ame 94]. Para dar suporte a este modelo, as distribuicoes GNU/Linux fornecem a biblioteca LinuxThreads, que implementa grande parte das exig ncias feitas por este padr o. e a A s rie 2.6 do kernel Linux est fazendo uso de um outro modelo de threads, chamado NPTL e a (Native POSIX Threading Library) [The 2003]. Este texto baseia-se na implementacao das Lin uxThreads. Em relacao as threads Win32, as threads POSIX s o muito mais leves, contendo primi ` a tivas mais simples de uso. Uma outra caracterstica e a de que threads Win32 mant m uma e depend ncia entre janelas e threads. Como nem todas threads constroem e usam janelas, isto e acarreta em um overhead desnecess rio para os uxos de execucao. a Para criar um novo uxo de execucao com o uso de threads no GNU/Linux, utiliza-se a funcao pthread create(3), denida pelo seguinte prot tipo: o #include int pthread create ( pthread t thread , pthread attr t attr , void ( start routine )( void ), void arg ); pthread create(3) cria uma nova thread de controle que executa concorrentemente com a thread que a gerou. A nova thread aplica a funcao start routine, passando arg como seu argumento. A nova thread termina explicitamente, chamando a funcao pthread exit(3), ou implicitamente, apenas retornando da funcao start routine. O ultimo caso e equivalente a chamar pthread exit(3) com o resultado retornado pela funcao start routine como retorno. O argumento attr especica algum atributo a ser aplicado para a nova thread, ou pode ser NULL caso os atributos padr o devam ser utilizados: a thread criada e joinable, ou seja, seus a recursos de mem ria n o ser o liberados at que algum processo sincronize com esta thread, e o a a e tem uma poltica de escalonamento normal (n o-realtime). Os atributos a serem passados para a a thread devem ser inicializados com a funcao pthread attr init(3), e ap s congurados o com o uso de alguma das funcoes listadas na p gina manual pthread attr init(3). a Para sincronizar um processo (ou uma thread) com o resultado de uma outra thread, utiliza-se a funcao pthread join(3): #include int pthread join ( pthread t th , void thread return ); pthread join(3) suspende a execucao da thread corrente at que a thread identicada e por th termine, tanto explicitamente, via pthread exit(3), ou sendo cancelada, atrav s da e funcao pthread cancel(3). Caso o argumento thread return seja n o nulo, ele e utilizado a para armazenar o valor de retorno da thread th, devendo ser desalocado ap s ter sido utilizado o pelo programa. O exemplo abaixo ilustra a criacao e sincronizacao de uma thread no GNU/Linux. Para com pilar este programa, e necess rio passar a ag -lpthread, para que o programa seja linkado a com a biblioteca de threads libpthread.so e possa fazer uso de suas funcoes. 1 2 3 4 5 6 7 8

#include #include < stdlib . h> #include #include void calcula ( void dados) {

CAPITULO 6. PROCESSOS E THREADS9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

35

int ret = NULL; int valor = ( int ) dados; ret = ( int ) malloc ( sizeof ( int )); ret = valor valor ; pthread exit ( ret ); } int main ( int argc , char argv) { pthread t tid ; int dados , resultado ; dados = ( int ) malloc ( sizeof ( int )); dados = 5; pthread create (& tid , NULL, calcula , dados ); pthread join ( tid , ( void ) & resultado ); printf ( %d %d = %d\n, dados, dados, resultado ); free ( dados ); free ( resultado ); exit (0); } Como threads podem compartilhar recursos entre si, n o e necess rio fazer uso de mecanisa a mos pesados de intercomunicacao entre processos. A forma mais comum de compartilhar dados entre diferentes threads e atrav s de vari veis globais a elas, e mantendo a coer ncia das leituras e a e e escritas destes dados com o uso de mecanismos de exclus o m tua (mutexes) e sem foros. a u a O uso de mutexes e feito basicamente atrav s das primitivas pthread mutex lock(3) e e pthread mutex unlock(3). Na primeira primitiva, caso alguma outra thread j tenha a adquirido o lock, a thread que tentou peg -lo pela segunda vez tem sua execucao interrompa ida at que a outra thread o libere. Caso n o desej va-se que a execucao seja interrompida, e a a pthread mutex trylock(3) pode ser usado: caso o lock n o esteja disponvel ela cona tinua a execucao do programa. O exemplo abaixo mostra como compartilhar um dado entre duas threads de execucao, uti lizando mecanismos de exclus o m tua: a u

1 2 3 4 5 6 7 8 9 10 11 12 13 14

#include #include < stdlib . h> #include #include static int valor = 0; pthread mutex t mutex = PTHREAD MUTEX INITIALIZER; void incrementa ( void dados) { int i ; for ( i = 0; i < 10000; i++) {

CAPITULO 6. PROCESSOS E THREADS15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48

36

pthread mutex lock (&mutex); valor ++; pthread mutex unlock (&mutex); } pthread exit ( NULL); } void decrementa ( void dados) { int i ; for ( i = 0; i < 10000; i++) { pthread mutex lock (&mutex); valor ; pthread mutex unlock (&mutex); } pthread exit ( NULL); } int main ( int argc , char argv) { pthread t tid , tid2 ; pthread pthread pthread pthread create (& tid , NULL, incrementa, NULL); create (& tid2 , NULL, decrementa, NULL); join ( tid , NULL); join ( tid2 , NULL);

printf ( valor da variavel global : %d\n , valor ); exit (0); } Sem foros tamb m tem um uso bastante comuns. Eles funcionam como contadores para a e recursos compartilhados entre threads. As operacoes b sicas realizadas em sem foros s o incre a a a mentar o contador atomicamente e aguardar at que o contador seja n o nulo e decrement -lo e a a atomicamente. Nestes casos, podem ser usadas as funcoes sem init(3), sem wait(3), sem post(3) e sem destroy(3): #include int sem init (sem t sem, int pshared , unsigned int value ); int sem wait(sem t sem); int sem trywait (sem t sem); int sem post(sem t sem); int sem destroy (sem t sem);

sem init(3) inicializa o sem foro apontado por sem com o valor especicado em value. a Este valor indica quantas threads poder o entrar na sess o crtica ao mesmo tempo. Nos casos a a mais comuns s o utilizados sem foros bin rios, e ent o este valor e iniciado com 1. O argumento a a a a pshared indica se o sem foro e local ao processo atual (neste caso pshared deve ser zero) ou se a ele n o e compartilhado entre v rios processos (neste caso pshared deve ser n o zero). No a a a entanto, a implementacao das LinuxThreads n o implementa o compartilhamento de sem foros a a entre v rios processos, retornando um erro na inicializacao do sem foro caso pshared seja um a a valor diferente de zero.

CAPITULO 6. PROCESSOS E THREADSO exemplo abaixo apresenta o uso de sem foros para controle de uma sess o crtica: a a 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52

37

#include #include < stdlib . h> #include #include #include static int valor = 0; sem t semaforo; void incrementa ( void dados) { int i ; for ( i = 0; i < 10000; i++) { sem wait (&semaforo); valor ++; sem post (&semaforo); } pthread exit ( NULL); } void decrementa ( void dados) { int i ; for ( i = 0; i < 10000; i++) { sem wait (&semaforo); valor ; sem post (&semaforo); } pthread exit ( NULL); } int main ( int argc , char argv) { pthread t tid , tid2 ; sem init (&semaforo , 0, 1); pthread pthread pthread pthread create (& tid , NULL, incrementa, NULL); create (& tid2 , NULL, decrementa, NULL); join ( tid , NULL); join ( tid2 , NULL);

printf ( valor da variavel global : %d\n , valor ); sem destroy (&semaforo); exit (0); }

Captulo 7

TCP/IPO conjunto de funcoes disponveis para serem utilizadas na comunicacao atrav s do protocolo e TCP/IP e muito grande. Este captulo visa apresentar a utilizacao de sockets no GNU/Linux para comunicacao entre cliente e servidor, nos modos bloqueante e n o-bloqueante no IPv4 [STE 98a]. a Provavelmente a maneira mais simples de explicar como programar sockets neste ambiente e com o uso de um exemplo comentado. Abaixo encontra-se listada uma implementacao de um cliente TCP de uma aplicacao do tipo timeofday. Este cliente estabelece uma conex o TCP com a um servidor, e o servidor apenas envia de volta a hora e data atuais, em um formato formatado e legvel. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

#include timeofday .h int main ( int argc , char argv) { int sockfd , n; char recvline [MAXLINE + 1]; struct sockaddr in servaddr ; if ( argc != 2) { fprintf ( stderr , Sintaxe: %s \n, argv [0]); exit (1); } if (( sockfd = socket ( AF INET, SOCK STREAM, 0)) < 0) err quit ( socket ); memset (&servaddr , 0, sizeof ( servaddr )); servaddr . sin family = AF INET; servaddr . sin port = htons (13); / servidor de daytime / if ( inet pton ( AF INET, argv[1], &servaddr . sin addr ) 0) { recvline [n ] = 0; / terminada por NULL /

38

CAPITULO 7. TCP/IP30 31 32 33 34 35 36 37 38

39

if ( fputs ( recvline , stdout ) == EOF) err quit ( fputs ); } if ( n < 0) err quit ( read ); exit (0); } Este exemplo, quando compilado, deve utilizar a ag -lnsl, para ser linkado com as funcoes de sockets da Glibc. Existem v rios detalhes a serem considerados neste programa: a 1: o cabecalho em comum com a aplicacao do servidor. Os detalhes de seu conte do s o u a mostrados mais a seguir; 15: criacao de um socket TCP. A funcao socket cria uma stream (SOCK STREAM) de Internet (AF INET), que e o nome dado para um socket TCP. Esta funcao retorna o descritor de um socket, utilizado para realizar as chamadas de funcoes futuras, como as chamadas ao connect e ao read; 18 23: especicacao do endereco IP de um servidor e uma porta. Uma estrutura do tipo Internet socket address (a sockaddr in sockaddr) e preenchida com o endereco IP do servidor e com o n mero da porta. A estrutura e zerada com o uso da funcao memset, u congurada para usar a famlia de enderecos AF INET e para usar a porta 13 (que e a porta conhecida para o servidor de daytime em qualquer host TCP/IP que suporte este servico). O IP fornecido e a porta nesta estrutura precisam estar em formatos especcos: para isto, a funcao htons (host to network short) para converter o n mero da porta, e e u utilizada a funcao inet pton (presentation to numeric) para converter o endereco IP em ASCII para o formato necess rio; a 25 26: estabelecimento da conex o com o servidor. A funcao connect, quando aplicada a a um socket TCP, estabelece uma conex o TCP com o servidor especicado pela estrutura a socket address apontada pelo segundo argumento; 28 35: l e mostra o reply do servidor. Como o TCP e um protocolo byte-stream, e ele n o tem registro sobre limites do conte do sendo transportado, ou seja: os bytes a u retornados podem vir de uma unica vez, ou em n pequenas leituras, at que os ultimos e bytes sejam lidos. Desta forma, nem sempre e garantido que com um unico read venha toda a resposta do servidor. Assim, sempre que estivermos lendo o conte do de um socket u TCP, e necess rio faz -lo em um loop, terminando-o quando read retornar 0 (o outro lado a e encerrou a conex o) ou um valor menor do que 0 (quando ocorre um erro). a Como o programa anterior n o pode executar corretamente sem um servidor, e necess rio a a escrever um. A vers o do servidor para este cliente encontra-se listado logo abaixo. a

1 2 3 4 5 6 7 8 9 10 11 12

#include timeofday .h int main ( int argc , char argv) { int listenfd , connfd; struct sockaddr in servaddr ; char buff [MAXLINE]; time t ticks ; if (( listenfd = socket ( AF INET, SOCK STREAM, 0)) < 0) err quit ( socket );

CAPITULO 7. TCP/IP13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

40

memset (&servaddr , 0, sizeof ( servaddr )); servaddr . sin family = AF INET; servaddr . sin addr . s addr = htonl ( INADDR ANY); servaddr . sin port = htons (13); / servidor de daytime / if (( bind ( listenfd , ( struct sockaddr ) & servaddr , sizeof ( servaddr ))) < 0) err quit ( bind ); if (( listen ( listenfd , MAX CONNECT)) < 0) err quit ( listen ); for (;;) { if (( connfd = accept ( listenfd , ( struct sockaddr ) NULL, NULL)) < 0) err quit ( accept ); ticks = time ( NULL); snprintf ( buff , sizeof ( buff ), %.24s\r\n, ctime (& ticks )); if (( write ( connfd , buff , strlen ( buff ))) < 0) err quit ( write ); close ( connfd ); } } Assim como no cliente, alguns detalhes precisam ser considerados na vers o do servidor: a 1920: realiza um binding (amarra) da porta conhecida com o socket. A porta conhecida do servidor (13, para o servico de daytime) e ligada ao socket, preenchendo uma estru tura de Internet socket address e chamando o bind. E especicado o endereco IP como INADDR ANY, que permite que o servidor aceite uma conex o em qualquer interface, a caso o host tenha mais de uma; 22 23: converte o socket para um listening socket. Quando e feita a chamada ao listen, conex es ao socket passam a ser aceitas pelo kernel. Estes tr s passos, socket, bind e o e listen s o os passos utilizados para preparar qualquer listening descriptor (descritor de a escuta) em um servidor TCP (que neste caso e o listenfd). A constante MAX CONNECT est especicada no header timeofday.h, e especica a quantidade m xima de clientes que a a ir o conectar-se ao listening descriptor. a 2632: aceita conex es dos clientes e envia um reply. Normalmente o processo do servio dor e posto para dormir na chamada a accept, aguardando que uma conex o de um cliente ` a chegue e seja aceita. Uma conex o TCP usa o que e chamado de um three-way handa shake para estabelecer uma conex o, e quando este handshake e completado, a funcao a accept retorna, e o valor de retorno desta funcao e um novo descritor (connfd), chamado de connected descriptor (descritor do cliente conectado). O novo descritor e usado para comunicar com o novo cliente. Um novo descritor e retornado pelo accept a cada cliente que conecta ao servidor. A funcao time retorna a hora e data atual, na forma dos segundos que ocorreram desde a epoca do Unix: 1o de janeiro de 1970, as 00:00:00 horas UTC (Coordinated Universal ` Time). A funcao ctime converte o valor inteiro retornado em uma forma legvel para humanos. Esta string e ent o escrita de volta para o cliente. a O arquivo header utilizado em ambos exemplos encontra-se abaixo: 34: a conex o com o cliente e encerrada. a 11 12: cria o socket TCP, id ntico a vers o do cliente; e ` a

CAPITULO 7. TCP/IP#ifndef timeofday h #dene timeofday h 1 #include #include #include < stdlib . h> #include #include #include #include < netinet / in .h> #include < string . h> #include

41

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

/ / / / /

socket () / inet pton () / htons () / memset() / time () /

#dene err quit (msg ) ({ perror (msg); exit (1); }) / SOMAXCONN ca denido em bits/socket .h / #dene MAX CONNECT SOMAXCONN #dene MAXLINE 1024 #endif / timeofday h / Ap s ter o servidor compilado e rodando em um terminal, um outro terminal pode ser utio lizado para efetuar a comunicacao entre o cliente e o servidor. Para isto, o cliente deve ser exe cutado passando um endereco IP v lido como par metro. Como estamos especicando qualquer a a interface para escuta no servidor, pode ser utilizada a interface loopback: $ timeofday-client 127.0.0.1 Mon Oct 13 00:50:12 2003 Estes exemplos funcionam e podem servir como base para programas maiores. No entanto, eles ainda usam valores num ricos para referenciar o servidor, e muitas vezes e desej vel utilizar e a o nome do host que disponibiliza o servico. Para isto algumas funcoes podem ser utilizadas, como: struct hostent *gethostbyname(const char *name): retorna um ponteiro para uma estrutura do tipo hostent para o host name, que pode estar representado tanto na notacao de um endereco IP num rico quanto como um hostname. A estrutura hostent, denida no e . e composta pelos seguintes elementos: struct hostent { char h name; char h aliases ; int h addrtype ; int h length ; char h addr list ; } #dene h addr h addr list / / / / / nome ocial do host / lista de aliases , terminada por NULL / tipo do endereco do host / tamanho do endereco / lista de anderecos , terminada por NULL /

[0] / backward compatibility /

struct hostent *gethostbyaddr(const char *addr, int len, int type): retorna um ponteiro para uma estrutura do tipo hostent para o endereco do host especicado em addr de tamanho len e de tipo type. O unico tipo v lido de endereco e atualmente AF INET. a struct servent *getservbyname(const char *name, const char *proto): retorna um ponteiro para uma estrutura do tipo servent para a linha do arquivo services do diret rio o de conguracoes da distribuicao, normalmente /etc/services, que coincida com o servico name que utiliza o protocolo proto. Caso proto seja NULL, qualquer protocolo ser aceito. a

CAPITULO 7. TCP/IPA estrutura servent e especicada no arquivo como segue: struct servent { char s name; char s aliases ; int s port ; char s proto ; } / / / / nome ocial do servico / lista de aliases , terminada por NULL / numero da porta / protocolo a ser usado /

42

struct servent *getservbyport(int port, const char *proto): retorna um ponteiro para uma estrutura do tipo servent para a linha do arquivo services que coincida com a porta especicada em port, usando o protocolo proto. Caso o protocolo seja NULL, qualquer um ser aceito no matching. A porta deve estar informada em network byte order a (htonl(3)). O pr ximo exemplo mostra como estes recursos podem ser incorporados ao programa anteo rior:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38

#include timeofday .h int main ( int argc , char argv) { int sockfd , n; char recvline [MAXLINE + 1]; struct sockaddr in servaddr ; struct hostent ht ; if ( argc != 3) { fprintf ( stderr , Sintaxe: %s \n, argv[0]); exit (1); } if (( sockfd = socket ( AF INET, SOCK STREAM, 0)) < 0) err quit ( socket ); if (( ht = gethostbyname (argv [1])) < 0) err quit ( gethostbyname); memset (&servaddr , 0, sizeof ( servaddr )); servaddr . sin family = AF INET; servaddr . sin port = htons ( atoi ( argv [2])); memcpy (&servaddr.sin addr , ht>h addr list [0], sizeof ( struct in addr )); if ( connect ( sockfd , ( struct sockaddr ) & servaddr , sizeof ( servaddr )) < 0) err quit ( connect ); while (( n = read ( sockfd , recvline , MAXLINE)) > 0) { recvline [n ] = 0; / terminada por NULL / if ( fputs ( recvline , stdout ) == EOF) err quit ( fputs ); } if ( n < 0) err quit ( read );

CAPITULO 7. TCP/IP39 40

43

exit (0); } Podemos ver na linha 19 que agora e feita uma chamada para gethostbyname(3), que realiza o lookup do nome fornecido como par metro na linha de comando. Na linha 24, infora mamos a porta atrav s do par metro passado pelo usu rio (a funcao atoi(3) realiza a cone a a vers o de uma string para um inteiro). A forma de realizar a c pia da interface a ser utilizada a o tamb m foi mudada: na linha 25, vemos que a partir de agora o endereco e copiado a partir das e informacoes disponveis na estrutura hostent >h addr list. Utilizamos o primeiro endereco referente as interfaces neste host, e conectamos a ele. O restante do programa mant m-se inal` e terado. A seguir vamos analisar as mudancas na vers o do servidor: a

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

#include timeofday .h int main ( int argc , char argv) { int listenfd , connfd; struct sockaddr in servaddr , cliaddr ; socklen t len ; char buff [MAXLINE]; time t ticks ; if ( argc != 2) { printf ( Sintaxe: %s \n, argv [0]); exit (1); } if (( listenfd = socket ( AF INET, SOCK STREAM, 0)) < 0) err quit ( socket ); memset (&servaddr , 0, sizeof ( servaddr )); servaddr . sin family = AF INET; servaddr . sin addr . s addr = htonl ( INADDR ANY); servaddr . sin port = htons ( atoi ( argv [1])); if (( bind ( listenfd , ( struct sockaddr ) & servaddr , sizeof ( servaddr ))) < 0) err quit ( bind ); if (( listen ( listenfd , MAX CONNECT)) < 0) err quit ( listen ); for (;;) { if (( connfd = accept ( listenfd , ( struct sockaddr ) & cliaddr , & len )) < 0) err quit ( accept ); printf ( conexao estabelecida com %s na porta %d\n, inet ntop ( AF INET, &cliaddr.sin addr , buff , sizeof ( buff )), ntohs ( cliaddr . sin port )); ticks = time ( NULL); snprintf ( buff , sizeof ( buff ), %.24s\r\n, ctime (& ticks )); if (( write ( connfd , buff , strlen ( buff ))) < 0) err quit ( write );

CAPITULO 7. TCP/IP43 44 45 46

44

close ( connfd ); } }

As modicacoes foram semelhantes as realizadas no cliente. Na linha 23, e feita a convers o ` a da string relativa a porta a ser usada para inteiro, passando o resultado ao campo sin port da ` estrutura servaddr para que esta porta seja usada. A funcao accept agora tamb m informa e um ponteiro para a estrutura sockaddr cliaddr. Isto permite que o accept armazene dados sobre a conex o do cliente. Estes dados s o ent o usados nas linhas 35 37: o inet ntop a a a converte o endereco passado em &cliaddr.sin addr que usa a famlia AF INET para uma string, que e copiada para buffer. Um ponteiro para este buffer e retornado, e usamos este ponteiro para imprimir o hostname do client. A funcao htohs e usada apenas para converter o n mero em u network byte order para host byte order, e ent o usa seu resultado para imprim-lo. a O header usado foi levemente modicado, declarando o cabecalho para prover suporte a chamada gethostbyname(3), al m de j incluir headers para suporte a chamada ` e a ` select(2):21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45

#ifndef timeofday h #dene timeofday h 1 #include #include #include < stdlib . h> #include #include #include #include < netinet / in .h> #include < string . h> #include #include

/ / / / / /

socket () / inet pton () / htons () / memset() / gethostbyname () / time () /

#include / select () / #include #include < fcntl .h> #include #dene err quit (msg ) ({ perror (msg); exit (1); }) #dene MAXLINE 1024 #dene MAX CONNECT 100 #endif / timeofday h /

7.1 SelectUma conex o n o-bloqueante envolve v rias operacoes sobre os descritores. Antes de apresent a a a a las, e necess rio introduzir a funcao select(2). Essa funcao aguarda at que um n mero de a e u descritores de arquivo mudem seu status. Seu prot tipo e denido como segue: o / de acordo com o POSIX 1003.12001 / include / de acordo com padroes mais novos /

CAPITULO 7. TCP/IPinclude include include int select ( int n , fd set readfds , fd set writefds , fd set exceptfds , struct timeval timeout ); FD CLR(int fd, fd set set ); FD ISSET(int fd , fd set set ); FD SET(int fd , fd set set ); FD ZERO(fd set set);

45

A funcao select(2) usa um timeout que e uma struct timeval (com segundos e microse gundos), que pode ser atualizado para indicar quanto tempo j se passou do timeout especidado. a Tr s conjuntos independentes de descritores s o monitorados. Os que est o listados em readfds e a a ser o monitorados para vericar se algum caracter ca disponvel para leitura (para vericar se a a leitura n o vai bl