Escolar Documentos
Profissional Documentos
Cultura Documentos
Conteudo
1 Operacao do GNU/Linux 1.1 Obtendo acesso ao GNU/Linux . . . . . . . . 1.2 Como executar comandos sobre o GNU/Linux 1.3 Obtendo ajuda sobre comandos . . . . . . . . 1.4 Consoles virtuais . . . . . . . . . . . . . . . 1.5 Processos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3 3 4 5 5 7 7 8 9 14 14 15 16 17 18 18 21 21 24 28 30 31 32 32 33 38 44 46 54 54 54
2 Ambiente de desenvolvimento 2.1 Compilador GCC . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 Arquivos headers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3 Utilizando os recursos de debug . . . . . . . . . . . . . . . . . . . . 2.4 Autoconf e Automake . . . . . . . . . . . . . . . . . . . . . . . . . . 2.4.1 Escrevendo um arquivo Makele.am . . . . . . . . . . . . . . 2.4.2 Escrevendo um arquivo congure.in . . . . . . . . . . . . . . 2.5 DDD - Data Display Debugger . . . . . . . . . . . . . . . . . . . . . 2.6 Criando aplicativos que rodem tanto no GNU/Linux como no Windows 3 Explorando os recursos b sicos do sistema operacional a 3.1 Lendo par metros curtos e longos passados pela linha de comando a 3.2 Trabalhando com arquivos . . . . . . . . . . . . . . . . . . . . . 3.3 Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.4 Descritores de Arquivos . . . . . . . . . . . . . . . . . . . . . . . 4 CGI: Common Gateway Interface 5 Bibliotecas din micas a 5.1 Criacao de bibliotecas din micas . . . . . . . . . . . . . . . . . . . . a 6 Processos e threads 6.1 Processos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.2 Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 TCP/IP 7.1 Select . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.2 Conex es n o-bloqueantes . . . . . . . . . . . . . . . . . . . . . . . o a 8 Bancos de dados 8.1 Implementacoes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.2 Programando com Bancos de Dados em C . . . . . . . . . . . . . . . 1 . . . . . . . .
CONTEUDO
9 Interface Gr ca a 9.1 Servidor X . . . . . . . . 9.2 A biblioteca Xlib . . . . 9.3 Gerenciadores de Janelas 9.4 GTK+ . . . . . . . . . .
2 58 58 59 59 59 62 62 64 64 66 70 71 73 79 79 80
. . . .
. . . .
. . . .
. . . .
. . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
10 Comunicacao 10.1 Porta Paralela . . . . . . . . . . . 10.2 Porta Serial . . . . . . . . . . . . 10.2.1 A interface Termios . . . . 10.2.2 Modo can nico . . . . . . o 10.2.3 Modo n o-can nico (raw) a o 10.2.4 Modo assncrono . . . . . 10.3 USB . . . . . . . . . . . . . . . .
Captulo 1
Operacao do GNU/Linux
1.1 Obtendo acesso ao GNU/Linux
Distribuicoes convencionais de GNU/Linux fornecem duas maneiras para que o usu rio a autentique-se no computador: uma delas e atrav s de login gr co, e a outra e atrav s e a e de login via console. A segunda maneira e a mais comum, onde e exigido um nome de usu rio e uma a senha. Todo o sistema GNU/Linux mant m uma conta de super-usu rio, normalmente e a associada ao nome root, e contas opcionais para cada usu rio que ir operar este host. a a Normalmente s o utilizados dois arquivos para efetuar a autenticacao de um usu rio: a a o passwd e o shadow, localizados no diret rio de conguracoes de programas da o distribuicao, freq entemente o /etc. O primeiro arquivo mant m uma listagem com u e os nomes de usu rios, seu identicador unico no sistema (um valor inteiro), o grupo a principal ao qual ele pertence, seu nome completo (al m de outros dados opcionais) e e a shell que ser utilizada em suas sess es. a o Ap s vericar a exist ncia deste usu rio a partir do passwd, o arquivo shadow o e a e processado. Este arquivo cont m uma listagem das senhas de cada usu rio, crip e a tografadas. A senha fornecida pelo usu rio e processada pelo mesmo algoritmo, e e a feito um matching para vericar se ambas senhas criptografadas s o id nticas, encera e rando o processo de autenticacao do usu rio no host e dando a ele o acesso a um console a com uma shell. Existem outras formas de autenticacao, como o Linux-PAM, que faz uso de m dulos o de autenticacao. Assim, e possvel escrever m dulos com rotinas especcas para au o tenticar o usu rio, como a validacao de algum cart o magn tico, reconhecimento facial a a e ou ainda pela voz.
implementacoes diferenciam-se pela sintaxe na qual o usu rio utiliza para a execucao a de tarefas e pelos recursos que cada uma oferece. Esta sess o trata da operacao b sica a a sobre o BASH, um dos interpretadores mais comuns, presente em grande parte das distribuicoes atuais. Ap s realizar o login no sistema, um prompt ca disponvel ao usu rio, aguaro a dando a entrada de algum comando. A sintaxe padr o para a execucao de um programa a no BASH e: $ programa A execucao deste comando ir criar um novo processo, enquanto o processo pai (o a BASH) ir aguardar pela sua nalizacao para continuar a execucao de outros comana dos. Nota-se que essa e a mesma sem ntica obtida com comandos executados no MS a DOS, onde a shell (command.com, no caso) executa o programa de forma sncrona, ou seja: Espera que os lhos terminem para retornar o controle do terminal ao usu rio. a Em alguns casos e desej vel que o programa seja executado de forma asncrona, a ou seja, deseja-se continuar a ter acesso ao console antes mesmo que o programa tenha terminado. Nestes casos, pode-se executar o programa usando a seguinte forma: $ programa & O & indica que o processo lho relativo a execucao de programa ser criado, mas ` a que o processo pai (o BASH) ir continuar seu uxo de execucao, sem aguardar pelo a t rmino do processo lho. e Bem como a execucao de programas, freq entemente e desej vel redirecionar a u a sada do programa para algum outro dispositivo que n o a sada padr o (stdout). Da a a mesma forma, isto pode querer ser feito para redirecionar a sada de erro padr o (stderr), a ou modicar a entrada de dados para que seja feita a partir de um outro uxo que n o a a entrada padr o (stdin). a A forma que o BASH utiliza para fazer o controle de redirecionamentos e atrav s e dos operadores <e >, processados na ordem em que aparecem, da esquerda para a direita. Caso o primeiro caracter do operador de redirecionamento seja >, o redirecionamento refere-se a sada padr o (stdout). ` a Tamb m h casos (em caso de depuracao de mensagem de erros, por exemplo) e a onde-se quer direcionar ambas sadas (stdout e stderr) para um arquivo, o que pode ser obtido com: $ programa >& arquivo_com_mensagens
Al m disso, uma man page e disposta na forma de uma p gina contnua de infore a macoes, podendo muitas vezes tornar difcil a localizacao de determinados assuntos. As info pages solucionam este problema, apresentando a documentacao em um for mato que permite uma navegacao mais f cil, principalmente quando existe um grande a volume de dados a ser informado. Este formato normalmente e utilizado para deta lhar a documentacao de um programa, enquanto as man pages explicam os recursos do programa de forma mais simplista.
1.5 Processos
Qualquer tarefa atribuda ao GNU/Linux e um processo. Todo comando executado gera um n mero de processo (PID), e atrav s deste n mero e possvel manipular este u e u processo. Alguns destes programas que permitem a manipulacao de processos s o: a top(1): lista os processos em execucao em tempo real. Ele mostra algumas informacoes sobre o sistema, bem como uma lista das tarefas sendo gerenciadas pelo kernel Linux. Algumas das informacoes incluem o nome do processo e a quantidade de mem ria e CPU consumidas por ele. o ps(1): lista os processos atuais. V rios par metros podem ser utilizados, a a como a opcao -x, que lista todos os processos da m quina, e a opcao -u, a que inclui o autor do processo. Outros par metros uteis s o o -C nome, que a a listam os processos com um nome especco, e -U usuario, que lista todos os processos pertencentes a usu rio. ` a nice(1): executa um comando com uma prioridade diferente de escalonamento. Prioridades no Linux v o de -20 (prioridade alta) a 19 (prioridade baixa). a ` A prioridade padr o para execucao de processos e 0. a
kill(1): envia um sinal para um processo. Uma lista de sinais encontra-se disponvel na man page do signal(7). killall(): envia um sinal para todos os processos especicados na linha de comando atrav s de seu nome. e nohup(): roda algum comando, ignorando sinais de hangup. Este comando e util quando deseja-se rodar algum programa e logo ap s efetuar o logoff da o m quina: o programa continuar em execucao at o t rmino de sua execucao. a a e e jobs: caso tenha algum programa (job) sendo executado em background, este comando mostra-os. Este comando e implementado pela shell (BASH e ZSH o implementam). A forma de deixar um programa em background e disparando o com o par metro &, como visto na Sess o 1.2, ou pressionando a tecla a a CTRL+Z durante a execucao do programa. fg: tamb m pertinente a controle de programas em background, este comando e ` permite trazer de volta algum job que esteja em background. Caso haja mais de um, e necess rio especicar o seu n mero, listado com o comando jobs. a u Algumas shells utilizam o nome do procsso, ao inv s do seu n mero, para tornar e u a dar o controle a ele. bg: assim como o fg, faz com que o controle seja dado a um processo. No entanto, este modo permite que o programa continue sua execucao em uma sub shell. Assim, a shell ca liberada para que o usu rio continue a execucao de a comandos nela, e o programa sai do estado de background e torna a executar. Este comando e bastante utilizado para chamar programas gr cos no ambiente a X sem bloquear o terminal.
Captulo 2
Ambiente de desenvolvimento
2.1 Compilador GCC
GCC [STA 2002] e uma abreviacao do termo GNU Compiler Collection. Ele leva este nome pelo fato de que v rias vers es do compilador est o integradas, com suporte a o a a linguagens como C, C++, Objective-C, Ada, Fortran, Java e Treelang. Al m disso, ` e ele representa tanto o nome geral do compilador quanto o nome do compilador de programas C, onde a abreviacao passa a ter o signicado de GNU C Compiler. Este e o compilador padr o de qualquer distribuicao GNU/Linux. a Quando o GCC e invocado, ele normalmente realiza quatro etapas para gerar o exe cut vel: pr -processamento, compilacao, montagem e ligacao, sempre nesta ordem, e a e sendo que e possvel parar o processo no nal de cada etapa. Os primeiros tr s est gios e a aplicam-se a um unico arquivo fonte, e encerram produzindo um arquivo objeto. A ligacao combina todos os arquivos objetos especicados como entrada em um arquivo execut vel. Estes passos podem ser melhor descritos como: a Pr -processamento: Esta etapa e respons vel pela resolucao de diretrizes do pr e a e processador, como #define, #if, #include. Nesta fase, o GCC utiliza o utilit rio cpp. a Compilacao: Nesta fase e produzida a linguagem de montagem dos arquivos de entrada. Montagem: Produz o arquivo objeto .o, levando em conta a linguagem de montagem dos arquivos de entrada. Nesta etapa, o GCC utiliza o utilit rio gas (GNU a Assembler), ou o montador nativo as, caso ele n o esteja disponvel. a Ligacao: Nesta fase os arquivos .o e as bibliotecas s o colocados no exe a cut vel. O utilit rio usado nessa fase e o ld (GNU Linker). a a Para parar o GCC em alguma determinada etapa, podem ser utilizados os seguintes comandos: Pr -processamento: e $ gcc -E teste.c -o teste.i Este comando redireciona a sada do pr -processador para o arquivo teste.i. e
Assim, para gerar um execut vel para um simples programa (teste.c, por exema plo), utiliza-se o seguinte comando:
$ gcc -o teste teste.c No entanto, a maioria dos programas consistem em v rios arquivos de c digo. Caso hajam a o dois arquivos, arquivo1.c e arquivo2.c, a seguinte linha de comando poderia ser utilizada para compilar o programa nal: $ gcc -o teste arquivo1.c arquivo2.c O GCC interpreta os arquivos de acordo com a sua extens o. Algumas das principais podem a ser vistas na Tabela 2.1.
Tabela 2.1: Algumas extens es reconhecidas pelo GCC o Extens o a .c .C .cc .i .ii .S .s .o .a .so Interpretacao Programa em linguagem C Programa em linguagem C++ Programa em C pr -processado e Programa em C++ pr -processado e Programa em linguagem Assembly Programa objeto Bibliotecas compiladas
Alguns par metros importantes suportados pelo GCC foram vistos, como o -c e -o. No a entanto, outras opcoes podem ser importantes para compilar um projeto. Algumas destas est o a listadas na Tabela 2.2.
Descricao Especica o nome do arquivo compilado (padr o a.out). a Gera somente o objeto (.o). Busca arquivos de headers primeiro em diretorio. Busca bibliotecas primeiro em diretorio. Liga a biblioteca libnome ao execut vel. a Executa a ligacao usando bibliotecas est ticas. a Inclui informacoes para o depurador. Otimiza o c digo. o Especica um nvel de otimizacao entre 0 e 3 (0 n o otimiza). a Desligando recursos do GCC incompatveis com o padr o ANSI. a Inibe as mensagens de warning (aviso). Emite todas as mensagens de warning. Converte todas as mensagens de warning em erro. Mostra os comandos usados em cada passo da compilacao.
/usr/include/g++: headers para compilar programas C++ que utilizam o compilador GNU C++; /usr/include/asm: Arquivos contendo as funcoes, par metros e denicoes es a peccas arquitetura na qual os bin rios gerados ser o criados; a a /usr/include/linux: link simb lico para o diret rio contendo os headers utilizao o dos na vers o do Linux usada para compilar a GNU libc. Algumas distribuicoes ainda a praticam o erro de apontar para o diret rio do kernel Linux atual sendo utilizado no host. o Estes diret rios s o uteis para localizar a denicao de uma funcao que n o apresenta uma o a a man page ou uma info page. Um comando que auxilia a localizacao de denicoes e o grep(1).
10
O GDB pode fazer quatro tipos de operacoes (al m de outras operacoes de suporte a estas) e para ajudar a encontrar problemas no c digo: o Parar o programa em uma condicao especca; Iniciar o programa, especicando algum par metro que possa afetar seu comportamento; a
Modicar vari veis no programa, de forma a poder corrigir os efeitos causados por um a problema e avancar para poder aprender sobre um novo. A utilizacao padr o do GDB ent o e feita da seguinte maneira, tomando como exemplo um a a programa chamado teste: $ gdb teste GNU gdb 5.2 Copyright 2002 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i686-pc-linux-gnu"... (gdb) O prompt de comando agora passa a ser (gdb), onde comandos especcos do GDB poder o a ser utilizados para entender o que est acontecendo no c digo do programa. O seguinte programa a o ser utilizado como exemplo para as explicacoes de operacao do GDB: a
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
#include <stdio .h> #include <unistd . h> int calcula ( int a , int b) { int resultado ; resultado = a + b; return resultado ; } int main() { int a , b , resultado ; a = 1; b = 2; resultado = calcula ( a , b ); printf ( Resultado: %d\n , resultado ); exit (0); } Para executar este programa no GDB, basta executar a operacao run: $ gdb teste ... (gdb) run
11
Como n o houve nenhum problema durante a execucao deste programa, ele encerrou e rea tornou ao prompt do GDB. No entanto, podemos querer executar o programa passo a passo. Para isto, invoca-se o comando break <linha>. Para listar o c digo sendo depurado e descobrir o a linha que deseja-se utilizar como ponto de parada (breakpoint), os comandos list e list - podem ser usados para listar as pr ximas 10 linhas de c digo, ou as ultimas 10 linhas de o o c digo, de forma progressiva. Outra forma bastante utilizada e marcar o breakpoint em detero minadas funcoes. No nosso exemplo, para iniciar a execucao passo a passo a partir da funcao calcula, ao inv s de especicar a linha desejada para o comando break, pode ser informada o e nome da funcao a ser utilizada como breakpoint: (gdb) break calcula Breakpoint 1 at 0x8048446: file teste.c, line 7. (gdb) run Starting program: /Users/lucasvr/teste Breakpoint 1, calcula (a=1, b=2) at teste.c:7 7 resultado = a + b; A partir deste ponto, podemos continuar a execucao do programa at que ele encerre nor e malmente, com o uso do comando continue, ou execut -lo passo a passo. Para isto, podea mos utilizar o comando step, que continua executando o programa at encontrar uma outra e linha do c digo, retornando ent o o controle ao GDB. Uma caracterstica importante do step o a [count] e que ele n o entra em funcoes ou loops: caso haja uma chamada a uma funcao, por a exemplo, ele ir retornar o controle ao GDB apenas ap s a execucao completa dessa funcao. a o Caso deseja-se ter um controle maior sobre a execucao de funcoes, loops e switches, pode-se utilizar o comando next [count], que segue o uxo do programa. Outros comandos interessantes s o os referentes a vari veis. O GDB permite que o usu rio a ` a a modique o valor de uma vari vel com o comando set variable <varivel=valor>. a a No caso do breakpoint realizado anteriormente na funcao calcula, poderamos alterar o valor de a para 2, alterando o resultado da soma. Para isto, basta executar: (gdb) break calcula Breakpoint 1 at 0x8048446: file teste.c, line 7. (gdb) run Starting program: /Users/lucasvr/teste Breakpoint 1, calcula (a=1, b=2) at teste.c:7 7 resultado = a + b; (gdb) set variable a=2 (gdb) continue Continuing. Resultado: 4 Program exited normally. (gdb) Outro recurso bastante utilizado e o watchpoint, que interrompe a execucao do programa quando uma determinada express o for realizada. Ele pode ser usado para vericar quando uma a vari vel tem seu valor modicado, como no exemplo abaixo, onde uma mudanca no valor de b e a a condicao para o retorno do comando ao depurador:
12
Nota-se que o controle e retomado logo ap s a execucao da operacao que modicou b, o logo a execucao continua tendo como indicacao a pr xima linha a ser executada pelo programa. o Utilizamos ainda o comando print, que e usado para imprimir o valor de alguma vari vel a neste caso a vari vel b. a O GDB permite ainda ser anexado a um processo em execucao. Para isto, o GDB deve ser executado passando o PID do processo como segundo argumento, seguindo o nome do programa. Como o programa a ser depurado estar em um ponto desconhecido de execucao, e interessante a vericar onde ele encontra-se. O comando backtrack do GDB imprime os stack frames do processo, e assim e possvel saber onde o programa est sendo executado. Cada frame e um dado a associado a uma chamada de funcao, e cont m os argumentos enviados a ela, as vari veis locais ` e ` a a esta funcao e o endereco no qual a funcao est executando. a Para fazer um teste, modicaremos nosso programa exemplo para que ele faca a leitura de uma tecla para continuar a execucao. Assim, poderemos depur -lo enquanto estiver em a execucao, j que ele executa muito r pido para podermos anexar o GDB a ele. A funcao calcula a a dever car da seguinte forma: a int calcula ( int a , int b) { int resultado ; resultado = a + b; getchar (); return resultado ; } Ap s recompilar o programa n o esquecendo da opcao -g , iremos execut -lo: o a a $ ./teste Agora, em outro terminal, iremos anexar o GDB a sua inst ncia. Para isto, e necess rio obter ` a a o PID do processo, executando o GDB em seguida: $ ps -C teste PID TTY 15247 pts/6 TIME CMD 00:00:00 teste
13
$ gdb teste 15247 -silent Attaching to program: /Users/lucasvr/teste, process 15247 Reading symbols from /System/Links/Libraries/libc.so.6...done. Loaded symbols for /System/Links/Libraries/libc.so.6 Reading symbols from /System/Links/Libraries/ld-linux.so.2...done. Loaded symbols for /System/Links/Libraries/ld-linux.so.2 0x400e76d4 in read () from /System/Links/Libraries/libc.so.6 (gdb) Neste exemplo, o processo teste encontrava-se no PID 15247, e este valor foi informado como par metro para a execucao do GDB. Nesse ponto temos controle sobre o processo, mas a n o sabemos ainda onde ele est . Para descobrir, vamos listar os stack frames do processo: a a (gdb) backtrace #0 0x400e76d4 in #1 0x40146238 in #2 0x40085b63 in #3 0x40087ffd in #4 0x40087e46 in #5 0x4008230a in #6 0x08048487 in #7 0x080484b8 in #8 0x4002cfe4 in read () from /System/Links/Libraries/libc.so.6 sys_sigabbrev () from /System/Links/Libraries/libc.so.6 _IO_file_underflow () from /System/Links/Libraries/libc.so.6 _IO_default_uflow () from /System/Links/Libraries/libc.so.6 __uflow () from /System/Links/Libraries/libc.so.6 getchar () from /System/Links/Libraries/libc.so.6 calcula (a=1, b=2) at teste.c:8 main () at teste.c:17 __libc_start_main () from /System/Links/Libraries/libc.so.6
Esta stack deve ser interpretada a partir do maior valor, que indica a base de execucao do programa neste caso a funcao libc start main(), da biblioteca padr o do C, a GNU libc. a Em seguida a funcao main() foi executada na linha 17 do programa teste.c, chamando a funcao calcula() a seguir, na linha 8, e nalmente realizando a chamada getchar(). A partir deste ponto, as funcoes referentes a implementacao desta chamada na GNU libc foram executadas, e pode ` se vericar que o processo encontra-se aguardando a leitura de algum dado com a chamada de sistema read(). Nota-se que para cada funcao chamada, um novo frame e criado, e a cada retorno de funcao, o frame para esta funcao e eliminado. Caso a funcao seja recursiva, podem existir v rios frames para a mesma funcao. a Para escolher algum frame especco, pode-se utilizar o comando frame, seguido do n mero u do frame desejado. Pode-se ainda utilizar os comandos up [n] e down [n], que avancam ou retrocedem a escolha do frame desejado na stack. Para modicarmos nosso programa agora, iremos modicar o valor do resultado a ser retornado para a funcao main(). Para isso, iremos escolher o frame referente a funcao calcula() (frame 6, como mostrou o backtrace), modicar o ` valor da vari vel resultado e continuar a execucao do programa: a (gdb) frame 6 #6 0x08048487 in calcula (a=1, b=2) at teste.c:8 8 getchar (); (gdb) set variable resultado=10 (gdb) continue Continuing. Agora, ao retornar ao terminal utilizado para disparar o processo teste e pressionar a tecla ENTER, veremos que nossa modicacao no GDB foi reetida no processo que foi modicado. A partir deste ponto pode-se retornar ao GDB e encerr -lo, com o comando quit. a (gdb) quit $
14
Rodar automake -a -c para gerar um arquivo Makele.in a partir do arquivo Makele.am (o automake verica o arquivo congure.in para obter informacoes sobre o pro jeto); A estrutura utilizada em projetos GNU segue a seguinte hierarquia de arquivos e diret rios, o a partir do diret rio base dos fontes do projeto: o BUGS: listagem dos problemas conhecidos do projeto, e de formas de submeter bugs encontrados pelo usu rio; a COPYING: cont m a licenca pela qual o projeto e distribudo; e ChangeLog: cont m um hist rico com modicacoes realizadas no projeto; e o INSTALL: processo de instalacao do programa; AUTHORS: arquivo contendo os nomes dos autores do projeto; Rodar autoconf para gerar o script configure.
Makefile.am: o arquivo a ser gerado pelo programador, informando alguns dados importantes para que o arquivo Makele seja construdo; NEWS: cont m novidades sobre o programa; e README: informacoes sobre o programa, como seu prop sito e formas de contato com o o autor e/ou mailing lists; configure.in: arquivo a ser gerado pelo programador, contendo vericacoes que devem ser feitas antes de compilar o programa; include/: diret rio contendo os arquivos headers do programa; o src/: diret rio contendo os arquivos fontes do programa; o src/Makefile.am: regras de compilacao dos fontes, como nome do bin rio e ar a quivos fontes usados para constru-lo.
15
foreign: verica se o projeto est de acordo com os padr es exigidos para distribuir o a o software; gnu: verica se o projeto inclui os arquivos exigidos pelo GNU standards para pacotes de software; no-dependencies: usado em situacoes onde n o existem informacoes sucientes para a fazer a deteccao autom tica de depend ncias no pacote. a e As opcoes suportadas s o muitas, e aconselha-se a leitura da info page do automake para a consult -las. a O campo SUBDIRS informa diret rios onde residem arquivos fontes do programa, e EXo TRA DIST informa que os arquivos ali listados devem ser ignorados pelo automake e pelo autoconf. O arquivo Makele.am interno ao diret rio src/ pode conter informacoes complexas com o regras que devem ser utilizadas para cada arquivo, mas no entanto o seguinte e suciente para gerar pacotes com estas ferramentas: bin_PROGRAMS = Programa Programa_SOURCES = arquivo1.c arquivo2.c arquivo3.c EXTRA_DIST = config.sample Ap s gerar estes arquivos, a pr xima etapa e a criacao de um arquivo congure.in. o o
16
AC_CONFIG_FILES([Makefile src/Makefile]) AC_OUTPUT Ap s preparados estes dois arquivos, congure.in e Makele.am, o projeto pode ser gerado o executando aclocal, automake e autoconf, como explicado anteriormente.
17
1 ver
http://www.cygwin.com
Captulo 3
#include <stdio .h> #include <unistd . h> int main ( int argc , char argv) { int i ; printf ( Numero de argumentos: %d\n, argc ); for ( i = 0; i < argc ; i++) printf ( Argumento %d: %s\n, i, argv[ i ]); exit (0); } Este programa ir imprimir a quantidade de argumentos recebidos, e em seguida ir mostrar a a cada argumento recebido como par metro. E normal que os programas realizem a consist ncia a e dos par metros recebidos. Dessa forma, e comum encontrar o seguinte tipo de c digo: a o
1 2 3
18
main ( int argc , char argv) { if ( argc != 3) { fprintf ( stderr , Sintaxe: %s valor1 valor2 \n , argv [0]); exit (1); } } O padr o POSIX.2 prov uma funcao para tratar argumentos de entrada, o getopt(3), a e denida pelo seguinte prot tipo: o #include <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 e argv s o, respectivamente, a contagem de argumentos e o array de strings passados para a funcao a main() na invocacao do programa. Um elemento de argv que inicia com - (e n o e exatamente - ou ) e um elemento de a opcao. Os caracteres deste elemento (fora o - inicial) s o caracteres de opcao. Se getopt(3) a e chamado repetidamente, ele retorna sucessivamente cada um dos caracteres de opcao de cada um dos elementos de opcao. Se a funcao getopt(3) encontra um outro caractere de opcao, ela retorna este caractere, atualizando a vari vel externa optind e uma vari vel est tica nextchar de forma que a pr xima a a a o chamada a getopt(3) pode continuar a busca com o caractere de opcao seguinte ou um ele mento de argv. Se n o h mais caracteres de opcao, getopt(3) retorna 1. Ent o optind e o ndice em a a a argv do primeiro elemento de argv que n o e uma opcao. a optstring e uma string contendo os caracteres de opcao legtimos. Se tal caractere e seguido por dois-pontos, a opcao requer um argumento, ent o getopt atribui um ponteiro para o texto a que segue no mesmo elemento de argv, ou para o texto do elemento seguinte de argv, em optarg. Dois dois-pontos signicam que uma opcao recebe um argumento opcional; se h texto no a elemento de argv atual, ele e retornado em optarg, caso contr rio optarg e zerado. Esta e uma ex a tens o GNU. Se optstring cont m W seguido de um ponto-e-vrgula, ent o -W foo e tratado como a e a a opcao longa foo. (A opcao -W e reservada pelo POSIX.2 para extens es de implementacao.) o Este comportamento e uma extens o GNU, n o disponvel em bibliotecas anteriores a GNU libc a a ` 2. A funcao getopt long(3) funciona como getopt(3), exceto que ele tamb m aceita e opcoes longas, iniciadas por dois tracos. Nomes de opcoes longas podem ser abreviados se a abreviacao for unica ou se iguala exatamente a alguma opcao denida. Uma opcao longa pode ` requerir um par metro da forma arg=param ou arg param. O seguinte prot tipo a dene: a o #dene GNU SOURCE #include <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 ag ; int val ; };
O seguinte exemplo ilustra o uso de getopt long(3) com a maioria de seus recursos:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
#include <stdio .h> #include <unistd . h> #dene GNU SOURCE #include <getopt . h> static struct option long options [] = { {add , 1, 0, 0}, {append, 0, 0, 0}, { delete , 1, 0, 0}, {verbose , 0, 0, 0}, { create , 1, 0, c }, { le , 1, 0, 0}, {0, 0, 0, 0} }; int main ( int argc , char argv) { int c; while (1) { int option index = 0; c = getopt long ( argc , argv , abc:d: , long options , & option index ); if ( c == 1) break; switch ( c ) { case 0: printf (opcao %s, long options [ option index ]. name); if ( optarg ) printf ( com argumento %s, optarg ); printf ( \n); break; case a : printf ( opcao a\n); break; case b :
printf break; case c : printf break; case d : printf break; case ? : break; default : printf } }
( opcao b\n); ( opcao c com valor %s\n , optarg ); ( opcao d com valor %s\n , optarg );
if ( optind < argc ) { printf ( parametros nao referentes a opcoes : ); while ( optind < argc ) printf ( \t%s\n, argv[ optind ++]); } exit (0); }
3.3 Streams
Quando a funcao main do programa e invocada, ela j disponibiliza tr s streams abertas e a e prontas para o uso. Elas representam os canais standard de entrada e sada que foram esta belecidos para o processo, e est o declaradas no arquivo header stdio.h: a FILE *stdin: e a stream de entrada padr o, que e a origem normal de entrada de dados a em um programa;
O programa a seguir ilustra o uso destas operacoes: ele abre um arquivo para leitura e um outro para escrita, escrevendo apenas as linhas pares para o segundo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
#include <stdio .h> #include <unistd . h> int copia arquivo ( char origem , char destino ) { FILE stream origem, stream destino ; char buffer [1024]; int i ; / abre arquivos de origem e destino , criando este ultimo / stream origem = fopen ( origem , r ); if (! stream origem ) { perror ( fopen ); return 1; } stream destino = fopen ( destino , w); if (! stream destino ) { perror ( fopen ); fclose ( stream origem ); return 1; } / processa linhas do arquivo origem / i = 1; while (! feof ( stream origem )) { fgets ( buffer , sizeof ( buffer ), stream origem ); if ( i % 2 == 0) / numero par / fputs ( buffer , stream destino );
i++; } / fecha as streams e retorna / fclose ( stream origem ); fclose ( stream destino ); return 0; } int main ( int argc , char argv) { int res ; if ( argc != 3) { printf ( Sintaxe: %s <arq origem> <arq destino>\n, argv [0]); exit (1); } res = copia arquivo ( argv [1], argv [2]); if ( res < 0) { fprintf ( stderr , Um erro ocorreu durante a copia\n); exit (1); } exit (0); }
Algumas outras operacoes costumam ser realizadas sobre descritores de arquivos. Podemos destacar: ssize t read(int fd, void *buf, size t count); l at count bytes a partir da posicao corrente do arquivo representado pelo descritor fd e e para o buffer apontado por buf. O valor retornado informa quantos bytes foram lidos com sucesso, ou -1 em caso de erro. ssize t write(int fd, const void *buf, size t count); escreve at count bytes no arquivo representado pelo descritor fd a partir do buffer apone tado por buf. O valor retornado informa quantos bytes foram escritos com sucesso, ou -1 em caso de erro. off t lseek(int fildes, off t offset, int whence); opera da mesma forma que o fseek(3), por m sobre descritores de arquivos. ldes e refere-se ao descritor de arquivo que ser utilizado na operacao de seek, offset informa a quantos bytes ser o movimentados na operacao e whence informa a posicao relativa a a ` qual ser feito o seek. a int fstat(int filedes, struct stat *buf); retorna informacoes sobre o arquivo representado pelo descritor ledes, armazenando-as no endereco da struct stat apontada por buf. Entre as informacoes retornadas, est o a a quantidade de hard links feitas para este arquivo, o seu inode number, a data de ultima alteracao e de ultimo acesso, entre outros. O exemplo abaixo lida com um arquivo bin rio atrav s de descritores de arquivos. Neste a e exemplo, ele adiciona um novo campo do mesmo tipo de estrutura da qual ele e composto ap s o seu ultimo byte de dados, e em seguida l e imprime o seu conte do. e u
1 2 3 4 5 6 7 8 9 10 11 12 13
#include <stdio .h> #include <unistd . h> #include < stdlib . h> #include <sys/ types .h> #include <sys/ stat .h> #include <sys/ fcntl .h> struct informacao { int quantidade ; oat valor ; char nome[128]; };
/ / / /
/ / / /
int cria arquivo ( char nome) { int fd ; fd = open (nome, O RDWR | O CREAT | O TRUNC, S IRWXU | S IRWXG | S IRWXO); if ( fd < 0) { perror ( open); exit (1); } return fd ; } void le arquivo ( int fd ) { size t n; struct informacao info ; lseek ( fd , 0, SEEK SET); while (1) { n = read ( fd, & info , sizeof ( info )); if ( n == 0) { / m de arquivo / break; } printf ( nome: %s\nvalor: %f\nquantidade: %d\n\n, info .nome, info . valor , info . quantidade ); } }
void adiciona entrada ( int fd , char numero, char arquivo ) { ssize t n; int num; struct informacao info ; num = atoi ( numero); info . quantidade = 10 num; info . valor = 123.45 ( oat ) num; snprintf ( info .nome, sizeof ( info .nome), arquivo %s , arquivo ); lseek ( fd , 0, SEEK END); n = write ( fd, & info , sizeof ( info )); if ( n < 0) perror ( write ); } int main ( int argc , char argv) {
int fd ; if ( argc != 3) { fprintf ( stderr , Sintaxe: %s <arquivo destino > <algum numero>\n, argv[0]); exit (1); } fd = open ( argv [1], O RDWR); if ( fd < 0) { perror ( open); printf ( tentando criar arquivo ...\ n\n); fd = cria arquivo ( argv [1]); } adiciona entrada ( fd , argv [2], argv [1]); le arquivo ( fd ); close ( fd ); exit (0); }
Captulo 4
#include <stdio .h> #include <unistd . h> #dene ARQUIVO /tmp/teste.txt int main ( int argc , char argv) { char buffer [256]; FILE fd; printf (Contenttype: text /html\r\r\n\n); printf (<html><head><title> Conteudo do arquivo %s </title></head>\n, ARQUIVO); fd = fopen ( ARQUIVO, r); if (! fd ) { printf ( <b> ERRO </b>: arquivo %s ano existe\n, ARQUIVO); printf (</body></html>\n); exit (1);
28
29
} while (! feof ( fd )) { fgets ( buffer , sizeof ( buffer ), fd ); printf ( %s <br>\n, buffer); } fclose ( fd ); printf (</body></html>\n); exit (0); } Ap s a compilacao do programa, basta copi -lo para o diret rio cgi-bin do Apache, ou o a o do httpd em uso, e acess -lo via browser, como no exemplo abaixo: a http://localhost/cgi-bin/teste No entanto, e bastante comum o uso de alguma biblioteca para facilitar a formatacao dos dados em HTML, para evitar erros e agilizar o desenvolvimento do programa CGI. Existem v rias bibliotecas disponveis cadastradas no site da Freshmeat1 . a
1 ver
http://www.freshmeat.net
Captulo 5
#include <stdio .h> #include <dlfcn .h> int main ( int argc , char argv) { void biblioteca ; double (seno )(double); char erro ; biblioteca = dlopen ( libm.so , RTLD LAZY); if (! biblioteca ) { fprintf ( stderr , %s\n, dlerror ()); exit (1); } seno = dlsym ( biblioteca , simbolo nao existe ); if (( erro = dlerror ()) != NULL) { fprintf ( stderr , %s\n, erro ); exit (1); } printf ( %f\n, (seno )(2.0)); dlclose ( biblioteca ); exit (0); } Existem basicamente 2 modos para resolucao dos smbolos disponibilizados por uma bi a blioteca: RTLD LAZY, que signica que os ponteiros para funcoes n o resolvidos em tempo de
30
31
compilacao devem ser apenas resolvidos pela biblioteca din mica em tempo de execucao, ou a RTLD NOW, que signica que todos os smbolos da biblioteca din mica devem ser resolvidos na a abertura da mesma, sendo que a abertura da biblioteca dever falhar caso algum smbolo n o a a possa ser denido.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
#include <stdio .h> #include <dlfcn .h> int main ( int argc , char argv) { void biblioteca ; double (seno )(double); char erro ; biblioteca = dlopen ( libm.so , RTLD NOW); if (! biblioteca ) { fprintf ( stderr , %s\n, dlerror ()); exit (1); } seno = dlsym ( biblioteca , sin ); if (( erro = dlerror ()) != NULL) { fprintf ( stderr , %s\n, erro ); exit (1); } printf ( %f\n, (seno )(2.0)); dlclose ( biblioteca ); exit (0); } Para usar as funcoes de bibliotecas din micas a biblioteca dl deve ser especicada em tempo a linkagem, tal como: $ gcc dl-now.c -o dl-now -ldl
#include <stdio .h> int mundo (void) { printf ( Bemvindo ao GNU/Linux\n); } Para compilar a biblioteca, basta: $ gcc minha_biblioteca -o libminha_biblioteca.so -shared
Captulo 6
Processos e threads
Existem duas maneiras de criar novos uxos de processos no GNU/Linux. Uma delas e atrav s e da chamada de sistema fork(2), e a outra atrav s da criacao de threads, que podem ser vistas e como processos leves [STE 98]
6.1 Processos
E possvel criar processos atrav s da chamada de sistema fork(2). A chamada de sistema n o e a recebe nada como par metro, retorna os seguintes valores: a >0: Sinalizando que o processo que est executando no momento e o do pai; a Um exemplo de criacao de um sub-processo pode ser visto no exemplo abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
0: Sinalizando que o processo que est executando no momento e o do lho criado; a <0: Sinaliza que a chamada de sistema n o p de ser completada com sucesso. a o
#include <stdio .h> #include <unistd . h> #include < stdlib . h> #include < string . h> #include <sys/ types .h> #include <sys/wait .h> int main ( int argc , char argv) { int status ; pid t pid ; pid = fork (); if ( pid == 0) { char args ; printf ( Processo lho executando \ ls a\\n); args = ( char ) malloc ( sizeof ( char ) 3); args [0] = strdup ( ls ); args [1] = strdup ( a); args [2] = NULL;
32
33
execvp ( args [0], args ); free ( args [0]); free ( args [1]); free ( args ); } else if ( pid > 0) { printf ( Processo pai aguardando processo lho ..\ n ); waitpid ( pid, & status , WUNTRACED); if ( WIFEXITED(status)) printf ( processo lho executou normalmente\n); else printf ( processo lho nao executou normalmente\n); } else if ( pid < 0) { / algum erro ocorreu / perror ( fork ); } exit (0); } A funcao execvp(3) e utilizada para trocar a imagem do processo corrente pela imagem de um novo processo. O primeiro argumento para ela e o pathname para um programa, e o segundo argumento e um array terminado por NULL, descrevendo os argumentos que este pro grama ir receber. O conjunto do uso do fork(3) com o execvp(3) e waitpid(3) aprea senta uma sem ntica semelhante a funcao spawnl() do Microsoft Windows, com a excess o a ` a de que a spawnl() j explicita em seus argumentos se o processo pai deve aguardar pelo seu a t rmino de execucao ou n o. e a Outra funcao utilizada na listagem do programa acima e a waitpid(3), que aguarda pelo t rmino da execucao de algum processo. Os par metros enviados a ela s o o pid do processo a ser e a a aguardado, um ponteiro para um inteiro, onde ser o armazenas informacoes sobre a execucao a do processo (como sinais que foram entregues a ele para que ele fosse encerrado e valor de retorno da execucao do processo), e uma constante chamada WUNTRACED, que signica que ela deve parar de aguardar pelo lho que j teve sua execucao parada e se encontra em um status a desconhecido.
6.2 Threads
Threads t m o mesmo prop sito de um processo: gerar um novo uxo de execucao. No entanto, e o elas s o mais adequadas para uso em programas que precisam disparar muitos processos lhos, a devido ao fato de elas implementarem o conceito de processos leves. Todas as threads em um processo compartilham o estado deste processo. Elas residem no mesmo enderecamento de mem ria, v em as mesmas funcoes e os mesmos dados. As maiores o e vantagens de utilizar threads em programas s o: a Aumento da responsividade do programa; Uso eciente dos recursos do sistema; Ganhos de performance em hardware multiprocessado;
Mudanca de m todos de comunicacao entre os processos; e Apenas um bin rio executa bem tanto em arquiteturas SMP (Symmetric Multi Processors) a quanto UP (Uni Processor); Possibilidade de criar programas bem estruturados;
34
Uma thread consiste de uma stack e um stack pointer, um program counter e algumas informacoes a respeito de sua inst ncia, como prioridades de escalonamento e m scaras de sinal, a a armazenadas na sua estrutura. Al m disso, os registradores da CPU s o tamb m armazenados e a e (sendo que a stack pointer e o program counter s o alguns destes registradores). a O Linux utiliza, at a s rie 2.4, o modelo de threads POSIX, aprovado pelo padr o IEEE e e a 1003.1b-1993 [Ame 94]. Para dar suporte a este modelo, as distribuicoes GNU/Linux fornecem a biblioteca LinuxThreads, que implementa grande parte das exig ncias feitas por este padr o. e a A s rie 2.6 do kernel Linux est fazendo uso de um outro modelo de threads, chamado NPTL e a (Native POSIX Threading Library) [The 2003]. Este texto baseia-se na implementacao das Lin uxThreads. Em relacao as threads Win32, as threads POSIX s o muito mais leves, contendo primi ` a tivas mais simples de uso. Uma outra caracterstica e a de que threads Win32 mant m uma e depend ncia entre janelas e threads. Como nem todas threads constroem e usam janelas, isto e acarreta em um overhead desnecess rio para os uxos de execucao. a Para criar um novo uxo de execucao com o uso de threads no GNU/Linux, utiliza-se a funcao pthread create(3), denida pelo seguinte prot tipo: o #include <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 concorrentemente com a thread que a gerou. A nova thread aplica a funcao start routine, passando arg como seu argumento. A nova thread termina explicitamente, chamando a funcao pthread exit(3), ou implicitamente, apenas retornando da funcao start routine. O ultimo caso e equivalente a chamar pthread exit(3) com o resultado retornado pela funcao start routine como retorno. O argumento attr especica algum atributo a ser aplicado para a nova thread, ou pode ser NULL caso os atributos padr o devam ser utilizados: a thread criada e joinable, ou seja, seus a recursos de mem ria n o ser o liberados at que algum processo sincronize com esta thread, e o a a e tem uma poltica de escalonamento normal (n o-realtime). Os atributos a serem passados para a a thread devem ser inicializados com a funcao pthread attr init(3), e ap s congurados o com o uso de alguma das funcoes listadas na p gina manual pthread attr init(3). a Para sincronizar um processo (ou uma thread) com o resultado de uma outra thread, utiliza-se a funcao pthread join(3): #include <pthread .h> int pthread join ( pthread t th , void thread return ); pthread join(3) suspende a execucao da thread corrente at que a thread identicada e por th termine, tanto explicitamente, via pthread exit(3), ou sendo cancelada, atrav s da e funcao pthread cancel(3). Caso o argumento thread return seja n o nulo, ele e utilizado a para armazenar o valor de retorno da thread th, devendo ser desalocado ap s ter sido utilizado o pelo programa. O exemplo abaixo ilustra a criacao e sincronizacao de uma thread no GNU/Linux. Para com pilar este programa, e necess rio passar a ag -lpthread, para que o programa seja linkado a com a biblioteca de threads libpthread.so e possa fazer uso de suas funcoes.
1 2 3 4 5 6 7 8
#include <stdio .h> #include < stdlib . h> #include <unistd . h> #include <pthread .h> void calcula ( void dados) {
35
int ret = NULL; int valor = ( int ) dados; ret = ( int ) malloc ( sizeof ( int )); ret = valor valor ; pthread exit ( ret ); } int main ( int argc , char argv) { pthread t tid ; int dados , resultado ; dados = ( int ) malloc ( sizeof ( int )); dados = 5; pthread create (& tid , NULL, calcula , dados ); pthread join ( tid , ( void ) & resultado ); printf ( %d %d = %d\n, dados, dados, resultado ); free ( dados ); free ( resultado ); exit (0); } Como threads podem compartilhar recursos entre si, n o e necess rio fazer uso de mecanisa a mos pesados de intercomunicacao entre processos. A forma mais comum de compartilhar dados entre diferentes threads e atrav s de vari veis globais a elas, e mantendo a coer ncia das leituras e a e e escritas destes dados com o uso de mecanismos de exclus o m tua (mutexes) e sem foros. a u a O uso de mutexes e feito basicamente atrav s das primitivas pthread mutex lock(3) e e pthread mutex unlock(3). Na primeira primitiva, caso alguma outra thread j tenha a adquirido o lock, a thread que tentou peg -lo pela segunda vez tem sua execucao interrompa ida at que a outra thread o libere. Caso n o desej va-se que a execucao seja interrompida, e a a pthread mutex trylock(3) pode ser usado: caso o lock n o esteja disponvel ela cona tinua a execucao do programa. O exemplo abaixo mostra como compartilhar um dado entre duas threads de execucao, uti lizando mecanismos de exclus o m tua: a u
1 2 3 4 5 6 7 8 9 10 11 12 13 14
#include <stdio .h> #include < stdlib . h> #include <unistd . h> #include <pthread .h> static int valor = 0; pthread mutex t mutex = PTHREAD MUTEX INITIALIZER; void incrementa ( void dados) { int i ; for ( i = 0; i < 10000; i++) {
36
pthread mutex lock (&mutex); valor ++; pthread mutex unlock (&mutex); } pthread exit ( NULL); } void decrementa ( void dados) { int i ; for ( i = 0; i < 10000; i++) { pthread mutex lock (&mutex); valor ; pthread mutex unlock (&mutex); } pthread exit ( NULL); } int main ( int argc , char argv) { pthread t tid , tid2 ; pthread pthread pthread pthread create (& tid , NULL, incrementa, NULL); create (& tid2 , NULL, decrementa, NULL); join ( tid , NULL); join ( tid2 , NULL);
printf ( valor da variavel global : %d\n , valor ); exit (0); } Sem foros tamb m tem um uso bastante comuns. Eles funcionam como contadores para a e recursos compartilhados entre threads. As operacoes b sicas realizadas em sem foros s o incre a a a mentar o contador atomicamente e aguardar at que o contador seja n o nulo e decrement -lo e a a atomicamente. Nestes casos, podem ser usadas as funcoes sem init(3), sem wait(3), sem post(3) e sem destroy(3): #include <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 sem foro apontado por sem com o valor especicado em value. a Este valor indica quantas threads poder o entrar na sess o crtica ao mesmo tempo. Nos casos a a mais comuns s o utilizados sem foros bin rios, e ent o este valor e iniciado com 1. O argumento a a a a pshared indica se o sem foro e local ao processo atual (neste caso pshared deve ser zero) ou se a ele n o e compartilhado entre v rios processos (neste caso pshared deve ser n o zero). No a a a entanto, a implementacao das LinuxThreads n o implementa o compartilhamento de sem foros a a entre v rios processos, retornando um erro na inicializacao do sem foro caso pshared seja um a a valor diferente de zero.
37
#include <stdio .h> #include < stdlib . h> #include <unistd . h> #include <pthread .h> #include <semaphore.h> static int valor = 0; sem t semaforo; void incrementa ( void dados) { int i ; for ( i = 0; i < 10000; i++) { sem wait (&semaforo); valor ++; sem post (&semaforo); } pthread exit ( NULL); } void decrementa ( void dados) { int i ; for ( i = 0; i < 10000; i++) { sem wait (&semaforo); valor ; sem post (&semaforo); } pthread exit ( NULL); } int main ( int argc , char argv) { pthread t tid , tid2 ; sem init (&semaforo , 0, 1); pthread pthread pthread pthread create (& tid , NULL, incrementa, NULL); create (& tid2 , NULL, decrementa, NULL); join ( tid , NULL); join ( tid2 , NULL);
printf ( valor da variavel global : %d\n , valor ); sem destroy (&semaforo); exit (0); }
Captulo 7
TCP/IP
O conjunto de funcoes disponveis para serem utilizadas na comunicacao atrav s do protocolo e TCP/IP e muito grande. Este captulo visa apresentar a utilizacao de sockets no GNU/Linux para comunicacao entre cliente e servidor, nos modos bloqueante e n o-bloqueante no IPv4 [STE 98a]. a Provavelmente a maneira mais simples de explicar como programar sockets neste ambiente e com o uso de um exemplo comentado. Abaixo encontra-se listada uma implementacao de um cliente TCP de uma aplicacao do tipo timeofday. Este cliente estabelece uma conex o TCP com a um servidor, e o servidor apenas envia de volta a hora e data atuais, em um formato formatado e legvel.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
#include timeofday .h int main ( int argc , char argv) { int sockfd , n; char recvline [MAXLINE + 1]; struct sockaddr in servaddr ; if ( argc != 2) { fprintf ( stderr , Sintaxe: %s <endereco IP>\n, argv [0]); exit (1); } if (( sockfd = socket ( AF INET, SOCK STREAM, 0)) < 0) err quit ( socket ); memset (&servaddr , 0, sizeof ( servaddr )); servaddr . sin family = AF INET; servaddr . sin port = htons (13); / servidor de daytime / if ( inet pton ( AF INET, argv[1], &servaddr . sin addr ) <= 0) err quit ( inet pton ); if ( connect ( sockfd , ( struct sockaddr ) & servaddr , sizeof ( servaddr )) < 0) err quit ( connect ); while (( n = read ( sockfd , recvline , MAXLINE)) > 0) { recvline [n ] = 0; / terminada por NULL /
38
CAPITULO 7. TCP/IP
30 31 32 33 34 35 36 37 38
39
if ( fputs ( recvline , stdout ) == EOF) err quit ( fputs ); } if ( n < 0) err quit ( read ); exit (0); } Este exemplo, quando compilado, deve utilizar a ag -lnsl, para ser linkado com as funcoes de sockets da Glibc. Existem v rios detalhes a serem considerados neste programa: a 1: o cabecalho em comum com a aplicacao do servidor. Os detalhes de seu conte do s o u a mostrados mais a seguir; 15: criacao de um socket TCP. A funcao socket cria uma stream (SOCK STREAM) de Internet (AF INET), que e o nome dado para um socket TCP. Esta funcao retorna o descritor de um socket, utilizado para realizar as chamadas de funcoes futuras, como as chamadas ao connect e ao read; 18 23: especicacao do endereco IP de um servidor e uma porta. Uma estrutura do tipo Internet socket address (a sockaddr in sockaddr) e preenchida com o endereco IP do servidor e com o n mero da porta. A estrutura e zerada com o uso da funcao memset, u congurada para usar a famlia de enderecos AF INET e para usar a porta 13 (que e a porta conhecida para o servidor de daytime em qualquer host TCP/IP que suporte este servico). O IP fornecido e a porta nesta estrutura precisam estar em formatos especcos: para isto, a funcao htons (host to network short) para converter o n mero da porta, e e u utilizada a funcao inet pton (presentation to numeric) para converter o endereco IP em ASCII para o formato necess rio; a 25 26: estabelecimento da conex o com o servidor. A funcao connect, quando aplicada a a um socket TCP, estabelece uma conex o TCP com o servidor especicado pela estrutura a socket address apontada pelo segundo argumento; 28 35: l e mostra o reply do servidor. Como o TCP e um protocolo byte-stream, e ele n o tem registro sobre limites do conte do sendo transportado, ou seja: os bytes a u retornados podem vir de uma unica vez, ou em n pequenas leituras, at que os ultimos e bytes sejam lidos. Desta forma, nem sempre e garantido que com um unico read venha toda a resposta do servidor. Assim, sempre que estivermos lendo o conte do de um socket u TCP, e necess rio faz -lo em um loop, terminando-o quando read retornar 0 (o outro lado a e encerrou a conex o) ou um valor menor do que 0 (quando ocorre um erro). a Como o programa anterior n o pode executar corretamente sem um servidor, e necess rio a a escrever um. A vers o do servidor para este cliente encontra-se listado logo abaixo. a
1 2 3 4 5 6 7 8 9 10 11 12
#include timeofday .h int main ( int argc , char argv) { int listenfd , connfd; struct sockaddr in servaddr ; char buff [MAXLINE]; time t ticks ; if (( listenfd = socket ( AF INET, SOCK STREAM, 0)) < 0) err quit ( socket );
CAPITULO 7. TCP/IP
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
40
memset (&servaddr , 0, sizeof ( servaddr )); servaddr . sin family = AF INET; servaddr . sin addr . s addr = htonl ( INADDR ANY); servaddr . sin port = htons (13); / servidor de daytime / if (( bind ( listenfd , ( struct sockaddr ) & servaddr , sizeof ( servaddr ))) < 0) err quit ( bind ); if (( listen ( listenfd , MAX CONNECT)) < 0) err quit ( listen ); for (;;) { if (( connfd = accept ( listenfd , ( struct sockaddr ) NULL, NULL)) < 0) err quit ( accept ); ticks = time ( NULL); snprintf ( buff , sizeof ( buff ), %.24s\r\n, ctime (& ticks )); if (( write ( connfd , buff , strlen ( buff ))) < 0) err quit ( write ); close ( connfd ); } } Assim como no cliente, alguns detalhes precisam ser considerados na vers o do servidor: a 1920: realiza um binding (amarra) da porta conhecida com o socket. A porta conhecida do servidor (13, para o servico de daytime) e ligada ao socket, preenchendo uma estru tura de Internet socket address e chamando o bind. E especicado o endereco IP como INADDR ANY, que permite que o servidor aceite uma conex o em qualquer interface, a caso o host tenha mais de uma; 22 23: converte o socket para um listening socket. Quando e feita a chamada ao listen, conex es ao socket passam a ser aceitas pelo kernel. Estes tr s passos, socket, bind e o e listen s o os passos utilizados para preparar qualquer listening descriptor (descritor de a escuta) em um servidor TCP (que neste caso e o listenfd). A constante MAX CONNECT est especicada no header timeofday.h, e especica a quantidade m xima de clientes que a a ir o conectar-se ao listening descriptor. a 2632: aceita conex es dos clientes e envia um reply. Normalmente o processo do servio dor e posto para dormir na chamada a accept, aguardando que uma conex o de um cliente ` a chegue e seja aceita. Uma conex o TCP usa o que e chamado de um three-way handa shake para estabelecer uma conex o, e quando este handshake e completado, a funcao a accept retorna, e o valor de retorno desta funcao e um novo descritor (connfd), chamado de connected descriptor (descritor do cliente conectado). O novo descritor e usado para comunicar com o novo cliente. Um novo descritor e retornado pelo accept a cada cliente que conecta ao servidor. A funcao time retorna a hora e data atual, na forma dos segundos que ocorreram desde a epoca do Unix: 1o de janeiro de 1970, as 00:00:00 horas UTC (Coordinated Universal ` Time). A funcao ctime converte o valor inteiro retornado em uma forma legvel para humanos. Esta string e ent o escrita de volta para o cliente. a O arquivo header utilizado em ambos exemplos encontra-se abaixo: 34: a conex o com o cliente e encerrada. a 11 12: cria o socket TCP, id ntico a vers o do cliente; e ` a
CAPITULO 7. TCP/IP
#ifndef timeofday h #dene timeofday h 1 #include <stdio .h> #include <unistd . h> #include < stdlib . h> #include <sys/ types .h> #include <sys/ socket .h> #include <arpa/ inet .h> #include < netinet / in .h> #include < string . h> #include <time.h>
41
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/ / / / /
#dene err quit (msg ) ({ perror (msg); exit (1); }) / SOMAXCONN ca denido em bits/socket .h / #dene MAX CONNECT SOMAXCONN #dene MAXLINE 1024 #endif / timeofday h / Ap s ter o servidor compilado e rodando em um terminal, um outro terminal pode ser utio lizado para efetuar a comunicacao entre o cliente e o servidor. Para isto, o cliente deve ser exe cutado passando um endereco IP v lido como par metro. Como estamos especicando qualquer a a interface para escuta no servidor, pode ser utilizada a interface loopback: $ timeofday-client 127.0.0.1 Mon Oct 13 00:50:12 2003 Estes exemplos funcionam e podem servir como base para programas maiores. No entanto, eles ainda usam valores num ricos para referenciar o servidor, e muitas vezes e desej vel utilizar e a o nome do host que disponibiliza o servico. Para isto algumas funcoes podem ser utilizadas, como: struct hostent *gethostbyname(const char *name): retorna um ponteiro para uma estrutura do tipo hostent para o host name, que pode estar representado tanto na notacao de um endereco IP num rico quanto como um hostname. A estrutura hostent, denida no e <netdb.h>. e composta pelos seguintes elementos: struct hostent { char h name; char h aliases ; int h addrtype ; int h length ; char h addr list ; } #dene h addr h addr list / / / / / nome ocial do host / lista de aliases , terminada por NULL / tipo do endereco do host / tamanho do endereco / lista de anderecos , terminada por NULL /
struct hostent *gethostbyaddr(const char *addr, int len, int type): retorna um ponteiro para uma estrutura do tipo hostent para o endereco do host especicado em addr de tamanho len e de tipo type. O unico tipo v lido de endereco e atualmente AF INET. a struct servent *getservbyname(const char *name, const char *proto): retorna um ponteiro para uma estrutura do tipo servent para a linha do arquivo services do diret rio o de conguracoes da distribuicao, normalmente /etc/services, que coincida com o servico name que utiliza o protocolo proto. Caso proto seja NULL, qualquer protocolo ser aceito. a
CAPITULO 7. TCP/IP
A estrutura servent e especicada no arquivo <netdb.h> como segue: struct servent { char s name; char s aliases ; int s port ; char s proto ; } / / / / nome ocial do servico / lista de aliases , terminada por NULL / numero da porta / protocolo a ser usado /
42
struct servent *getservbyport(int port, const char *proto): retorna um ponteiro para uma estrutura do tipo servent para a linha do arquivo services que coincida com a porta especicada em port, usando o protocolo proto. Caso o protocolo seja NULL, qualquer um ser aceito no matching. A porta deve estar informada em network byte order a (htonl(3)). O pr ximo exemplo mostra como estes recursos podem ser incorporados ao programa anteo rior:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
#include timeofday .h int main ( int argc , char argv) { int sockfd , n; char recvline [MAXLINE + 1]; struct sockaddr in servaddr ; struct hostent ht ; if ( argc != 3) { fprintf ( stderr , Sintaxe: %s <endereco IP> <porta>\n, argv[0]); exit (1); } if (( sockfd = socket ( AF INET, SOCK STREAM, 0)) < 0) err quit ( socket ); if (( ht = gethostbyname (argv [1])) < 0) err quit ( gethostbyname); memset (&servaddr , 0, sizeof ( servaddr )); servaddr . sin family = AF INET; servaddr . sin port = htons ( atoi ( argv [2])); memcpy (&servaddr.sin addr , ht>h addr list [0], sizeof ( struct in addr )); if ( connect ( sockfd , ( struct sockaddr ) & servaddr , sizeof ( servaddr )) < 0) err quit ( connect ); while (( n = read ( sockfd , recvline , MAXLINE)) > 0) { recvline [n ] = 0; / terminada por NULL / if ( fputs ( recvline , stdout ) == EOF) err quit ( fputs ); } if ( n < 0) err quit ( read );
CAPITULO 7. TCP/IP
39 40
43
exit (0); } Podemos ver na linha 19 que agora e feita uma chamada para gethostbyname(3), que realiza o lookup do nome fornecido como par metro na linha de comando. Na linha 24, infora mamos a porta atrav s do par metro passado pelo usu rio (a funcao atoi(3) realiza a cone a a vers o de uma string para um inteiro). A forma de realizar a c pia da interface a ser utilizada a o tamb m foi mudada: na linha 25, vemos que a partir de agora o endereco e copiado a partir das e informacoes disponveis na estrutura hostent >h addr list. Utilizamos o primeiro endereco referente as interfaces neste host, e conectamos a ele. O restante do programa mant m-se inal` e terado. A seguir vamos analisar as mudancas na vers o do servidor: a
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
#include timeofday .h int main ( int argc , char argv) { int listenfd , connfd; struct sockaddr in servaddr , cliaddr ; socklen t len ; char buff [MAXLINE]; time t ticks ; if ( argc != 2) { printf ( Sintaxe: %s <porta>\n, argv [0]); exit (1); } if (( listenfd = socket ( AF INET, SOCK STREAM, 0)) < 0) err quit ( socket ); memset (&servaddr , 0, sizeof ( servaddr )); servaddr . sin family = AF INET; servaddr . sin addr . s addr = htonl ( INADDR ANY); servaddr . sin port = htons ( atoi ( argv [1])); if (( bind ( listenfd , ( struct sockaddr ) & servaddr , sizeof ( servaddr ))) < 0) err quit ( bind ); if (( listen ( listenfd , MAX CONNECT)) < 0) err quit ( listen ); for (;;) { if (( connfd = accept ( listenfd , ( struct sockaddr ) & cliaddr , & len )) < 0) err quit ( accept ); printf ( conexao estabelecida com %s na porta %d\n, inet ntop ( AF INET, &cliaddr.sin addr , buff , sizeof ( buff )), ntohs ( cliaddr . sin port )); ticks = time ( NULL); snprintf ( buff , sizeof ( buff ), %.24s\r\n, ctime (& ticks )); if (( write ( connfd , buff , strlen ( buff ))) < 0) err quit ( write );
CAPITULO 7. TCP/IP
43 44 45 46
44
close ( connfd ); } }
As modicacoes foram semelhantes as realizadas no cliente. Na linha 23, e feita a convers o ` a da string relativa a porta a ser usada para inteiro, passando o resultado ao campo sin port da ` estrutura servaddr para que esta porta seja usada. A funcao accept agora tamb m informa e um ponteiro para a estrutura sockaddr cliaddr. Isto permite que o accept armazene dados sobre a conex o do cliente. Estes dados s o ent o usados nas linhas 35 37: o inet ntop a a a converte o endereco passado em &cliaddr.sin addr que usa a famlia AF INET para uma string, que e copiada para buffer. Um ponteiro para este buffer e retornado, e usamos este ponteiro para imprimir o hostname do client. A funcao htohs e usada apenas para converter o n mero em u network byte order para host byte order, e ent o usa seu resultado para imprim-lo. a O header usado foi levemente modicado, declarando o cabecalho <netdb.h> para prover suporte a chamada gethostbyname(3), al m de j incluir headers para suporte a chamada ` e a ` select(2):
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
#ifndef timeofday h #dene timeofday h 1 #include <stdio .h> #include <unistd . h> #include < stdlib . h> #include <sys/ types .h> #include <sys/ socket .h> #include <arpa/ inet .h> #include < netinet / in .h> #include < string . h> #include <netdb.h> #include <time.h>
/ / / / / /
#include <sys/ select .h> / select () / #include <sys/ stat .h> #include < fcntl .h> #include <errno .h> #dene err quit (msg ) ({ perror (msg); exit (1); }) #dene MAXLINE 1024 #dene MAX CONNECT 100 #endif / timeofday h /
7.1 Select
Uma conex o n o-bloqueante envolve v rias operacoes sobre os descritores. Antes de apresent a a a a las, e necess rio introduzir a funcao select(2). Essa funcao aguarda at que um n mero de a e u descritores de arquivo mudem seu status. Seu prot tipo e denido como segue: o / de acordo com o POSIX 1003.12001 / include <sys/ select .h> / de acordo com padroes mais novos /
CAPITULO 7. TCP/IP
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);
45
A funcao select(2) usa um timeout que e uma struct timeval (com segundos e microse gundos), que pode ser atualizado para indicar quanto tempo j se passou do timeout especidado. a Tr s conjuntos independentes de descritores s o monitorados. Os que est o listados em readfds e a a ser o monitorados para vericar se algum caracter ca disponvel para leitura (para vericar se a a leitura n o vai bloquear). Os listados em writefds ser o monitorados para vericar se alguma a a escrita n o ir bloquear, e os em exceptfds ser o monitorados quanto a excecoes. Na sada, os a a a conjuntos s o modicados para indicar os descritores que tiveram seu status alterado. a Para operar esses conjuntos, quatro macros s o disponibilizadas. FD ZERO zera um cona junto. FD SET e FD CLEAR adicionam ou removem um descritor de um conjunto, e FD ISSET testa para ver se um descritor faz parte do conjunto. Isso e util ap s o retorno de select(2), o para garantir que o descritor que foi modicado e o mesmo que tinha-se interesse em monitorar. O valor n indica o descritor de maior n mero em qualquer um dos tr s conjuntos, mais 1. u e timeout e o tempo que deve ser aguardado at que select(2) retorne. Caso timeout seja e NULL, a chamada pode bloquear indenidamente. A seguinte estruturas e usada para denir 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 n mero de descritores contigo no u conjunto de descritores, que pode ser zero caso o timeout tenha expirado antes que qualquer coisa acontecesse. O exemplo abaixo mostra o uso de select(2) para aguardar at que algum dado seja lido e da stdin. O timeout e congurado para 5 segundos.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#include <stdio .h> #include <sys/time .h> #include <sys/ types .h> #include <unistd . h> int main ( int argc , char argv) { fd set rfds ; struct timeval tv ; int retval ; / monitora a stdin (0) / FD ZERO (&rfds); FD SET (0, &rfds); tv . tv sec = 5;
CAPITULO 7. TCP/IP
18 19 20 21 22 23 24 25 26 27 28
46
tv . tv usec = 0; retval = select (1, & rfds , NULL, NULL, &tv); if ( retval ) { printf ( Dados disponiveis .\n ); } else printf (Nenhum dado veio em 5 segundos\n); exit (0); } Vale notar que ap s a chamada a select(2), o valor de tv e desconhecido, e n o deve ser o ` a usado para obter informacoes a respeito do tempo que se passou. Um tutorial completo no uso do select(2) e fornecido na man page do select tut(2).
CAPITULO 7. TCP/IP
#include timeofday .h void processa clientes ( int listenfd , int connfd) { int select sock in = 1; int select sock out = 1; int ret ; char buff [MAXLINE]; int int int int max fd = connfd; sock EOF = 0; in size = 0; done = 0;
47
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
if ( listenfd > max fd) max fd = listenfd ; max fd++; while (! done) { fd set write set , read set ; FD ZERO (&write set); FD ZERO (&read set); if ( select sock in ) FD SET (connfd, &read set ); if ( select sock out ) FD SET (connfd, &write set ); ret = select ( max fd, & read set , & write set , NULL, NULL); if ( ret < 0) err quit ( select ); if ( FD ISSET (connfd, &read set )) { in size = read ( connfd , buff , sizeof ( buff )); if ( in size == 0) sock EOF = 1; else if ( in size < 0) err quit ( read ); } if ( in size > 0) { ret = write ( connfd , buff , in size ); if ( ret < in size ) { if ( errno != EAGAIN) { err quit ( write ); } else { printf (EAGAIN em write\n); } } else {
CAPITULO 7. TCP/IP
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
48
in size = 0; } }
if ( sock EOF && in size == 0) { close ( connfd ); done = 1; } select sock out = ( in size > 0 || sock EOF); select sock in = (! sock EOF && in size == 0); } }
int main ( int argc , char argv) { int listenfd , optval , connfd; struct sockaddr in servaddr ; long ags ; fd set read set ; if ( argc != 2) { printf ( Sintaxe: %s <porta>\n, argv [0]); exit (1); } if (( listenfd = socket ( AF INET, SOCK STREAM, 0)) < 0) err quit ( socket ); optval = 1; if ( setsockopt ( listenfd , SOL SOCKET, SO REUSEADDR, &optval, sizeof(optval)) < 0) err quit ( setsockopt ); memset (&servaddr , 0, sizeof ( servaddr )); servaddr . sin family = AF INET; servaddr . sin addr . s addr = htonl ( INADDR ANY); servaddr . sin port = htons ( atoi ( argv [1])); if ( bind ( listenfd , ( struct sockaddr ) & servaddr , sizeof ( servaddr )) < 0) err quit ( bind ); if ( listen ( listenfd , 5) < 0) err quit ( listen ); ags = fcntl ( listenfd , F GETFL); fcntl ( listenfd , F SETFL, ags | O NONBLOCK); / prepara o select para leitura em listenfd / FD ZERO (&read set); FD SET ( listenfd , & read set ); if ( select ( listenfd + 1, & read set , NULL, NULL, NULL) <= 0) err quit ( select );
CAPITULO 7. TCP/IP
108 109 110 111 112 113 114 115 116 117 118
49
if (! FD ISSET ( listenfd , & read set )) err quit ( select ); if (( connfd = accept ( listenfd , NULL, NULL)) < 0) err quit ( accept ); ags = fcntl ( listenfd , F GETFL); fcntl ( connfd , F SETFL, ags | O NONBLOCK); processa clientes ( listenfd , connfd ); exit (0); } Ao receber uma requisicao de um cliente, este servidor l os dados enviados pelo cliente e os e retorna ao cliente. Este e um servico conhecido como um servico de echo. A an lise do c digo a o ser feita a partir da funcao main(). a 81 82: cria um socket TCP;
8485: congura as opcoes deste socket com a funcao setsockopt(2). Os par metros a passados a ela indicam que estamos trabalhando no nvel de socket, com a constante SOL SOCKET, e com a opcao SO REUSEADDR indicamos que as regras usadas para a validacao do endereco usada no bind(2) devem permitir a reutilizacao de um endereco local. Neste caso em que utilizamos um socket da famlia AF INET, isto signica que o socket deve realizar o bind, a n o ser que exista um outro socket ativo escutando no a mesmo endereco; 87 90: especicacao do protocolo, interfaces e portas para usar no bind; 92 93: faz o bind; 98 99: congura as opcoes do descritor listenfd de forma a manter as opcoes atuais (linha 98), adicionando uma constante informando que ele dever operar no modo n oa a bloqueante; 95 96: prepara o socket para aceitar requisicoes;
110 111: aguarda pela conex o de algum cliente; a 113 114: ap s receber a conex o de um cliente, prepara o descritor novamente para o a operar em modo n o bloqueante para as pr ximas operacoes; a o 115: processa a requisicao, explicada abaixo;
17 19: dene o valor m ximo dos descritores em uso para utilizar na chamada ao a select(2); 2435: congura os conjuntos de descritores de leitura e escrita para uso no select(2). Ap s, e aguardado at que o status de algum deles seja modicado. Nota-se que n o est o e a a sendo usado nenhum timeout nessa operacao; 37 43: caso o descritor connfd tenha sido modicado, o servidor l os dados a partir do e socket representado por este descritor para o buffer buff ; 45 56: se algum dado foi lido anteriormente, ent o o devolve no mesmo socket para o a cliente, escrevendo a quantidade de dados lidas at o momento; e 5861: caso j tenha lido a stream toda e reconhecido o nal de arquivo, fecha o descritor a e encerra a execucao da funcao.
O arquivo utilizado como header e o mesmo timeofday.h, visto que os programas nos exemplos precisam 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 bytes para o servidor, contendo os dados de um arquivo texto. A seguir, realiza a leitura da resposta do servidor, e a imprime. Como visto no exemplo do servidor, a resposta vinda do servidor e o pr prio conte do do arquivo. o u
CAPITULO 7. TCP/IP
#include timeofday .h void conecta ao servidor ( char lename , int sockfd) { int select sock in = 1; int select sock out = 1; int fd , ret ; int in size = 0; int out size = 0; char buff [MAXLINE]; char outbuff [MAXLINE]; int int int int int max fd = sockfd ; le EOF = 0; sock EOF = 0; shutdown write sock = 0; done = 0;
50
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
if (( fd = open(lename , O RDONLY)) <= 0) err quit ( open); if ( fd > max fd) max fd = fd ; max fd++; while (! done) { fd set write set , read set ; FD ZERO (&write set); FD ZERO (&read set); if ( select sock in ) FD SET (sockfd, &read set ); if ( select sock out ) FD SET (sockfd, & write set ); ret = select ( max fd, & read set , & write set , NULL, NULL); if ( ret < 0) err quit ( select ); / le conteudo do arquivo para o buffer buff / if (! le EOF && in size == 0) { in size = read ( fd , buff , sizeof ( buff )); if ( in size == 0) { le EOF = 1; close (fd ); } else if ( in size < 0) { err quit ( read ); }
CAPITULO 7. TCP/IP
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
51
} / escreve o conteudo de buff para o sockfd / if ( in size > 0) { ret = write ( sockfd , buff , in size ); if ( ret < in size ) { if ( errno == EAGAIN) { printf (EAGAIN em write\n); } else { err quit ( write ); } } else { in size = 0; } } if ( le EOF && in size == 0 && !shutdown write sock ) { shutdown (sockfd , SHUT WR); shutdown write sock = 1; } / le reply do servidor / if ( FD ISSET (sockfd, &read set )) { out size = read ( sockfd , outbuff , sizeof ( outbuff )); if ( out size == 0) { sock EOF = 1; shutdown (sockfd , SHUT RD); } } if ( out size > 0) out size = 0; if ( sock EOF) done = 1; select sock in = (! sock EOF && out size == 0); } printf ( %s, outbuff ); } int main ( int argc , char argv) { int sockfd ; struct sockaddr in servaddr ; struct timeval tv ; long ags ; fd set write set ; int sock error = 0; socklen t sock error len ;
CAPITULO 7. TCP/IP
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
52
if ( argc < 3) { printf (Sintaxe: %s <arquivo in> <porta>\n, argv[0]); exit (0); } if (( sockfd = socket ( AF INET, SOCK STREAM, 0)) < 0) err quit (socket ); ags = fcntl ( sockfd , F GETFL); fcntl ( sockfd , F SETFL, ags | O NONBLOCK); memset (&servaddr , 0, sizeof ( servaddr )); servaddr . sin family = AF INET; servaddr . sin addr . s addr = htonl ( INADDR LOOPBACK); servaddr . sin port = htons ( atoi ( argv [2])); / aguarda no maximo 30 segundos / tv . tv sec = 30; tv . tv usec = 0; if ( connect ( sockfd , ( struct sockaddr ) & servaddr , sizeof ( servaddr )) == 0) { / conexao ocorreu imediatamente , nao e necessario usar select () / } else { switch ( errno ) { case EINPROGRESS: / select para escrita / FD ZERO (&write set); FD SET (sockfd, & write set ); if ( select ( sockfd + 1, NULL, &write set, NULL, &tv) <= 0) err quit ( select ); if (! FD ISSET (sockfd, & write set )) err quit ( select ); break; default : err quit ( connect ); } }
sock error len = sizeof ( sock error ); if ( getsockopt (sockfd , SOL SOCKET, SO ERROR, &sock error, &sock error len) < 0) err quit ( getsockopt ); conecta ao servidor ( argv [1], sockfd ); exit (0); } Nesta vers o do cliente temos tamb m algumas informacoes relevantes e que valem a pena a e serem descritas. Novamente, comecando a partir da funcao main(): 113 117: cria um socket TCP e congura opcoes para o socket; 119 122: conecta-se ao host local. Uma m quina em particular pode ter mais de um a endereco IP, cada um correpondendo a uma interface de rede diferente. O uso da constante INADDR ANY permite que o bind seja feito em todas elas simultaneamente;
CAPITULO 7. TCP/IP
53
128: tenta conectar-se ao servidor. Caso consiga de imediato, segue adiante, ou aguarda pela mudanca do estado do descritor de escrita do socket sockfd at receber o reply do e servidor. O timeout imposto no exemplo e de 30 segundos; 151: conecta-se ao servidor, executando os passos descritos abaixo; 147 149: pega e limpa qualquer erro que tenha acontecido no socket;
32 43: prepara os conjuntos de descritores de leitura e escrita a serem monitorados pelo select(2); Esta chamada e desbloqueada assim que algum deles tiver seu valor modicado. 46 54: l o conte do do arquivo para o buffer especicado por buff ; e u 57 68: caso tenha sido lido algum conte do, escreve-o para o socket sockfd, onde est u a estabelecida a conex o com o servidor; a
69 72: caso o m de arquivo tenha sido encontrado, chama shutdown(2). Esta funcao naliza totalmente ou parcialmente uma conex o full-duplex no socket especiado a como primeiro argumento. Caso o segundo par metro seja SHUT RD, n o s o permitidas a a a recepcoes futuras neste socket. Ele pode ainda ser SHUT WR, que impede transmiss es o o sobre o socket, ou ainda SHUT RDWR, que impede recepcoes e transmiss es sobre este socket. Neste exemplo, ele naliza o socket para escrita; 75 80: l o reply do servidor para o buffer outbuff. Caso ele tenha lido o sinal de m de e arquivo, naliza o socket para leitura, usando a chamada shutdown(2); 91: imprime o buffer lido, que e o mesmo que foi enviado.
Captulo 8
Bancos de dados
8.1 Implementacoes
Existem diversas implementacoes de servidores de banco de dados disponveis para o GNU/Linux. Entre eles, pode-se citar o MySQL, PostgreSQL e o Firebird, tr s implementacoes excelentes e e bastante est veis de banco de dados relacionais para o GNU/Linux. O banco de dados usado nos a exemplos ser o MySQL. a
54
55
Com estas descricoes j e possvel escrever um aplicativo que comunique-se com o servi a dor de banco de dados, mas antes e necess rio criar o banco de dados e a tabela. Para isto, e a necess rio executar alguns passos, descritos logo abaixo: a $ mysql -u root -p Enter 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, update e exclus o para o usu rio user@localhost. A senha deve ser informada entre aspas simples. a a Ap s criado o banco de dados, e necess rio criar a tabela sobre a qual as operacoes ser o o a a feitas: mysql> USE tabela_teste; Database changed mysql> 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 chamada varchar e um inteiro chamado num. A partir de agora e possvel operar sobre estas estruturas utilizando um programa em C. O exemplo abaixo conecta-se ao servidor MySQL e realiza uma insercao de dados sobre a tabela criada, produtos. Para compilar este exemplo basta: gcc insert_test.c -o insert_test -Wall mysql_config --libs
56
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
sock = mysql real connect ( mysql , localhost , lucasvr , senha , argv [1], 0, NULL, 0); if (! sock ) { fprintf ( stderr , mysql real connect : %s\n , mysql error ( mysql )); exit (1); } num = atoi ( argv [2]); count = 0; while ( count < num) { sprintf ( qbuf , INSERT QUERY, count, count); if ( mysql query (sock , qbuf )) { fprintf ( stderr , mysql query: %s\n , mysql error ( sock )); exit (1); } count++; } mysql close ( sock ); exit (0); } A execucao deste programa pode ser feita da seguinte maneira: $ ./insert_test tabela_teste 5 E o resultado ser reetido imediatamente no banco de dados: a
57
Captulo 9
Interface Gr ca a
9.1 Servidor X
O 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 janelas que pudesse ser usado em centenas de estacoes de trabalho e o projeto Argus, que precisava de um ambiente de depuracao para aplicacoes multiprocessadas distribudas [KOJ 2000]. O X Window System dene padr es que devem ser adotados por implementacoes deste o ambiente. O ambiente que o implementa e d suporte para os sistemas operacionais UNIX e a GNU/Linux e o XFree86. Nestas plataformas ele e distribudo sob uma licenca livre, e em outras ele 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 trabalho do usu rio e prov acesso transparente ao hardware gr co, mecanismos de comunicacao ena e a tre clientes e entrada de dados por dispositivos como mouse e teclado. Os clientes podem ser executados na pr pria estacao ou remotamente, conectados ao servidor atrav s de uma rede. o e 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 interface podem ser construdos com janelas que se sobrep em as demais. As janelas s o organizadas em o ` a uma 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 polticas de interface com o usu rio pudessem ser implementadas, ou seja, e como um driver de vdeo, teclado e a mouse. Portanto, o X n o e nem possui uma interface com o usu rio. A funcao de prover a a tal interface e delegada aos clientes e bibliotecas especiais externos ao servidor X, chamadas toolkits. A seguinte estrutura deve ser respeitada para a criacao de uma aplicacao no X: Inicializacao: durante a inicializacao, o programa far o tratamento de opcoes de linha de a comando, conex o com o servidor X e alocacao de estruturas internas. Tudo isto e feito a internamente pelo toolkit, caso seja utilizado um. Montagem da interface gr ca: ap s aberta a conex o com o servidor, o programa pode a o a criar e montar sua interface (janelas, menus, bot es etc.) utilizando os widgets (elementos o da interface) oferecidos pelo toolkit, ou usando as funcoes primitivas da Xlib, explicada na Sess o 9.2 a Laco de tratamento de eventos: nalmente, o programa entra em um laco, onde ca esperando por eventos vindos do servidor, como pressionamento de teclas ou bot es do o mouse, movimento do mouse ou requisicao do servidor para redesenhar o conte do de u suas janelas. Em toolkits, estes eventos s o primeiro tratados internamente e, dependendo a
58
59
do caso (quando o mouse e clicado em um widget bot o, por exemplo), s o repassados ao a a programa atrav s de callbacks ou mecanismos similares. e
9.4 GTK+
GTK+ e uma biblioteca de widgets escrita em C, com inspiracao em orientacao a objetos. Devido a essa caracterstica, a programacao nela pode se tornar um tanto tediosa, pois e necess rio a emular a programacao orientada a objetos na m o, por meio de uma disciplina de programacao. a A criacao de um programa GTK+ e feita conforme os seguintes passos mostrados no exem plo abaixo (este programa deve ser compilado com a ag -lgtk para ser linkado com a biblioteca GTK+):
1 2 3 4 5 6 7 8 9
#include <unistd . h> #include <gtk/gtk .h> void quit hello ( GtkWidget widget, gpointer data ) { gtk main quit (); }
60
int main ( int argc , char argv) { GtkWidget window, button; gtk init (&argc, &argv ); window = gtk window new (GTK WINDOW TOPLEVEL); gtk signal connect ( GTK OBJECT (window), destroy, GTK SIGNAL FUNC (quit hello), NULL); button = gtk button new with label ( Ola ); gtk signal connect object ( GTK OBJECT (button), clicked, GTK SIGNAL FUNC (gtk widget destroy), GTK OBJECT (window)); gtk container add ( GTK CONTAINER (window), button); gtk widget show ( button ); gtk widget show ( window); gtk main (); exit (0); } Este e um dos programas mais simples que pode ser escrito em GTK+, que faz o seguinte: 4 8: declara a funcao que ser chamada quando a janela receber o sinal destroy; a 15: inicializacao do GTK+. Nesta etapa e realizada a conex o com o servidor X, o a tratamento de opcoes de linha de comando padr o e outras inicializacoes internas; a 18 19: conex o do sinal destroy a funcao quit hello(); a ` 21: criacao de um bot o; a 22 24: quando o bot o receber o sinal clicked, chama a funcao gtk widget destroy, a passando como par metro o objeto window; a 17: criacao de uma janela principal (TOPLEVEL);
26: o bot o e colocado dentro da janela principal; a 28: faz com que a janela seja visvel na tela; 27: faz com que o widget seja visvel;
Como pode-se imaginar, na medida em que o programa cresce, torna-se invi vel escrev -lo a e diretamente sobre o GTK+, pois o usu rio ter o compromisso de escrever todas as rotinas de a a posicionamento de janelas e bot es manualmente, o que acaba tomando muito tempo e levando o a ocorr ncia dos bugs mais diversos. ` e Uma solucao que v rios projetos t m criado e a adocao de uma interface gr ca para mani a e a pular as bibliotecas de widgets. No caso do QT, pode ser utilizado o Designer para desenvolver aplicacoes gr cas muito rapidamente, e com o GTK+ pode ser usado o Glade, que tamb m a e aumenta muito a producao de c digos baseados no ambiente de janelas do XFree86. o As Figuras 9.1 e 9.2 mostram o processo de criacao de um bot o em uma nova janela e uma a janela mais complexa, contendo bot es e combo boxes. o
61
Captulo 10
Comunicacao
Em UNIX, tudo e um arquivo. Seguindo esta losoa, 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 suciente para n o necessitar a o uso destes recursos, ou complexas demais a ponto de ser importante utilizar alguma biblioteca para auxiliar as operacoes sobre estes dispositivos. A seguir s o apresentadas as formas de acesso aos dispositivos serial, paralelo e USB. a
62
63
Estas macros {in,out}s* operam com os mesmo tipos das macros anteriores, com a diferenca de que estas s o orientadas a strings. Alguns processadores implementam a instrucoes especiais para transferir uma seq encia de bytes, words ou longs de e para u uma porta de I/O. No caso da arquitetura n o suportar este tipo de instrucao, elas s o a a implementadas via software na forma de um loop. void insw (unsigned short int port, void *addr, unsigned long int count); void outsw (unsigned short int port, void *addr, unsigned long int count); l em ou escrevem valores de 16-bits para uma porta de 16-bits. e void insl (unsigned short int port, void *addr, unsigned long int count); void outsl (unsigned short int port, void *addr, unsigned long int count); l em ou escrevem valores de 32-bits para uma porta de 32-bits. e 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 processador tenta transferir dadas muito rapidamente do ou para o bus. Os problemas podem ocorrer porque o processador e overclocked em respeito ao bus ISA, que podem acontecer quando os dispositivos da placa s o muito lentos. A solucao nestes casos e inserir um pequeno a delay entre cada instrucao de I/O. Nas arquiteturas que n o enfrentam este problema, esta a macro e expandida para as funcoes correspondentes sem o p. Estas funcoes s o na verdade denidas como macros inline, e precisam ser compiladas com a nvel de otimizacao habilitado para serem substitudas, caso contr rio as refer ncias n o poder o a e a a ser resolvidas em tempo de linkagem do programa. Podem ser utilizadas as ags -O ou -O2, por exemplo. Para poder operar sobre as portas seriais e paralelas em espaco de usu rio, e necess rio a a requisitar acesso para o kernel para as portas de I/O em quest o. Para isto s o utilizados os a a programas 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) congura os bits de acesso a porta para o processo para num bytes, comecando ` na porta from, para o valor turn on. O uso desta funcao requer privil gios de superusu rio. e a As permiss es de acesso n o s o herdadas no fork(2), mas s o no exec(3). Al m disto, o a a a e ioperm(2) permite acesso para as primeiras 0x3ff portas de I/O. Para portas fora deste intervalo, o iopl(2) deve ser utilizado: #include <sys/io . h> int iopl ( int level ); Esta funcao altera os privil gios de I/O do processo atual para o nvel especicado em level. e Assim, o processo tem acesso irrestrito a portas de acesso I/O, inclusive permitindo que o pro` cesso desabilite interrupcoes (o que provavelmente ir causar um crash no sistema). Diferente a da ioperm(2), esta funcao permite que as permiss es sejam herdadas tanto pelo fork(2) o como pelo exec(3). O nvel padr o para um processo normal e 0, e vai at 3. a e
64
As opcoes de controle, representadas pela c cag, controlam a baud rate, a quantidade de bits de dados, paridade, stop bits e o controle de uxo de hardware. A constantes disponveis para serem usadas podem ser vistas na Tabela 10.1 Duas opcoes da c cag devem estar sempre habilitadas: CLOCAL e CREAD. Elas ir o garan a tir que o programa n o vir a ser o propriet rio da porta relativo a controles espor dicos de job a a a ` a e de sinais de hangup, e que o driver da interface serial ser capaz de ler dados de entrada. a Devido as diferentes interfaces que s o disponveis para congurar a baud rate entre v rios ` a a sistemas operacionais, costuma-se usar as funcoes cfsetispeed(3) e cfsetospeed(3) para informar a taxa de entrada e sada, respectivamente. Relativo a paridade, basta combinar as ags sobre a c cag para obter a paridade desejada. ` Na listagem abaixo assume-se que a c cag foi previamente inicializada com algum outro campo (como o CLOCAL | CREAD): Com paridade par (7E1): options.c cag |= CS7 | PARENB; Sem paridade (8N1): options.c cag |= CS8;
65
Com paridade de espaco (7S1): utiliza a mesma conguracao do modo sem paridade (8N1). As opcoes locais podem ser conguradas sobre o membro c lag, e ditam como os caracteres de entrada (leitura) ser o manipulados pelo driver serial. Normalmente ele ser congurado para a a o modo can nico ou n o-can nico (raw). A Tabela 10.2 mostra as opcoes disponveis para este o a o campo. As opcoes de entrada s o informadas pelo membro c iag, de acordo com os valores vistos a na Tabela 10.3. O membro c oag cont m opcoes de ltro de sada. Como nos modos de entrada, pode ser e escolhido modo processado ou raw. A Tabela 10.4 mostra as opcoes disponveis. De todas estas opcoes, provavelmente a unica utilizada ser a ONLCR para mapear novas a linhas para pares CR-LF. O restante das opcoes s o mantidas por raz es hist ricas e datam da a o o epoca em que impressoras e terminais n o conseguiam manter-se em sincronia com as streams a
66
de dados da porta serial. O ultimo conjunto de opcoes que restam para permitir a conguracao da porta serial s o a os caracteres de controle. Eles s o congur veis em um array chamado c cc que cont m tanto a a e denicoes de caracteres de controles como par metros de timeout. Alguns ds dados suportados a neste array est o listados na Tabela 10.5. O restante pode ser consultado a partir do header a bits/termios.h.
67
68
pois o loop de leitura e innito. Em um caso real, seria necess rio vericar o conte do do buffer a u lido para decidir quando encerrar o loop.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
#include <stdio .h> #include <unistd . h> #include < stdlib . h> #include < string . h> #include <sys/ types .h> #include <sys/ stat .h> #include < fcntl .h> #include <termios .h> / Conguracoes de baud rate sao denidas em <asm/termbits.h> / #dene BAUDRATE B9600 #dene DEVICE /dev/ttyS0 int main ( int argc , char argv) { int fd , res ; struct termios oldtio , newtio; char buf [255]; / Abre porta para leitura / escrita e nao como uma tty de controle , para evitar que o programa encerre se vier um CTRL+C na linha / fd = open (DEVICE, O RDWR | O NOCTTY); if ( fd < 0) { perror ( open); exit (1); } / Salva a conguracao atual da porta serial e inicia uma nova / tcgetattr ( fd, & oldtio ); memset (&newtio , 0, sizeof ( newtio )); / CRTSCTS : Controle de uxo de saida do hardware CS8 : 8 n1 (8 bit , no parity , 1 stopbit ) CLOCAL : Conexao local , sem controle do modem CREAD : Habilita a recepcao de caracteres / newtio . c cag = CRTSCTS | CS8 | CLOCAL | CREAD; / IGNPAR : ignora bytes com erros de paridade ICRNL : mapeia CR (carriage return ) para NL (new line ), senao um CR nos dados de entrada pode nao terminar a leitura / newtio . c iag = IGNPAR | ICRNL; / Seta baud rate de entrada e saida / cfsetispeed (&newtio , BAUDRATE);
69
cfsetospeed (&newtio , BAUDRATE); / Saida em modo raw / newtio . c oag = 0; / ICANON : habilita entrada em modo canonico desabilita funcionalidades de echo , e nao envia sinais para o programa / newtio . c lag = ICANON; / Inicializa todos os caracteres de controle . Os valores padrao podem ser encontrados em / usr / include / termios .h / newtio . c cc [VINTR] = 0; // CtrlC newtio . c cc [VQUIT] = 0; // Ctrl\ newtio . c cc [VERASE] = 0; // DEL newtio . c cc [VKILL] = 0; // @ newtio . c cc [VEOF] = 4; // CtrlD / newtio . c cc [VTIME] = 0; // timeout entre leituras ( t=VTIME 0.1 segs) = 5; // bloqueia leitura ate ler 5 caracteres newtio . c cc [VMIN] // \0 newtio . c cc [VSWTC] = 0; newtio . c cc [VSTART] = 0; // CtrlQ newtio . c cc [VSTOP] = 0; // CtrlS newtio . c cc [VSUSP] = 0; // CtrlZ newtio . c cc [VEOL] = 0; // CR // CtrlR newtio . c cc [VREPRINT] = 0; newtio . c cc [VDISCARD] = 0; // CtrlU newtio . c cc [VWERASE] = 0; // CtrlW newtio . c cc [VLNEXT] = 0; // CtrlV // LF newtio . c cc [VEOL2] = 0; / Limpa a linha e ativa as conguracoes para a porta / tcush ( fd , TCIFLUSH); tcsetattr ( fd , TCSANOW, &newtio); / reseta o dispositivo ( modem) / if ( write ( fd , ATZ\r, 4) < 4) perror ( write ); while (1) { res = read ( fd , buf , 255); if ( res < 0) { perror ( read ); exit (1); } / Seta nal de string , para poder imprimir o conteudo com printf / buf[ res ] = 0; fprintf ( stdout , [%s] (%d)\n , buf , res ); } / Restaura as conguracoes antigas da porta /
70
tcsetattr ( fd , TCSANOW, &oldtio); close ( fd ); exit (0); } Nesse exemplo, vale notar que alguns caracteres de controle s o inicializados com 0 mesmo a ap s a estrutura ter sido preenchida com 0s atrav s da funcao memset(3). Isto foi feito apenas o e para mostrar no c digo a possibilidade de trocar os caracteres de controle. Os valores default o est o comentados a direita de cada um. a ` As funcoes cfsetispeed(3) e cfsetospeed(3) permitem que seja utilizada uma taxa de baud rate diferente para a entrada e a sada de dados. Neste exemplo foi utilizada a mesma taxa, o que seria equivalente a passar a constante BAUDRATE juntamente na vari vel a newtio.c cag no Linux.
#include <stdio .h> #include <unistd . h> #include < stdlib . h> #include < string . h> #include <sys/ types .h> #include <sys/ stat .h> #include < fcntl .h> #include <termios .h> / Conguracoes de baudrate sao denidas em <asm/termbits.h> / #dene BAUDRATE B9600 #dene DEVICE /dev/ttyS0 int main ( int argc , char argv) { int fd , res ; struct termios oldtio , newtio; char buf [255]; fd = open (DEVICE, O RDWR | O NOCTTY); if ( fd < 0) { perror ( open); exit (1); } / Salva a conguracao atual da porta serial e inicia uma nova / tcgetattr ( fd, & oldtio ); memset (&newtio , 0, sizeof ( newtio )); /
71
BAUDRATE: Congura o bps rate. CRTSCTS : Controle de uxo de saida do hardware CS8 : 8 n1 (8 bit , no parity , 1 stopbit ) CLOCAL : Conexao local , sem controle do modem CREAD : Habilita a recepcao de caracteres / newtio . c cag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; newtio . c iag = IGNPAR; / Saida em modo raw / newtio . c oag = 0; / habilita entrada em modo naocanonico / newtio . c lag = 0; newtio . c cc [VTIME] newtio . c cc [VMIN] = 5; = 5; // timeout entre leituras ( t=VTIME 0.1 segs) // bloqueia leitura ate ler 5 caracteres
/ Limpa a linha e ativa as conguracoes para a porta / tcush ( fd , TCIFLUSH); tcsetattr ( fd , TCSANOW, &newtio); / reseta o dispositivo ( modem) / if ( write ( fd , ATZ\r, 4) < 4) perror ( write ); while (1) { res = read ( fd , buf , 255); if ( res < 0) { perror ( read ); exit (1); } / Seta nal de string , para poder imprimir o conteudo com printf / buf[ res ] = 0; fprintf ( stdout , [%s] (%d)\n , buf , res ); } / Restaura as conguracoes antigas da porta / tcsetattr ( fd , TCSANOW, &oldtio); close ( fd ); exit (0); }
72
#include <stdio .h> #include <unistd . h> #include < stdlib . h> #include < string . h> #include <sys/ types .h> #include <sys/ stat .h> #include < fcntl .h> #include <termios .h> #include <signal . h> / Conguracoes de baudrate sao denidas em <asm/termbits.h> / #dene BAUDRATE B9600 #dene DEVICE /dev/ttyS0 void trata sinal ( int signum) { printf ( agora posso ler da porta \n); } int main ( int argc , char argv) { int fd , res ; struct termios oldtio , newtio; char buf [255]; fd set readfds ; fd = open (DEVICE, O RDWR | O NOCTTY | O NONBLOCK); if ( fd < 0) { perror ( open); exit (1); } / Instala tratador de sinal para SIGIO / signal ( SIGIO, trata sinal ); / congura o descritor para operacoes assincronas / fcntl ( fd , F SETFL, FASYNC); / Salva a conguracao atual da porta serial e inicia uma nova / tcgetattr ( fd, & oldtio ); memset (&newtio , 0, sizeof ( newtio )); / BAUDRATE: Congura o bps rate. CRTSCTS : Controle de uxo de saida do hardware CS8 : 8 n1 (8 bit , no parity , 1 stopbit ) CLOCAL : Conexao local , sem controle do modem CREAD : Habilita a recepcao de caracteres / newtio . c cag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
73
newtio . c iag = IGNPAR; / Saida em modo raw / newtio . c oag = 0; / habilita entrada em modo naocanonico / newtio . c lag = 0; newtio . c cc [VTIME] newtio . c cc [VMIN] = 5; = 5; // timeout entre leituras ( t=VTIME 0.1 segs) // bloqueia leitura ate ler 5 caracteres
/ Limpa a linha e ativa as conguracoes para a porta / tcush ( fd , TCIFLUSH); tcsetattr ( fd , TCSANOW, &newtio); / reseta o dispositivo ( modem) / if ( write ( fd , ATZ\r, 4) < 4) perror ( write ); while (1) { FD SET (fd, &readfds ); select ( fd + 1, & readfds , NULL, NULL, NULL); if (! FD ISSET (fd, &readfds )) { fprintf ( stderr , Erro em select ()!\ n ); exit (1); } res = read ( fd , buf , 255); if ( res < 0) { perror ( read ); exit (1); } / Seta nal de string , para poder imprimir o conteudo com printf / buf[ res ] = 0; fprintf ( stdout , [%s] (%d)\n , buf , res ); } / Restaura as conguracoes antigas da porta / tcsetattr ( fd , TCSANOW, &oldtio); close ( fd ); exit (0); }
10.3 USB
O acesso a dispositivos USB pelo Linux pode ser feito atrav s da LibUSB 1 . Ela e uma biblioteca ` e para ser usada por aplicacoes em espaco de usu rio para acessar dispositivos USB 1.1 indepen a dente do sistema operacional. A respeito de dispositivos e interfaces, a API da libusb associa um dispositivo aberto a uma interface em especco. Isso signica que caso deseje-se abrir m ltiplas interfaces em um dis u positivo, ele dever ser aberto m ltiplas vezes para receber um manipulador para cada interface. a u
1 http://libusb.sourceforge.net/
74
As seguintes funcoes s o usadas para preparar o uso da biblioteca para comunicar com algum a dispositivo: void usb init(void): inicializa estruturas internas da biblioteca; int usb nd busses(void): scaneia todos os BUSses USB no sistema, retornando a quantidade de modicacoes desde a ultima chamada a esta funcao (o total de BUSses adiciona dos e removidos);
int usb nd devices(void): scaneia todos os dispositivos em cada BUS. Essa funcao deve ser chamada logo ap s usb find busses(). O valor de retorno indica a quantidade o de dispositivos removidos ou adicionados at a ultima chamada a funcao; e ` 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 diret rio na entrada USB do procfs normalmente local o izado em /proc/bus/usb. Para cada BUS, um sub-diret rio e criado neste diret rio, sendo que o o dirname indica qual e o sub-diret rio associado a um determinado BUS. Esta informacao n o o a chega a ser util enquanto lida-se com o dispositivo pela libusb. Os campos next e prev s o usados a para iterar na lista duplamente encadeada que e retornada pela funcao usb get busses(). J a struct usb device apresenta informacoes sobre os dispositivos encontrados. Ela e dea 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 lename informa qual o arquivo dentro do diret rio informado na struct o usb bus est associado ao dispositivo atual. Destes campos, o que ser util para as aplicacoes a a ser o descriptor. Ele nos d as seguintes caractersticas sobre o dispositivo: a a 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; }; struct usb bus *usb get busses(void): retorna a lista dos busses USB encontrados;
75
A partir destas informacoes, e possvel obter o identicador do dispositivo que deseja-se trabalhar. Feito isso, as seguintes funcoes s o usadas: a usb dev handle *usb open(struct *usb device dev): abre o dispositivo dev para uso. Esta funcao deve ser chamada antes de realizar qualquer operacao sobre o dispositivo. Ela retorna um handle para comunicar-se com ele; int usb close(usb dev handle *dev): fecha um dispositivo aberto com usb open(). Retorna 0 ou um valor < 0 caso ocorra um erro; int usb claim interface(usb dev handle *dev, int interface): reivindica a interface para o sistema operacional. O par metro interface e o valor especicado no campo bIntera faceNumber do descritor. Este valor pode ser recuperado a partir de conginterfacealtsetting, presente na struct usb device. Essa funcao deve ser chamada antes de realizar qualquer operacao 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 previamente requisitada pela usb claim interface(); int usb set conguration(usb dev handle *dev, int conguration): escolhe a conguracao ativa de um dispositivo. O par metro conguration e o valor especicado no campo a bCongurationValue do descritor; int usb set altinterface(usb dev handle *dev, int alternate): escolhe a conguracao ativa alternada da interface atual. O par metro alternate e o valor especicado no campo bAla ternateSetting do descritor; int usb reset(usb dev handle *dev): reseta o dispositivo especicado, enviando um RESET para a porta que ele estiver conectado. Vale notar que ap s chamar essa funcao, o o dispositivo precisar ser renumerado, sendo que o handle usado para chamar a usb reset() a n o funcionar mais. Para tornar a usar o dispositivo, e necess rio procur -lo novamente a a a a e abrir um novo handle; int usb resetep(usb dev handle *dev, unsigned int ep): reseta todos os estados para o endpoint especicado em ep. O valor ep e o encontrado no campo bEndpointAddress do descritor; int usb clear halt(usb dev handle *dev, unsigned int ep): limpa qualquer halt status do endpoint especicado por ep. Para realizar transfer ncia de dados sobre bulk pipes, podem ser usadas as seguintes funcoes: e int usb bulk write(usb dev handle *dev, int ep, char *bytes, int size, int timeout): realiza uma requisicao de escrita em volume (bulk write) para o endpoint ep, escrevendo size bytes do array bytes para o dispositivo dev. O timeout deve ser especicado em milisegundos. Essa funcao rtorna o n mero de bytes escritos ou um valor < 0 caso ocorra um u erro; int usb bulk read(usb dev handle *dev, int ep, char *bytes, int size, int timeout): realiza uma requisicao de leitura em volume (bulk read) para o endpoint ep. Retorna o n mero u de bytes lidos ou um valor < 0 caso ocorra um erro. A biblioteca permite tamb m realizar transfer ncia de dados sobre interrupt pipes: e e int usb interrupt write(usb dev handle *dev, int ep, char *bytes, int size, int timeout): realiza uma requisicao de escrita interruptvel para o endpoint ep. Retorna o n mero de u bytes 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 interruptvel para o endpoint ep. Retorna o n mero de u bytes lidos com sucesso ou um valor < 0 caso haja um erro; Algumas outras funcoes s o providas pela biblioteca, com opcoes para obter strings de a identicacao para dispositivos e realizar transfer ncias de controle. S o elas: e a
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 padr o a de controle em um dispositivo. Os par metros s o os de mesmo nome na especicacao a a USB. Retorna o n mero de bytes escritos/lidos ou um valor < 0 caso ocorra um erro; u int usb get string(usb dev handle *dev, int index, int langid, char *buf, size t buen): obt m o descritor de string especicado pelos par metros index e langid de um disposie a tivo. A string ser retornada em formato Unicode, de acordo com a especicacao USB. O a valor de retorno e o n mero de bytes em buf ou < 0 caso haja um erro; u int usb get string simple(usb dev handle *dev, int index, char *buf, size t buen): esta funcao e um wrapper para a usb get string() que retorna a string de descricao especicada pelo index na primeira lngua para o descritor, convertendo-o para ASCII. Essa funcao retorna o n mero de bytes em buf ou um valor < 0 se houver um erro; u int usb get descriptor(usb dev handle *dev, unsigned char type, unsigned char index, void *buf, int size): obt m o descritor para o dispositivo identicado por type e index e do descritor a partir do pipe de controle padr o. Retorna o n mero de bytes lidos para o a u descritor ou um valor < 0 em caso de erro; int usb get descriptor by endpoint(usb dev handle *dev, int ep, unsigned char type, unsigned char index, void *buf, int size): idem a funcao usb get descriptor(), por m o obt m ` e e a partir do pipe de controle identicado por ep; int usb get driver np(usb dev handle *dev, int interface, char *name, int namelen): essa funcao ir obter o nome do driver associado a interface especicada pelo par metro inter a ` a face e ir armazen -lo no buffer name de tamanho namelen. Essa funcao est implemena a a tada apenas para o Linux; int usb detach kernel driver np(usb dev handle *dev, int interface): desconecta o driver de kernel da interface especicada pelo par metro interface. Aplicacoes usando a libusb a podem ent o realizar a usb claim interface(). Ela est implementada apenas no Linux. a a O conjunto de funcoes da API permitem que se manipule de forma bastante abstrata os dis positivos USB. O c digo abaixo busca todos os dispositivos e, para cada um, obt m as suas o e strings de informacao e as imprime na sada padr o. Durante a procura por dispositivos, e bus a cado um em especial (como pode ser visto nas linhas 45 e 46), e este dispositivo ser usado a depois para realizar a escrita de um comando e a leitura de uma resposta. No entanto, vale lembrar que como os comandos (e os identicadores do dispositivo) s o especcos, o c digo a o precisa ser adaptado para lidar com algum outro dispositivo. Para compilar este exemplo, basta rodar: $ gcc teste_usb.c -o teste_usb -Wall libusb-config --cflags --libs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
#include <stdio .h> #include < string . h> #include <usb.h> void show info ( usb dev handle handle ) { int i ; char buffer [1024]; / mostra ate 3 linhas de informacao sobre o dispositivo / for ( i = 0; i < 3; i++) { int len = usb get string simple ( handle , i , buffer , 1024); if ( len < 0) break; buffer [ len ] = \0;
77
printf ( %s\n, buffer ); } printf ( \n); } int main ( int argc , char argv) { struct usb bus busses ; struct usb bus bus; struct usb device dev; struct usb device gp32 = NULL; usb dev handle handle ; char buffer [1024]; int result ; / Inicializa a biblioteca e procura os devices / usb init (); usb nd busses (); usb nd devices (); busses= usb get busses (); for ( bus=busses ; bus ; bus=bus>next) { for ( dev=bus>devices; dev; dev=dev>next) { handle = usb open ( dev ); show info ( handle ); usb close ( handle ); if (( dev>descriptor.idVendor == 0xeb6) && (dev>descriptor.idProduct == 0x3232)) { printf ( Achei dispositivo que procurava\n); gp32 = dev; }
if ( gp32 == NULL) { fprintf ( stderr , Nao encontrei o dispositivo na USB\n); exit (1); } / Abre a interface representada pelo dispositivo gp32 / handle = usb open ( gp32); result = usb claim interface ( handle , 0 x0 ); if ( result < 0) { fprintf ( stderr , %s\n, usb strerror ()); exit (1); } / Estamos prontos para fazer leituras e escritas ao device . Note que os dados a serem escritos aso especicos de cada dispositivo , e devem ser consultados na especicacao dos mesmos. Neste exemplo,
78
estao sendo usados os endpoints 0x03 para escrita e 0x81 para leitura . / memset (buffer , 0, sizeof ( buffer )); snprintf ( buffer , sizeof ( buffer ), comandos especicos ); result = usb bulk write ( handle , 0 x03 , buffer , sizeof ( buffer ), 100000); if ( result < 0) { fprintf ( stderr , %s\n, usb strerror ()); exit (1); } / le a resposta / memset (buffer , 0, sizeof ( buffer )); result = usb bulk read ( handle , 0 x81 , buffer , sizeof ( buffer ), 100000); if ( result < 0) { fprintf ( stderr , %s\n, usb strerror ()); exit (1); } / libera a interface e encerra o programa / usb release interface ( handle , 0 x0 ); usb close ( handle ); exit (0); }
Captulo 11
Timers
11.1 Sinais
Timers s o recursos que permitem sinalizar o processo em intervalos de tempo xos. Como eles a s o baseados em sinais, esta sess o os apresenta, permitindo fazer um programa que lide com a a sinais diversos. A chamada de sistema usada para capturar sinais e a signal(2). Esta chamada de sistema instala um novo signal handler para o sinal com o n mero signum. O signal handler e conguu rado para sighandler, que pode ser qualquer funcao escrita pelo usu rio, ou SIG IGN para ig a nor -lo ou SIG DFL, para que a acao padr o seja tomada (descrita na man page do signal(7). a a O seu prot tipo e o seguinte: o #include <signal . h> typedef void ( sighandler t )( int ); sighandler t signal ( int signum, sighandler t handler );
Alguns dos sinais mais comuns de serem tratados s o: a SIGKILL: processo recebeu um sinal de kill; SIGINT: interrupcao do teclado (CTRL+C);
Seu uso e bastante simples, como pode ser visto no exemplo abaixo. O programa declara um handler para quando receber o sinal SIGINT, e aguarda at que seja pressionada alguma tecla e para encerrar normalmente, ou atrav s da recepcao do sinal especicado. e
1 2 3 4 5 6 7 8 9 10 11
#include <stdio .h> #include < stdlib . h> #include <signal . h> void suicide ( int signum) { printf ( Recebi o sinal %d, encerrando ..\ n , signum); exit (1); }
79
80
int main ( int argc , char argv) { signal ( SIGINT, suicide ); getchar (); printf ( Encerrando normalmente\n); exit (0); }
ITIMER PROF: decrementa quando o processo executa e quando o sistema est execua tando no contexto do processo. Este m todo costuma ser usado para fazer prole do e tempo que um processo passou executando tanto em espaco de usu rio quanto de kernel. a O sinal SIGPROF e entreque quando este timer expira.
O prot tipo para uso de timers e o seguinte: o #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 s o usados s o denidos pelas estruturas: a a struct itimerval { struct timeval it interval ; / proximo valor / struct timeval it value ; / valor atual / }; struct timeval { / segundos / long tv sec ; long tv usec ; / micro segundos / }; A funcao getitimer(2) preenche a estrutura indicada por value com as conguracoes do timer indicado por wich, que pode ser uma das ITIMER REAL, ITIMER VIRTUAL ou ITIMER PROF. O elemento it value e congurado para a quantidade de tempo restante no timer, ou zero caso o timer seja desabilitado. Similarmente, it interval e congurado para o valor de reset. A funcao setitimer(2) congura o timer indicado para o valor em timer. Se ovalue e n o-zero, o valor antigo do timer e armazenado ali. a e Timers decrementam de it value at zero, gerando um sinal, e resetam para it interval. Um timer que e congurado para um it value zero expira.
81
Como exemplo de uso, podemos tomar o programa abaixo, que executa um timer de 2 em 2 segundos.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
#include <stdio .h> #include <unistd . h> #include <signal . h> #include <sys/time .h> void timer func ( int signum) { printf ( recebi o sinal %d\n, signum); } int main ( int argc , char argv) { struct itimerval tempo; / como vamos usar ITIMER REAL, preparamos um signal handler para SIGALRM / signal ( SIGALRM, timer func); / especica o intervalo e seta o timer / tempo. it interval . tv sec = 2; tempo. it interval . tv usec = 0; tempo. it value . tv sec = 2; tempo. it value . tv usec = 0; setitimer ( ITIMER REAL, &tempo, NULL); while (1) { usleep (100); } exit (0); }
Bibliograa
[Ame 94] American National Standards Institute. IEEE standard for information technology: Portable Operating Sytem Interface (POSIX). part 1, system application program interface (API) amendment 1 realtime extension [C language]. 1109 Spring Street, Suite 300, Silver Spring, MD 20910, USA: IEEE Computer Society Press, 1994. xxiii + 590p. IEEE Std 1003.1b-1993 (formerly known as IEEE P1003.4; includes IEEE Std 1003.1-1990). Approved September 15, 1993, IEEE Standards 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. Desenvolvimento de Aplicacoes Gr cas para o X Window System. Mini-curso apresen a tado no Software Livre 2000. [MyS 2003] MySQL AB. MySQL http://www.mysql.com/documentation/. Reference Manual.
[RUB 2001] RUBINI, A.; CORBET, J. (Eds.). LINUX device drivers. [S.l.]: OREILLY, 2001. v.2. [SCH 86] SCHEIFLER, R.; GETTYS, J. The X Window System. ACM Transactions on Graphics, v.5, n.2, p.79109, 1986.
[STA 2002] STALLMAN, R. M. Using the GNU Compiler Collection (GCC). [S.l.]: Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA, 2002. [STE 98] [STE 98a] [The 2003] STEVENS, W. R. (Ed.). UNIX Network Programming - Interprocess Communication. [S.l.]: Prentice Hall, 1998. STEVENS, W. R. (Ed.). UNIX Network Programming - Networking APIs: Sockets and XTI. [S.l.]: Prentice Hall, 1998. The Native POSIX Thread Library for Linux, 2003, http://people.redhat.com/drepper/nptl-design.pdf. Anais. . . [S.l.: s.n.], 2003.
82