apostila c 2.0

83
Programac ¸˜ ao em C no GNU/Linux Lucas Correia Villa Real [email protected] 29 de Fevereiro de 2004

Upload: olvioneto

Post on 28-Oct-2015

33 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: Apostila C 2.0

Programacao em C no GNU/Linux

Lucas Correia Villa [email protected]

29 de Fevereiro de 2004

Page 2: Apostila C 2.0

Conteudo

1 Operacao do GNU/Linux 31.1 Obtendo acesso ao GNU/Linux . . . . . . . . . . . . . . . . . . . . . 31.2 Como executar comandos sobre o GNU/Linux . . . . . . . . . . . . . 31.3 Obtendo ajuda sobre comandos . . . . . . . . . . . . . . . . . . . . . 41.4 Consoles virtuais . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51.5 Processos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

2 Ambiente de desenvolvimento 72.1 Compilador GCC . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72.2 Arquivos headers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82.3 Utilizando os recursos de debug . . . . . . . . . . . . . . . . . . . . 92.4 Autoconf e Automake . . . . . . . . . . . . . . . . . . . . . . . . . . 14

2.4.1 Escrevendo um arquivo Makefile.am . . . . . . . . . . . . . . 142.4.2 Escrevendo um arquivo configure.in . . . . . . . . . . . . . . 15

2.5 DDD - Data Display Debugger . . . . . . . . . . . . . . . . . . . . . 162.6 Criando aplicativos que rodem tanto no GNU/Linux como no Windows 17

3 Explorando os recursos basicos do sistema operacional 183.1 Lendo parametros curtos e longos passados pela linha de comando . . 183.2 Trabalhando com arquivos . . . . . . . . . . . . . . . . . . . . . . . 213.3 Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213.4 Descritores de Arquivos . . . . . . . . . . . . . . . . . . . . . . . . . 24

4 CGI: Common Gateway Interface 28

5 Bibliotecas dinamicas 305.1 Criacao de bibliotecas dinamicas . . . . . . . . . . . . . . . . . . . . 31

6 Processos e threads 326.1 Processos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326.2 Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

7 TCP/IP 387.1 Select . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447.2 Conexoes nao-bloqueantes . . . . . . . . . . . . . . . . . . . . . . . 46

8 Bancos de dados 548.1 Implementacoes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 548.2 Programando com Bancos de Dados em C . . . . . . . . . . . . . . . 54

1

Page 3: Apostila C 2.0

CONTEUDO 2

9 Interface Grafica 589.1 Servidor X . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 589.2 A biblioteca Xlib . . . . . . . . . . . . . . . . . . . . . . . . . . . . 599.3 Gerenciadores de Janelas . . . . . . . . . . . . . . . . . . . . . . . . 599.4 GTK+ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

10 Comunicacao 6210.1 Porta Paralela . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6210.2 Porta Serial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

10.2.1 A interface Termios . . . . . . . . . . . . . . . . . . . . . . . 6410.2.2 Modo canonico . . . . . . . . . . . . . . . . . . . . . . . . . 6610.2.3 Modo nao-canonico (raw) . . . . . . . . . . . . . . . . . . . 7010.2.4 Modo assıncrono . . . . . . . . . . . . . . . . . . . . . . . . 71

10.3 USB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

11 Timers 7911.1 Sinais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7911.2 Utilizando timers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80

Page 4: Apostila C 2.0

Capıtulo 1

Operacao do GNU/Linux

1.1 Obtendo acesso ao GNU/LinuxDistribuicoes convencionais de GNU/Linux fornecem duas maneiras para que o usuarioautentique-se no computador: uma delas e atraves de login grafico, e a outra e atravesde login via console.

A segunda maneira e a mais comum, onde e exigido um nome de usuario e umasenha. Todo o sistema GNU/Linux mantem uma conta de super-usuario, normalmenteassociada ao nome root, e contas opcionais para cada usuario que ira operar este host.

Normalmente sao utilizados dois arquivos para efetuar a autenticacao de um usuario:o passwd e o shadow, localizados no diretorio de configuracoes de programas dadistribuicao, frequentemente o /etc. O primeiro arquivo mantem uma listagem comos nomes de usuarios, seu identificador unico no sistema (um valor inteiro), o grupoprincipal ao qual ele pertence, seu nome completo (alem de outros dados opcionais) ea shell que sera utilizada em suas sessoes.

Apos verificar a existencia deste usuario a partir do passwd, o arquivo shadowe processado. Este arquivo contem uma listagem das senhas de cada usuario, crip-tografadas. A senha fornecida pelo usuario e processada pelo mesmo algoritmo, e efeito um matching para verificar se ambas senhas criptografadas sao identicas, encer-rando o processo de autenticacao do usuario no host e dando a ele o acesso a um consolecom uma shell.

Existem outras formas de autenticacao, como o Linux-PAM, que faz uso de modulosde autenticacao. Assim, e possıvel escrever modulos com rotinas especıficas para au-tenticar o usuario, como a validacao de algum cartao magnetico, reconhecimento facialou ainda pela voz.

1.2 Como executar comandos sobre o GNU/LinuxAs distribuicoes GNU/Linux costumam ser compostas por um conjunto de pacotesbase, que capacitam a utilizacao de diferentes servicos, alem de interfaces para permitirque o usuario faca uso do sistema – normalmente compostas por uma shell, um servidorpara a interface grafica e um gerenciador de janelas.

Shells sao interfaces em modo texto, onde comandos digitados pelo usuario saointerpretados 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

Page 5: Apostila C 2.0

CAPITULO 1. OPERACAO DO GNU/LINUX 4

implementacoes diferenciam-se pela sintaxe na qual o usuario utiliza para a execucaode tarefas e pelos recursos que cada uma oferece. Esta sessao trata da operacao basicasobre o BASH, um dos interpretadores mais comuns, presente em grande parte dasdistribuicoes atuais.

Apos realizar o login no sistema, um prompt fica disponıvel ao usuario, aguar-dando a entrada de algum comando. A sintaxe padrao para a execucao de um programano BASH e:

$ programa

A execucao deste comando ira criar um novo processo, enquanto o processo pai (oBASH) ira aguardar pela sua finalizacao para continuar a execucao de outros coman-dos. Nota-se que essa e a mesma semantica obtida com comandos executados no MS-DOS, onde a shell (command.com, no caso) executa o programa de forma sıncrona, ouseja: Espera que os filhos terminem para retornar o controle do terminal ao usuario.

Em alguns casos e desejavel que o programa seja executado de forma asıncrona,ou seja, deseja-se continuar a ter acesso ao console antes mesmo que o programa tenhaterminado. Nestes casos, pode-se executar o programa usando a seguinte forma:

$ programa &

O “&” indica que o processo filho relativo a execucao de “programa” sera criado, masque o processo pai (o BASH) ira continuar seu fluxo de execucao, sem aguardar pelotermino do processo filho.

Bem como a execucao de programas, frequentemente e desejavel redirecionar asaıda do programa para algum outro dispositivo que nao a saıda padrao (stdout). Damesma forma, isto pode querer ser feito para redirecionar a saıda de erro padrao (stderr),ou modificar a entrada de dados para que seja feita a partir de um outro fluxo que naoa entrada padrao (stdin).

A forma que o BASH utiliza para fazer o controle de redirecionamentos e atravesdos operadores “<”e “>”, processados na ordem em que aparecem, da esquerda paraa direita. Caso o primeiro caracter do operador de redirecionamento seja “>”, o redi-recionamento refere-se a saıda padrao (stdout).

Tambem ha casos (em caso de depuracao de mensagem de erros, por exemplo)onde-se quer direcionar ambas saıdas (stdout e stderr) para um arquivo, o que pode serobtido com:

$ programa >& arquivo_com_mensagens

1.3 Obtendo ajuda sobre comandosCada programa base do GNU/Linux costuma apresentar uma pagina de manual, con-tendo explicacoes sobre a sua operacao.

Existem dois formatos predominantes como forma de documentacao de aplicativosno GNU/Linux: as man pages e as info pages.

O que as diferencia e a forma na qual a informacao e apresentada ao usuario. Asman pages apresentam basicamente:

• A sinopse do programa;

• Uma descricao do que ele faz;

Page 6: Apostila C 2.0

CAPITULO 1. OPERACAO DO GNU/LINUX 5

• Opcoes e argumentos que sao tratados por este programa;

• Referencias a paginas manuais semelhantes que sejam relevantes;

• Problemas conhecidos;

• Autor(es).

Alem disso, uma man page e disposta na forma de uma pagina contınua de infor-macoes, podendo muitas vezes tornar difıcil a localizacao de determinados assuntos.

As info pages solucionam este problema, apresentando a documentacao em um for-mato que permite uma navegacao mais facil, principalmente quando existe um grandevolume 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 doprograma de forma mais simplista.

1.4 Consoles virtuaisO kernel Linux oferece suporte a dispositivos de terminais virtuais que suportam dis-positivos de vıdeo e teclado. Eles sao chamados de “virtuais” pela possibilidade deexecutar varias instancias de terminais virtuais – tambem chamados de consoles vir-tuais – em um unico terminal fısico. Este recurso e bastante util, por permitir queum terminal virtual colete mensagens do sistema e warnings, enquanto outro terminalvirtual seja utilizado para gerenciar uma sessao de um usuario em modo texto, e umterceiro terminal rodando uma sessao X, todas em paralelo.

A maneira utilizada para alternar entre os diferentes consoles virtuais e normal-mente realizada atraves da combinacao de teclas Alt-<Fn>.

Para modificar as fontes utilizadas em um terminal, utiliza-se o comandosetfont(8).

1.5 ProcessosQualquer tarefa atribuıda ao GNU/Linux e um processo. Todo comando executadogera um numero de processo (PID), e atraves deste numero e possıvel manipular esteprocesso. Alguns destes programas que permitem a manipulacao de processos sao:

• top(1): lista os processos em execucao em tempo real. Ele mostra algumasinformacoes sobre o sistema, bem como uma lista das tarefas sendo gerenciadaspelo kernel Linux. Algumas das informacoes incluem o nome do processo e aquantidade de memoria e CPU consumidas por ele.

• ps(1): lista os processos atuais. Varios parametros podem ser utilizados,como a opcao “-x”, que lista todos os processos da maquina, e a opcao “-u”,que inclui o autor do processo. Outros parametros uteis sao o “-C nome”, quelistam os processos com um nome especıfico, e “-U usuario”, que lista todos osprocessos pertencentes a usuario.

• nice(1): executa um comando com uma prioridade diferente de escalona-mento. Prioridades no Linux vao de -20 (prioridade alta) a 19 (prioridade baixa).A prioridade padrao para execucao de processos e 0.

Page 7: Apostila C 2.0

CAPITULO 1. OPERACAO DO GNU/LINUX 6

• renice(1): altera a prioridade de escalonamento de algum processo.

• kill(1): envia um sinal para um processo. Uma lista de sinais encontra-sedisponıvel na man page do signal(7).

• killall(): envia um sinal para todos os processos especificados na linha decomando atraves de seu nome.

• nohup(): roda algum comando, ignorando sinais de hangup. Este comandoe util quando deseja-se rodar algum programa e logo apos efetuar o logoff damaquina: o programa continuara em execucao ate o termino de sua execucao.

• jobs: caso tenha algum programa (job) sendo executado em background, estecomando mostra-os. Este comando e implementado pela shell (BASH e ZSH oimplementam). A forma de deixar um programa em background e disparando-o com o parametro “&”, como visto na Sessao 1.2, ou pressionando a teclaCTRL+Z durante a execucao do programa.

• fg: tambem pertinente a controle de programas em background, este comandopermite trazer de volta algum job que esteja em background. Caso haja maisde um, e necessario especificar o seu numero, listado com o comando jobs.Algumas shells utilizam o nome do procsso, ao inves do seu numero, para tornara dar o controle a ele.

• bg: assim como o fg, faz com que o controle seja dado a um processo. Noentanto, este modo permite que o programa continue sua execucao em uma sub-shell. Assim, a shell fica liberada para que o usuario continue a execucao decomandos nela, e o programa sai do estado de background e torna a executar.Este comando e bastante utilizado para chamar programas graficos no ambienteX sem bloquear o terminal.

Page 8: Apostila C 2.0

Capıtulo 2

Ambiente de desenvolvimento

2.1 Compilador GCC“GCC” [STA 2002] e uma abreviacao do termo GNU Compiler Collection. Ele levaeste nome pelo fato de que varias versoes do compilador estao integradas, com suportea linguagens como C, C++, Objective-C, Ada, Fortran, Java e Treelang. Alem disso,ele representa tanto o nome geral do compilador quanto o nome do compilador deprogramas C, onde a abreviacao passa a ter o significado de “GNU C Compiler”. Estee o compilador padrao de qualquer distribuicao GNU/Linux.

Quando o GCC e invocado, ele normalmente realiza quatro etapas para gerar o exe-cutavel: pre-processamento, compilacao, montagem e ligacao, sempre nesta ordem, esendo que e possıvel parar o processo no final de cada etapa. Os primeiros tres estagiosaplicam-se a um unico arquivo fonte, e encerram produzindo um arquivo objeto. Aligacao combina todos os arquivos objetos especificados como entrada em um arquivoexecutavel. Estes passos podem ser melhor descritos como:

• Pre-processamento: Esta etapa e responsavel pela resolucao de diretrizes do pre-processador, como #define, #if, #include. Nesta fase, o GCC utilizao utilitario cpp.

• Compilacao: Nesta fase e produzida a linguagem de montagem dos arquivos deentrada.

• Montagem: Produz o arquivo objeto .o, levando em conta a linguagem de mon-tagem dos arquivos de entrada. Nesta etapa, o GCC utiliza o utilitario gas (GNUAssembler), ou o montador nativo as, caso ele nao esteja disponıvel.

• Ligacao: Nesta fase os arquivos .o e as bibliotecas sao “colocados” no exe-cutavel. O utilitario usado nessa fase e o ld (GNU Linker).

Para parar o GCC em alguma determinada etapa, podem ser utilizados os seguintescomandos:

• Pre-processamento:$ gcc -E teste.c -o teste.iEste comando redireciona a saıda do pre-processador para o arquivo teste.i.

7

Page 9: Apostila C 2.0

CAPITULO 2. AMBIENTE DE DESENVOLVIMENTO 8

• Compilacao:$ gcc -S teste.cO arquivo teste.s sera gerado, ja em linguagem assembly da arquitetura.

• Montagem:$ gcc -c teste.cGera o arquivo objeto teste.o.

• Ligacao:$ gcc -o teste teste.cGera o arquivo executavel teste.

Assim, para gerar um executavel para um simples programa (teste.c, por exem-plo), utiliza-se o seguinte comando:

$ gcc -o teste teste.c

No entanto, a maioria dos programas consistem em varios arquivos de codigo. Caso hajamdois arquivos, arquivo1.c e arquivo2.c, a seguinte linha de comando poderia ser uti-lizada para compilar o programa final:$ gcc -o teste arquivo1.c arquivo2.c

O GCC interpreta os arquivos de acordo com a sua extensao. Algumas das principais podemser vistas na Tabela 2.1.

Tabela 2.1: Algumas extensoes reconhecidas pelo GCC

Extensao Interpretacao.c Programa em linguagem C.C .cc Programa em linguagem C++.i Programa em C pre-processado.ii Programa em C++ pre-processado.S .s Programa em linguagem Assembly.o Programa objeto.a .so Bibliotecas compiladas

Alguns parametros importantes suportados pelo GCC foram vistos, como o -c e -o. Noentanto, outras opcoes podem ser importantes para compilar um projeto. Algumas destas estaolistadas na Tabela 2.2.

2.2 Arquivos headersArquivos headers contem basicamente definicoes utilizadas por bibliotecas do sistema e proto-tipos de funcoes, de forma que um programa sendo compilado possa realizar a verificacao desintaxe e tipos corretamente.

A localizacao dos arquivos headers em uma distribuicao GNU/Linux convencional fica nodiretorio /usr/include. Alguns outros diretorios importantes sao:

• /usr/X11R6/include/X11: contem headers necessarios para compilar programasque usam o X11 Window System.

• /usr/include/X11: headers para compilar programas que usam o X11. Normal-mente e um symlink para o diretorio /usr/X11R6/include/X11;

Page 10: Apostila C 2.0

CAPITULO 2. AMBIENTE DE DESENVOLVIMENTO 9

Tabela 2.2: Algumas extensoes reconhecidas pelo GCC

Opcao Descricao-o FILE Especifica o nome do arquivo compilado (padrao a.out).-c Gera somente o objeto (.o).-I <diretorio> Busca arquivos de headers primeiro em diretorio.-L <diretorio> Busca bibliotecas primeiro em diretorio.-lnome Liga a biblioteca libnome ao executavel.-static Executa a ligacao usando bibliotecas estaticas.-g, -ggdb Inclui informacoes para o depurador.-O Otimiza o codigo.-On Especifica um nıvel de otimizacao entre 0 e 3 (0 nao otimiza).-ansi Desligando recursos do GCC incompatıveis com o padrao ANSI.-w Inibe as mensagens de warning (aviso).-Wall Emite todas as mensagens de warning.-Werror Converte todas as mensagens de warning em erro.-v Mostra os comandos usados em cada passo da compilacao.

• /usr/include/g++: headers para compilar programas C++ que utilizam o compi-lador GNU C++;

• /usr/include/asm: Arquivos contendo as funcoes, parametros e definicoes es-pecıficas arquitetura na qual os binarios gerados serao criados;

• /usr/include/linux: link simbolico para o diretorio contendo os headers utiliza-dos na versao do Linux usada para compilar a GNU libc. Algumas distribuicoes aindapraticam o erro de apontar para o diretorio do kernel Linux atual sendo utilizado no host.

Estes diretorios sao uteis para localizar a definicao de uma funcao que nao apresenta umaman page ou uma info page. Um comando que auxilia a localizacao de definicoes e o grep(1).

2.3 Utilizando os recursos de debugEstes recursos sao muito uteis, pois frequentemente erros acontecem. A opcao -g do GCC per-mite que sejam gerados codigos de informacao para o depurador. Para compilar um programacom suporte ao uso em um depurador, e necessario utilizar a seguinte linha de comando:$ gcc -g -o teste teste.c

Para retirar os sımbolos gerados pela opcao -g, pode-se utilizar o comando strip(1) coma opcao –strip-debug sobre o arquivo binario (neste caso chamado de teste):$ strip --strip-debug teste

O depurador padrao da GNU e o GDB [GIL 2003], o GNU Debugger. Para utilizar o depu-rador, chama-se o executavel a partir da shell, passando o nome do programa a ser depuradocomo argumento. Em alguns casos, quando o programa esta sendo executado e realiza umaoperacao 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 possıvel passar o arquivocore como parametro extra, de forma que o depurador salte imediatamente para a instrucao quecausou o erro. Vale ressaltar que esta operacao so gera algum dado relevante quando o programaque gerou esta excessao tiver sido compilado com suporte a sımbolos de depuracao.

Page 11: Apostila C 2.0

CAPITULO 2. AMBIENTE DE DESENVOLVIMENTO 10

O GDB pode fazer quatro tipos de operacoes (alem de outras operacoes de suporte a estas)para ajudar a encontrar problemas no codigo:

• Iniciar o programa, especificando algum parametro que possa afetar seu comportamento;

• Parar o programa em uma condicao especıfica;

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

• Modificar variaveis no programa, de forma a poder corrigir os efeitos causados por umproblema e avancar para poder aprender sobre um novo.

A utilizacao padrao do GDB entao e feita da seguinte maneira, tomando como exemplo umprograma chamado teste:

$ gdb teste

GNU gdb 5.2Copyright 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 itunder 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 especıficos do GDB poderaoser utilizados para entender o que esta acontecendo no codigo do programa. O seguinte programasera utilizado como exemplo para as explicacoes de operacao do GDB:

1 #include <stdio .h>2 #include <unistd .h>3

4 int5 calcula ( int a , int b)6 {7 int resultado ;8 resultado = a + b;9 return resultado ;

10 }11

12 int13 main()14 {15 int a , b , resultado ;16 a = 1;17 b = 2;18 resultado = calcula ( a , b );19 printf ( ”Resultado: %d\n”, resultado );20 exit (0);21 }

Para executar este programa no GDB, basta executar a operacao run:

$ gdb teste...

(gdb) run

Page 12: Apostila C 2.0

CAPITULO 2. AMBIENTE DE DESENVOLVIMENTO 11

Starting program: /Users/lucasvr/testeResultado: 3

Program exited normally.

Como nao houve nenhum problema durante a execucao deste programa, ele encerrou e re-tornou ao prompt do GDB. No entanto, podemos querer executar o programa passo a passo. Paraisto, invoca-se o comando break <linha>. Para listar o codigo sendo depurado e descobrira linha que deseja-se utilizar como ponto de parada (breakpoint), os comandos “list” e “list-” podem ser usados para listar as proximas 10 linhas de codigo, ou as ultimas 10 linhas decodigo, de forma progressiva. Outra forma bastante utilizada e marcar o breakpoint em deter-minadas funcoes. No nosso exemplo, para iniciar a execucao passo a passo a partir da funcaocalcula, ao inves de especificar a linha desejada para o comando break, pode ser informada onome da funcao a ser utilizada como breakpoint:

(gdb) break calculaBreakpoint 1 at 0x8048446: file teste.c, line 7.(gdb) runStarting program: /Users/lucasvr/teste

Breakpoint 1, calcula (a=1, b=2) at teste.c:77 resultado = a + b;

A partir deste ponto, podemos continuar a execucao do programa ate que ele encerre nor-malmente, com o uso do comando continue, ou executa-lo passo a passo. Para isto, pode-mos utilizar o comando step, que continua executando o programa ate encontrar uma outralinha do codigo, retornando entao o controle ao GDB. Uma caracterıstica importante do step[count] e que ele nao entra em funcoes ou loops: caso haja uma chamada a uma funcao, porexemplo, ele ira retornar o controle ao GDB apenas apos a execucao completa dessa funcao.Caso deseja-se ter um controle maior sobre a execucao de funcoes, loops e switches, pode-seutilizar o comando next [count], que segue o fluxo do programa.

Outros comandos interessantes sao os referentes a variaveis. O GDB permite que o usuariomodifique o valor de uma variavel com o comando set variable <variavel=valor>.No caso do breakpoint realizado anteriormente na funcao calcula, poderıamos alterar o valor dea para 2, alterando o resultado da soma. Para isto, basta executar:

(gdb) break calculaBreakpoint 1 at 0x8048446: file teste.c, line 7.(gdb) runStarting program: /Users/lucasvr/teste

Breakpoint 1, calcula (a=1, b=2) at teste.c:77 resultado = a + b;(gdb) set variable a=2(gdb) continueContinuing.Resultado: 4

Program exited normally.(gdb)

Outro recurso bastante utilizado e o watchpoint, que interrompe a execucao do programaquando uma determinada expressao for realizada. Ele pode ser usado para verificar quando umavariavel tem seu valor modificado, como no exemplo abaixo, onde uma mudanca no valor de b ea condicao para o retorno do comando ao depurador:

Page 13: Apostila C 2.0

CAPITULO 2. AMBIENTE DE DESENVOLVIMENTO 12

(gdb) break mainBreakpoint 1 at 0x804846a: file teste.c, line 14.(gdb) runStarting program: /Users/lucasvr/teste

Breakpoint 1, main () at teste.c:1414 a = 1;(gdb) watch bHardware watchpoint 2: b(gdb) continueContinuing.Hardware watchpoint 2: b

Old value = 0New value = 2main () at teste.c:1616 resultado = calcula (a, b);(gdb) print b$1 = 2

Nota-se que o controle e retomado logo apos a execucao da operacao que modificou b,logo a execucao continua tendo como indicacao a proxima linha a ser executada pelo programa.Utilizamos ainda o comando print, que e usado para imprimir o valor de alguma variavel –neste caso a variavel b.

O GDB permite ainda ser anexado a um processo em execucao. Para isto, o GDB deve serexecutado passando o PID do processo como segundo argumento, seguindo o nome do programa.Como o programa a ser depurado estara em um ponto desconhecido de execucao, e interessanteverificar onde ele encontra-se. O comando backtrack do GDB imprime os stack frames doprocesso, e assim e possıvel saber onde o programa esta sendo executado. Cada frame e um dadoassociado a uma chamada de funcao, e contem os argumentos enviados a ela, as variaveis locaisa esta funcao e o endereco no qual a funcao esta executando.

Para fazer um teste, modificaremos nosso programa exemplo para que ele faca a leiturade uma tecla para continuar a execucao. Assim, poderemos depura-lo enquanto estiver emexecucao, ja que ele executa muito rapido para podermos anexar o GDB a ele. A funcao calculadevera ficar da seguinte forma:

intcalcula ( int a , int b){

int resultado ;resultado = a + b;getchar ();return resultado ;

}

Apos recompilar o programa – nao esquecendo da opcao -g –, iremos executa-lo:

$ ./teste

Agora, em outro terminal, iremos anexar o GDB a sua instancia. Para isto, e necessario obtero PID do processo, executando o GDB em seguida:

$ ps -C testePID TTY TIME CMD

15247 pts/6 00:00:00 teste

Page 14: Apostila C 2.0

CAPITULO 2. AMBIENTE DE DESENVOLVIMENTO 13

$ gdb teste 15247 -silentAttaching to program: /Users/lucasvr/teste, process 15247Reading symbols from /System/Links/Libraries/libc.so.6...done.Loaded symbols for /System/Links/Libraries/libc.so.6Reading symbols from /System/Links/Libraries/ld-linux.so.2...done.Loaded symbols for /System/Links/Libraries/ld-linux.so.20x400e76d4 in read () from /System/Links/Libraries/libc.so.6(gdb)

Neste exemplo, o processo teste encontrava-se no PID 15247, e este valor foi informadocomo parametro para a execucao do GDB. Nesse ponto temos controle sobre o processo, masnao sabemos ainda onde ele esta. Para descobrir, vamos listar os stack frames do processo:

(gdb) backtrace#0 0x400e76d4 in read () from /System/Links/Libraries/libc.so.6#1 0x40146238 in sys_sigabbrev () from /System/Links/Libraries/libc.so.6#2 0x40085b63 in _IO_file_underflow () from /System/Links/Libraries/libc.so.6#3 0x40087ffd in _IO_default_uflow () from /System/Links/Libraries/libc.so.6#4 0x40087e46 in __uflow () from /System/Links/Libraries/libc.so.6#5 0x4008230a in getchar () from /System/Links/Libraries/libc.so.6#6 0x08048487 in calcula (a=1, b=2) at teste.c:8#7 0x080484b8 in main () at teste.c:17#8 0x4002cfe4 in __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 doprograma – neste caso a funcao libc start main(), da biblioteca padrao do C, a GNU libc.Em seguida a funcao main() foi executada na linha 17 do programa teste.c, chamando a funcaocalcula() a seguir, na linha 8, e finalmente realizando a chamada getchar(). A partir deste ponto,as funcoes referentes a implementacao desta chamada na GNU libc foram executadas, e pode-se verificar que o processo encontra-se aguardando a leitura de algum dado com a chamada desistema read(). Nota-se que para cada funcao chamada, um novo frame e criado, e a cada retornode funcao, o frame para esta funcao e eliminado. Caso a funcao seja recursiva, podem existirvarios frames para a mesma funcao.

Para escolher algum frame especıfico, pode-se utilizar o comando frame, seguido do numerodo frame desejado. Pode-se ainda utilizar os comandos up [n] e down [n], que avancamou retrocedem a escolha do frame desejado na stack. Para modificarmos nosso programa agora,iremos modificar o valor do resultado a ser retornado para a funcao main(). Para isso, iremosescolher o frame referente a funcao calcula() (frame 6, como mostrou o backtrace), modificar ovalor da variavel resultado e continuar a execucao do programa:

(gdb) frame 6#6 0x08048487 in calcula (a=1, b=2) at teste.c:88 getchar ();(gdb) set variable resultado=10(gdb) continueContinuing.

Agora, ao retornar ao terminal utilizado para disparar o processo teste e pressionar a teclaENTER, veremos que nossa modificacao no GDB foi refletida no processo que foi modificado.A partir deste ponto pode-se retornar ao GDB e encerra-lo, com o comando quit.

(gdb) quit

$

Page 15: Apostila C 2.0

CAPITULO 2. AMBIENTE DE DESENVOLVIMENTO 14

2.4 Autoconf e AutomakeO Autoconf e Automake sao duas ferramentas que auxiliam o programador a gerar arquivosMakefile para o projeto. O Autoconf e responsavel por realizar uma serie de checagens no host,como a versao do compilador, tamanhos de tipos de variaveis, existencia de headers, bibliotecas(bem como suas versoes), entre muitos outros. Estas verificacoes sao importantes, pois muitasvezes os programas rodam nas plataformas mais diversas, em arquiteturas diferentes da utilizadapelo programador. Apos a realizacao destas verificacoes, o Automake e utilizado para gerar umarquivo Makefile a partir das exigencias feitas pelo Autoconf.

O processo para gerar arquivos Makefile utilizando estas ferramentas consiste nos seguintespassos:

• Escrever um arquivo Makefile.am;

• Escrever um arquivo configure.in;

• Rodar aclocal para criar copias locais de macros utilizadas pelo autoconf;

• Rodar automake -a -c para gerar um arquivo Makefile.in a partir do arquivo Make-file.am (o automake verifica o arquivo configure.in para obter informacoes sobre o pro-jeto);

• Rodar autoconf para gerar o script configure.

A estrutura utilizada em projetos GNU segue a seguinte hierarquia de arquivos e diretorios,a partir do diretorio base dos fontes do projeto:

• AUTHORS: arquivo contendo os nomes dos autores do projeto;

• BUGS: listagem dos problemas conhecidos do projeto, e de formas de submeter bugsencontrados pelo usuario;

• COPYING: contem a licenca pela qual o projeto e distribuıdo;

• ChangeLog: contem um historico com modificacoes realizadas no projeto;

• FAQ: perguntas frequentemente feitas a respeito de alguma caracterıstica do programa;

• INSTALL: processo de instalacao do programa;

• Makefile.am: o arquivo a ser gerado pelo programador, informando alguns dadosimportantes para que o arquivo Makefile seja construıdo;

• NEWS: contem novidades sobre o programa;

• README: informacoes sobre o programa, como seu proposito e formas de contato como autor e/ou mailing lists;

• configure.in: arquivo a ser gerado pelo programador, contendo verificacoes quedevem ser feitas antes de compilar o programa;

• include/: diretorio contendo os arquivos headers do programa;

• src/: diretorio contendo os arquivos fontes do programa;

• src/Makefile.am: regras de compilacao dos fontes, como nome do binario e ar-quivos fontes usados para construı-lo.

2.4.1 Escrevendo um arquivo Makefile.amComo visto, existem dois arquivos Makefile.am. O que reside no diretorio raiz do projeto in-forma os subdiretorios que contem os fontes e algumas opcoes extras. O conteudo dele normal-mente e:

AUTOMAKE_OPTIONS = foreign gnuSUBDIRS = srcEXTRA_DIST = BUGS COPYING ChangeLog INSTALL README FAQ

Page 16: Apostila C 2.0

CAPITULO 2. AMBIENTE DE DESENVOLVIMENTO 15

O campo AUTOMAKE OPTIONS aceita alguns nıveis, dentre os quais:

• foreign: verifica se o projeto esta de acordo com os padroes exigidos para distribuir osoftware;

• gnu: verifica se o projeto inclui os arquivos exigidos pelo GNU standards para pacotes desoftware;

• no-dependencies: usado em situacoes onde nao existem informacoes suficientes parafazer a deteccao automatica de dependencias no pacote.

As opcoes suportadas sao muitas, e aconselha-se a leitura da info page do automake paraconsulta-las.

O campo SUBDIRS informa diretorios onde residem arquivos fontes do programa, e EX-TRA DIST informa que os arquivos ali listados devem ser ignorados pelo automake e peloautoconf.

O arquivo Makefile.am interno ao diretorio src/ pode conter informacoes complexas comregras que devem ser utilizadas para cada arquivo, mas no entanto o seguinte e suficiente paragerar pacotes com estas ferramentas:

bin_PROGRAMS = ProgramaPrograma_SOURCES = arquivo1.c arquivo2.c arquivo3.c

EXTRA_DIST = config.sample

Apos gerar estes arquivos, a proxima etapa e a criacao de um arquivo configure.in.

2.4.2 Escrevendo um arquivo configure.inA maneira mais facil de gerar um arquivo configure.in e atraves do comando autoscan(1).Ele examina os arquivos fonte a partir do diretorio corrente, recursivamente, e gera um arquivoconfigure.scan base no qual pode ser gerado o arquivo configure.in. Apos renomeado para con-figure.in, algumas mudancas devem ser feitas, como informar o nome e versao do projeto, e-mailpara reportar bugs, entre outros.

Para uma estrutura contendo apenas um arquivo chamado teste.c no diretorio src/ com umprintf 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_STDCAC_CHECK_HEADERS([stdlib.h])

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.

Page 17: Apostila C 2.0

CAPITULO 2. AMBIENTE DE DESENVOLVIMENTO 16

AC_CONFIG_FILES([Makefilesrc/Makefile])

AC_OUTPUT

Apos preparados estes dois arquivos, configure.in e Makefile.am, o projeto pode ser geradoexecutando aclocal, automake e autoconf, como explicado anteriormente.

2.5 DDD - Data Display DebuggerApesar do GDB ser um poderoso debugger, sendo ate possıvel com ele debuggar o propriokernel do sistema operacional, assim com aplicativos multi-threaded, a sua interface em linhade comando nao e amigavel para usuarios pouco familirizados com a ferramenta, o que frustraa maioria dos usuarios iniciantes em GNU/Linux e nao permite que toda a potencialidade dodebugger seja aproveitada em sua totalidade.

Para suprir tal dificuldade, foi desenvolvido o DDD [ZEL 2000], que e simplesmente umainterface grafica para o debugger.

Porem, o DDD possui ainda extensoes ao processo de debugging tradicional, permitindo quegraficos sejam plotados a partir do valor de variaveis, como matrizes e arrays. Um exemplo deoperacao do DDD pode ser visto na Figura 2.5

Figura 2.1: Operando com o DDD sobre o programa exemplo

Vale notar que mesmo com todas as extensoes do DDD ao GDB, o mesmo ainda continuasendo um mero front-end para o debugger da GNU, sendo possıvel executar todos os comandosdo GDB na janela no final da janela principal do DDD, como pode ser visto na janela inferior daFigura 2.5

Page 18: Apostila C 2.0

CAPITULO 2. AMBIENTE DE DESENVOLVIMENTO 17

2.6 Criando aplicativos que rodem tanto no GNU/Linuxcomo no Windows

Nao existe nenhuma regra especıfica a ser seguida para executar o mesmo codigo em sistemasoperacionais diferentes, apenas alguns cuidados que devem ser atendidos. O mais comum e ape-nas utilizar funcoes ANSI, que estao disponıveis em ambos, e evitar ao maximo o uso de funcoesespecıficas de um determinado sistema operacional. Assim, pode-se usar a flag -ansi do GCCpara restringir as funcoes usadas as definidas e suportadas pelo ANSI.

Em alguns casos, entretanto, isto nao e possıvel, e entao pode ser necessario utilizar diretivasde compilacao #ifdef para verificar o sistema em uso. E bastante comum isolar codigosespecificos de uma determinada arquitetura ou sistema operacional desta forma. Por exemplo:

# if defined ( linux )# define DIR SEPARATOR ’\\’#else if defined ( WIN32)# define DIR SEPARATOR ’/’#endif

Existe ainda uma forma de portar codigo nativo criado no GNU/Linux para o Windows,usando um ambiente no Windows chamado Cygwin1. Ele consiste de uma camada de bibliote-cas que atuam como wrappers, de forma a mapear nomes de dispositivos existentes apenas noGNU/Linux para o Windows, como dispositivos IDE, SCSI e seriais, entre outros.

Uma grande variedade de bibliotecas e interfaces e aplicativos ja encontram suporte nesteambiente, como bibliotecas de threads, a biblioteca GTK+, o servidor Apache e o servidorXFree86.

1ver http://www.cygwin.com

Page 19: Apostila C 2.0

Capıtulo 3

Explorando os recursos basicosdo sistema operacional

3.1 Lendo parametros curtos e longos passados pela linhade comando

Em “C” e possıvel a um programa coletar parametros passados na linha de comando no mo-mento de sua ativacao. Assim, podemos escrever um programa chamado soma que recebera osparametros 1 e 2 que serao passados via linha de comando:

$ soma 1 2

Estes parametros sao passados para o programa atraves dos argumentos da funcao main().O modulo principal do programa (main) pode receber dois parametros externos opcionais, quesao o argv, um vetor de ponteiros para strings, e o argc, que indica a quantidade de stringscontidas neste array. Caso um programa nao receba nenhum parametro, existira apenas umastring no array (no ındice 0), contendo o nome do programa executado, e argc tera o valor 1.

Tomando por exemplo o programa soma, poderıamos escreve-lo da seguinte forma:

1 #include <stdio .h>2 #include <unistd .h>3

4 int5 main ( int argc , char ∗∗argv)6 {7 int i ;8 printf ( ”Numero de argumentos: %d\n”, argc );9 for ( i = 0; i < argc ; i++)

10 printf ( ”Argumento %d: %s\n”, i , argv[ i ]);11 exit (0);12 }

Este programa ira imprimir a quantidade de argumentos recebidos, e em seguida ira mostrarcada argumento recebido como parametro. E normal que os programas realizem a consistenciados parametros recebidos. Dessa forma, e comum encontrar o seguinte tipo de codigo:

1 #include <stdio .h>2

3 int

18

Page 20: Apostila C 2.0

CAPITULO 3. EXPLORANDO OS RECURSOS BASICOS DO SISTEMA OPERACIONAL19

4 main ( int argc , char ∗∗argv)5 {6 if ( argc != 3) {7 fprintf ( stderr , ”Sintaxe: %s valor1 valor2\n” , argv [0]);8 exit (1);9 }

10 }

O padrao POSIX.2 prove uma funcao para tratar argumentos de entrada, o getopt(3),definida pelo seguinte prototipo:

#include <unistd .h>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 eargv sao, respectivamente, a contagem de argumentos e o array de strings passados para a funcaomain() na invocacao do programa.

Um elemento de argv que inicia com “-” (e nao e exatamente “-” ou “–”) e um elemento deopcao. Os caracteres deste elemento (fora o “-” inicial) sao caracteres de opcao. Se getopt(3)e chamado repetidamente, ele retorna sucessivamente cada um dos caracteres de opcao de cadaum dos elementos de opcao.

Se a funcao getopt(3) encontra um outro caractere de opcao, ela retorna este caractere,atualizando a variavel externa optind e uma variavel estatica nextchar de forma que a proximachamada a getopt(3) pode continuar a busca com o caractere de opcao seguinte ou um ele-mento de argv.

Se nao ha mais caracteres de opcao, getopt(3) retorna −1. Entao optind e o ındice emargv do primeiro elemento de argv que nao e uma opcao.

optstring e uma string contendo os caracteres de opcao legıtimos. Se tal caractere e seguidopor dois-pontos, a opcao requer um argumento, entao getopt atribui um ponteiro para o textoque segue no mesmo elemento de argv, ou para o texto do elemento seguinte de argv, em op-targ. Dois dois-pontos significam que uma opcao recebe um argumento opcional; se ha texto noelemento de argv atual, ele e retornado em optarg, caso contrario optarg e zerado. Esta e uma ex-tensao GNU. Se optstring contem W seguido de um ponto-e-vırgula, entao -W foo e tratado comoa opcao longa –foo. (A opcao -W e reservada pelo POSIX.2 para extensoes de implementacao.)Este comportamento e uma extensao GNU, nao disponıvel em bibliotecas anteriores a GNU libc2.

A funcao getopt long(3) funciona como getopt(3), exceto que ele tambem aceitaopcoes longas, iniciadas por dois tracos. Nomes de opcoes longas podem ser abreviados se aabreviacao for unica ou se iguala exatamente a alguma opcao definida. Uma opcao longa poderequerir um parametro da forma –arg=param ou –arg param. O seguinte prototipo a define:

#define GNU SOURCE#include <getopt .h>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 <getopt.h> como:

struct option {const char ∗name;int has arg ;int ∗ flag ;int val ;

};

Page 21: Apostila C 2.0

CAPITULO 3. EXPLORANDO OS RECURSOS BASICOS DO SISTEMA OPERACIONAL20

O significado destes campos sao:

• nome: e o nome da opcao longa;

• has arg: no argument (ou 0) se a opcao nao recebe um argumento, required argument(ou 1) se a opcao requer um argumento, ou optional argument (ou 2) se a opcao recebeum argumento opcional;

• flag: especifica como os resultados sao retornados para uma opcao longa. Se flag e NULL,entao getopt long(3) retorna val. Caso contrario, getopt long(3) retorna 0, eflag aponta para uma variavel, a qual e ajustada para val se a opcao e encontrada, oudeixada intocada se a opcao nao e encontrada;

• val: e o valor a retornar, ou a carregar para a variavel apontada por flag.

O ultimo elemento do array deve ser preenchido com zeros.

O seguinte exemplo ilustra o uso de getopt long(3) com a maioria de seus recursos:

1 #include <stdio .h>2 #include <unistd .h>3 #define GNU SOURCE4 #include <getopt .h>5

6 static struct option long options [] = {7 {”add” , 1, 0, 0},8 {”append”, 0, 0, 0},9 {” delete ” , 1, 0, 0},

10 {”verbose” , 0, 0, 0},11 {” create ” , 1, 0, ’c’},12 {” file ” , 1, 0, 0},13 {0, 0, 0, 0}14 };15

16 int17 main ( int argc , char ∗∗argv)18 {19 int c;20

21 while (1) {22 int option index = 0;23

24 c = getopt long ( argc , argv , ”abc:d:” ,25 long options , & option index );26 if ( c == −1)27 break;28

29 switch ( c ) {30 case 0:31 printf (”opcao %s”, long options [ option index ]. name);32 if ( optarg )33 printf ( ” com argumento %s”, optarg );34 printf ( ”\n”);35 break;36 case ’a’ :37 printf ( ”opcao a\n”);38 break;39 case ’b’ :

Page 22: Apostila C 2.0

CAPITULO 3. EXPLORANDO OS RECURSOS BASICOS DO SISTEMA OPERACIONAL21

40 printf ( ”opcao b\n”);41 break;42 case ’c’ :43 printf ( ”opcao c com valor ‘%s’\n” , optarg );44 break;45 case ’d’ :46 printf ( ”opcao d com valor ‘%s’\n” , optarg );47 break;48 case ’?’ :49 break;50 default :51 printf ( ”erro : getopt retornou %d\n”, c );52 }53 }54

55 if ( optind < argc ) {56 printf ( ”parametros nao referentes a opcoes : ” );57 while ( optind < argc)58 printf ( ”\t%s\n”, argv[optind ++]);59 }60 exit (0);61 }

3.2 Trabalhando com arquivosArquivos em C podem ser manipulados com o uso de descritores de arquivos, representados porobjetos do tipo int, ou streams, representados por objetos do tipo FILE *.

Descritores de arquivos proveem uma interface primitiva e de baixo-nıvel para operacoes deentrada e saıda. Tanto descritores de arquivos como streams podem representar uma conexao aum 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 sao usados quando enecessario realizar operacoes de controle sobre algum dispositivo especıfico, ou ainda quandoprecisam realizar I/O em modos especiais, como nao-bloqueante, dado que streams nao oferecemrecursos de baixo nıvel.

A maior vantagem de utilizar a interface de streams e que o conjunto de funcoes para realizaroperacoes 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 prove apenasoperacoes simples para transferencia de blocos de caracteres, enquanto a interface de streamsprove funcoes poderosas para formatacao de funcoes de I/O, bem como funcoes orientadas acaracter e linhas.

E importante ressaltar que streams sao mais portaveis que descritores de arquivos, pois nemtodas as operacoes sobre descritores de arquivos estao implementadas em outros sistemas opera-cionais – sobretudo o conjunto de operacoes GNU, apesar de que grande parte delas encontram-se no padrao POSIX 1.1

3.3 StreamsQuando a funcao main do programa e invocada, ela ja disponibiliza tres streams abertas eprontas para o uso. Elas representam os canais standard de entrada e saıda que foram esta-belecidos para o processo, e estao declaradas no arquivo header stdio.h:

• FILE *stdin: e a stream de entrada padrao, que e a origem normal de entrada de dadosem um programa;

Page 23: Apostila C 2.0

CAPITULO 3. EXPLORANDO OS RECURSOS BASICOS DO SISTEMA OPERACIONAL22

• FILE *stout: e a stream padrao de saıda, que e usada para imprimir os dados de saıda doprograma;

• FILE *stderr: da mesma forma que a stream stdout, porem para imprimir mensagens deerro e de diagnostico pelo programa.

Algumas das operacoes mais comuns de serem realizadas sobre streams sao:

• FILE *fopen (const char *path, const char *mode);abre o arquivo apontado pela string “path” e retorna uma stream associada a ele. Osseguintes modos sao utilizados para abrir o arquivo:

– r: abre um arquivo texto para leitura, posicionando a stream no inıcio do arquivo.

– r+: abre um arquivo texto para leitura e escrita, posicionando a stream no inıcio doarquivo.

– w: trunca o arquivo para zero bytes e cria o arquivo texto para escrita. A stream eposicionada no inıcio do arquivo.

– w+: abre o arquivo para leitura e escrita. O arquivo e criado caso ele nao exista, outruncado caso contrario. A stream e posicionada no inıcio do arquivo.

– a: abre o arquivo para append (escrita no final do arquivo). Ele e criado caso elenao exista, e a stream e posicionada no final do arquivo.

– a+: abre o arquivo para leitura e append. Ele e criado caso ele nao exista, e a streame posicionada no final do arquivo.

A string modo pode ainda incluir a letra “b” apos indicar o modo desejado para explicitarque o arquivo a ser trabalhado sera binario, e nao texto. Apesar de nao ter efeito em umprograma que ira operar no GNU/Linux, ela e muito importante caso o programa ira rodarem algum ambiente nao-UNIX.

• int fclose (FILE *stream);desassocia a stream “stream” do arquivo ao qual ela estava operando. Caso a streamestivesse sendo usada para dados de saıda, os dados em buffer sao escritos antes, com ouso da operacao fflush(3).

• int fflush (FILE *stream);forca a escrita de todos os dados em espaco de usuario que estejam mantidos em bufferpara a saıda informada ou atualizam a stream “stream” atraves da funcao write associadaa ela.

• int feof (FILE *stream);testa o indicador de fim-de-arquivo para a stream apontada por “stream”, retornando umvalor nao-zero caso este indicador esteja setado, ou 0 caso ainda nao seja fim de arquivo.

• 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 final de string.

• char *fgets (char *s, int size, FILE *stream);le size caracteres a partir de stream e os armazena no buffer apontado por s, que ja deveestar pre-alocado. A leitura encerra apos encontrar EOF ou uma nova linha. Se umcaracter de nova linha for lida, ele e armazenada no buffer. Um caracter “0” e armazenado apos o ultimo caracter do buffer.

• size t fread(void *ptr, size t size, size t nmemb,FILE *stream);le nmemb elementos de dados, cada um tendo o tamanho de size bytes, a partir da streamapontada por stream, e armazenando-os no local apontado por ptr. Esta operacao costumaser utilizada para operacoes em arquivos binarios. fread retorna o numero de itens lidoscom sucesso.

Page 24: Apostila C 2.0

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 streamapontada por stream, obtidos a partir do local apontado por ptr. Esta operacao costumaser utilizada para operacoes em arquivos binarios. fwrite retorna o numero de itensescritos com sucesso.

• int fseek (FILE *stream, long offset, int whence);posiciona o indicador de posicao de arquivo para a stream apontada por stream. A novaposicao, medida em bytes, e obtida atraves da soma de offset bytes para a posicao es-pecificada por whence. Caso whence seja definido como SEEK SET, SEEK CUR ouSEEK END, o offset e relativo ao inıcio do arquivo, a posicao corrente do indicador ouao final do arquivo, respectivamente.

• long ftell (FILE *stream);obtem o valor atual do indicador de posicao de arquivo para a stream apontada por stream.

• void rewind (FILE *stream);aponta o indicador de posicao de arquivo para a stream apontada por stream para o inıciodo 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 umoutro para escrita, escrevendo apenas as linhas pares para o segundo.

1 #include <stdio .h>2 #include <unistd .h>3

4 int5 copia arquivo ( char ∗origem , char ∗destino )6 {7 FILE ∗stream origem, ∗ stream destino ;8 char buffer [1024];9 int i ;

10

11 /∗ abre arquivos de origem e destino , criando este ultimo ∗/12 stream origem = fopen ( origem , ”r” );13 if (! stream origem ) {14 perror ( ”fopen” );15 return −1;16 }17

18 stream destino = fopen ( destino , ”w”);19 if (! stream destino ) {20 perror ( ”fopen” );21 fclose ( stream origem );22 return −1;23 }24

25 /∗ processa linhas do arquivo origem ∗/26 i = 1;27 while (! feof ( stream origem )) {28 fgets ( buffer , sizeof ( buffer ), stream origem );29

30 if ( i % 2 == 0) /∗ numero par ∗/31 fputs ( buffer , stream destino );

Page 25: Apostila C 2.0

CAPITULO 3. EXPLORANDO OS RECURSOS BASICOS DO SISTEMA OPERACIONAL24

32

33 i++;34 }35

36 /∗ fecha as streams e retorna ∗/37 fclose ( stream origem );38 fclose ( stream destino );39 return 0;40 }41

42 int43 main ( int argc , char ∗∗argv)44 {45 int res ;46

47 if ( argc != 3) {48 printf ( ”Sintaxe: %s <arq origem> <arq destino>\n”, argv [0]);49 exit (1);50 }51

52 res = copia arquivo ( argv [1], argv [2]);53 if ( res < 0) {54 fprintf ( stderr , ”Um erro ocorreu durante a copia\n”);55 exit (1);56 }57 exit (0);58 }

3.4 Descritores de ArquivosAssim como quando operando sobre streams, quando utilizando descritores de arquivos temosos tres canais de entrada e saıda abertos automaticamente (stdin, stdout e stderr, ou 0, 1 e 2,respectivamente).

Um conceito importante e o de que em sistemas operacionais UNIX, tudo e um arquivo. Ouseja, dispositivos de rede, de terminais, de som, vıdeo, diretorios e os proprios arquivos de dadosdo usuario sao representados por arquivos. Desta forma e possıvel manipular todos os disposi-tivos do sistema com o uso de operacoes sobre arquivos: por isto as operacoes sobre descritoresde arquivos sao tao importantes.

As operacoes basicas sobre descritores de arquivos sao:• int open(const char *pathname, int flags);

abre o arquivo especificado por pathname, retornando um descritor de arquivo represen-tado por um inteiro. As flags devem ser O RDONLY, O WRONLY ou O RDWR, querequisitam a abertura do arquivo no modo somente leitura, somente escrita, ou leitura eescrita, respectivamente. Estes modos podem ser combinados via bitwise OR com zeroou mais opcoes, dentre as quais estao:

– O CREAT: se o arquivo nao existe, ele sera criado. Neste caso, o prototipo dafuncao open listado abaixo sera utilizado.

– O EXCL: quando usado em conjunto com O CREAT e o arquivo ja existir, a funcaoretornara um erro.

– O TRUNC: caso o arquivo esteja sendo aberto em modo de escrita em conjuntocom este modo, o arquivo sera truncado para 0 bytes.

Page 26: Apostila C 2.0

CAPITULO 3. EXPLORANDO OS RECURSOS BASICOS DO SISTEMA OPERACIONAL25

– O APPEND: abre o arquivo no modo append, para adicionar novos dados ao finaldo arquivo.

– O NOFOLLOW: caso pathname seja um link simbolico, a chamada e interrompidae retorna um erro.

– O DIRECTORY: caso pathname nao seja um diretorio, a chamada retorna um erro.

• int open(const char *pathname, int flags, mode t mode);caso o modo O CREAT seja passado como flag para a funcao open, este prototipo deveraser utilizado, para explicitar o modo no qual o arquivo sera aberto. Maiores informacoessobre os parametros podem ser consultados na pagina manual do open(2).

• int creat(const char *pathname, mode t mode);esta funcao e equivalente a chamada open com as flags 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. Podemosdestacar:

• ssize t read(int fd, void *buf, size t count);le ate count bytes a partir da posicao corrente do arquivo representado pelo descritor fdpara o buffer apontado por buf. O valor retornado informa quantos bytes foram lidos comsucesso, ou -1 em caso de erro.

• ssize t write(int fd, const void *buf, size t count);escreve ate count bytes no arquivo representado pelo descritor fd a partir do buffer apon-tado por buf. O valor retornado informa quantos bytes foram escritos com sucesso, ou -1em caso de erro.

• off t lseek(int fildes, off t offset, int whence);opera da mesma forma que o fseek(3), porem sobre descritores de arquivos. fildesrefere-se ao descritor de arquivo que sera utilizado na operacao de seek, offset informaquantos bytes serao movimentados na operacao e whence informa a posicao relativa aqual sera feito o seek.

• int fstat(int filedes, struct stat *buf);retorna informacoes sobre o arquivo representado pelo descritor filedes, armazenando-asno endereco da struct stat apontada por buf. Entre as informacoes retornadas, estao aquantidade de hard links feitas para este arquivo, o seu inode number, a data de ultimaalteracao e de ultimo acesso, entre outros.

O exemplo abaixo lida com um arquivo binario atraves de descritores de arquivos. Nesteexemplo, ele adiciona um novo campo do mesmo tipo de estrutura da qual ele e composto aposseu ultimo byte de dados, e em seguida le e imprime o seu conteudo.

1 #include <stdio .h>2 #include <unistd .h>3 #include < stdlib .h> /∗ atoi (3) ∗/4 #include <sys/ types .h> /∗ open (2) ∗/5 #include <sys/ stat .h> /∗ open (2) ∗/6 #include <sys/ fcntl .h> /∗ open (2) ∗/7

8 struct informacao {9 int quantidade ;

10 float valor ;11 char nome[128];12 };13

Page 27: Apostila C 2.0

CAPITULO 3. EXPLORANDO OS RECURSOS BASICOS DO SISTEMA OPERACIONAL26

14 int15 cria arquivo ( char ∗nome)16 {17 int fd ;18

19 fd = open (nome, O RDWR | O CREAT | O TRUNC, S IRWXU | S IRWXG | S IRWXO);20 if ( fd < 0) {21 perror ( ”open”);22 exit (1);23 }24 return fd ;25 }26

27 void28 le arquivo ( int fd)29 {30 size t n;31 struct informacao info ;32

33 lseek ( fd , 0, SEEK SET);34 while (1) {35 n = read ( fd, &info , sizeof ( info ));36 if ( n == 0) {37 /∗ fim de arquivo ∗/38 break;39 }40

41 printf ( ”nome: %s\nvalor: %f\nquantidade: %d\n\n”,42 info .nome, info . valor , info . quantidade );43 }44 }45

46 void47 adiciona entrada ( int fd , char ∗numero, char ∗arquivo )48 {49 ssize t n;50 int num;51 struct informacao info ;52

53 num = atoi ( numero);54

55 info . quantidade = 10 ∗ num;56 info . valor = 123.45 ∗ ( float ) num;57 snprintf ( info .nome, sizeof ( info .nome), ”arquivo %s” , arquivo );58

59 lseek ( fd , 0, SEEK END);60 n = write ( fd, & info , sizeof ( info ));61 if ( n < 0)62 perror ( ”write” );63 }64

65 int66 main ( int argc , char ∗∗argv)67 {

Page 28: Apostila C 2.0

CAPITULO 3. EXPLORANDO OS RECURSOS BASICOS DO SISTEMA OPERACIONAL27

68 int fd ;69

70 if ( argc != 3) {71 fprintf ( stderr , ”Sintaxe: %s <arquivo destino> <algum numero>\n”, argv[0]);72 exit (1);73 }74

75 fd = open (argv [1], O RDWR);76 if ( fd < 0) {77 perror ( ”open”);78 printf ( ”tentando criar arquivo ...\ n\n”);79 fd = cria arquivo ( argv [1]);80 }81

82 adiciona entrada ( fd , argv [2], argv [1]);83 le arquivo ( fd );84

85 close ( fd );86 exit (0);87 }

Page 29: Apostila C 2.0

Capıtulo 4

CGI: Common GatewayInterface

O CGI define uma maneira de interacao entre o web browser e programas externos de geracaode conteudo, normalmente referidos como programas CGI ou scripts CGI.

Existem duas principais diferencas entre criar um programa convencional e um CGI. Primeira-mente, toda a saıda do programa CGI deve ser precedida por um header do tipo MIME. Isto eum cabecalho HTTP que informa ao client o tipo de conteudo que ele esta recebendo. Na maiorparte do tempo, o conteudo sera:Content-type: text/html

Em segundo lugar, toda a saıda precisa estar formatada em HTML ou em algum outro for-mato que o browser sera capaz de mostrar. Na maior parte do tempo isto e HTML, mas ocasio-nalmente podera ser necessario escrever algum programa que gere a saıda de uma imagem GIFou algum outro conteudo nao-HTML.

Fora estes dois detalhes, um programa em CGI parece-se com um programa convencionalqualquer. O exemplo abaixo lista o conteudo de um arquivo texto:

1 #include <stdio .h>2 #include <unistd .h>3

4 #define ARQUIVO ”/tmp/teste.txt”5

6 int7 main ( int argc , char ∗∗argv)8 {9 char buffer [256];

10 FILE ∗fd;11

12

13 printf (”Content−type: text /html\r\r\n\n”);14 printf (”<html><head><title> Conteudo do arquivo %s </title></head>\n”,15 ARQUIVO);16

17 fd = fopen ( ARQUIVO, ”r”);18 if (! fd ) {19 printf ( ”<b> ERRO </b>: arquivo %s ano existe\n”, ARQUIVO);20 printf (”</body></html>\n”);21 exit (1);

28

Page 30: Apostila C 2.0

CAPITULO 4. CGI: COMMON GATEWAY INTERFACE 29

22 }23

24

25 while (! feof ( fd )) {26 fgets ( buffer , sizeof ( buffer ), fd );27 printf ( ”%s <br>\n”, buffer);28 }29

30 fclose ( fd );31 printf (”</body></html>\n”);32 exit (0);33 }

Apos a compilacao do programa, basta copia-lo para o diretorio cgi-bin do Apache, oudo httpd em uso, e acessa-lo via browser, como no exemplo abaixo:

http://localhost/cgi-bin/teste

No entanto, e bastante comum o uso de alguma biblioteca para facilitar a formatacao dosdados em HTML, para evitar erros e agilizar o desenvolvimento do programa CGI. Existemvarias bibliotecas disponıveis cadastradas no site da Freshmeat1.

1ver http://www.freshmeat.net

Page 31: Apostila C 2.0

Capıtulo 5

Bibliotecas dinamicas

O objetivo de bibliotecas dinamicas e a centralizacao de implementacao de funcoes em bibliote-cas especıficas, permitindo que o gerenciamento e atualizacao das mesmas se torne propagavela todos os programas que usam suas funcoes de forma trivial.

No GNU/Linux, existe o conceito de shared objects, que e analogo ao conceito de DLL doMicrosoft Windows.

Para utilizar bibliotecas dinamicas no GNU/Linux, se usam as funcoes dlopen(3) edlclose(3). Para carregar os sımbolos das funcoes a serem utilizadas a funcao dlsym(3)deve ser utilizada.

Um exemplo do uso de bibliotecas dinamicas pode ser visto a seguir:

1 #include <stdio .h>2 #include <dlfcn .h>3

4 int5 main ( int argc , char ∗∗argv)6 {7 void ∗ biblioteca ;8 double (∗seno)(double);9 char ∗erro ;

10

11 biblioteca = dlopen ( ”libm.so” , RTLD LAZY);12 if (! biblioteca ) {13 fprintf ( stderr , ”%s\n”, dlerror ());14 exit (1);15 }16

17 seno = dlsym ( biblioteca , ” simbolo nao existe ” );18 if (( erro = dlerror ()) != NULL) {19 fprintf ( stderr , ”%s\n”, erro );20 exit (1);21 }22

23 printf ( ”%f\n”, (∗seno )(2.0));24 dlclose ( biblioteca );25 exit (0);26 }

Existem basicamente 2 modos para resolucao dos sımbolos disponibilizados por uma bi-blioteca: RTLD LAZY, que significa que os ponteiros para funcoes nao resolvidos em tempo de

30

Page 32: Apostila C 2.0

CAPITULO 5. BIBLIOTECAS DINAMICAS 31

compilacao devem ser apenas resolvidos pela biblioteca dinamica em tempo de execucao, ouRTLD NOW, que significa que todos os sımbolos da biblioteca dinamica devem ser resolvidos naabertura da mesma, sendo que a abertura da biblioteca devera falhar caso algum sımbolo naopossa ser definido.

1 #include <stdio .h>2 #include <dlfcn .h>3

4 int5 main ( int argc , char ∗∗argv)6 {7 void ∗ biblioteca ;8 double (∗seno)(double);9 char ∗erro ;

10

11 biblioteca = dlopen ( ”libm.so” , RTLD NOW);12 if (! biblioteca ) {13 fprintf ( stderr , ”%s\n”, dlerror ());14 exit (1);15 }16

17 seno = dlsym ( biblioteca , ”sin” );18 if (( erro = dlerror ()) != NULL) {19 fprintf ( stderr , ”%s\n”, erro );20 exit (1);21 }22

23 printf ( ”%f\n”, (∗seno )(2.0));24 dlclose ( biblioteca );25 exit (0);26 }

Para usar as funcoes de bibliotecas dinamicas a biblioteca dl deve ser especificada em tempolinkagem, tal como:

$ gcc dl-now.c -o dl-now -ldl

5.1 Criacao de bibliotecas dinamicasPara criar bibliotecas dinamicas no GNU/Linux o processo e simples: basta compilar o codigocom a flag -shared, gerando a biblioteca.

Um exemplo de uma biblioteca simples que exporta apenas uma funcao pode ser visto aseguir:

1 #include <stdio .h>2

3 int4 mundo (void)5 {6 printf ( ”Bem−vindo ao GNU/Linux\n”);7 }

Para compilar a biblioteca, basta:

$ gcc minha_biblioteca -o libminha_biblioteca.so -shared

Page 33: Apostila C 2.0

Capıtulo 6

Processos e threads

Existem duas maneiras de criar novos fluxos de processos no GNU/Linux. Uma delas e atravesda chamada de sistema fork(2), e a outra atraves da criacao de threads, que podem ser vistascomo processos leves [STE 98]

6.1 ProcessosE possıvel criar processos atraves da chamada de sistema fork(2). A chamada de sistema naorecebe nada como parametro, retorna os seguintes valores:

• 0: Sinalizando que o processo que esta executando no momento e o do filho criado;

• >0: Sinalizando que o processo que esta executando no momento e o do pai;

• <0: Sinaliza que a chamada de sistema nao pode ser completada com sucesso.

Um exemplo de criacao de um sub-processo pode ser visto no exemplo abaixo:

1 #include <stdio .h>2 #include <unistd .h>3 #include < stdlib .h>4 #include < string .h>5 #include <sys/ types .h>6 #include <sys/wait .h>7

8 int9 main ( int argc , char ∗∗argv)

10 {11 int status ;12 pid t pid ;13

14 pid = fork ();15 if ( pid == 0) {16 char ∗∗args ;17

18 printf ( ”Processo filho executando \” ls −a\”\n”);19 args = ( char ∗∗) malloc ( sizeof ( char ∗) ∗ 3);20 args [0] = strdup ( ” ls” );21 args [1] = strdup ( ”−a”);22 args [2] = NULL;23

32

Page 34: Apostila C 2.0

CAPITULO 6. PROCESSOS E THREADS 33

24 execvp ( args [0], args );25 free ( args [0]);26 free ( args [1]);27 free ( args );28

29 } else if ( pid > 0) {30 printf ( ”Processo pai aguardando processo filho ..\ n” );31 waitpid ( pid, & status , WUNTRACED);32

33 if ( WIFEXITED(status))34 printf ( ”processo filho executou normalmente\n”);35 else36 printf ( ”processo filho nao executou normalmente\n”);37

38 } else if ( pid < 0) {39 /∗ algum erro ocorreu ∗/40 perror ( ”fork” );41 }42

43 exit (0);44 }

A funcao execvp(3) e utilizada para trocar a imagem do processo corrente pela imagemde um novo processo. O primeiro argumento para ela e o pathname para um programa, e osegundo argumento e um array terminado por NULL, descrevendo os argumentos que este pro-grama ira receber. O conjunto do uso do fork(3) com o execvp(3) e waitpid(3) apre-senta uma semantica semelhante a funcao spawnl() do Microsoft Windows, com a excessaode que a spawnl() ja explicita em seus argumentos se o processo pai deve aguardar pelo seutermino de execucao ou nao.

Outra funcao utilizada na listagem do programa acima e a waitpid(3), que aguarda pelotermino da execucao de algum processo. Os parametros enviados a ela sao o pid do processo a seraguardado, um ponteiro para um inteiro, onde serao armazenas informacoes sobre a execucaodo processo (como sinais que foram entregues a ele para que ele fosse encerrado e valor deretorno da execucao do processo), e uma constante chamada WUNTRACED, que significa que eladeve parar de aguardar pelo filho que ja teve sua execucao parada e se encontra em um statusdesconhecido.

6.2 ThreadsThreads tem o mesmo proposito de um processo: gerar um novo fluxo de execucao. No entanto,elas sao mais adequadas para uso em programas que precisam disparar muitos processos filhos,devido ao fato de elas implementarem o conceito de processos leves.

Todas as threads em um processo compartilham o estado deste processo. Elas residem nomesmo enderecamento de memoria, veem as mesmas funcoes e os mesmos dados. As maioresvantagens de utilizar threads em programas sao:

• Ganhos de performance em hardware multiprocessado;

• Aumento da responsividade do programa;

• Mudanca de metodos de comunicacao entre os processos;

• Uso eficiente dos recursos do sistema;

• Apenas um binario executa bem tanto em arquiteturas SMP (Symmetric Multi Processors)quanto UP (Uni Processor);

• Possibilidade de criar programas bem estruturados;

Page 35: Apostila C 2.0

CAPITULO 6. PROCESSOS E THREADS 34

• Apenas um unico arquivo fonte pode ser usado em multiplas plataformas.

Uma thread consiste de uma stack e um stack pointer, um program counter e algumas in-formacoes a respeito de sua instancia, como prioridades de escalonamento e mascaras de sinal,armazenadas na sua estrutura. Alem disso, os registradores da CPU sao tambem armazenados(sendo que a stack pointer e o program counter sao alguns destes registradores).

O Linux utiliza, ate a serie 2.4, o modelo de threads POSIX, aprovado pelo padrao IEEE1003.1b-1993 [Ame 94]. Para dar suporte a este modelo, as distribuicoes GNU/Linux fornecema biblioteca LinuxThreads, que implementa grande parte das exigencias feitas por este padrao.A serie 2.6 do kernel Linux esta fazendo uso de um outro modelo de threads, chamado NPTL(Native POSIX Threading Library) [The 2003]. Este texto baseia-se na implementacao das Lin-uxThreads.

Em relacao as threads Win32, as threads POSIX sao muito mais leves, contendo primi-tivas mais simples de uso. Uma outra caracterıstica e a de que threads Win32 mantem umadependencia entre janelas e threads. Como nem todas threads constroem e usam janelas, istoacarreta em um overhead desnecessario para os fluxos de execucao.

Para criar um novo fluxo de execucao com o uso de threads no GNU/Linux, utiliza-se afuncao pthread create(3), definida pelo seguinte prototipo:

#include <pthread.h>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 concorrentementecom a thread que a gerou. A nova thread aplica a funcao start routine, passando arg como seuargumento. A nova thread termina explicitamente, chamando a funcao pthread exit(3), ouimplicitamente, apenas retornando da funcao start routine. O ultimo caso e equivalente a chamarpthread exit(3) com o resultado retornado pela funcao start routine como retorno.

O argumento attr especifica algum atributo a ser aplicado para a nova thread, ou pode serNULL caso os atributos padrao devam ser utilizados: a thread criada e joinable, ou seja, seusrecursos de memoria nao serao liberados ate que algum processo sincronize com esta thread, etem uma polıtica de escalonamento normal (nao-realtime). Os atributos a serem passados para athread devem ser inicializados com a funcao pthread attr init(3), e apos configuradoscom o uso de alguma das funcoes listadas na pagina manual pthread attr init(3).

Para sincronizar um processo (ou uma thread) com o resultado de uma outra thread, utiliza-sea funcao pthread join(3):

#include <pthread.h>int pthread join ( pthread t th , void ∗∗ thread return );

pthread join(3) suspende a execucao da thread corrente ate que a thread identificadapor th termine, tanto explicitamente, via pthread exit(3), ou sendo cancelada, atraves dafuncao pthread cancel(3). Caso o argumento thread return seja nao nulo, ele e utilizadopara armazenar o valor de retorno da thread th, devendo ser desalocado apos ter sido utilizadopelo programa.

O exemplo abaixo ilustra a criacao e sincronizacao de uma thread no GNU/Linux. Para com-pilar este programa, e necessario passar a flag -lpthread, para que o programa seja linkadocom a biblioteca de threads libpthread.so e possa fazer uso de suas funcoes.

1 #include <stdio .h>2 #include < stdlib .h>3 #include <unistd .h>4 #include <pthread.h>5

6 void ∗7 calcula ( void ∗dados)8 {

Page 36: Apostila C 2.0

CAPITULO 6. PROCESSOS E THREADS 35

9 int ∗ ret = NULL;10 int ∗ valor = ( int ∗) dados;11

12 ret = ( int ∗) malloc ( sizeof ( int ));13 ∗ ret = ∗ valor ∗ ∗ valor ;14

15 pthread exit ( ret );16 }17

18 int19 main ( int argc , char ∗∗argv)20 {21 pthread t tid ;22 int ∗dados , ∗ resultado ;23

24 dados = ( int ∗) malloc ( sizeof ( int ));25 ∗dados = 5;26

27 pthread create (& tid , NULL, calcula , dados );28 pthread join ( tid , ( void ∗∗) & resultado );29

30 printf ( ”%d ∗ %d = %d\n”, ∗dados, ∗dados, ∗ resultado );31

32 free ( dados );33 free ( resultado );34 exit (0);35 }

Como threads podem compartilhar recursos entre si, nao e necessario fazer uso de mecanis-mos pesados de intercomunicacao entre processos. A forma mais comum de compartilhar dadosentre diferentes threads e atraves de variaveis globais a elas, e mantendo a coerencia das leiturase escritas destes dados com o uso de mecanismos de exclusao mutua (mutexes) e semaforos.

O uso de mutexes e feito basicamente atraves das primitivas pthread mutex lock(3)e pthread mutex unlock(3). Na primeira primitiva, caso alguma outra thread ja tenhaadquirido o lock, a thread que tentou pega-lo pela segunda vez tem sua execucao interromp-ida ate que a outra thread o libere. Caso nao desejava-se que a execucao seja interrompida,pthread mutex trylock(3) pode ser usado: caso o lock nao esteja disponıvel ela con-tinua a execucao do programa.

O exemplo abaixo mostra como compartilhar um dado entre duas threads de execucao, uti-lizando mecanismos de exclusao mutua:

1 #include <stdio .h>2 #include < stdlib .h>3 #include <unistd .h>4 #include <pthread.h>5

6 static int valor = 0;7 pthread mutex t mutex = PTHREAD MUTEX INITIALIZER;8

9 void ∗10 incrementa ( void ∗dados)11 {12 int i ;13

14 for ( i = 0; i < 10000; i++) {

Page 37: Apostila C 2.0

CAPITULO 6. PROCESSOS E THREADS 36

15 pthread mutex lock (&mutex);16 valor++;17 pthread mutex unlock (&mutex);18 }19 pthread exit ( NULL);20 }21

22 void ∗23 decrementa (void ∗dados)24 {25 int i ;26

27 for ( i = 0; i < 10000; i++) {28 pthread mutex lock (&mutex);29 valor−−;30 pthread mutex unlock (&mutex);31 }32 pthread exit ( NULL);33 }34

35

36 int37 main ( int argc , char ∗∗argv)38 {39 pthread t tid , tid2 ;40

41 pthread create (& tid , NULL, incrementa, NULL);42 pthread create (&tid2 , NULL, decrementa, NULL);43 pthread join ( tid , NULL);44 pthread join ( tid2 , NULL);45

46 printf ( ”valor da variavel global : %d\n” , valor );47 exit (0);48 }

Semaforos tambem tem um uso bastante comuns. Eles funcionam como contadores pararecursos compartilhados entre threads. As operacoes basicas realizadas em semaforos sao incre-mentar o contador atomicamente e aguardar ate que o contador seja nao nulo e decrementa-loatomicamente. Nestes casos, podem ser usadas as funcoes sem init(3), sem wait(3),sem post(3) e sem destroy(3):

#include <semaphore.h>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 semaforo apontado por sem com o valor especificado em value.Este valor indica quantas threads poderao entrar na sessao crıtica ao mesmo tempo. Nos casosmais comuns sao utilizados semaforos binarios, e entao este valor e iniciado com 1. O argumentopshared indica se o semaforo e local ao processo atual (neste caso pshared deve ser zero) ou seele nao e compartilhado entre varios processos (neste caso pshared deve ser nao zero). Noentanto, a implementacao das LinuxThreads nao implementa o compartilhamento de semaforosentre varios processos, retornando um erro na inicializacao do semaforo caso pshared seja umvalor diferente de zero.

Page 38: Apostila C 2.0

CAPITULO 6. PROCESSOS E THREADS 37

O exemplo abaixo apresenta o uso de semaforos para controle de uma sessao crıtica:

1 #include <stdio .h>2 #include < stdlib .h>3 #include <unistd .h>4 #include <pthread.h>5 #include <semaphore.h>6

7 static int valor = 0;8 sem t semaforo;9

10 void ∗11 incrementa ( void ∗dados)12 {13 int i ;14

15 for ( i = 0; i < 10000; i++) {16 sem wait (&semaforo);17 valor++;18 sem post (&semaforo);19 }20 pthread exit ( NULL);21 }22

23 void ∗24 decrementa (void ∗dados)25 {26 int i ;27

28 for ( i = 0; i < 10000; i++) {29 sem wait (&semaforo);30 valor−−;31 sem post (&semaforo);32 }33 pthread exit ( NULL);34 }35

36

37 int38 main ( int argc , char ∗∗argv)39 {40 pthread t tid , tid2 ;41

42 sem init (&semaforo , 0, 1);43

44 pthread create (& tid , NULL, incrementa, NULL);45 pthread create (&tid2 , NULL, decrementa, NULL);46 pthread join ( tid , NULL);47 pthread join ( tid2 , NULL);48

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

Page 39: Apostila C 2.0

Capıtulo 7

TCP/IP

O conjunto de funcoes disponıveis para serem utilizadas na comunicacao atraves do protocoloTCP/IP e muito grande. Este capıtulo visa apresentar a utilizacao de sockets no GNU/Linux paracomunicacao entre cliente e servidor, nos modos bloqueante e nao-bloqueante no IPv4 [STE 98a].

Provavelmente a maneira mais simples de explicar como programar sockets neste ambientee com o uso de um exemplo comentado. Abaixo encontra-se listada uma implementacao de umcliente TCP de uma aplicacao do tipo timeofday. Este cliente estabelece uma conexao TCP comum servidor, e o servidor apenas envia de volta a hora e data atuais, em um formato formatado elegıvel.

1 #include ”timeofday .h”2

3 int4 main ( int argc , char ∗∗argv)5 {6 int sockfd , n;7 char recvline [MAXLINE + 1];8 struct sockaddr in servaddr ;9

10 if ( argc != 2) {11 fprintf ( stderr , ”Sintaxe: %s <endereco IP>\n”, argv [0]);12 exit (1);13 }14

15 if (( sockfd = socket ( AF INET, SOCK STREAM, 0)) < 0)16 err quit ( ”socket” );17

18 memset (&servaddr , 0, sizeof ( servaddr ));19 servaddr . sin family = AF INET;20 servaddr . sin port = htons (13); /∗ servidor de daytime ∗/21

22 if ( inet pton ( AF INET, argv[1], &servaddr . sin addr ) <= 0)23 err quit ( ” inet pton ” );24

25 if ( connect ( sockfd , ( struct sockaddr ∗) & servaddr , sizeof ( servaddr )) < 0)26 err quit ( ”connect” );27

28 while (( n = read ( sockfd , recvline , MAXLINE)) > 0) {29 recvline [n ] = 0; /∗ terminada por NULL ∗/

38

Page 40: Apostila C 2.0

CAPITULO 7. TCP/IP 39

30 if ( fputs ( recvline , stdout ) == EOF)31 err quit ( ” fputs ” );32 }33

34 if ( n < 0)35 err quit ( ”read” );36

37 exit (0);38 }

Este exemplo, quando compilado, deve utilizar a flag -lnsl, para ser linkado com asfuncoes de sockets da Glibc.

Existem varios detalhes a serem considerados neste programa:

• 1: o cabecalho em comum com a aplicacao do servidor. Os detalhes de seu conteudo saomostrados mais a seguir;

• 15: criacao de um socket TCP. A funcao socket cria uma stream (SOCK STREAM) deInternet (AF INET), que e o nome dado para um socket TCP. Esta funcao retorna o de-scritor de um socket, utilizado para realizar as chamadas de funcoes futuras, como aschamadas ao connect e ao read;

• 18 − 23: especificacao do endereco IP de um servidor e uma porta. Uma estrutura dotipo Internet socket address (a sockaddr in sockaddr) e preenchida com o endereco IP doservidor e com o numero da porta. A estrutura e zerada com o uso da funcao memset,configurada para usar a famılia de enderecos AF INET e para usar a porta 13 (que e aporta conhecida para o servidor de daytime em qualquer host TCP/IP que suporte esteservico). O IP fornecido e a porta nesta estrutura precisam estar em formatos especıficos:para isto, a funcao htons (host to network short) para converter o numero da porta, e eutilizada a funcao inet pton (presentation to numeric) para converter o endereco IP emASCII para o formato necessario;

• 25−26: estabelecimento da conexao com o servidor. A funcao connect, quando aplicadaa um socket TCP, estabelece uma conexao TCP com o servidor especificado pela estruturasocket address apontada pelo segundo argumento;

• 28 − 35: le e mostra o reply do servidor. Como o TCP e um protocolo byte-stream,ele nao tem registro sobre limites do conteudo sendo transportado, ou seja: os bytesretornados podem vir de uma unica vez, ou em n pequenas leituras, ate que os ultimosbytes sejam lidos. Desta forma, nem sempre e garantido que com um unico read venhatoda a resposta do servidor. Assim, sempre que estivermos lendo o conteudo de um socketTCP, e necessario faze-lo em um loop, terminando-o quando read retornar 0 (o outro ladoencerrou a conexao) ou um valor menor do que 0 (quando ocorre um erro).

Como o programa anterior nao pode executar corretamente sem um servidor, e necessarioescrever um. A versao do servidor para este cliente encontra-se listado logo abaixo.

1 #include ”timeofday .h”2

3 int4 main ( int argc , char ∗∗argv)5 {6 int listenfd , connfd;7 struct sockaddr in servaddr ;8 char buff [MAXLINE];9 time t ticks ;

10

11 if (( listenfd = socket ( AF INET, SOCK STREAM, 0)) < 0)12 err quit ( ”socket” );

Page 41: Apostila C 2.0

CAPITULO 7. TCP/IP 40

13

14 memset (&servaddr , 0, sizeof ( servaddr ));15 servaddr . sin family = AF INET;16 servaddr . sin addr . s addr = htonl ( INADDR ANY);17 servaddr . sin port = htons (13); /∗ servidor de daytime ∗/18

19 if (( bind ( listenfd , ( struct sockaddr ∗) & servaddr , sizeof ( servaddr ))) < 0)20 err quit ( ”bind” );21

22 if (( listen ( listenfd , MAX CONNECT)) < 0)23 err quit ( ” listen ” );24

25 for (;;) {26 if (( connfd = accept ( listenfd , ( struct sockaddr ∗) NULL, NULL)) < 0)27 err quit ( ”accept” );28

29 ticks = time ( NULL);30 snprintf ( buff , sizeof ( buff ), ”%.24s\r\n”, ctime (& ticks ));31 if (( write ( connfd , buff , strlen ( buff ))) < 0)32 err quit ( ”write” );33

34 close ( connfd );35 }36 }

Assim como no cliente, alguns detalhes precisam ser considerados na versao do servidor:

• 11− 12: cria o socket TCP, identico a versao do cliente;

• 19−20: realiza um binding (amarra) da porta conhecida com o socket. A porta conhecidado servidor (13, para o servico de daytime) e ligada ao socket, preenchendo uma estru-tura de Internet socket address e chamando o bind. E especificado o endereco IP comoINADDR ANY, que permite que o servidor aceite uma conexao em qualquer interface,caso o host tenha mais de uma;

• 22− 23: converte o socket para um listening socket. Quando e feita a chamada ao listen,conexoes ao socket passam a ser aceitas pelo kernel. Estes tres passos, socket, bind elisten sao os passos utilizados para preparar qualquer listening descriptor (descritor deescuta) em um servidor TCP (que neste caso e o listenfd). A constante MAX CONNECTesta especificada no header timeofday.h, e especifica a quantidade maxima de clientes queirao conectar-se ao listening descriptor.

• 26−32: aceita conexoes dos clientes e envia um reply. Normalmente o processo do servi-dor e posto para dormir na chamada a accept, aguardando que uma conexao de um clientechegue e seja aceita. Uma conexao TCP usa o que e chamado de um three-way hand-shake para estabelecer uma conexao, e quando este handshake e completado, a funcaoaccept retorna, e o valor de retorno desta funcao e um novo descritor (connfd), chamadode connected descriptor (descritor do cliente conectado). O novo descritor e usado paracomunicar com o novo cliente. Um novo descritor e retornado pelo accept a cada clienteque conecta ao servidor.A funcao time retorna a hora e data atual, na forma dos segundos que ocorreram desde aepoca do Unix: 1o de janeiro de 1970, as 00:00:00 horas UTC (Coordinated UniversalTime). A funcao ctime converte o valor inteiro retornado em uma forma legıvel parahumanos. Esta string e entao escrita de volta para o cliente.

• 34: a conexao com o cliente e encerrada.

O arquivo header utilizado em ambos exemplos encontra-se abaixo:

Page 42: Apostila C 2.0

CAPITULO 7. TCP/IP 41

1 #ifndef timeofday h2 #define timeofday h 13

4 #include <stdio .h>5 #include <unistd .h>6 #include < stdlib .h>7 #include <sys/ types .h>8 #include <sys/ socket .h> /∗ socket () ∗/9 #include <arpa/ inet .h> /∗ inet pton () ∗/

10 #include <netinet / in .h> /∗ htons () ∗/11 #include < string .h> /∗ memset() ∗/12 #include <time.h> /∗ time () ∗/13

14 #define err quit (msg ) ({ perror (msg); exit (1); })15

16 /∗ SOMAXCONN fica definido em bits/socket .h ∗/17 #define MAX CONNECT SOMAXCONN18 #define MAXLINE 102419

20 #endif /∗ timeofday h ∗/

Apos ter o servidor compilado e rodando em um terminal, um outro terminal pode ser uti-lizado para efetuar a comunicacao entre o cliente e o servidor. Para isto, o cliente deve ser exe-cutado passando um endereco IP valido como parametro. Como estamos especificando qualquerinterface para escuta no servidor, pode ser utilizada a interface loopback:

$ timeofday-client 127.0.0.1Mon Oct 13 00:50:12 2003

Estes exemplos funcionam e podem servir como base para programas maiores. No entanto,eles ainda usam valores numericos para referenciar o servidor, e muitas vezes e desejavel utilizaro 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 estru-tura do tipo hostent para o host name, que pode estar representado tanto na notacao deum endereco IP numerico quanto como um hostname. A estrutura hostent, definida no<netdb.h>. e composta pelos seguintes elementos:

struct hostent {char ∗h name; /∗ nome oficial do host ∗/char ∗∗ h aliases ; /∗ lista de aliases , terminada por NULL ∗/int h addrtype ; /∗ tipo do endereco do host ∗/int h length ; /∗ tamanho do endereco ∗/char ∗∗ h addr list ; /∗ lista de anderecos , terminada por NULL ∗/

}#define h addr h addr list [0] /∗ backward compatibility ∗/

• struct hostent *gethostbyaddr(const char *addr, int len, int type): retorna um ponteiropara uma estrutura do tipo hostent para o endereco do host especificado em addr detamanho len e de tipo type. O unico tipo valido de endereco e atualmente AF INET.

• struct servent *getservbyname(const char *name, const char *proto): retorna um pon-teiro para uma estrutura do tipo servent para a linha do arquivo services do diretoriode configuracoes da distribuicao, normalmente /etc/services, que coincida com oservico name que utiliza o protocolo proto. Caso proto seja NULL, qualquer protocolosera aceito.

Page 43: Apostila C 2.0

CAPITULO 7. TCP/IP 42

A estrutura servent e especificada no arquivo <netdb.h> como segue:

struct servent {char ∗s name; /∗ nome oficial do servico ∗/char ∗∗ s aliases ; /∗ lista de aliases , terminada por NULL ∗/int s port ; /∗ numero da porta ∗/char ∗ s proto ; /∗ protocolo a ser usado ∗/

}

• struct servent *getservbyport(int port, const char *proto): retorna um ponteiro para umaestrutura do tipo servent para a linha do arquivo services que coincida com a portaespecificada em port, usando o protocolo proto. Caso o protocolo seja NULL, qual-quer um sera aceito no matching. A porta deve estar informada em network byte order(htonl(3)).

O proximo exemplo mostra como estes recursos podem ser incorporados ao programa ante-rior:

1 #include ”timeofday .h”2

3 int4 main ( int argc , char ∗∗argv)5 {6 int sockfd , n;7 char recvline [MAXLINE + 1];8 struct sockaddr in servaddr ;9 struct hostent ∗ht ;

10

11 if ( argc != 3) {12 fprintf ( stderr , ”Sintaxe: %s <endereco IP> <porta>\n”, argv[0]);13 exit (1);14 }15

16 if (( sockfd = socket ( AF INET, SOCK STREAM, 0)) < 0)17 err quit ( ”socket” );18

19 if (( ht = gethostbyname (argv [1])) < 0)20 err quit ( ”gethostbyname”);21

22 memset (&servaddr , 0, sizeof ( servaddr ));23 servaddr . sin family = AF INET;24 servaddr . sin port = htons ( atoi ( argv [2]));25 memcpy (&servaddr.sin addr , ht−>h addr list [0], sizeof ( struct in addr ));26

27 if ( connect ( sockfd , ( struct sockaddr ∗) & servaddr , sizeof ( servaddr )) < 0)28 err quit ( ”connect” );29

30 while (( n = read ( sockfd , recvline , MAXLINE)) > 0) {31 recvline [n ] = 0; /∗ terminada por NULL ∗/32 if ( fputs ( recvline , stdout ) == EOF)33 err quit ( ” fputs ” );34 }35

36 if ( n < 0)37 err quit ( ”read” );38

Page 44: Apostila C 2.0

CAPITULO 7. TCP/IP 43

39 exit (0);40 }

Podemos ver na linha 19 que agora e feita uma chamada para gethostbyname(3), querealiza o lookup do nome fornecido como parametro na linha de comando. Na linha 24, infor-mamos a porta atraves do parametro passado pelo usuario (a funcao atoi(3) realiza a con-versao de uma string para um inteiro). A forma de realizar a copia da interface a ser utilizadatambem foi mudada: na linha 25, vemos que a partir de agora o endereco e copiado a partir dasinformacoes disponıveis na estrutura hostent− >h addr list. Utilizamos o primeiro enderecoreferente as interfaces neste host, e conectamos a ele. O restante do programa mantem-se inal-terado.

A seguir vamos analisar as mudancas na versao do servidor:

1 #include ”timeofday .h”2

3 int4 main ( int argc , char ∗∗argv)5 {6 int listenfd , connfd;7 struct sockaddr in servaddr , cliaddr ;8 socklen t len ;9 char buff [MAXLINE];

10 time t ticks ;11

12 if ( argc != 2) {13 printf ( ”Sintaxe: %s <porta>\n”, argv [0]);14 exit (1);15 }16

17 if (( listenfd = socket ( AF INET, SOCK STREAM, 0)) < 0)18 err quit ( ”socket” );19

20 memset (&servaddr , 0, sizeof ( servaddr ));21 servaddr . sin family = AF INET;22 servaddr . sin addr . s addr = htonl ( INADDR ANY);23 servaddr . sin port = htons ( atoi ( argv [1]));24

25 if (( bind ( listenfd , ( struct sockaddr ∗) & servaddr , sizeof ( servaddr ))) < 0)26 err quit ( ”bind” );27

28 if (( listen ( listenfd , MAX CONNECT)) < 0)29 err quit ( ” listen ” );30

31 for (;;) {32 if (( connfd = accept ( listenfd , ( struct sockaddr ∗) & cliaddr , &len )) < 0)33 err quit ( ”accept” );34

35 printf ( ”conexao estabelecida com %s na porta %d\n”,36 inet ntop ( AF INET, &cliaddr.sin addr , buff , sizeof ( buff )),37 ntohs ( cliaddr . sin port ));38

39 ticks = time ( NULL);40 snprintf ( buff , sizeof ( buff ), ”%.24s\r\n”, ctime (& ticks ));41 if (( write ( connfd , buff , strlen ( buff ))) < 0)42 err quit ( ”write” );

Page 45: Apostila C 2.0

CAPITULO 7. TCP/IP 44

43

44 close ( connfd );45 }46 }

As modificacoes foram semelhantes as realizadas no cliente. Na linha 23, e feita a conversaoda string relativa a porta a ser usada para inteiro, passando o resultado ao campo sin port daestrutura servaddr para que esta porta seja usada. A funcao accept agora tambem informaum ponteiro para a estrutura sockaddr cliaddr. Isto permite que o accept armazene dadossobre a conexao do cliente. Estes dados sao entao usados nas linhas 35 − 37: o inet ntopconverte o endereco passado em &cliaddr.sin addr que usa a famılia AF INET para uma string,que e copiada para buffer. Um ponteiro para este buffer e retornado, e usamos este ponteiro paraimprimir o hostname do client. A funcao htohs e usada apenas para converter o numero emnetwork byte order para host byte order, e entao usa seu resultado para imprimı-lo.

O header usado foi levemente modificado, declarando o cabecalho <netdb.h> para proversuporte a chamada gethostbyname(3), alem de ja incluir headers para suporte a chamadaselect(2):

21 #ifndef timeofday h22 #define timeofday h 123

24 #include <stdio .h>25 #include <unistd .h>26 #include < stdlib .h>27 #include <sys/ types .h>28 #include <sys/ socket .h> /∗ socket () ∗/29 #include <arpa/ inet .h> /∗ inet pton () ∗/30 #include <netinet / in .h> /∗ htons () ∗/31 #include < string .h> /∗ memset() ∗/32 #include <netdb.h> /∗ gethostbyname () ∗/33 #include <time.h> /∗ time () ∗/34

35 #include <sys/ select .h> /∗ select () ∗/36 #include <sys/ stat .h>37 #include < fcntl .h>38 #include <errno.h>39

40 #define err quit (msg ) ({ perror (msg); exit (1); })41

42 #define MAXLINE 102443 #define MAX CONNECT 10044

45 #endif /∗ timeofday h ∗/

7.1 SelectUma conexao nao-bloqueante envolve varias operacoes sobre os descritores. Antes de apresenta-las, e necessario introduzir a funcao select(2). Essa funcao aguarda ate que um numero dedescritores de arquivo mudem seu status. Seu prototipo e definido como segue:

/∗ de acordo com o POSIX 1003.1−2001 ∗/include <sys/ select .h>

/∗ de acordo com padroes mais novos ∗/

Page 46: Apostila C 2.0

CAPITULO 7. TCP/IP 45

include <sys/time.h>include <sys/ types .h>include <unistd .h>

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);

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 ja se passou do timeout especifidado.Tres conjuntos independentes de descritores sao monitorados. Os que estao listados em readfdsserao monitorados para verificar se algum caracter fica disponıvel para leitura (para verificar sea leitura nao vai bloquear). Os listados em writefds serao monitorados para verificar se algumaescrita nao ira bloquear, e os em exceptfds serao monitorados quanto a excecoes. Na saıda, osconjuntos sao modificados para indicar os descritores que tiveram seu status alterado.

Para operar esses conjuntos, quatro macros sao disponibilizadas. FD ZERO zera um con-junto. FD SET e FD CLEAR adicionam ou removem um descritor de um conjunto, e FD ISSETtesta para ver se um descritor faz parte do conjunto. Isso e util apos o retorno de select(2),para garantir que o descritor que foi modificado e o mesmo que tinha-se interesse em monitorar.

O valor n indica o descritor de maior numero em qualquer um dos tres conjuntos, mais 1.timeout e o tempo que deve ser aguardado ate que select(2) retorne. Caso timeout sejaNULL, a chamada pode bloquear indefinidamente.

A seguinte estruturas e usada para definir os timeouts:

#include <sys/time.h>struct timeval {

long tv sec ; /∗ segundos ∗/long tv usec ; /∗ micro segundos ∗/

}O valor de retorno de uma chamada ao select(2) e o numero de descritores contigo no

conjunto de descritores, que pode ser zero caso o timeout tenha expirado antes que qualquercoisa acontecesse.

O exemplo abaixo mostra o uso de select(2) para aguardar ate que algum dado seja lidoda stdin. O timeout e configurado para 5 segundos.

1 #include <stdio .h>2 #include <sys/time.h>3 #include <sys/ types .h>4 #include <unistd .h>5

6 int7 main ( int argc , char ∗∗argv)8 {9 fd set rfds ;

10 struct timeval tv ;11 int retval ;12

13 /∗ monitora a stdin (0) ∗/14 FD ZERO (&rfds);15 FD SET (0, &rfds);16

17 tv . tv sec = 5;

Page 47: Apostila C 2.0

CAPITULO 7. TCP/IP 46

18 tv . tv usec = 0;19

20 retval = select (1, & rfds , NULL, NULL, &tv);21 if ( retval ) {22 printf ( ”Dados disponiveis .\n” );23 }24 else25 printf (”Nenhum dado veio em 5 segundos\n”);26

27 exit (0);28 }

Vale notar que apos a chamada a select(2), o valor de tv e desconhecido, e nao deve serusado para obter informacoes a respeito do tempo que se passou. Um tutorial completo no usodo select(2) e fornecido na man page do select tut(2).

7.2 Conexoes nao-bloqueantesApos visto como funciona o uso da operacao select(2), fica mais facil entender o processode uma conexao nao-bloqueante. Antes de apresentar os exemplos de um cliente e um servidorescritos seguindo este modelo, algumas funcoes sao importantes de serem vistas:

• int getsockopt (int s, int level, int optname, void *optval,socklen t *optlen);int setsockopt (int s, int level, int optname,const void *optval, socklen t *optlen);Estas funcoes manipulam as opcoes associadas a um socket s, que podem existir emmultiplos nıveis do protocolo, e estao sempre presentes no nıvel mais alto de sock-ets. Quando as opcoes do socket sao manipuladas, o nıvel em que a opcao reside e onome desta opcao devem ser especificadas. Para manipular opcoes no nıvel de socket,o parametro level e especificado como SOL SOCKET. Para manipular opcoes em algumoutro nıvel do protocolo, o numero do protocolo deve ser informado. Por exemplo, paraindicar que uma opcao sera interpretada pelo protocolo TCP, o parametro level deve contero valor TCP.Os parametros optval e optlen sao usados para acessar os valores das opcoes, quandousado com setsockopt(2). Para getsockopt(2) elas identificam um buffer noqual o valor para a opcao requisitada devera ser retornado. Neste caso, optlen e umparametro informando o tamanho do buffer apontado por optval. Caso nao haja nenhumvalor para ser informado ou retornado, optval pode ser NULL. As opcoes disponıveisestao documentadas na man page do socket(7).optname e passado para o modulo apropriado do protocolo, para que la ele seja interpre-tado. O arquivo header <sys/socket.h> contem definicoes para opcoes a serem utilizadasno nıvel de sockets.

• int fcntl(int fd, int cmd);int fcntl(int fd, int cmd, long arg);int fcntl(int fd, int cmd, struct flock *lock);A funcao fcntl(2) realiza operacoes sobre descritores de arquivo (neste caso, especi-ficados por fd). A operacao e determinada por cmd, e dependendo da operacao, pode serutilizado um parametro extra, que pode ser um long ou um ponteiro para uma struct flock.Esta funcao permite operar com operacoes do tipo close-on-exec, alem de permitir obteras flags de status de um arquivo, realizar advisory lockings (locks baseados em arquivos)e manipular sinais, entre outros.

A seguir e visto o codigo de um servidor que faz o uso de conexoes nao-bloqueantes:

Page 48: Apostila C 2.0

CAPITULO 7. TCP/IP 47

1 #include ”timeofday .h”2

3 void4 processa clientes ( int listenfd , int connfd)5 {6 int select sock in = 1;7 int select sock out = 1;8 int ret ;9 char buff [MAXLINE];

10

11 int max fd = connfd;12 int sock EOF = 0;13 int in size = 0;14 int done = 0;15

16

17 if ( listenfd > max fd)18 max fd = listenfd ;19 max fd++;20

21 while (! done) {22 fd set write set , read set ;23

24 FD ZERO (&write set);25 FD ZERO (&read set);26

27 if ( select sock in )28 FD SET (connfd, &read set );29

30 if ( select sock out )31 FD SET (connfd, &write set );32

33 ret = select ( max fd, & read set , & write set , NULL, NULL);34 if ( ret < 0)35 err quit ( ” select ” );36

37 if ( FD ISSET (connfd, &read set )) {38 in size = read ( connfd , buff , sizeof ( buff ));39 if ( in size == 0)40 sock EOF = 1;41 else if ( in size < 0)42 err quit ( ”read” );43 }44

45 if ( in size > 0) {46 ret = write ( connfd , buff , in size );47 if ( ret < in size ) {48 if ( errno != EAGAIN) {49 err quit ( ”write” );50 } else {51 printf (”EAGAIN em write\n”);52 }53 } else {

Page 49: Apostila C 2.0

CAPITULO 7. TCP/IP 48

54 in size = 0;55 }56 }57

58 if ( sock EOF && in size == 0) {59 close ( connfd );60 done = 1;61 }62

63 select sock out = ( in size > 0 || sock EOF);64 select sock in = (!sock EOF && in size == 0);65 }66 }67

68 int69 main ( int argc , char ∗∗argv)70 {71 int listenfd , optval , connfd;72 struct sockaddr in servaddr ;73 long flags ;74 fd set read set ;75

76 if ( argc != 2) {77 printf ( ”Sintaxe: %s <porta>\n”, argv [0]);78 exit (1);79 }80

81 if (( listenfd = socket ( AF INET, SOCK STREAM, 0)) < 0)82 err quit ( ”socket” );83

84 optval = 1;85 if ( setsockopt ( listenfd , SOL SOCKET, SO REUSEADDR, &optval, sizeof(optval)) < 0)86 err quit ( ” setsockopt ” );87

88 memset (&servaddr , 0, sizeof ( servaddr ));89 servaddr . sin family = AF INET;90 servaddr . sin addr . s addr = htonl ( INADDR ANY);91 servaddr . sin port = htons ( atoi ( argv [1]));92

93 if ( bind ( listenfd , ( struct sockaddr ∗) & servaddr , sizeof ( servaddr )) < 0)94 err quit ( ”bind” );95

96 if ( listen ( listenfd , 5) < 0)97 err quit ( ” listen ” );98

99 flags = fcntl ( listenfd , F GETFL);100 fcntl ( listenfd , F SETFL, flags | O NONBLOCK);101

102 /∗ prepara o select para leitura em listenfd ∗/103 FD ZERO (&read set);104 FD SET (listenfd , & read set );105 if ( select ( listenfd + 1, & read set , NULL, NULL, NULL) <= 0)106 err quit ( ” select ” );107

Page 50: Apostila C 2.0

CAPITULO 7. TCP/IP 49

108 if (! FD ISSET (listenfd , & read set ))109 err quit ( ” select ” );110

111 if (( connfd = accept ( listenfd , NULL, NULL)) < 0)112 err quit ( ”accept” );113

114 flags = fcntl ( listenfd , F GETFL);115 fcntl ( connfd , F SETFL, flags | O NONBLOCK);116 processa clientes ( listenfd , connfd );117 exit (0);118 }

Ao receber uma requisicao de um cliente, este servidor le os dados enviados pelo cliente e osretorna ao cliente. Este e um servico conhecido como um servico de echo. A analise do codigosera feita a partir da funcao main().• 81− 82: cria um socket TCP;

• 84−85: configura as opcoes deste socket com a funcao setsockopt(2). Os parametrospassados a ela indicam que estamos trabalhando no nıvel de socket, com a constanteSOL SOCKET, e com a opcao SO REUSEADDR indicamos que as regras usadas para avalidacao do endereco usada no bind(2) devem permitir a reutilizacao de um enderecolocal. Neste caso em que utilizamos um socket da famılia AF INET, isto significa queo socket deve realizar o bind, a nao ser que exista um outro socket ativo escutando nomesmo endereco;

• 87− 90: especificacao do protocolo, interfaces e portas para usar no bind;• 92− 93: faz o bind;

• 95− 96: prepara o socket para aceitar requisicoes;

• 98 − 99: configura as opcoes do descritor listenfd de forma a manter as opcoes atuais(linha 98), adicionando uma constante informando que ele devera operar no modo nao-bloqueante;

• 102 − 108: prepara o uso do select(2) para o descritor listenfd;

• 110 − 111: aguarda pela conexao de algum cliente;• 113 − 114: apos receber a conexao de um cliente, prepara o descritor novamente para

operar em modo nao bloqueante para as proximas operacoes;• 115: processa a requisicao, explicada abaixo;

• 17 − 19: define o valor maximo dos descritores em uso para utilizar na chamada aoselect(2);

• 24−35: configura os conjuntos de descritores de leitura e escrita para uso no select(2).Apos, e aguardado ate que o status de algum deles seja modificado. Nota-se que nao estasendo usado nenhum timeout nessa operacao;

• 37− 43: caso o descritor connfd tenha sido modificado, o servidor le os dados a partir dosocket representado por este descritor para o buffer buff ;

• 45 − 56: se algum dado foi lido anteriormente, entao o devolve no mesmo socket para ocliente, escrevendo a quantidade de dados lidas ate o momento;

• 58−61: caso ja tenha lido a stream toda e reconhecido o final de arquivo, fecha o descritore encerra a execucao da funcao.

O arquivo utilizado como header e o mesmo timeofday.h, visto que os programas nos exemplosprecisam dos mesmos headers e usam a mesma constantes MAXLINE e a macro err quit().

A seguir e mostrado o programa cliente para este servidor. Ele envia uma stream de bytespara o servidor, contendo os dados de um arquivo texto. A seguir, realiza a leitura da respostado servidor, e a imprime. Como visto no exemplo do servidor, a resposta vinda do servidor e oproprio conteudo do arquivo.

Page 51: Apostila C 2.0

CAPITULO 7. TCP/IP 50

1 #include ”timeofday .h”2

3 void4 conecta ao servidor ( char ∗filename , int sockfd)5 {6 int select sock in = 1;7 int select sock out = 1;8 int fd , ret ;9 int in size = 0;

10 int out size = 0;11 char buff [MAXLINE];12 char outbuff [MAXLINE];13

14 int max fd = sockfd ;15 int file EOF = 0;16 int sock EOF = 0;17 int shutdown write sock = 0;18 int done = 0;19

20

21 if (( fd = open(filename , O RDONLY)) <= 0)22 err quit ( ”open”);23

24 if ( fd > max fd)25 max fd = fd ;26 max fd++;27

28

29 while (! done) {30 fd set write set , read set ;31

32 FD ZERO (&write set);33 FD ZERO (&read set);34

35 if ( select sock in )36 FD SET (sockfd, &read set );37

38 if ( select sock out )39 FD SET (sockfd, &write set );40

41 ret = select ( max fd, & read set , & write set , NULL, NULL);42 if ( ret < 0)43 err quit ( ” select ” );44

45 /∗ le conteudo do arquivo para o buffer buff ∗/46 if (! file EOF && in size == 0) {47 in size = read ( fd , buff , sizeof ( buff ));48 if ( in size == 0) {49 file EOF = 1;50 close (fd );51 } else if ( in size < 0) {52 err quit ( ”read” );53 }

Page 52: Apostila C 2.0

CAPITULO 7. TCP/IP 51

54 }55

56 /∗ escreve o conteudo de buff para o sockfd ∗/57 if ( in size > 0) {58 ret = write ( sockfd , buff , in size );59 if ( ret < in size ) {60 if ( errno == EAGAIN) {61 printf (”EAGAIN em write\n”);62 } else {63 err quit ( ”write” );64 }65 } else {66 in size = 0;67 }68 }69 if ( file EOF && in size == 0 && !shutdown write sock ) {70 shutdown (sockfd , SHUT WR);71 shutdown write sock = 1;72 }73

74 /∗ le reply do servidor ∗/75 if ( FD ISSET (sockfd, &read set )) {76 out size = read ( sockfd , outbuff , sizeof ( outbuff ));77 if ( out size == 0) {78 sock EOF = 1;79 shutdown (sockfd , SHUT RD);80 }81 }82

83 if ( out size > 0)84 out size = 0;85

86 if ( sock EOF)87 done = 1;88

89 select sock in = (! sock EOF && out size == 0);90 }91 printf ( ”%s”, outbuff );92 }93

94

95 int96 main ( int argc , char ∗∗argv)97 {98 int sockfd ;99 struct sockaddr in servaddr ;

100 struct timeval tv ;101 long flags ;102 fd set write set ;103

104 int sock error = 0;105 socklen t sock error len ;106

107

Page 53: Apostila C 2.0

CAPITULO 7. TCP/IP 52

108 if ( argc < 3) {109 printf (”Sintaxe: %s <arquivo in> <porta>\n”, argv[0]);110 exit (0);111 }112

113 if (( sockfd = socket ( AF INET, SOCK STREAM, 0)) < 0)114 err quit (”socket” );115

116 flags = fcntl ( sockfd , F GETFL);117 fcntl ( sockfd , F SETFL, flags | O NONBLOCK);118

119 memset (&servaddr , 0, sizeof ( servaddr ));120 servaddr . sin family = AF INET;121 servaddr . sin addr . s addr = htonl ( INADDR LOOPBACK);122 servaddr . sin port = htons ( atoi ( argv [2]));123

124 /∗ aguarda no maximo 30 segundos ∗/125 tv . tv sec = 30;126 tv . tv usec = 0;127

128 if ( connect ( sockfd , ( struct sockaddr ∗) & servaddr , sizeof ( servaddr )) == 0) {129 /∗ conexao ocorreu imediatamente , nao e ’ necessario usar select () ∗/130 } else {131 switch ( errno ) {132 case EINPROGRESS: /∗ select para escrita ∗/133 FD ZERO (&write set);134 FD SET (sockfd, &write set );135 if ( select ( sockfd + 1, NULL, &write set, NULL, &tv) <= 0)136 err quit ( ” select ” );137

138 if (! FD ISSET (sockfd, &write set ))139 err quit ( ” select ” );140 break;141

142 default :143 err quit ( ”connect” );144 }145 }146

147 sock error len = sizeof ( sock error );148 if ( getsockopt (sockfd , SOL SOCKET, SO ERROR, &sock error, &sock error len) < 0)149 err quit ( ”getsockopt” );150

151 conecta ao servidor ( argv [1], sockfd );152 exit (0);153 }

Nesta versao do cliente temos tambem algumas informacoes relevantes e que valem a penaserem descritas. Novamente, comecando a partir da funcao main():

• 113 − 117: cria um socket TCP e configura opcoes para o socket;

• 119 − 122: conecta-se ao host local. Uma maquina em particular pode ter mais de umendereco IP, cada um correpondendo a uma interface de rede diferente. O uso da constanteINADDR ANY permite que o bind seja feito em todas elas simultaneamente;

• 125 − 126: especifica o timeout a ser usado na chamada ao select(2);

Page 54: Apostila C 2.0

CAPITULO 7. TCP/IP 53

• 128: tenta conectar-se ao servidor. Caso consiga de imediato, segue adiante, ou aguardapela mudanca do estado do descritor de escrita do socket sockfd ate receber o reply doservidor. O timeout imposto no exemplo e de 30 segundos;

• 147 − 149: pega e limpa qualquer erro que tenha acontecido no socket;

• 151: conecta-se ao servidor, executando os passos descritos abaixo;

• 21− 22: abre o arquivo origem a ser enviado para o servidor;

• 24− 26: determina o maior descritor utilizado, para uso no select(2);

• 32 − 43: prepara os conjuntos de descritores de leitura e escrita a serem monitoradospelo select(2); Esta chamada e desbloqueada assim que algum deles tiver seu valormodificado.

• 46− 54: le o conteudo do arquivo para o buffer especificado por buff ;

• 57 − 68: caso tenha sido lido algum conteudo, escreve-o para o socket sockfd, onde estaestabelecida a conexao com o servidor;

• 69 − 72: caso o fim de arquivo tenha sido encontrado, chama shutdown(2). Estafuncao finaliza totalmente ou parcialmente uma conexao full-duplex no socket especifiadocomo primeiro argumento. Caso o segundo parametro seja SHUT RD, nao sao permitidasrecepcoes futuras neste socket. Ele pode ainda ser SHUT WR, que impede transmissoessobre o socket, ou ainda SHUT RDWR, que impede recepcoes e transmissoes sobre estesocket. Neste exemplo, ele finaliza o socket para escrita;

• 75− 80: le o reply do servidor para o buffer outbuff. Caso ele tenha lido o sinal de fim dearquivo, finaliza o socket para leitura, usando a chamada shutdown(2);

• 91: imprime o buffer lido, que e o mesmo que foi enviado.

Page 55: Apostila C 2.0

Capıtulo 8

Bancos de dados

8.1 ImplementacoesExistem diversas implementacoes de servidores de banco de dados disponıveis para o GNU/Linux.Entre eles, pode-se citar o MySQL, PostgreSQL e o Firebird, tres implementacoes excelentes ebastante estaveis de banco de dados relacionais para o GNU/Linux. O banco de dados usado nosexemplos sera o MySQL.

8.2 Programando com Bancos de Dados em CA API para programacao em C e distribuıda com o MySQL. Ele inclui a biblioteca mysqlcliente permite que programas em C acessem bancos de dados [MyS 2003].

Para conectar ao servidor, e feita uma chamada a funcao mysql init() para inicializar ohandler da conexao, e entao a funcao mysql real connect() e chamada com este handler(alem de outras informacoes como o hostname, nome do usuario e senha). Sobre esta conexao,mysql real connect() configura o valor da flag reconnect (parte da estrutura do MySQL)para 1. Esta flag indica que, caso uma query nao possa ser efetuada devido a uma conexaoperdida, uma nova tentativa de reconexao deve ser feita antes de retornar um erro. Quando aconexao estiver encerrada, a funcao mysql close() a termina.

Enquanto uma conexao estiver ativa, o cliente pode enviar queries SQL para o servidorusando mysql query() ou mysql real query(). A diferenca entre estas duas funcoesesta em que mysql query() espera que a query seja especificada como uma string terminadapor NULL, enquanto a funcao mysql real query() espera uma string de tamanho ja con-tado. Caso a string contenha algum dado binario, deve ser utilizada a mysql real query().

Para queries que nao envolvem selecao, como INSERT, UPDATE e DELETE, e possıvel verquantas colunas foram afetadas atraves da funcao mysql affected rows(). Para queriesdo tipo SELECT, as colunas selecionadas sao obtidas na forma de um conjunto de resultados.

Estes conjuntos de resultados podem ser processados de duas formas pelo cliente. Umadelas e pela chamada a mysql store result(). Essa funcao obtem do servidor todas ascolunas retornadas pela query e as armazena no cliente. A segunda forma e pelo uso da funcaomysql use result(), que inicia uma recuperacao coluna a coluna a partir do conjunto deresultados retornado.

Em ambos casos, o acesso as colunas e feito usando a chamada mysql fetch row().Com mysql store result(), mysql fetch row() acessa as colunas que ja foram lidasdo servidor. O tamanho dos dados em cada coluna pode entao ser informado atraves da funcaomysql fetch lengths().

Apos o processamento do conjunto de resultados, a funcao mysql free result() liberaa memoria utilizada durante ele.

54

Page 56: Apostila C 2.0

CAPITULO 8. BANCOS DE DADOS 55

Com estas descricoes ja e possıvel escrever um aplicativo que comunique-se com o servi-dor de banco de dados, mas antes e necessario criar o banco de dados e a tabela. Para isto, enecessario executar alguns passos, descritos logo abaixo:

$ mysql -u root -pEnter password:Welcome to the MySQL monitor. Commands end with ; or \g.Your MySQL connection id is 4 to server version: 4.0.15a

Type ’help;’ or ’\h’ for help. Type ’\c’ to clear the buffer.

mysql> CREATE DATABASE tabela_teste;Query OK, 1 row affected (0.06 sec)

mysql> GRANT select, insert, update, delete-> ON tabela_teste.* TO user@localhost IDENTIFIED by ’senha’

Com isto o banco de dados tabela teste foi criada, e foi garantido acesso de insercao, updatee exclusao para o usuario user@localhost. A senha deve ser informada entre aspas simples.

Apos criado o banco de dados, e necessario criar a tabela sobre a qual as operacoes seraofeitas:

mysql> USE tabela_teste;Database changedmysql> CREATE TABLE produtos (

-> name varchar(80) not null,-> num int not null,-> PRIMARY KEY (num)-> );

Query OK, 0 rows affected (0.01 sec)

mysql> SHOW TABLES;+------------------------+| Tables_in_tabela_teste |+------------------------+| produtos |+------------------------+1 row in set (0.00 sec)

mysql> DESCRIBE produtos;+-------+-------------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+-------+-------------+------+-----+---------+-------+| name | varchar(80) | | | | || num | int(11) | | PRI | 0 | |+-------+-------------+------+-----+---------+-------+2 rows in set (0.01 sec)

Uma tabela chamada produtos foi criada sobre o banco tabela teste, com uma string chamadavarchar e um inteiro chamado num. A partir de agora e possıvel operar sobre estas estruturasutilizando um programa em C. O exemplo abaixo conecta-se ao servidor MySQL e realiza umainsercao de dados sobre a tabela criada, produtos. Para compilar este exemplo basta:

gcc insert_test.c -o insert_test -Wall ‘mysql_config --libs‘

Page 57: Apostila C 2.0

CAPITULO 8. BANCOS DE DADOS 56

1 #include <stdio .h>2 #include < stdlib .h>3 #include <mysql/mysql.h>4

5 #define INSERT QUERY ”insert into produtos (name,num) values (’ item %d’, %d)”6

7

8 int9 main(int argc , char ∗∗argv)

10 {11 MYSQL ∗sock = NULL;12 MYSQL ∗mysql = NULL;13 char qbuf [160];14 int count , num;15

16 if ( argc != 3) {17 fprintf ( stderr , ”Sintaxe: %s <dbname> <numero>\n”, argv[0]);18 exit (1);19 }20

21 mysql = mysql init ( mysql);22 if (! mysql) {23 fprintf ( stderr , ” mysql init : %s\n” , mysql error ( mysql ));24 exit (1);25 }26

27 sock = mysql real connect ( mysql , ” localhost ” , ” lucasvr ” , ”senha” , argv [1], 0, NULL, 0);28 if (! sock ) {29 fprintf ( stderr , ” mysql real connect : %s\n” , mysql error ( mysql ));30 exit (1);31 }32

33 num = atoi ( argv [2]);34 count = 0;35 while ( count < num) {36 sprintf ( qbuf , INSERT QUERY, count, count);37 if ( mysql query (sock , qbuf )) {38 fprintf ( stderr , ”mysql query: %s\n”, mysql error ( sock ));39 exit (1);40 }41 count++;42 }43

44 mysql close ( sock );45 exit (0);46 }

A execucao deste programa pode ser feita da seguinte maneira:

$ ./insert_test tabela_teste 5

E o resultado sera refletido imediatamente no banco de dados:

Page 58: Apostila C 2.0

CAPITULO 8. BANCOS DE DADOS 57

mysql> SELECT * from produtos;+--------+-----+| name | num |+--------+-----+| item 0 | 0 || item 1 | 1 || item 2 | 2 || item 3 | 3 || item 4 | 4 |+--------+-----+5 rows in set (0.00 sec)

Page 59: Apostila C 2.0

Capıtulo 9

Interface Grafica

9.1 Servidor XO X Window System[SCH 86] foi criado em 1984 no Massachussets Institute of Technology(MIT) para dar suporte a dois projetos: o projeto Athena, que precisava de um sistema de janelasque pudesse ser usado em centenas de estacoes de trabalho e o projeto Argus, que precisava deum ambiente de depuracao para aplicacoes multiprocessadas distribuıdas [KOJ 2000].

O X Window System define padroes que devem ser adotados por implementacoes desteambiente. O ambiente que o implementa e da suporte para os sistemas operacionais UNIX eGNU/Linux e o XFree86. Nestas plataformas ele e distribuıdo sob uma licenca livre, e em outrasele o e sobre a forma de uma licenca comercialial, como no OS/2 e no QNX.

O sistema possui arquitetura cliente-servidor. O servidor localiza-se na estacao de trabalhodo usuario e prove acesso transparente ao hardware grafico, mecanismos de comunicacao en-tre clientes e entrada de dados por dispositivos como mouse e teclado. Os clientes podem serexecutados na propria estacao ou remotamente, conectados ao servidor atraves de uma rede.

Qualquer coisa que se queira apresentar na tela deve ser desenhada dentro de uma janela.Uma aplicacao pode usar muitas janelas de cada vez. Menus e outros elementos da interfacepodem ser construıdos com janelas que se sobrepoem as demais. As janelas sao organizadas emuma hierarquia, o que facilita o gerenciamento. Qualquer janela, exceto uma, chamada de raiz,e subjanela de outra.

O X foi projetado de forma a fornecer mecanismos para que diferentes polıticas de interfacecom o usuario pudessem ser implementadas, ou seja, e como um driver de vıdeo, teclado emouse. Portanto, o X nao e nem possui uma interface com o usuario. A funcao de provertal interface e delegada aos clientes e bibliotecas especiais externos ao servidor X, chamadastoolkits.

A seguinte estrutura deve ser respeitada para a criacao de uma aplicacao no X:

• Inicializacao: durante a inicializacao, o programa fara o tratamento de opcoes de linha decomando, conexao com o servidor X e alocacao de estruturas internas. Tudo isto e feitointernamente pelo toolkit, caso seja utilizado um.

• Montagem da interface grafica: apos aberta a conexao com o servidor, o programa podecriar e montar sua interface (janelas, menus, botoes etc.) utilizando os widgets (elementosda interface) oferecidos pelo toolkit, ou usando as funcoes primitivas da Xlib, explicadana Sessao 9.2

• Laco de tratamento de eventos: finalmente, o programa entra em um laco, onde ficaesperando por eventos vindos do servidor, como pressionamento de teclas ou botoes domouse, movimento do mouse ou requisicao do servidor para redesenhar o conteudo desuas janelas. Em toolkits, estes eventos sao primeiro tratados internamente e, dependendo

58

Page 60: Apostila C 2.0

CAPITULO 9. INTERFACE GRAFICA 59

do caso (quando o mouse e clicado em um widget botao, por exemplo), sao repassados aoprograma atraves de callbacks ou mecanismos similares.

9.2 A biblioteca XlibA Xlib e a API (Application Programs Interface) de mais baixo nıvel disponıvel para se pro-gramar aplicacoes para X (a nao ser que a aplicacao utilize o protocolo X diretamente para secomunicar com o servidor). Ela foi definida como padrao pelo X Consortium e e uma bibliotecaem linguagem C, que oferece funcoes com correspondencia direta, ou quase, com o protocoloX. Possui funcoes para abrir uma conexao com um servidor (display), criacao, destruicao emanipulacao de janelas, desenho e recebimento de eventos, entre outros.

Nao e aconselhavel usar apenas a Xlib para escrever aplicacoes complexas, que possuaminterfaces com usuario sofisticadas, pois alem disso ser muito trabalhoso levara a existencia deaplicacoes com interfaces inconsistentes umas com a as outras. E por isto que existem os toolkits,que utilizam a Xlib na sua implementacao, mas oferecem um maior grau de abstracao e funcoesde mais alto nıvel. Os toolkits permitem, alem da construcao de interfaces com o usuario, tratarde outras necessidades da aplicacao, tais como configuracao, tratamento de erros e acesso aservicos do sistema operacional.

9.3 Gerenciadores de JanelasProgramas que realizam operacoes de baixo nıvel, tais como gerenciadores de janelas e pro-cessadores de imagens, terao obrigatoriamente que usar as funcoes da Xlib. Uma abordagembastante comum em aplicacoes complexas e usar um toolkit para construir a interface e funcoesda Xlib para operacoes de baixo nıvel.

Gerenciadores de janelas sao muito uteis porque implementam as polıticas de atribuicao dofoco, redimensionamento e sobreposicao de janelas. Eles fazem isto com base em um conjuntode convencoes estabelecidas entre ele e os clientes, usando mecanismos diversos como trocas demensagens e eventos.

Normalmente o gerenciador de janelas utilizado no desktop do programador o influenciana tomada de decisao sobre qual biblioteca de widgets usar para criar suas aplicacoes graficas.Alguns exemplos sao o KDE, que utiliza o toolkit Qt (C++) e o GNOME, que usa o GTK+ (C).

A Sessao 9.4 apresenta o modelo de programacao usado com a toolkit GTK+.

9.4 GTK+GTK+ e uma biblioteca de widgets escrita em C, com inspiracao em orientacao a objetos. Devidoa essa caracterıstica, a programacao nela pode se tornar um tanto tediosa, pois e necessarioemular a programacao orientada a objetos “na mao”, por meio de uma disciplina de programacao.

A criacao de um programa GTK+ e feita conforme os seguintes passos mostrados no exem-plo abaixo (este programa deve ser compilado com a flag -lgtk para ser linkado com a biblio-teca GTK+):

1 #include <unistd .h>2 #include <gtk/gtk .h>3

4 void5 quit hello ( GtkWidget ∗widget, gpointer data )6 {7 gtk main quit ();8 }9

Page 61: Apostila C 2.0

CAPITULO 9. INTERFACE GRAFICA 60

10 int11 main ( int argc , char ∗∗argv)12 {13 GtkWidget ∗window, ∗button;14

15 gtk init (&argc, &argv );16

17 window = gtk window new (GTK WINDOW TOPLEVEL);18 gtk signal connect ( GTK OBJECT (window), ”destroy”,19 GTK SIGNAL FUNC (quit hello), NULL);20

21 button = gtk button new with label ( ”Ola”);22 gtk signal connect object ( GTK OBJECT (button), ”clicked”,23 GTK SIGNAL FUNC (gtk widget destroy),24 GTK OBJECT (window));25

26 gtk container add ( GTK CONTAINER (window), button);27 gtk widget show ( button );28 gtk widget show ( window);29

30 gtk main ();31 exit (0);32 }

Este e um dos programas mais simples que pode ser escrito em GTK+, que faz o seguinte:

• 4− 8: declara a funcao que sera chamada quando a janela receber o sinal destroy;

• 15: inicializacao do GTK+. Nesta etapa e realizada a conexao com o servidor X, otratamento de opcoes de linha de comando padrao e outras inicializacoes internas;

• 17: criacao de uma janela principal (TOPLEVEL);

• 18− 19: conexao do sinal destroy a funcao quit hello();

• 21: criacao de um botao;

• 22 − 24: quando o botao receber o sinal clicked, chama a funcao gtk widget destroy,passando como parametro o objeto window;

• 26: o botao e colocado dentro da janela principal;

• 27: faz com que o widget seja visıvel;

• 28: faz com que a janela seja visıvel na tela;

• 30: entra em um loop infinito de processamento de eventos.

Como pode-se imaginar, na medida em que o programa cresce, torna-se inviavel escreve-lodiretamente sobre o GTK+, pois o usuario tera o compromisso de escrever todas as rotinas deposicionamento de janelas e botoes manualmente, o que acaba tomando muito tempo e levandoa ocorrencia dos bugs mais diversos.

Uma solucao que varios projetos tem criado e a adocao de uma interface grafica para mani-pular as bibliotecas de widgets. No caso do QT, pode ser utilizado o Designer para desenvolveraplicacoes graficas muito rapidamente, e com o GTK+ pode ser usado o Glade, que tambemaumenta muito a producao de codigos baseados no ambiente de janelas do XFree86.

As Figuras 9.1 e 9.2 mostram o processo de criacao de um botao em uma nova janela e umajanela mais complexa, contendo botoes e combo boxes.

Page 62: Apostila C 2.0

CAPITULO 9. INTERFACE GRAFICA 61

Figura 9.1: Inserindo um botao em uma nova janela com o Glade

Figura 9.2: Construcoes mais complexas sao construıdas rapidamente no Glade

Page 63: Apostila C 2.0

Capıtulo 10

Comunicacao

Em UNIX, tudo e um arquivo. Seguindo esta filosofia, a maneira utilizada para programar dis-positivos e a mesma usada para tratar arquivos utilizando operacoes com descritores de arquivos.No entanto, operacoes sobre certos dispositivos podem ser triviais o suficiente para nao necessitaro uso destes recursos, ou complexas demais a ponto de ser importante utilizar alguma bibliotecapara auxiliar as operacoes sobre estes dispositivos.

A seguir sao apresentadas as formas de acesso aos dispositivos serial, paralelo e USB.

10.1 Porta ParalelaExistem duas formas de acesso a dispositivos na porta paralela. O que acontece com ela no Linuxe que existe um driver padrao chamado lp, que ja da suporte ao acesso a porta. Neste caso, odispositivo criado na entrada /dev (ou outro diretorio usado para armazenar os dispositivos,dependendo da distribuicao) e o lp0 para a primeira porta paralela, lp1 para a segunda e assimsucessivamente. A porta paralela tambem pode ser programada utilizando diretamente o dispos-itivo port, usando operacoes sobre descritores de arquivos como read(2) e write(2).

No entanto, a forma mais simples e rapida de programa-la e com o uso da famılia inb(2)e outb(2) [RUB 2001]. A seguir sao definidos os prototipos das macros associadas a elas:

• unsigned char inb (unsigned short int port);void outb (unsigned char value, unsigned short int port);leem ou escrevem bytes (8 bits) a porta port. O argumento port e definido como unsignedlong para algumas plataformas, e unsigned short para outras. O tipo de retorno de inbtambem difere entre diferentes arquiteturas.

• unsigned short int inw (unsigned short int port);void outw (unsigned char value, unsigned short int port);acessam portas de 16-bits. Elas nao estao disponıveis nas plataformas M68K e S390, quesuportam apenas I/O orientado a bytes.

• unsigned int inl (unsigned short int port);void outl (unsigned char value, unsigned short int port);estas macros acessam portas de 32-bits. value e declarado unsigned long ou unsignedint, de acordo com a plataforma. Assim como em I/O de 16-bits, esta operacao nao estadisponıvel nos M68K e S390.

• void insb (unsigned short int port, void *addr, unsignedlong int count);void outsb (unsigned short int port, void *addr, unsignedlong int count);leem ou escrevem count bytes, iniciando no endereco de memoria addr. Os dados sao

62

Page 64: Apostila C 2.0

CAPITULO 10. COMUNICACAO 63

lidos e escritos na porta port.

Estas macros {in,out}s* operam com os mesmo tipos das macros anteriores, coma diferenca de que estas sao orientadas a strings. Alguns processadores implementaminstrucoes especiais para transferir uma sequencia de bytes, words ou longs de e parauma porta de I/O. No caso da arquitetura nao suportar este tipo de instrucao, elas saoimplementadas via software na forma de um loop.

• void insw (unsigned short int port, void *addr, unsignedlong int count);void outsw (unsigned short int port, void *addr, unsignedlong int count);leem ou escrevem valores de 16-bits para uma porta de 16-bits.

• void insl (unsigned short int port, void *addr, unsignedlong int count);void outsl (unsigned short int port, void *addr, unsignedlong int count);leem ou escrevem valores de 32-bits para uma porta de 32-bits.

• unsigned char inb p (unsigned short int port);void outb p (unsigned char value, unsigned short int port);unsigned short int inw p (unsigned short int port);void outw p (unsigned char value, unsigned short int port);unsigned int inl p (unsigned short int port);void outl p (unsigned char value, unsigned short int port);Algumas plataformas – principalmente o i386 – pode ter problemas quando o processadortenta transferir dadas muito rapidamente do ou para o bus. Os problemas podem ocorrerporque o processador e overclocked em respeito ao bus ISA, que podem acontecer quandoos dispositivos da placa sao muito lentos. A solucao nestes casos e inserir um pequenodelay entre cada instrucao de I/O. Nas arquiteturas que nao enfrentam este problema, estamacro e expandida para as funcoes correspondentes sem o “ p”.

Estas funcoes sao na verdade definidas como macros inline, e precisam ser compiladas comnıvel de otimizacao habilitado para serem substituıdas, caso contrario as referencias nao poderaoser resolvidas em tempo de linkagem do programa. Podem ser utilizadas as flags -O ou -O2, porexemplo.

Para poder operar sobre as portas seriais e paralelas em espaco de usuario, e necessariorequisitar acesso para o kernel para as portas de I/O em questao. Para isto sao utilizados osprogramas ioperm(2) ou o iopl(2):

#include <unistd .h> /∗ para a libc5 ∗/#include <sys/io .h> /∗ para a glibc ∗/int ioperm (unsigned long from , unsigned long num, int turn on );

ioperm(2) configura os bits de acesso a porta para o processo para num bytes, comecandona porta from, para o valor turn on. O uso desta funcao requer privilegios de superusuario.As permissoes de acesso nao sao herdadas no fork(2), mas sao no exec(3). Alem disto,ioperm(2) permite acesso para as primeiras 0x3ff portas de I/O. Para portas fora deste inter-valo, o iopl(2) deve ser utilizado:

#include <sys/io .h>int iopl ( int level );

Esta funcao altera os privilegios de I/O do processo atual para o nıvel especificado em level.Assim, o processo tem acesso irrestrito a portas de acesso I/O, inclusive permitindo que o pro-cesso desabilite interrupcoes (o que provavelmente ira causar um crash no sistema). Diferenteda ioperm(2), esta funcao permite que as permissoes sejam herdadas tanto pelo fork(2)como pelo exec(3). O nıvel padrao para um processo normal e 0, e vai ate 3.

Page 65: Apostila C 2.0

CAPITULO 10. COMUNICACAO 64

10.2 Porta SerialPortas seriais sao identificadas no Linux atraves dos dispositivos ttySn, onde n indica o numeroda porta serial. Por exemplo, a porta ttyS0 referencia a primeira porta serial, equivalente aCOM1 no DOS. No entanto, pelo fato de ter caracterısticas extras a serem consideradas quandoprogramadas – como a baud rate e paridade de bits –, a programacao sobre portas paralelasenvolve alguns passos extras.

Existem tres modos de operacao de leitura sobre portas seriais, que sao:

• Modo Canonico: trabalha sobre linhas, ou seja: uma operacao de leitura so ira retornarquando tiver lido um caracter de fim de linha (\n) ou um caracter de carriage return (\r).Nesse modo, caracteres especiais como erase, kill, delete sao reconhecidos e tratadoscomo tal.

• Modo Nao-Canonico (raw): o processamento nao e realizado sobre linhas, mas sobrequantidades fixas de caracteres, e a leitura nao e afetada caso caracteres especiais sejamlidos, como o erase e o delete.

• Modo Assıncrono: este modo permite que a chamada read(2) retorne imediatamente,sendo que um sinal e enviado ao programa (SIGIO) quando os dados estiverem disponıveispara leitura.

Outro detalhe a ser visto e o da configuracao da porta serial: como o programa ira modifica-la, e interessante salvar o estado da porta antes de altera-la, e entao restaurar o seu estado aotermino do programa. Feito isso, basta especificar as informacoes necessarias para configurara porta serial e iniciar a leitura e escrita de dados a porta com o uso da chamada read(2) ewrite(2).

10.2.1 A interface TermiosPara lidar com a serial no Linux, uma estrutura chamada struct termios e usada. Ela apresentaos seguinte membros:

• tcflag t c iflag: opcoes de entrada de dados;

• tcflag t c oflag: opcoes para a saıda de dados;

• tcflag t c cflag: descreve opcoes de controle;

• tcflag t c lflag: flags para o modo local (canonico, nao-canonico);

• cc t c line: disciplina da linha;

• cc t c cc[]: caracteres de controle.

As opcoes de controle, representadas pela c cflag, controlam a baud rate, a quantidade debits de dados, paridade, stop bits e o controle de fluxo de hardware. A constantes disponıveispara serem usadas podem ser vistas na Tabela 10.1

Duas opcoes da c cflag devem estar sempre habilitadas: CLOCAL e CREAD. Elas irao garan-tir que o programa nao vira a ser o “proprietario” da porta relativo a controles esporadicos de jobe de sinais de hangup, e que o driver da interface serial sera capaz de ler dados de entrada.

Devido as diferentes interfaces que sao disponıveis para configurar a baud rate entre variossistemas operacionais, costuma-se usar as funcoes cfsetispeed(3) e cfsetospeed(3)para informar a taxa de entrada e saıda, respectivamente.

Relativo a paridade, basta combinar as flags sobre a c cflag para obter a paridade desejada.Na listagem abaixo assume-se que a c cflag foi previamente inicializada com algum outro campo(como o CLOCAL | CREAD):

• Sem paridade (8N1): options.c cflag |= CS8;

• Com paridade par (7E1): options.c cflag |= CS7 | PARENB;

• Com paridade ımpar (7O1): options.c cflag |= CS7 | PARENB | PARODD;

Page 66: Apostila C 2.0

CAPITULO 10. COMUNICACAO 65

Tabela 10.1: Constantes para o membro c cflag

Constante DescricaoB0 0 baud (descarta informacao de Data Terminal Ready)B50 50 baudB75 75 baudB110 110 baudB134 134.5 baudB150 150 baudB200 200 baudB300 300 baudB600 600 baudB1200 1200 baudB1800 1800 baudB2400 2400 baudB4800 4800 baudB9600 9600 baudB19200 19200 baudB38400 38400 baudB57600 57,600 baudB76800 76,800 baudB115200 115,200 baudEXTA Taxa externa de clockEXTB Taxa externa de clockCS5 5 bits de dadosCS6 6 bits de dadosCS7 7 bits de dadosCS8 8 bits de dadosCSTOPB 2 stop bits (1 otherwise)CREAD Habilita recepcao de dadosPARENB Habilita bit de paridadePARODD Usa paridade ımpar ao inves de parHUPCL Hangup (descarta DTR) no ultimo closeCLOCAL Linha local - nao modifica o “proprietario” da portaLOBLK Bloqueia saıda de controle de jobsCRTSCTS Habilita fluxo de controle em hardware

• Com paridade de espaco (7S1): utiliza a mesma configuracao do modo sem paridade(8N1).

As opcoes locais podem ser configuradas sobre o membro c lflag, e ditam como os caracteresde entrada (leitura) serao manipulados pelo driver serial. Normalmente ele sera configurado parao modo canonico ou nao-canonico (raw). A Tabela 10.2 mostra as opcoes disponıveis para estecampo.

As opcoes de entrada sao informadas pelo membro c iflag, de acordo com os valores vistosna Tabela 10.3.

O membro c oflag contem opcoes de filtro de saıda. Como nos modos de entrada, pode serescolhido modo processado ou raw. A Tabela 10.4 mostra as opcoes disponıveis.

De todas estas opcoes, provavelmente a unica utilizada sera a ONLCR para mapear novaslinhas para pares CR-LF. O restante das opcoes sao mantidas por razoes historicas e datam daepoca em que impressoras e terminais nao conseguiam manter-se em sincronia com as streams

Page 67: Apostila C 2.0

CAPITULO 10. COMUNICACAO 66

Tabela 10.2: Constantes para o membro c lflag

Constante DescricaoISIG Habilita sinais SIGINTR, SIGSUSP, SIGDSUSP e SIGQUITICANON Habilita modo de entrada canonico (raw mode caso contrario)XCASE Mapeia uppercase para lowercase (obsoleto)ECHO Habilita eco de caracteres de entradaECHOE Ecoa o caracter de erase como BS-SP-BSECHOK Ecoa nova linha (NL) apos o caracter de killECHONL Ecoa NLNOFLSH Desabilita flush de buffers de enrada apos caracteres de interrupt ou quitIEXTEN Habilita funcoes estendidasECHOCTL Ecoa caracteres de controle como char e delete como ?ECHOPRT Ecoa caracteres apagados como um caractere apagadoECHOKE BS-SP-BS em toda a linha quando receber um line killFLUSHO Saıda sendo flushedPENDIN Processa entrada pendente na proxima leitura ou caracter de entradaTOSTOP Envia SIGTTOU para saıda em background

Tabela 10.3: Constantes para o membro c iflag

Constante DescricaoINPCK Habilita checagem de paridadeIGNPAR Ignora erros de paridadePARMRK Marca erros de paridadeISTRIP Tira bits de paridadeIXON Habilita controle de fluxo de software para saıdaIXOFF Habilita controle de fluxo de software para entradaIXANY Permite que qualquer caracter inicie o fluxo novamenteIGNBRK Ignora condicao de breakBRKINT Envia um sinal SIGINT quando uma condicao de break for detectadaINLCR Mapeia nova linha (NL) para carriage return (CR)IGNCR Ignora caracter CRICRNL Mapeia CR para NLIUCLC Mapeia uppercase para lowercaseIMAXBEL Ecoa BEL para linhas de entrada muito longas

de dados da porta serial.O ultimo conjunto de opcoes que restam para permitir a configuracao da porta serial sao

os caracteres de controle. Eles sao configuraveis em um array chamado c cc que contem tantodefinicoes de caracteres de controles como parametros de timeout. Alguns ds dados suportadosneste array estao listados na Tabela 10.5. O restante pode ser consultado a partir do headerbits/termios.h.

10.2.2 Modo canonicoPara comunicar-se com um modem configuravel via serial, pode-se utilizar o modo canonico –orientado a linhas. O programa a seguir pode ser utilizado para resetar o modem e ler os dadosenviados por ele. Neste exemplo, nao ha uma forma de encerrar o programa de forma natural,

Page 68: Apostila C 2.0

CAPITULO 10. COMUNICACAO 67

Tabela 10.4: Constantes para o membro c oflag

Constante DescricaoOPOST Pos-processa a saıda (nao setado = saıda raw)OLCUC Mapeia lowercase para uppercaseONLCR Mapeia NL para CR-NLOCRNL Mapeia CR para NLNOCR Nao gera CR na saıda na coluna 0ONLRET NL realiza a funcao de CROFILL Usa caracteres de preenchimento (fill) para delayOFDEL Caractere de preenchimento e DELNLDLY Mascara para delay entre linhasNL0 Nenhum delay para NLsNL1 Delay de novas saıdas apos NL para 100 mili-segundosCRDLY Mascara para delay para retornar o carro para a coluna da esquerdaCR0 Nenhum delay para CRsCR1 Delay apos CRs dependendo da posicao da coluna atualCR2 Delay de 100 mili-segundos apos enviar CRsCR3 Delay de 150 mili-segundos apos enviar CRsTABDLY Mascara para delay apos TABsTAB0 Nenhum delay para TABsTAB1 Delay apos TABs dependendo da posicao da coluna atualTAB2 Delay de 100 mili-segundos apos enviar TABsTAB3 Expande caracteres de TAB para espacosBSDLY Mascara para delay apos BSsBS0 Nenhum delay para BSsBS1 Delay de 50 mili-segundos apos enviar BSsVTDLY Mascara para delay apos VTsVT0 Nenhum delay para VTsVT1 Delay de 2 segundos apos enviar VTsFFDLY Mascara para delay apos FFsFF0 Nenhum delay para FFsFF1 Delay de 2 segundos apos enviar FFs

Tabela 10.5: Constantes para o membro c cc[]

Constante Descricao Valor defaultVINTR Interrupt CTRL-CVQUIT Quit CTRL-ZVERASE Erase Backspace (BS)VKILL Kill-line CTRL-UVEOF Fim de arquivo CTRL-DVEOL Fim de linha Carriage return (CR)VEOL2 Segundo fim de linha Line feed (LF)VMIN Numero mınimo de caracteres para serem lidos -VTIME Tempo para aguardar por dados (decimos de segundos) -

Page 69: Apostila C 2.0

CAPITULO 10. COMUNICACAO 68

pois o loop de leitura e infinito. Em um caso real, seria necessario verificar o conteudo do bufferlido para decidir quando encerrar o loop.

1 #include <stdio .h>2 #include <unistd .h>3 #include < stdlib .h>4 #include < string .h>5 #include <sys/ types .h>6 #include <sys/ stat .h>7 #include < fcntl .h>8 #include <termios .h>9

10 /∗ Configuracoes de baud rate sao definidas em <asm/termbits.h> ∗/11 #define BAUDRATE B960012 #define DEVICE ”/dev/ttyS0”13

14 int15 main ( int argc , char ∗∗argv)16 {17 int fd , res ;18 struct termios oldtio , newtio;19 char buf [255];20

21 /∗22 ∗ Abre porta para leitura / escrita e nao como uma tty de controle , para23 ∗ evitar que o programa encerre se vier um CTRL+C na linha24 ∗/25 fd = open (DEVICE, O RDWR | O NOCTTY);26 if ( fd < 0) {27 perror ( ”open”);28 exit (1);29 }30

31 /∗ Salva a configuracao atual da porta serial e inicia uma nova ∗/32 tcgetattr ( fd, & oldtio );33 memset (&newtio , 0, sizeof ( newtio ));34

35 /∗36 ∗ CRTSCTS : Controle de fluxo de saida do hardware37 ∗ CS8 : 8 n1 (8 bit , no parity , 1 stopbit )38 ∗ CLOCAL : Conexao local , sem controle do modem39 ∗ CREAD : Habilita a recepcao de caracteres40 ∗/41 newtio . c cflag = CRTSCTS | CS8 | CLOCAL | CREAD;42

43 /∗44 ∗ IGNPAR : ignora bytes com erros de paridade45 ∗ ICRNL : mapeia CR (carriage return ) para NL (new line ), senao um CR nos46 ∗ dados de entrada pode nao terminar a leitura47 ∗/48 newtio . c iflag = IGNPAR | ICRNL;49

50 /∗ Seta baud rate de entrada e saida ∗/51 cfsetispeed (&newtio , BAUDRATE);

Page 70: Apostila C 2.0

CAPITULO 10. COMUNICACAO 69

52 cfsetospeed (&newtio , BAUDRATE);53

54 /∗ Saida em modo ’raw’ ∗/55 newtio . c oflag = 0;56

57 /∗58 ∗ ICANON : habilita entrada em modo canonico59 ∗ desabilita funcionalidades de ’ echo ’, e nao envia sinais para o programa60 ∗/61 newtio . c lflag = ICANON;62

63 /∗64 ∗ Inicializa todos os caracteres de controle .65 ∗ Os valores padrao podem ser encontrados em ’/ usr / include / termios .h’66 ∗/67 newtio . c cc [VINTR] = 0; // ’ Ctrl−C68 newtio . c cc [VQUIT] = 0; // Ctrl−’\’69 newtio . c cc [VERASE] = 0; // DEL70 newtio . c cc [VKILL] = 0; // @71 newtio . c cc [VEOF] = 4; // Ctrl−D ∗/72 newtio . c cc [VTIME] = 0; // timeout entre leituras ( t=VTIME ∗ 0.1 segs)73 newtio . c cc [VMIN] = 5; // bloqueia leitura ate ’ ler 5 caracteres74 newtio . c cc [VSWTC] = 0; // ’\0’75 newtio . c cc [VSTART] = 0; // Ctrl−Q76 newtio . c cc [VSTOP] = 0; // Ctrl−S77 newtio . c cc [VSUSP] = 0; // Ctrl−Z78 newtio . c cc [VEOL] = 0; // CR79 newtio . c cc [VREPRINT] = 0; // Ctrl−R80 newtio . c cc [VDISCARD] = 0; // Ctrl−U81 newtio . c cc [VWERASE] = 0; // Ctrl−W82 newtio . c cc [VLNEXT] = 0; // Ctrl−V83 newtio . c cc [VEOL2] = 0; // LF84

85 /∗ ’ Limpa’ a linha e ativa as configuracoes para a porta ∗/86 tcflush ( fd , TCIFLUSH);87 tcsetattr ( fd , TCSANOW, &newtio);88

89 /∗ reseta o dispositivo ( modem) ∗/90 if ( write ( fd , ”ATZ\r”, 4) < 4)91 perror ( ”write” );92

93 while (1) {94 res = read ( fd , buf , 255);95 if ( res < 0) {96 perror ( ”read” );97 exit (1);98 }99

100 /∗ Seta final de string , para poder imprimir o conteudo com printf ∗/101 buf[ res ] = 0;102 fprintf ( stdout , ”[%s] (%d)\n”, buf , res );103 }104

105 /∗ Restaura as configuracoes antigas da porta ∗/

Page 71: Apostila C 2.0

CAPITULO 10. COMUNICACAO 70

106 tcsetattr ( fd , TCSANOW, &oldtio);107 close ( fd );108 exit (0);109 }

Nesse exemplo, vale notar que alguns caracteres de controle sao inicializados com 0 mesmoapos a estrutura ter sido preenchida com 0’s atraves da funcao memset(3). Isto foi feito apenaspara mostrar no codigo a possibilidade de trocar os caracteres de controle. Os valores defaultestao comentados a direita de cada um.

As funcoes cfsetispeed(3) e cfsetospeed(3) permitem que seja utilizada umataxa de baud rate diferente para a entrada e a saıda de dados. Neste exemplo foi utilizada amesma taxa, o que seria equivalente a passar a constante BAUDRATE juntamente na variavelnewtio.c cflag no Linux.

10.2.3 Modo nao-canonico (raw)A diferenca basica entre o modo canonico e o nao-canonico, no que diz respeito ao codigo, e onao-mapeamento do carriage return para o caracter de nova linha (pois nao estamos trabalhandobaseados em linhas, mas sim em caracteres) e da especificacao da variavel newtio.c lflag como0, habilitando a entrada em modo nao-canonico.

O exemplo abaixo mostra como trabalhar neste modo. Ele configura um timeout entreleituras de caracteres (apos o sinal de nova linha) de 0.5 segundos, alem de aguardar a leitura depelo menos 5 caracteres.

1 #include <stdio .h>2 #include <unistd .h>3 #include < stdlib .h>4 #include < string .h>5 #include <sys/ types .h>6 #include <sys/ stat .h>7 #include < fcntl .h>8 #include <termios .h>9

10 /∗ Configuracoes de baudrate sao definidas em <asm/termbits.h> ∗/11 #define BAUDRATE B960012 #define DEVICE ”/dev/ttyS0”13

14 int15 main ( int argc , char ∗∗argv)16 {17 int fd , res ;18 struct termios oldtio , newtio;19 char buf [255];20

21 fd = open (DEVICE, O RDWR | O NOCTTY);22 if ( fd < 0) {23 perror ( ”open”);24 exit (1);25 }26

27 /∗ Salva a configuracao atual da porta serial e inicia uma nova ∗/28 tcgetattr ( fd, & oldtio );29 memset (&newtio , 0, sizeof ( newtio ));30

31 /∗

Page 72: Apostila C 2.0

CAPITULO 10. COMUNICACAO 71

32 ∗ BAUDRATE: Configura o bps rate.33 ∗ CRTSCTS : Controle de fluxo de saida do hardware34 ∗ CS8 : 8 n1 (8 bit , no parity , 1 stopbit )35 ∗ CLOCAL : Conexao local , sem controle do modem36 ∗ CREAD : Habilita a recepcao de caracteres37 ∗/38 newtio . c cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;39 newtio . c iflag = IGNPAR;40

41 /∗ Saida em modo ’raw’ ∗/42 newtio . c oflag = 0;43

44 /∗ habilita entrada em modo nao−canonico ∗/45 newtio . c lflag = 0;46

47 newtio . c cc [VTIME] = 5; // timeout entre leituras ( t=VTIME ∗ 0.1 segs)48 newtio . c cc [VMIN] = 5; // bloqueia leitura ate ’ ler 5 caracteres49

50 /∗ ’ Limpa’ a linha e ativa as configuracoes para a porta ∗/51 tcflush ( fd , TCIFLUSH);52 tcsetattr ( fd , TCSANOW, &newtio);53

54 /∗ reseta o dispositivo ( modem) ∗/55 if ( write ( fd , ”ATZ\r”, 4) < 4)56 perror ( ”write” );57

58 while (1) {59 res = read ( fd , buf , 255);60 if ( res < 0) {61 perror ( ”read” );62 exit (1);63 }64

65 /∗ Seta final de string , para poder imprimir o conteudo com printf ∗/66 buf[ res ] = 0;67 fprintf ( stdout , ”[%s] (%d)\n”, buf , res );68 }69

70 /∗ Restaura as configuracoes antigas da porta ∗/71 tcsetattr ( fd , TCSANOW, &oldtio);72 close ( fd );73 exit (0);74 }

10.2.4 Modo assıncronoEste modo e utilizado quando nao deseja-se ter uma chamada read(2) bloqueante. Nesse caso,configura-se o select(2) para monitorar o descritor da porta serial, avisando-nos assim queela tiver algum dado para ser lido. Alem disso, um tratador para o sinal SIGIO e criado; dessaforma, assim que o select(2) nos informar que existem dados disponıveis para a leitura, otratador do sinal sera chamado, e podera preparar quaisquer buffers e opcoes antes de efetuar aleitura de fato.

Abaixo segue um exemplo de codigo para usar o modo assıncrono para a leitura da porta

Page 73: Apostila C 2.0

CAPITULO 10. COMUNICACAO 72

serial.

1 #include <stdio .h>2 #include <unistd .h>3 #include < stdlib .h>4 #include < string .h>5 #include <sys/ types .h>6 #include <sys/ stat .h>7 #include < fcntl .h>8 #include <termios .h>9 #include <signal .h>

10

11 /∗ Configuracoes de baudrate sao definidas em <asm/termbits.h> ∗/12 #define BAUDRATE B960013 #define DEVICE ”/dev/ttyS0”14

15 void16 trata sinal ( int signum)17 {18 printf ( ”agora posso ler da porta\n”);19 }20

21 int22 main ( int argc , char ∗∗argv)23 {24 int fd , res ;25 struct termios oldtio , newtio;26 char buf [255];27 fd set readfds ;28

29 fd = open (DEVICE, O RDWR | O NOCTTY | O NONBLOCK);30 if ( fd < 0) {31 perror ( ”open”);32 exit (1);33 }34

35 /∗ Instala tratador de sinal para SIGIO ∗/36 signal ( SIGIO, trata sinal );37

38 /∗ configura o descritor para operacoes assincronas ∗/39 fcntl ( fd , F SETFL, FASYNC);40

41 /∗ Salva a configuracao atual da porta serial e inicia uma nova ∗/42 tcgetattr ( fd, & oldtio );43 memset (&newtio , 0, sizeof ( newtio ));44

45 /∗46 ∗ BAUDRATE: Configura o bps rate.47 ∗ CRTSCTS : Controle de fluxo de saida do hardware48 ∗ CS8 : 8 n1 (8 bit , no parity , 1 stopbit )49 ∗ CLOCAL : Conexao local , sem controle do modem50 ∗ CREAD : Habilita a recepcao de caracteres51 ∗/52 newtio . c cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;

Page 74: Apostila C 2.0

CAPITULO 10. COMUNICACAO 73

53 newtio . c iflag = IGNPAR;54

55 /∗ Saida em modo ’raw’ ∗/56 newtio . c oflag = 0;57

58 /∗ habilita entrada em modo nao−canonico ∗/59 newtio . c lflag = 0;60

61 newtio . c cc [VTIME] = 5; // timeout entre leituras ( t=VTIME ∗ 0.1 segs)62 newtio . c cc [VMIN] = 5; // bloqueia leitura ate ’ ler 5 caracteres63

64 /∗ ’ Limpa’ a linha e ativa as configuracoes para a porta ∗/65 tcflush ( fd , TCIFLUSH);66 tcsetattr ( fd , TCSANOW, &newtio);67

68 /∗ reseta o dispositivo ( modem) ∗/69 if ( write ( fd , ”ATZ\r”, 4) < 4)70 perror ( ”write” );71

72 while (1) {73 FD SET (fd, &readfds );74 select ( fd + 1, & readfds , NULL, NULL, NULL);75 if (! FD ISSET (fd, &readfds )) {76 fprintf ( stderr , ”Erro em select ()!\n” );77 exit (1);78 }79 res = read ( fd , buf , 255);80 if ( res < 0) {81 perror ( ”read” );82 exit (1);83 }84

85 /∗ Seta final de string , para poder imprimir o conteudo com printf ∗/86 buf[ res ] = 0;87 fprintf ( stdout , ”[%s] (%d)\n”, buf , res );88 }89

90 /∗ Restaura as configuracoes antigas da porta ∗/91 tcsetattr ( fd , TCSANOW, &oldtio);92 close ( fd );93 exit (0);94 }

10.3 USBO acesso a dispositivos USB pelo Linux pode ser feito atraves da LibUSB 1. Ela e uma bibliotecapara ser usada por aplicacoes em espaco de usuario para acessar dispositivos USB 1.1 indepen-dente do sistema operacional.

A respeito de dispositivos e interfaces, a API da libusb associa um dispositivo aberto a umainterface em especıfico. Isso significa que caso deseje-se abrir multiplas interfaces em um dis-positivo, ele devera ser aberto multiplas vezes para receber um manipulador para cada interface.

1http://libusb.sourceforge.net/

Page 75: Apostila C 2.0

CAPITULO 10. COMUNICACAO 74

As seguintes funcoes sao usadas para preparar o uso da biblioteca para comunicar com algumdispositivo:

• void usb init(void): inicializa estruturas internas da biblioteca;

• int usb find busses(void): scaneia todos os BUSses USB no sistema, retornando a quanti-dade de modificacoes desde a ultima chamada a esta funcao (o total de BUSses adiciona-dos e removidos);

• int usb find devices(void): scaneia todos os dispositivos em cada BUS. Essa funcao deveser chamada logo apos usb find busses(). O valor de retorno indica a quantidadede dispositivos removidos ou adicionados ate a ultima chamada a funcao;

• struct usb bus *usb get busses(void): retorna a lista dos busses USB encontrados;

A struct usb bus apresenta os seguintes campos:

struct usb_bus {struct usb_bus *next, *prev;char dirname[PATH_MAX + 1];struct usb_device *devices;

};

O campo dirname e relativo ao diretorio na entrada USB do procfs – normalmente local-izado em /proc/bus/usb. Para cada BUS, um sub-diretorio e criado neste diretorio, sendo quedirname indica qual e o sub-diretorio associado a um determinado BUS. Esta informacao naochega a ser util enquanto lida-se com o dispositivo pela libusb. Os campos next e prev sao usadospara iterar na lista duplamente encadeada que e retornada pela funcao usb get busses().

Ja a struct usb device apresenta informacoes sobre os dispositivos encontrados. Ela e de-scrita como segue:

struct usb_device {struct usb_device *next, *prev;char filename[PATH_MAX + 1];struct usb_bus *bus;struct usb_device_descriptor descriptor;struct usb_config_descriptor *config;void *dev; /* Darwin support */

};

Agora, o campo filename informa qual o arquivo dentro do diretorio informado na structusb bus esta associado ao dispositivo atual. Destes campos, o que sera util para as aplicacoessera o descriptor. Ele nos da as seguintes caracterısticas sobre o dispositivo:

struct usb_device_descriptor {u_int8_t bLength;u_int8_t bDescriptorType;u_int16_t bcdUSB;u_int8_t bDeviceClass;u_int8_t bDeviceSubClass;u_int8_t bDeviceProtocol;u_int8_t bMaxPacketSize0;u_int16_t idVendor;u_int16_t idProduct;u_int16_t bcdDevice;u_int8_t iManufacturer;u_int8_t iProduct;u_int8_t iSerialNumber;u_int8_t bNumConfigurations;

};

Page 76: Apostila C 2.0

CAPITULO 10. COMUNICACAO 75

A partir destas informacoes, e possıvel obter o identificador do dispositivo que deseja-setrabalhar. Feito isso, as seguintes funcoes sao usadas:

• usb dev handle *usb open(struct *usb device dev): abre o dispositivo dev para uso. Estafuncao deve ser chamada antes de realizar qualquer operacao sobre o dispositivo. Elaretorna um handle para comunicar-se com ele;

• int usb close(usb dev handle *dev): fecha um dispositivo aberto com usb open(). Re-torna 0 ou um valor < 0 caso ocorra um erro;

• int usb claim interface(usb dev handle *dev, int interface): reivindica a interface parao sistema operacional. O parametro interface e o valor especificado no campo bInter-faceNumber do descritor. Este valor pode ser recuperado a partir de config→interface→altsetting,presente na struct usb device. Essa funcao deve ser chamada antes de realizar qualqueroperacao relacionada a interface requisitada (como usb set altinferface() ou usb bulk write();

• int usb release interface(usb dev handle *dev, int interface): libera uma interface previ-amente requisitada pela usb claim interface();

• int usb set configuration(usb dev handle *dev, int configuration): escolhe a configuracaoativa de um dispositivo. O parametro configuration e o valor especificado no campobConfigurationValue do descritor;

• int usb set altinterface(usb dev handle *dev, int alternate): escolhe a configuracao ativaalternada da interface atual. O parametro alternate e o valor especificado no campo bAl-ternateSetting do descritor;

• int usb reset(usb dev handle *dev): reseta o dispositivo especificado, enviando um RE-SET para a porta que ele estiver conectado. Vale notar que apos chamar essa funcao, odispositivo precisara ser renumerado, sendo que o handle usado para chamar a usb reset()nao funcionara mais. Para tornar a usar o dispositivo, e necessario procura-lo novamentee abrir um novo handle;

• int usb resetep(usb dev handle *dev, unsigned int ep): reseta todos os estados para oendpoint especificado em ep. O valor ep e o encontrado no campo bEndpointAddress dodescritor;

• int usb clear halt(usb dev handle *dev, unsigned int ep): limpa qualquer halt status doendpoint especificado por ep.

Para realizar transferencia de dados sobre bulk pipes, podem ser usadas as seguintes funcoes:

• int usb bulk write(usb dev handle *dev, int ep, char *bytes, int size, int timeout): realizauma requisicao de escrita em volume (bulk write) para o endpoint ep, escrevendo sizebytes do array bytes para o dispositivo dev. O timeout deve ser especificado em mili-segundos. Essa funcao rtorna o numero de bytes escritos ou um valor < 0 caso ocorra umerro;

• int usb bulk read(usb dev handle *dev, int ep, char *bytes, int size, int timeout): realizauma requisicao de leitura em volume (bulk read) para o endpoint ep. Retorna o numerode bytes lidos ou um valor < 0 caso ocorra um erro.

A biblioteca permite tambem realizar transferencia de dados sobre interrupt pipes:

• int usb interrupt write(usb dev handle *dev, int ep, char *bytes, int size, int timeout):realiza uma requisicao de escrita interruptıvel para o endpoint ep. Retorna o numero debytes escritos com sucesso ou um valor < 0 caso haja um erro;

• int usb interrupt read(usb dev handle *dev, int ep, char *bytes, int size, int timeout):realiza uma requisicao de leitura interruptıvel para o endpoint ep. Retorna o numero debytes lidos com sucesso ou um valor < 0 caso haja um erro;

Algumas outras funcoes sao providas pela biblioteca, com opcoes para obter strings deidentificacao para dispositivos e realizar transferencias de controle. Sao elas:

Page 77: Apostila C 2.0

CAPITULO 10. COMUNICACAO 76

• int usb control msg(usb dev handle *dev, int requesttype, int request, int value, int index,char *bytes, int size, int timeout): realiza uma requisicao de controle para o pipe padraode controle em um dispositivo. Os parametros sao os de mesmo nome na especificacaoUSB. Retorna o numero de bytes escritos/lidos ou um valor < 0 caso ocorra um erro;

• int usb get string(usb dev handle *dev, int index, int langid, char *buf, size t buflen):obtem o descritor de string especificado pelos parametros index e langid de um disposi-tivo. A string sera retornada em formato Unicode, de acordo com a especificacao USB. Ovalor de retorno e o numero de bytes em buf ou < 0 caso haja um erro;

• int usb get string simple(usb dev handle *dev, int index, char *buf, size t buflen): estafuncao e um wrapper para a usb get string() que retorna a string de descricao especificadapelo index na primeira lıngua para o descritor, convertendo-o para ASCII. Essa funcaoretorna o numero de bytes em buf ou um valor < 0 se houver um erro;

• int usb get descriptor(usb dev handle *dev, unsigned char type, unsigned char index,void *buf, int size): obtem o descritor para o dispositivo identificado por type e indexdo descritor a partir do pipe de controle padrao. Retorna o numero de bytes lidos para odescritor ou um valor < 0 em caso de erro;

• int usb get descriptor by endpoint(usb dev handle *dev, int ep, unsigned char type, un-signed char index, void *buf, int size): idem a funcao usb get descriptor(), porem o obtema partir do pipe de controle identificado por ep;

• int usb get driver np(usb dev handle *dev, int interface, char *name, int namelen): essafuncao ira obter o nome do driver associado a interface especificada pelo parametro inter-face e ira armazena-lo no buffer name de tamanho namelen. Essa funcao esta implemen-tada apenas para o Linux;

• int usb detach kernel driver np(usb dev handle *dev, int interface): desconecta o driverde kernel da interface especificada pelo parametro interface. Aplicacoes usando a libusbpodem entao realizar a usb claim interface(). Ela esta implementada apenas no Linux.

O conjunto de funcoes da API permitem que se manipule de forma bastante abstrata os dis-positivos USB. O codigo abaixo busca todos os dispositivos e, para cada um, obtem as suasstrings de informacao e as imprime na saıda padrao. Durante a procura por dispositivos, e bus-cado um em especial (como pode ser visto nas linhas 45 e 46), e este dispositivo sera usadodepois para realizar a escrita de um comando e a leitura de uma resposta. No entanto, valelembrar que como os comandos (e os identificadores do dispositivo) sao especıficos, o codigoprecisa ser adaptado para lidar com algum outro dispositivo. Para compilar este exemplo, bastarodar:

$ gcc teste_usb.c -o teste_usb -Wall ‘libusb-config --cflags --libs‘

1 #include <stdio .h>2 #include < string .h>3 #include <usb.h>4

5 void show info ( usb dev handle ∗handle ) {6 int i ;7 char buffer [1024];8

9 /∗ mostra ate 3 linhas de informacao sobre o dispositivo ∗/10 for ( i = 0; i < 3; i++) {11 int len = usb get string simple ( handle , i , buffer , 1024);12 if ( len < 0)13 break;14

15 buffer [ len ] = ’\0’;

Page 78: Apostila C 2.0

CAPITULO 10. COMUNICACAO 77

16 printf ( ”%s\n”, buffer );17 }18 printf ( ”\n”);19 }20

21 int22 main ( int argc , char ∗∗argv)23 {24 struct usb bus ∗busses ;25 struct usb bus ∗bus;26 struct usb device ∗dev;27 struct usb device ∗gp32 = NULL;28 usb dev handle ∗handle ;29 char buffer [1024];30 int result ;31

32 /∗ Inicializa a biblioteca e procura os devices ∗/33 usb init ();34 usb find busses ();35 usb find devices ();36

37 busses= usb get busses ();38 for ( bus=busses ; bus ; bus=bus−>next) {39 for ( dev=bus−>devices; dev; dev=dev−>next) {40

41 handle = usb open (dev );42 show info ( handle );43 usb close ( handle );44

45 if (( dev−>descriptor.idVendor == 0xeb6) &&46 (dev−>descriptor.idProduct == 0x3232)) {47 printf ( ”Achei dispositivo que procurava\n”);48 gp32 = dev;49 }50 }51 }52

53 if ( gp32 == NULL) {54 fprintf ( stderr , ”Nao encontrei o dispositivo na USB\n”);55 exit (1);56 }57

58 /∗ Abre a interface representada pelo dispositivo ’ gp32’ ∗/59 handle = usb open (gp32);60 result = usb claim interface ( handle , 0 x0);61 if ( result < 0) {62 fprintf ( stderr , ”%s\n”, usb strerror ());63 exit (1);64 }65

66 /∗67 ∗ Estamos prontos para fazer leituras e escritas ao device .68 ∗ Note que os dados a serem escritos aso especificos de cada dispositivo ,69 ∗ e devem ser consultados na especificacao dos mesmos. Neste exemplo,

Page 79: Apostila C 2.0

CAPITULO 10. COMUNICACAO 78

70 ∗ estao sendo usados os endpoints 0x03 para escrita e 0x81 para leitura .71 ∗/72 memset (buffer , 0, sizeof ( buffer ));73 snprintf ( buffer , sizeof ( buffer ), ”comandos especificos ” );74

75 result = usb bulk write ( handle , 0 x03 , buffer , sizeof ( buffer ), 100000);76 if ( result < 0) {77 fprintf ( stderr , ”%s\n”, usb strerror ());78 exit (1);79 }80

81 /∗ le a resposta ∗/82 memset (buffer , 0, sizeof ( buffer ));83 result = usb bulk read ( handle , 0 x81 , buffer , sizeof ( buffer ), 100000);84 if ( result < 0) {85 fprintf ( stderr , ”%s\n”, usb strerror ());86 exit (1);87 }88

89 /∗ libera a interface e encerra o programa ∗/90 usb release interface ( handle , 0 x0);91 usb close ( handle );92

93 exit (0);94 }

Page 80: Apostila C 2.0

Capıtulo 11

Timers

11.1 SinaisTimers sao recursos que permitem sinalizar o processo em intervalos de tempo fixos. Como elessao baseados em sinais, esta sessao os apresenta, permitindo fazer um programa que lide comsinais diversos.

A chamada de sistema usada para capturar sinais e a signal(2). Esta chamada de sistemainstala um novo signal handler para o sinal com o numero signum. O signal handler e configu-rado para sighandler, que pode ser qualquer funcao escrita pelo usuario, ou SIG IGN para ig-nora-lo ou SIG DFL, para que a acao padrao seja tomada (descrita na man page do signal(7).

O seu prototipo e o seguinte:

#include <signal .h>typedef void (∗ sighandler t )( int );sighandler t signal ( int signum, sighandler t handler );

Alguns dos sinais mais comuns de serem tratados sao:

• SIGINT: interrupcao do teclado (CTRL+C);

• SIGKILL: processo recebeu um sinal de kill;

• SIGTERM: processo recebeu sinal de termino.

Seu uso e bastante simples, como pode ser visto no exemplo abaixo. O programa declaraum handler para quando receber o sinal SIGINT, e aguarda ate que seja pressionada alguma teclapara encerrar normalmente, ou atraves da recepcao do sinal especificado.

1 #include <stdio .h>2 #include < stdlib .h>3 #include <signal .h>4

5 void6 suicide ( int signum)7 {8 printf ( ”Recebi o sinal %d, encerrando ..\ n” , signum);9 exit (1);

10 }11

79

Page 81: Apostila C 2.0

CAPITULO 11. TIMERS 80

12 int13 main ( int argc , char ∗∗argv)14 {15 signal ( SIGINT, suicide );16 getchar ();17 printf ( ”Encerrando normalmente\n”);18 exit (0);19 }

11.2 Utilizando timersO sistema prove a cada processo tres timers, cada um decrementando em um domınio de tempodiferente. Quando um timer expira, um sinal e enviado para o processo, e o timer potencialmentereinicia. Os timers disponıveis sao:

• ITIMER REAL: decrementa em tempo real, e entrega o sinal SIGALRM quando expira;

• ITIMER VIRTUAL: decrementa apenas enquanto o processo esta executando, e entrega osinal SIGVTALARM quando expira;

• ITIMER PROF: decrementa quando o processo executa e quando o sistema esta execu-tando no contexto do processo. Este metodo costuma ser usado para fazer profile dotempo que um processo passou executando tanto em espaco de usuario quanto de kernel.O sinal SIGPROF e entreque quando este timer expira.

O prototipo para uso de timers e o seguinte:

#include <sys/time.h>int getitimer ( int which , struct itimerval ∗value );int setitimer ( int which , const struct itimerval ∗value , struct itimerval ∗ovalue );

Os valores que sao usados sao definidos pelas estruturas:

struct itimerval {struct timeval it interval ; /∗ proximo valor ∗/struct timeval it value ; /∗ valor atual ∗/

};struct timeval {

long tv sec ; /∗ segundos ∗/long tv usec ; /∗ micro segundos ∗/

};

A funcao getitimer(2) preenche a estrutura indicada por value com as configuracoesdo timer indicado por wich, que pode ser uma das ITIMER REAL, ITIMER VIRTUAL ouITIMER PROF. O elemento it value e configurado para a quantidade de tempo restante no timer,ou zero caso o timer seja desabilitado. Similarmente, it interval e configurado para o valor dereset.

A funcao setitimer(2) configura o timer indicado para o valor em timer. Se ovalue enao-zero, o valor antigo do timer e armazenado ali.

Timers decrementam de it value ate zero, gerando um sinal, e resetam para it interval. Umtimer que e configurado para um it value zero expira.

Page 82: Apostila C 2.0

CAPITULO 11. TIMERS 81

Como exemplo de uso, podemos tomar o programa abaixo, que executa um timer de 2 em 2segundos.

1 #include <stdio .h>2 #include <unistd .h>3 #include <signal .h>4 #include <sys/time.h>5

6 void7 timer func ( int signum)8 {9 printf ( ” recebi o sinal %d\n”, signum);

10 }11

12 int13 main ( int argc , char ∗∗argv)14 {15 struct itimerval tempo;16

17 /∗ como vamos usar ITIMER REAL, preparamos um signal handler para SIGALRM ∗/18 signal ( SIGALRM, timer func);19

20 /∗ especifica o intervalo e seta o timer ∗/21 tempo. it interval . tv sec = 2;22 tempo. it interval . tv usec = 0;23 tempo. it value . tv sec = 2;24 tempo. it value . tv usec = 0;25 setitimer ( ITIMER REAL, &tempo, NULL);26

27 while (1) {28 usleep (100);29 }30 exit (0);31 }

Page 83: Apostila C 2.0

Bibliografia

[Ame 94] American National Standards Institute. IEEE standard for information technol-ogy: Portable Operating Sytem Interface (POSIX). part 1, system application pro-gram interface (API) — amendment 1 — realtime extension [C language]. 1109Spring Street, Suite 300, Silver Spring, MD 20910, USA: IEEE Computer Soci-ety Press, 1994. xxiii + 590p. IEEE Std 1003.1b-1993 (formerly known as IEEEP1003.4; includes IEEE Std 1003.1-1990). Approved September 15, 1993, IEEEStandards Board. Approved April 14, 1994, American National Standards Institute.

[GIL 2003] GILMORE, J. GDB Internals. [S.l.]: Free Software Foundation, 59 Temple Place- Suite 330, Boston, MA 02111-1307 USA, 2003.

[KOJ 2000] KOJIMA, A. K.; ANDRADE, P. C. P. de; SANTOS, C. A. M. dos. Desenvolvi-mento de Aplicacoes Graficas para o X Window System. Mini-curso apresen-tado no Software Livre 2000.

[MyS 2003] MySQL AB. MySQL Reference Manual.http://www.mysql.com/documentation/.

[RUB 2001] RUBINI, A.; CORBET, J. (Eds.). LINUX device drivers. [S.l.]: O’REILLY, 2001.v.2.

[SCH 86] SCHEIFLER, R.; GETTYS, J. The X Window System. ACM Transactions onGraphics, v.5, n.2, p.79–109, 1986.

[STA 2002] STALLMAN, R. M. Using the GNU Compiler Collection (GCC). [S.l.]: FreeSoftware Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA,2002.

[STE 98] STEVENS, W. R. (Ed.). UNIX Network Programming - Interprocess Commu-nication. [S.l.]: Prentice Hall, 1998.

[STE 98a] STEVENS, W. R. (Ed.). UNIX Network Programming - Networking APIs:Sockets and XTI. [S.l.]: Prentice Hall, 1998.

[The 2003] The Native POSIX Thread Library for Linux, 2003,http://people.redhat.com/drepper/nptl-design.pdf. Anais. . . [S.l.: s.n.], 2003.

[ZEL 2000] ZELLER, A. Debugging with DDD. http://www.gnu.org/manual/ddd/.

82