programacao linux

Upload: josuecc

Post on 12-Jul-2015

602 views

Category:

Documents


0 download

TRANSCRIPT

Programao Linux Avanada ca c

(...) Pero, con todo eso, me parece que el traducir de una lengua en otra, como no sea de las reinas de las lenguas, griega y latina, es como quien mira los tapices amencos por el revs, que aunque se veen las guras, son llenas e de hilos que las escurecen y no se veen con la lisura y tez de la haz, y el traducir de lenguas fciles ni arguye ingenio ni elocucin, como no le arguye a o el que traslada ni el que copia un papel de otro papel. (...) [II, 62] El ingenioso hidalgo Don Quijote de la Mancha Miguel de Cervantes

Traduzido por Jorge Barros de Abreu http://sites.google.com/site/cmatinf Verso - 0.05 - 05/2011 a

Sumrio aI Programao UNIX Avanada com Linux ca c. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

913 13 13 14 15 15 17 18 19 21 22 22 23 25 25 26 27 29 29 30 31 32 35 37 38 40 43 43 45

1 Iniciando 1.1 Editando com Emacs . . . . . . . . . . . . . . . . . . . . 1.1.1 Abrindo um Arquivo Fonte em C ou em C++ . . 1.1.2 Formatando Automaticamente . . . . . . . . . . . 1.1.3 Destaque Sinttico para Palavras Importantes . . a 1.2 Compilando com GCC . . . . . . . . . . . . . . . . . . . 1.2.1 Compilando um Arquivo Simples de Cdigo Fonte o 1.2.2 Linkando Arquivos Objeto . . . . . . . . . . . . . 1.3 Automatizando o Processo com GNU Make . . . . . . . 1.4 Depurando com o Depurador GNU (GDB) . . . . . . . . 1.4.1 Depurando com GNU Debugger (GDB) . . . . . . 1.4.2 Compilando com Informaes de Depurao . . . co ca 1.4.3 Executando o GDB . . . . . . . . . . . . . . . . . 1.5 Encontrando mais Informao . . . . . . . . . . . . . . . ca 1.5.1 Pginas de Manual . . . . . . . . . . . . . . . . . a 1.5.2 Info . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.3 Arquivos de Cabealho . . . . . . . . . . . . . . . c 2 Escrevendo Bom Software GNU/Linux 2.1 Interao Com o Ambiente de Execuo ca ca 2.1.1 A Lista de Argumentos . . . . . . 2.1.2 Convenes GNU/Linux de Linha co 2.1.3 Usando getopt long . . . . . . . . 2.1.4 E/S Padro . . . . . . . . . . . . a 2.1.5 Cdigos de Sa de Programa . . o da 2.1.6 O Ambiente . . . . . . . . . . . . 2.1.7 Usando Arquivos Temporrios . . a 2.2 Fazendo Cdigo Defensivamente . . . . . o 2.2.1 Usando assert . . . . . . . . . . . 2.2.2 Falhas em Chamadas de Sistema 3

. . . . . . . . . . . . . . . . de Comando . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

2.3

2.2.3 Cdigos de Erro de Chamadas de Sistema . o 2.2.4 Erros e Alocao de Recursos . . . . . . . . ca Escrevendo e Usando Bibliotecas . . . . . . . . . . 2.3.1 Agrupando Arquivos Objeto . . . . . . . . . 2.3.2 Bibliotecas Compartilhadas . . . . . . . . . 2.3.3 Bibliotecas Padronizadas . . . . . . . . . . . 2.3.4 Dependncia de uma Biblioteca . . . . . . . e 2.3.5 Prs e Contras . . . . . . . . . . . . . . . . o 2.3.6 Carregamento e Descarregamento Dinmico a

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

46 49 50 51 52 55 55 57 58 61 61 62 62 64 64 64 65 69 71 74 76 76 77 79 83 84 86 87 89 90 91 92 93 94 96 96 99 100 101

3 Processos 3.1 Visualizando Processos . . . . . . . . . . . . . . . . . 3.1.1 Identicadores de Processos . . . . . . . . . . 3.1.2 Visualizando os Processos Ativos . . . . . . . 3.1.3 Encerrando um Processo . . . . . . . . . . . . 3.2 Criando Processos . . . . . . . . . . . . . . . . . . . . 3.2.1 Usando system . . . . . . . . . . . . . . . . . 3.2.2 Usando bifurcar e executar . . . . . . . . . . . 3.2.3 Agendamento de Processo . . . . . . . . . . . 3.3 Sinais . . . . . . . . . . . . . . . . . . . . . . . . . . 3.3.1 Encerramento de Processo . . . . . . . . . . . 3.3.2 Esperando pelo Encerramento de um Processo 3.3.3 As Chamadas de Sistema da Fam wait . . lia 3.3.4 Processos do Tipo Zumbi . . . . . . . . . . . . 3.3.5 Limpando Filhos de Forma No Sincronizada a 4 Tarefas 4.1 Criao de Tarefas . . . . . . . . . . . ca 4.1.1 Enviando Dados a uma Tarefa . 4.1.2 Vinculando Tarefas . . . . . . . 4.1.3 Valores de Retorno de Tarefas . 4.1.4 Mais sobre IDs de Tarefas . . . 4.1.5 Atributos de Tarefa . . . . . . . 4.2 Cancelar Tarefas . . . . . . . . . . . . 4.2.1 Tarefas Sincronas e Assincronas 4.2.2 Sees Cr co ticas Incancelveis . . a 4.2.3 Quando Cancelar uma Tarefa . 4.3 Area de Dados Espec cos de Tarefa . 4.3.1 Manipuladores de Limpeza . . . 4.3.2 Limpeza de Tarefa em C++ . . 4.4 Sincronizao e Sees Cr ca co ticas . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

4.4.1 Condies de CCorrida . . . . . . . . . . . co 4.4.2 Mutexes . . . . . . . . . . . . . . . . . . . 4.4.3 Travas Mortas de Mutex . . . . . . . . . . 4.4.4 Testes de Mutex sem Bloqueio . . . . . . . 4.4.5 Semaforos para Tarefas . . . . . . . . . . . 4.4.6 Variveis Condicionais . . . . . . . . . . . a 4.4.7 Travas Mortas com Duas ou Mais Tarefas 4.5 Implementao de Tarefa em GNU/Linux . . . . ca 4.5.1 Manipulando Sinal . . . . . . . . . . . . . 4.5.2 Chamada de Sistema clone . . . . . . . . . 4.6 Processes Vs. Threads . . . . . . . . . . . . . . . 5 Open Publication License 6 Licena de Livre Publicao c ca

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

102 104 107 108 109 112 117 118 119 120 120 123 127

Listagem Cdigos Fonte o1.1 1.2 1.3 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3.1 3.2 3.3 3.4 3.5 3.6 3.7 4.1 4.2 4.3 4.4 4.5 4.6 4.7 Arquivo main.c cdigo fonte em C . . . . . . . . . . . . . . . o Arquivo reciprocal.cpp cdigo fonte em C++ . . . . . . . . . o Arquivo de cabealho reciprocal.hpp . . . . . . . . . . . . . c (Arquivo arglist.c) Usando argc e argv. . . . . . . . . . . . . . (getopt long.c) Usando a funo getopt long . . . . . . . . . . ca (print-env.c) Mostrando o Ambiente de Execuo . . . . . . . ca (client.c) Parte de um Programa Cliente de Rede . . . . . . . (temp le.c) Usando mkstemp . . . . . . . . . . . . . . . . . . (readle.c) Liberando Recursos em Condies Inesperadas . . co (test.c) Area da Biblioteca . . . . . . . . . . . . . . . . . . . . (app.c) Um Programa Que Utiliza as Funes da Biblioteca co Acima . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . (titest.c) Usando a libti . . . . . . . . . . . . . . . . . . . . ( print-pid.c) Mostrando o ID do Processo . . . . . . . . . . . (system.c) Usando uma chamada ` funo system . . . . . . . a ca ( fork.c) Usando fork para Duplicar o Processo de um Programa ( fork-exec.c) Usando fork e exec Juntas . . . . . . . . . . . . (sigusr1.c) Usando um Manipulador de Sinal . . . . . . . . . . (zombie.c) Fazendo um Processo Zumbi . . . . . . . . . . . . . (sigchld.c) Limpando Processos lhos pelo manuseio de SIGCHLD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ( thread-create.c) Criando uma Tarefa . . . . . . . . . . . . . ( thread-create2) Cria Duas Tarefas . . . . . . . . . . . . . . . Funo main revisada para thread-create2.c . . . . . . . . . . ca ( primes.c) Calcula Nmeros Primos em uma Tarefa . . . . . . u (detached.c) Programa Esqueleto Que Cria uma Tarefa Desvinculada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . (critical-section.c) Protege uma Transao Bancria com uma ca a Seo Cr ca tica . . . . . . . . . . . . . . . . . . . . . . . . . . . (tsd.c) Log Por Tarefa Implementado com Dados Espec cos de Tarefa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 16 16 16 31 34 39 40 42 50 52 52 56 62 65 66 69 74 78 80 85 87 88 90 92 95 98

4.8 4.9 4.10 4.11 4.12 4.13 4.14 4.15

(cleanup.c) Fragmento de Programa Demonstrando uma Tarefa100 (cxx-exit.cpp) Implementando Sa Segura de uma Tarefa da com Excees de C++ . . . . . . . . . . . . . . . . . . . . . . 101 co ( job-queue1.c) Thread Function to Process Jobs from the Queue103 ( job-queue2.c) Funo de Tarefa da Fila de Trabalho, Proteca gida por um Mutex . . . . . . . . . . . . . . . . . . . . . . . . 106 ( job-queue3.c) Fila de Trabalhos Controlada por um Semforo 111 a (spin-condvar.c) Uma Implementao Simples de Varivel Conca a dicional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 (condvar.c) Control a Thread Using a Condition Variable . . . 116 (thread-pid) Imprime IDs de processos para Tarefas . . . . . . 118

Parte I Programao UNIX Avanada ca c com Linux

9

ESSE CAP ITULO MOSTRA COMO EXECUTAR OS PASSOS bsicos a requeridos para criar um programa Linux usando a linguagem C ou a linguagem C++. Em particular, esse cap tulo mostra como criar e modicar cdigo fonte C e C++, compilar aquele cdigo, e depurar o resultado. Se voc o o e tem experincia em programao em ambiente Linux, voc pode pular agora e ca e para o Cap tulo 2, Escrevendo Bom Software GNU/Linuxpreste cuidadosa ateno ` seo 2.3, Escrevendo e Usando Bibliotecaspara informaes ca a ca co sobre linkagem/vinculao esttica versus linkagem/vinculao dinmica `s ca a ca a a quais voc pode no conhecer ainda. e a No decorrer desse livro, assumiremos que voc est familiarizado com as e a linguagens de programao C ou C++ e as funes mais comuns da biblioca co teca padro C. Os exemplos de cdigo fonte nesse livro esto em C, exceto a o a quando demonstrando um recurso particular ou complicao de programao ca ca em C++. Tambm assumiremos que voc conhece como executar operaes e e co bsicas na linha de comando do Linux, tais como criar diretrios e copiar a o arquivos. Pelo fato de muitos programadores de ambiente Linux terem iniciado programao no ambiente Windows, iremos ocasionalmente mostrar ca semelhanas e diferenas entre Windows e Linux. c c

11

12

Cap tulo 1 Iniciando1.1 Editando com Emacs

Um editor o programa que voc usa para editar o cdigo fonte. Muitos e e o editores esto dispon a veis para Linux, mas o editor mais popular e cheio de recursos provavelmente GNU Emacs. eSobre o Emacs: Emacs muito mais que um editor. Emacs um programa inacreditavelmente e e poderoso, tanto que em CodeSourcery, Emacs afetuosamente conhecido como e Um Verdadeiro Programa, ou apenas o UVP de forma curta. Voc pode ler e e enviar mensagens eletrnicas de dentro do Emacs, e voc pode personalizar e o e extender o Emacs de formas muito numerosas para discorrer aqui. Voc pode e at mesmo navegar na web de dentro do Emacs! e

Caso voc esteja familiarizado com outro editor, voc pode certamente e e us-lo no lugar do Emacs. Note que o restante desse livro est vinculado ao a a uso do Emacs. Se ainda no tem ainda um editor Linux favorito, ento voc a a e deve seguir adiante com o mini-tutorial fornecido aqui. Se voc gosta do Emacs e deseja aprender sobre seus recursos avanados, e c voc pode considerar ler um dos muitos livros sobre Emacs dispon e veis. Um excelente tutorial Learning GNU Emacs, escrito por Debra Cameron, e Bill Rosenblatt, e Eric S. Raymond (Editora OReilly, 1996).

1.1.1

Abrindo um Arquivo Fonte em C ou em C++

Voc pode iniciar o Emacs digitando emacs em sua janela de terminal e prese sionado a tecla Enter. Quando Emacs tiver iniciado, voc pode usar os menus e localizados na parte superior para criar um novo arquivo fonte. Clique no menu File, escolha Open File, ento digite o nome do arquivo que voc a e 13

deseja abrir no minibuerlocalizado na parte inferior da tela.1 Se quiser criar um arquivo fonte na linguagem C, use um nome de arquivo que termine em .c ou em .h. Se voc quiser criar um arquivo fonte em C++, use um e nome de arquivo que termine em .cpp, .hpp, .cxx, .hxx, .C, ou .H. Quando o arquivo estiver aberto, voc pode digitar da mesma forma que faria em e qualquer programa processador de palavras comum. Para gravar o arquivo, escolha a entrada Saveno menu File. Quando voc tiver encerrado a utie lizao do Emacs, voc pode escolher a opo Exit Emacsno menuFile. ca e ca Se voc no gosta de apontar e clicar, voc pode usar tclas de atalho de tee a e clado para automaticamente abrir arquivos, gravar arquivos, e sair do Emacs. Para abrir um arquivo, digite C-x C-f. (O C-x signica pressionar a tecla ctrl e ento pressionar a tecla x.) Para gravar um arquivo, digite C-x C-s. Para a sair do Emacs, apenas digite C-x C-c. Se voc desejar adquirir um pouco e mais de habilidade com Emacs, escolha a entrada Emacs Tutorialno menu Help.O tutorial abastece voc com uma quantidade grande de dicas sobre e como usar Emacs efetivamente.

1.1.2

Formatando Automaticamente

Se voc est acostumado a programar em um Ambiente Integrado de Dee a senvolvimento (IDE)2 , voc conseqentemente estar tambm acostumado a e u a e ter o editor ajudando voc a formatar seu cdigo. Emacs pode fornecer o e o mesmo tipo de funcionalidade. Se voc abre um arquivo de cdigo em C e o ou em C++, Emacs automaticamente detecta que o arquivo contm cdigo e o fonte, no apenas texto comum. Se voc pressiona a tecla Tab em uma linha a e em branco, Emacs move o cursor para um ponto ajustado apropriadamente. Se voc pressionar a tecla Tab em uma linha que j contm algum texto, e a e Emacs ajusta o texto. Ento, por exemplo, suponha que voc tenha digitado a e o seguinte: int main ( ) { p r i n t f ( Alo , mundo\n ) ; } Se voc pressionar a tecla Tab na linha com a chamada ` funo printf, e a ca Emacs ir reformatar seu cdigo para parecer como mostrado abaixo: a o int main ( )Se voc no est executando em um sistema X Window, voc ter de pressionar F10 e a a e a para acessar os menus. 2 Do ingls Integrated Development Environment. Em nosso bom portugus seria e e ADI1

14

{ p r i n t f ( Alo , mundo\n ) ; } Note como a linha foi apropriadamente indentada. ` medida que seu uso do Emacs for acontecendo, voc ver como o Emacs A e a pode ajudar voc a executar todo tipo de complicadas tarefas de formatao. e ca Se voc for ambicioso, voc pode programar o Emacs para executar literale e mente qualquer tipo de formatao automtica que voc puder imaginar. ca a e Pessoas tm usado essa facilidade de programao para implementar modos e ca Emacs para editar todo tipo de documento, para implementar jogos3 , e para implementar interfaces para usurios acessarem bases de dados. a

1.1.3

Destaque Sinttico para Palavras Importantes a

Adicionalmente ` formatao de seu cdigo, Emacs pode destacar palavras a ca o facilmente ao ler cdigo em C e em C++ atravs da colorao de diferentes o e ca elementos sintticos. Por exemplo, Emacs pode atribuir a palavra chaves uma a certa cor, atribuir uma segunda cor diferente da anterior a tipos de dados internos tais como int, e atribuir a comentrios outra terceira cor diferente a das duas primeiras. A utilizao de cor torna muito mais fcil destacar alguns ca a erros comum de sintaxe. A forma mais fcil de habilitar cores editar o arquivo a e /.emacs e inserir aseguinte seqncia de caracteres: ue (global-font-lock-mode t) Grave o arquivo, saia do Emacs, e volte a ele em seguida. Agora abra um cdigo fonte em C ou em C++ e aproveite! o Voc possivelmente pode ter notado que a sequncia de caracteres que voc e e e inseriu dentro do seu .emacs semelhante a um cdigo da linguagem de e o programao LISP.Isso ocorre pelo fato de ser um cdigo LISP! Muitas partes ca o de cdigo do Emacs atualmente escrita em LISP. Voc pode adicionar o e e funcionalidades ao Emacs por meio de acrscimos em cdigo LISP. e o

1.2

Compilando com GCC

Um compilador converte um cdigo fonte leg o vel a seres humanos em um cdigo objeto leg o vel a computadores que pode ento ser executado. Os aTente executar o comando M-x dunnetse voc desejar divertir-se com um antiquae dro jogo de aventura em modo texto. Nota do tradutor: Dunnet um jogo distribu e do junto com o emacs cuja primeira verso datava dos idos de 1983. a3

15

compiladores dispon veis em sistemas linux so todos parte da coleo de a ca 4 compiladores GNU, comumente conhecido como GCC. GCC tambm inclui e compiladores para as linguagens C, C++, Java, Objective-C, Fortran, e Ada. Esse livro est dirigido em sua grande parte em programao em C e C++. a ca Suponhamos que voc tenha um projeto como o da listagem 1.2 com um e arquivo de cdigo em C++ (reciprocal.cpp) e um arquivo de cdigo fonte em o o C (main.c) como o da listagem 1.1. Esses dois arquivos so supostamente a para serem compilados e ento linkados juntos para produzir um programa a chamado reciprocal.5 Esse programa ir calcular o rec a proco/inverso de um inteiro. Listagem 1.1: Arquivo main.c cdigo fonte em C o#include #include < s t d l i b . h> #include r e c i p r o c a l . hpp i n t main ( i n t a r g c , char a r g v ) { int i ; i = a t o i ( argv [ 1 ] ) ; p r i n t f ( The r e c i p r o c a l return 0 ; }

o f %d i s %g \n , i ,

reciprocal

(i));

Listagem 1.2: Arquivo reciprocal.cpp cdigo fonte em C++ o#include #include r e c i p r o c a l . hpp double r e c i p r o c a l ( i n t i ) { // I s h o u l d b e nonz e r o . a s s e r t ( i != 0 ) ; return 1 . 0 / i ; }

Existe tambm um arquivo de cabealho chamado reciprocal.hpp (veja a e c listagem 1.3). Listagem 1.3: Arquivo de cabealho reciprocal.hpp c#i f d e f cplusplus extern C { #e n d i f extern #i f d e f } #e n d i f double r e c i p r o c a l cplusplus ( int i);

O primeiro passo converter o cdigo fonte em C e em C++ em cdigo e o o objeto.Para mais informao sobre GCC, visite http://gcc.gnu.org. ca em Windows, arqu vos executveis geralmente possuem nomes que terminam em a .exe. Programas GNU/Linux, por outro lado, geralmente no possuem extenso. Ento, a a a o equivalente Windows do programa reciprocalpode provavelmente ser chamado reciprocal.exe; a verso Linux somente reciprocal. a e5 4

16

1.2.1

Compilando um Arquivo Simples de Cdigo Fonte o

O nome do compilador C gcc. Para compilar um cdigo fonte em C, voc e o e usa a opo -c. Ento, por exemplo, inserindo isso no prompt de comando ca a compila o arquivo de cdigo fonte main.c: o % gcc -c main.c O arquivo objeto resultante chamado main.o. O compilador C++ chae e mado g++. Sua operao muito similar ao gcc; compilando reciprocal.cpp ca e realizada atravs do seguinte comando: e e % g++ -c reciprocal.cpp A opo -c diz ao compilador g++ para fornecer como sa um arquivo ca da objeto somente; sem essa opo, g++ iria tentar linkar o programa para proca duzir um executvel. Aps voc ter digitado esse comando, voc ir ter um a o e e a arquivo objeto chamado reciprocal.o. Voc ir provavelmente precisar de algumas outras opes para construir e a co qualquer programa razovelmente grande. A opo -I usada para dizer a ca e ao GCC onde procurar por arquivos de cabealho. Por padro, GCC olha c a no diretrio atual e nos diretrios onde cabealhos para bibliotecas padro o o c a esto instalados. Se voc precisar incluir arquivos de cabealho localizados a e c em algum outro lugar, voc ir precisar da opo -I. Por exemplo, supoe a ca nhamos que seu projecto tenha um diretrio chamado src, para arquivos o fonte, e outro diretrio chamado include. Voc pode compilar o arquivo o e reciprocal.cpp como segue abaixo para indicar que g++ deve usar o diretrio o ../includeadicionalmente para encontrar o arquivo de cabealho reciproc cal.hpp: % g++ -c -I ../include reciprocal.cpp Algumas vezes voc ir desejar denir macros na linha de comando. Por e a exemplo, no cdigo de produo, voc no ir querer o trabalho adicional da o ca e a a checagem de declarao presente em reciprocal.cpp; a checagem s existe para ca o ajudar a voc a depurar o programa. Voc desabilita a checagem denindo a e e macro NDEBUG. Voc pode ter adicionado uma declarao expl e ca cita #dene em reciprocal.cpp, mas issor requer modocao no cdigo fonte em si. E ca o mais fcil simplesmente denir NDEBUG na linha de comando, como segue: a % g++ -c -D NDEBUG reciprocal.cpp 17

Se voc tiver desejado denir NDEBUG para algum valor particular, voc e e pode ter feito algo como: % g++ -c -D NDEBUG=3 reciprocal.cpp Se voc estiver realmente construindo cdigo fonte de produo, voc e o ca e provavelmente deseja que o GCC otimize o cdigo de forma que ele rode to o a rapidamente quanto poss vel.Voc pode fazer isso atravs da utilizao da e e ca opo -O2 de linha de comando. (GCC tem muitos diferentes n ca veis de otimizao; o segundo n apropriado para a maioria dos programas.) Por ca vel e exemplo, o comando adiante compila reciprocal.cpp com otimizao habilica tada: % g++ -c -O2 reciprocal.cpp Note que compilando com otimizao pode fazer seu programa mais dif ca cil de depurar com um depurador (veja a Seo 1.4, Depurando com o Depuca rador GNU (GDB)). Tambm, em certas instncias, compilando com otie a mizao pode revelar erros em seu programa que no apareceriam em outras ca a situaes anteriores. co Voc pode enviar muitas outras opes ao compilador gcc e ao compilador e co g++. A melhor forma de pegar uma lista completa ver a documentao e ca em tempo real. Voc pode fazer isso digitando o seguinte na sua linha de e comando: % info gcc

1.2.2

Linkando Arquivos Objeto

Agora que voc compilou main.c e reciprocal.cpp, voc ir desejar link-los. e e a a Voc deve sempre usar o g++ para linkar um programa que contm cdigo e e o em C++, mesmo se ese cdigo C++ tambm contenha cdigo em C. Se seu o e o programa contiver somente cdigo em C, voc deve usar o gcc no lugar do o e g++. Pelo fato de o g++ est apto a tratar ambos os arquivos em C e em a C++, voc deve usar g++, como segue adiante: e % g++ -o reciprocal main.o reciprocal.o A opo -o fornece o nome do arquivo a ser gerado como sa no passo ca da de linkagem. Agora voc pode executar o reciprocal como segue: e % ./reciprocal 7 The reciprocal of 7 is 0.142857 18

Como voc pode ver, g++ linkou/vinculou automaticamente a biblioteca e C padro em tempo de execuo contendo a implementao da funo. Se a ca ca ca voc tiver precisado linkar outra biblioteca (tal como uma coleo de roe ca tinas/cdigos prontos para facilitar a criao de uma interface grca de o ca a 6 usurio) , voc pode ter especicado a biblioteca com a opo -l. Em GNU/a e ca Linux, nomes de biblioeca quase sempre comeam com lib. Por exemplo, a c biblioteca Pluggable Authentication Module(PAM) chamada libpam.a. e Para linkar a libpam.a, voc usa um comando como o seguinte: e % g++ -o reciprocal main.o reciprocal.o -lpam O compilador automaticamente adiciona o prexo libe o suxo .a7 . Da mesma forma que para os arquivos de cabealho, o linkador procura por c bibliotecarem alguns lugares padro, incluindo os diretrios /lib e /usr/lib a o onde esto localizadas as bibliotecas padro do sistema. Se voc deseja que a a e o linkador procure em outros diretrios tambm, voc deve usar a opo -L, o e e ca que a correspondente da opo -I discutida anteriormente. Voc pode usar e ca e essa linha para instruir o linkador a procurar por bibliotecas no diretrio o /usr/local/lib/pam antes de procurar nos lugares usuais: % g++ -o reciprocal main.o reciprocal.o -L/usr/local/lib/pam -lpam Embora voc no tenha a opo -I para instruir o preprocessor para proe a ca curar o diretrio atual, voc deve usar a opo -L para instruir o linkador o e ca a procurar no diretrio atual. Dizendo mais claramente, voc pode usar o e a seguinte linha para instruir o linkador a encontrar a biblioteca testno diretrio atual: o % gcc -o app app.o -L. -ltest

1.3

Automatizando o Processo com GNU Make

Se voc est acostumado a programar para o sistema operacional Windows, e a voc est provavelmente acostumado a trabalhar com um Ambiente Intee a grado de Desenvolvimento (IDE).Voc adiciona arquivos de cdigo fonte a e o seu projeto, e ento o IDE contri seu projeto automaticamente. Embora a o IDEs sejam dispon veis para GNU/Linux, esse livro no vai discut a -las. Em lugar de discutir IDEs, esse livro mostra a voc como usar o GNU Make para eNota do tradutor: QT ou Gtk Nota do tradutor: a biblioteca PAM pode ser encontrada em http://ftp.mgts.by/ pub/linux/libs/pam/library/.7 6

19

automaticamente recompilar seu cdigo, que o que a maioria dos prograo e madores GNU/Linux atualmente fazem. A idia bsica por trs do make simples. Voc diz ao make os alvos que e a a e e voc deseja construir e ento fornece regras explanatria de como construir os e a o alvos desejados. Voc tambm especica dependncias que indicam quando e e e um alvo em particular deve ser reconstru do. Em nosso projeto exemplo reciprocal, existem trs alvos bvios: reciprocal.o, e o main.o, e o reciprocal executvel propriamente dito. Voc j tinha regras a e a em mente para reconstruir esses alvos na forma da linha de comando fornecidas previamente. As dependncias requerem um pouco de racioc e nio. Claramente, reciprocal depende de reciprocal.o e de main.o pelo fato de voc e no poder linkar o programa at voc ter constru cada um dos arquivos a e e do objetos. Os arquivos objetos devem ser reconstru dos sempre que o correspondente arquivo fonte mudar. Acontece mais uma modicao em reciproca cal.hpp isso tambm deve fazer com que ambos os arquivos objetos sejam e reconstru dos pelo fato de ambos os arquivos fontes incluirem aquele arquivo de cabealho. c Adicionalmente aos alvos bvios, deve-se ter sempre um alvo de limpeza. o Esse alvo remove todos os arquivos objetos gerados e programas de forma que voc possa iniciar de forma suave. A regra para esse alvo utiliza o coe mando rm para remover os arquivos. Voc pode reunir toda essa informao para o make colocando a informao e ca ca em um arquivo chamado Makele. Aqui est um exemplo de contedo de a u Makele: reciprocal: main.o reciprocal.o g++ $(CFLAGS) -o reciprocal main.o reciprocal.o main.o: main.c reciprocal.hpp gcc $(CFLAGS) -c main.c reciprocal.o: reciprocal.cpp reciprocal.hpp g++ $(CFLAGS) -c reciprocal.cpp clean: rm -f *.o reciprocal Voc pode ver que alvos so listados do lado esquerdo, seguidos por dois e a pontos e ento quaisquer dependncia so colocadas adiante dos dois pontos. a e a A regra para construir o referido alvo localiza-se na linha seguinte. (Ignore o $(CFLAGS) um pouco por um momento.) A linha com a regra para esse alvo 20

deve iniciar com um caractere de tabulao, ou make ir se confundir. Se ca a voc editar seu Makele no Emacs, Emacs ir ajudar voc com a formatao. e a e ca Se voc tiver removido os arquivos objetos que voc construiu anteriormente, e e e apenas digitar % make na linha de comando, voc ir ver o seguinte: e a % make gcc -c main.c g++ -c reciprocal.cpp g++ -o reciprocal main.o reciprocal.o Voc pode ver que make contri automaticamente os arquivos objetos e e o ento linka-os. Se voc agora modicar por algum motivo o main.c e digitar a e make novemente, voc ir ver o seguinte: e a % make gcc -c main.c g++ -o reciprocal main.o reciprocal.o Voc pode ver que make soube reconstruir main.o e re-linkar o programa, e mas o make no se incomodou em recompilar reciprocal.cpp pelo fato de a nenhuma das dependncias para reciprocal.o ter sofrido alguma modicao. e ca O $(CFLAGS) uma varivel do make. Vco pode denir essa varvel ou no e a e a Makele mesmo ou na linha de comando. GNU make ir substituir o valor a da varivel quando executar a regra. Ento, por exemplo, para recompilar a a com otimizao habilitada, voc deve fazer o seguinte: ca e % make clean rm -f *.o reciprocal % make CFLAGS=-O2 gcc -O2 -c main.c g++ -O2 -c reciprocal.cpp g++ -O2 -o reciprocal main.o reciprocal.o

1.4

Depurando com o Depurador GNU (GDB)

Note que o sinalizador -O2foi inserido no lugar de $(CFLAGS) na regra. Nessa seo, voc viu somente as mais bsicas capacidades do make. Voc ca e a e pode encontrar mais informaes digitando: co 21

% info make Nas pginas info de manual, voc ir encontrar informaes sobre como a e a co fazer para manuter um Makele simples, como reduzir o nmero de regras u que voc precisa escrever, e como automaticamente calcular dependncias. e e Voc pode tambm encontrar mais informao no livro GNU Autoconf, Aue e ca tomake, and Libtoolescrito por Gary V.Vaughan, Ben Elliston,Tom Tromey, e Ian Lance Taylor (New Riders Publishing, 2000).8

1.4.1

Depurando com GNU Debugger (GDB)

O depurador um programa que voc usa para descobrir porque seu proe e grama no est seguindo o caminho que voc pensa que ele deveria. Voc a a e e 9 far isso muitas vezes. O depurador GNU (GDB) o depurador usado pela a e maioria dos programadores em ambiente Linux. Voc pode usar GDB para e passear atravs de seu cdigo fonte, escolhendo pontos de parada, e examinar e o o valor de variveis locais. a

1.4.2

Compilando com Informaes de Depurao co ca

Para usar o GDB, voc ir ter que compilar com as informaes de depurao e a co ca habilitadas. Faa isso adicionado o comutador -g na linha de comando de c compilao. Se voc estiver usando um Makele como descrito anteriormente, ca e voc pode apenas escolher CFLAGS para -g quando voc executar o make, e e como mostrado aqui: % make CFLAGS=-g g++ -c -o reciprocal.o reciprocal.cpp cc -g -O2 main.c reciprocal.o -o main Quando voc compila com -g, o compilador inclui informaes extras nos e co arquivos objetos e executveis. O depurador usa essas informaes para a co descobrir quais endereos correspodem a determinada linha de cdigo e em c o qual arquivo fonte, como mostrar os valores armazenados em variveis locais, a e assim por diante.Nota do tradutor: A verso eletrnica do livro pode ser encontrada em http:// a o sources.redhat.com/autobook/download.html. 9 ...a menos que seus programas sempre funcionem da primeira vez.8

22

1.4.3

Executando o GDB

Voc pode iniciar digitando: e % gdb reciprocal Quando o gdb iniciar, voc ver o prompt do GDB: e a (gdb) O primeiro passo executar sseu programa dentro do depurador. Apenas e insira o comando rune quaisquer argumentos do programa que voc est e a depurando. Tente executar o programa sem qualquer argumento, dessa forma 10 : (gdb) run Starting program: reciprocal Program received signal SIGSEGV, Segmentation fault. 0xb7e7e41b in ____strtol_l_internal () from /lib/libc.so.6 O problema que no existe nenhum cdigo de vericao de entradas e a o ca errneas na funo main. O programa espera um argumento, mas nesse o ca caso o programa estava sendo executado sem argumentos. A mensagem de SIGSEGV indicar uma interrupo anormal do programa11 . GDB sabe que ca a interrupo anormal que ocorreu agora aconteceu em uma funo chamada ca ca strtol l internal. Aquela funo est na biblioteca padro. Voc pode ver ca a a e a pilha usando o comando where12 : (gdb) where #0 0xb7e7e41b #1 0xb7e7e180 #2 0xb7e7b401 #3 0x08048486 ) at main.c:9 in in in in ____strtol_l_internal () from /lib/libc.so.6 strtol () from /lib/libc.so.6 atoi () from /lib/libc.so.6 main (argc=Cannot access memory at address 0x0

Voc pode ver a partir dessa tela que a funo main chamou a funo e ca ca atoicom um apontador NULL, que a fonte de todo o problema. e Voc pode subir dois n e veis na pilha at encontrar a funo main atravs do e ca e uso do comando up:Nota do tradutor: a sa foi obtida em um gdb verso 6.8 em 2009 sendo portanto da a uma atualizao da verso dispon em 2000 que foi o ano da publicao original ca a vel ca 11 Em ingls: crash e 12 Nota do tradutor: a sa foi obtida em um gdb verso 6.8 em 2009 sendo portanto da a uma atualizao da verso dispon em 2000 que foi o ano da publicao original ca a vel ca10

23

(gdb) up 2 #2 0xb7e7b401 in atoi () from /lib/libc.so.6 Note que gdb capaz de encontrar o cdigo de main.c, e mostra a linha e o onde a chamada errnea de funo ocorreu. Voc pode ver os valores das o ca e variveis usando o comando print: a (gdb) print argv[1] No symbol "argv" in current context. O que conrma que o problema relamente um apontador NULL passado e dentro da funo atoi. ca Voc pode escolher um ponto de parada atravs do uso do comando break: e e (gdb) break main Breakpoint 1 at 0x8048475: file main.c, line 9. Esse comando dene um ponto de parada na primeira linha de main. Agora tente executar novamente o programa com um argumento, dessa forma:13

(gdb) run 7 The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: reciprocal 7 Breakpoint 1, main (argc=2, argv=0xbfa0d334) at main.c:9 9 i = atoi (argv[1]); Voc pode ver que o depurador alcanou o ponto de parada. Voc pode e c e dar um passo adiante da chamada ` funo atoi usando o comando next: a ca (gdb) next 10 printf ("The reciprocal of \%d is \%g\\n", i, reciprocal (i)); Se voc desejar ver o que est acontecendo dentro de reciprocal, use o e a comando stepcomo segue:Algumas pessoas tm comentado que colocando um ponto de parada em main um e e pouco esquisito porque de maneira geral voc somente desejar fazer isso quando main j e a a estiver quebrada.13

24

(gdb) step reciprocal (i=7) at reciprocal.cpp:6 6 assert (i != 0); Current language: auto; currently c++ Voc est agora no corpo da funo reciprocal. Voc pode perceber que e a ca e mais conveniente o uso do gdb de dentro do Emacs em lugar de usar o gdb e diretamente na linha de comando. Use o comando M-x gdb para iniciar o gdb em uma janela Emacs. Se voc tiver parado em um ponto de parada, e Emacs automaticamente mostra o arquivo fonte apropriado. Dessa forma ca mais fcil descobrir o que est acontecendo quando voc olha no arquivo a a e completo em lugar de apenas em uma linha de texto.

1.5

Encontrando mais Informao ca

Praticamente toda distribuio GNU/Linux vem com uma grande quantica dade de documentao util. Voc pode ter aprendido mais do que estamos ca e falando aqui nesse livro por meio da leitura da documentao em sua disca tribuio Linux (embora isso possa provavelmente levar mais tempo). A ca documentao no est sempre bem organizada, de forma que a parte comca a a plicada encontrar o que precisa. Documentao tambm algumas vezes e ca e e desatualizada, ento tome tudo que voc vier a ler como pouca informao. a e ca Se o sistema no comportar-se no caminho apontado pela pgina de manual a a e como ela diz que deve ser, por exemplo, isso pode estar ocorrendo pelo fato de a pgina de manual estar desatualizada. Para ajudar voc a navegar, a e aqui est as mais uteis fontes de informao sobre programao avanada em a ca ca c GNU/Linux.

1.5.1

Pginas de Manual a

Distribuies GNU/Linux incluem pginas de manual para os comandos mais co a padronizados, chamadas de sistema, e funes da biblioteca padro. As man co a pagesso divididas em sees numeradas; para programadores, as mais ima co portantes so as seguintes: a (1) Comandos de usurio a (2) Chamadas de sistema (3) Funes da biblioteca padro co a (8) Comandos de Sistema/administrativos 25

Os nmeros denotam sees das pginas de manual. As pginas de mau co a a nual do GNU/Linux vm instaladas no seu sistema; use o comando manpara e acess-las. Para ver uma pgina de manual, simplesmente chame-a escrea a vendo man nome, onde nome um comando ou um nome de funo. Em e ca alguns poucos casos, o mesmo nome aparece em mais de uma seo; voc ca e pode especicar a seo explicitamente colocando o nmero da seo antes ca u ca do nome. Por exemplo, se voc digitar o seguinte, voc ir receber de volta a e e a pgina de manual para o comando sleep(na seo 1 da pagina de manual a ca do GNU/Linux): % man sleep Para ver a pgina de manual da funo de biblioteca sleep, use o coa ca mando adiante: % man 3 sleep Cada pgina de manual inclui um sumrio on-line do comando ou da a a funo. O comando whatis nomemostra todas as pginas de manual (em ca a todas as sees) para um comando ou funo que coincidir com nome. Se co ca voc no tiver certeza acerca de qual comando ou funo voc deseja, voc e a ca e e pode executar uma pesquisa por palavra chave sobre as linhas de sumrio, a usando man -k palavrachave. Pginas de manual incluem uma grande quantidade de informaes muito a co uteis e deve ser o primeiro lugar onde voc vai para obter ajuda. A pgina e a de manual para um comando descreve as opes de linha de comando e arguco mentos, entrada e sa da, cdigos de erro, congurao, e coisas semelhantes. o ca A pgina de manual para um chamada de sistema ou para uma funo de a ca biblioteca descreve os parmetros e valores de retorno, listas de cdigos de a o efeitos colaterais, e especica quais arquivos devem ser inclu dos 14 usar se voc desejar chamar essa funo. e ca

1.5.2

Info

A documentao de sistema do tipo Infopossuem documentao mais detaca ca lhada para muitos dos principais componentes do sistema GNU/Linux, alm e de muitos outros programas. Pginas Infoso documentos no formato de a a hipertexto, semelhantes a pginas Web. Para ativar o navegador de pginas a a Info no formato texto, apenas digite infoem uma janela de shell. Voc ir e a ser presenteado com um menu de documentos Info instalado em seu sistema.14

Nota do tradutor: usado na diretiva #include

26

(Pressione Ctrl+H para mostrar teclas de navegao em um documento Info.) ca O conjunto de documentos Info que so mais uteis em nosso contexto so esa a ses 15 : Emacs: (emacs). The extensible self-documenting text editor. gcc: (gcc). The GNU Compiler Collection. Gdb: (gdb). The GNU debugger. Libc: (libc). C library. Info: (info). Documentation browsing system. A maioria de todas as ferramentas padronizadas de programao em amca biente GNU/Linux (incluindo o ld, o linkador; como, o assemblador; e gprof, o proler) so acompanhados com pginas Info bastante uteis. Voc a a e pode ir diretamente a uma documento Info em particular especicando o nome da pgina Info na linha de comando: a % info libc Se voc zer a maioria de sua programao no Emacs, voc pode acessar e ca e o navegador interno de pginas Info digitando M-x infoou C-h i. a

1.5.3

Arquivos de Cabealho c

Voc pode aprender muito sobre funes de sistema que esto dispon e co a veis e como us-las olhando nos arquivos de cabealho do sistema. Esses arquivos a c localizam-se em /usr/include e em /usr/include/sys. Se voc estiver recee bendo erros de compilao ao utilizar uma chamada de sistema, por exemplo, ca d uma olhada no arquivo de cabealho correspondente para vericar se a e c assinatura da funo a mesma que a que est listada na pgina de manual. ca e a a Em sistemas GNU/Linux, muitos dos detalhes b

Nota do tradutor:colocados na ordem e na forma em que aparecem e com a redao ca que aparece no GNU texinfo verso 4.13 em janeiro de 2009. a

15

27

28

Cap tulo 2 Escrevendo Bom Software GNU/Linux ESSE CAP ITULO ABRANGE ALGUMAS TECNICAS BASICAS QUE GRANDE PARTE dos programadores GNU/Linux utilizam. Atravs das e orientaes apresentadas adiante, voc estar apto a escrever programas que co e a trabalhem bem dentro do ambiente GNU/Linux e atenda `s expectativas a dos usurios GNU/Linux no que corresponde a como os programas devem a trabalhar.

2.1

Interao Com o Ambiente de Execuo ca ca

Quando voc estudou inicialmente C ou C++, aprendeu que a funo especial e ca main o ponto de entrada principal para um programa. Quando o sistema e operacional executa seu programa, o referido sistema operacional fornece automaticamente certas facilidades que ajudam ao programa comunicar-se com o prprio sistema operacional e com o usurio. Voc provavelmente o a e aprendeu sobre os dois primeiros parmetros para a funo principal main, a ca comumente chamados argce argv, os quais recebem entradas para o seu programa. Voc aprendeu sobre stdoute stdin(ou sobre os uxos coute e cinna linguagem C++) que fornecem entrada e sa no console. Esses da recursos so fornecidos atravs das linguagens C e C++, e eles interagem a e com o sistema GNU/Linux de certas maneiras. GNU/Linux fornece outras formas de interagir com o sistema operacional alm das especicadas nesse e pargrafo. a 29

2.1.1

A Lista de Argumentos

Voc executa um programa a partir de um prompt de shell atravs da e e digitao do nome do programa. Opcionalmente, voc pode fornecer inca e formaes adicionais para o programa atravs da digitao de uma ou mais co e ca palavras aps o nome do programa, separadas por espaos. Essas palao c vras adiconais so chamadas argumentos de linha de comando. (Voc pode a e tambm incluir um argumento que contm espaos, empacotando os argue e c mentos entre apstrofos.) De forma mais geral, isso referente a como a lista o e de argumentos do programa passada pelo fato de essa lista no precisar ser e a originria de linha de comando de shell. No Cap a tulo 3, Processosvoc ir e a ver outro caminho para chamar um programa, no qual um programa pode especicar a lista de argumentos de outro programa diretamente. Quando um programa chamado a partir do shell, a lista de argumentos contm a e e linha de comando completa, incluindo o nome do programa e quaisquer argumentos de linha de comando que possa ter sido fornecido. Suponhamos, por exemplo, que voc chame o comando lsem seu shell para mostrar o e contedo do diretrio ra e os correspondentes tamanhos dos arquivos com u o z essa linha de comando: % ls -s / A lista de argumentos que o programa lsacima possui consta de trs are gumentos. O primeiro deles o nome do programa propriamente dito, como e especicado na linha de comando, lsa saber. O segundo e o terceiro elementos da lista de argumentos so os dois argumentos de linha de comando, a o -se a /. A funo mainde seu programa pode acessar a lista de argumentos por ca meio dos parmetros da funo mainargce argv(se voc por acaso no a ca e a utiliza esses dois argumentos, voc pode simplesmente omit e -los). O primeiro parmetro, argc, um inteiro que representa o nmero de argumentos na lista a e u de argumentos. O segundo parmentro, argv, um vetor de apontadores de a e caracteres. O tamanho do vetor argc, e os elementos do vetor apontam para e os elementos da lista de argumentos, com cada elemento da lista terminado com o caractere nulo /0.1 A utilizao de argumentos de linha de comando to fcil quanto examinar ca e a a os contedos de argc e argv. Se voc no estiver interessado no nome do u e a programa propriamente dito, lembre-se de ignorar o primeiro elemento. Logo abaixo temos a listagem 2.1 demonstra como usar argc e argv.1

Nota do tradutor: ver [?] p. 113.

30

Listagem 2.1: (Arquivo arglist.c) Usando argc e argv.#include i n t main ( i n t a r g c , char a r g v [ ] ) { p r i n t f ( The name o f t h i s program i s % s . \ n , a r g v [ 0 ] ) ; p r i n t f ( T h i s program was i n v o k e d w i t h %d a r g u m e n t s . \ n , a r g c 1 ) ; / Were any commandl i n e a r g u m e n t s i f ( argc > 1) { / Yes , p r i n t them . / int i ; p r i n t f ( The a r g u m e n t s a r e : \ n ) ; f o r ( i = 1 ; i < a r g c ; ++i ) p r i n t f ( %s \n , a r g v [ i ] ) ; } return 0 ; } specified? /

2.1.2

Convenes GNU/Linux de Linha de Comando co

Quase todos os programas GNU/Linux obedecem algumas convenes sobre co como argumentos de linha de comando so interpretados. O argumentos que a so passados a programas so de duas categorias: opes (ou sinalizadores) a a co e outros argumentos. As opes modicam como o programa se comporta, co enquanto outros argumentos fornecem entradas (por exemplo, os nomes de arquivos de entrada). Opes chegam de duas formas: co Opes curtas consistindo de um hifen simples e um caractere simples co (comunmente em caixa baixa ou caixa alta). Opes curtas so rpidas co a a de digitar. Opes longas consistindo de dois h co fens, seguidos por um nome composto em caixa baixa e caixa baixa e h fens. Opes longas so fceis co a a de lembrar e fceis de ler (em scripts shell, por exemplo). a Habitualmente, um programa fornece ambas as formas curta e longa para a maioria das opes que so suportadas pelo referido programa, a forma curta co a por uma questo de brevidades e a forma longa por uma questo de clareza. a a Por exemplo, a maioria dos programas entendem as opes -h e --help, e as co tratam de forma identica. Normalmente, quando um programa chamado e em um shell sem nenhum argumento, quaisquer opes desejadas seguem o co nome do programa imediatamente. Algumas opes esperam que um arguco mento as siga imediatamente. Muitos programas, por exemplo, interpretam a opo --output qualquercoisa como especicando que a sa de um proca da grama deva ser colocada em um arquivo chamado qualquercoisa. Aps as o opes, podem existir adiante delas outros argumentos de linha de comando, co tipicamente arquivos de entrada ou dados de entrada. Por exemplo, o comando ls -s / mostra o contedo do diretrio ra A Opo -s modica u o z. ca 31

o comportamento padro do ls instruindo o ls a mostrar o tamanho (em kia lobytes) de cada entrada.O argumento / diz ao ls qual diretrio listar. A o opo --size sinnimo da opo -s, de forma que o mesmo comando ca e o ca pode poderia ter sido digitado como ls --size /. A codicao GNU padro lista os nomes de alguns opes de linha de ca a co comando comumente usadas. Se voc planeja fornecer qualquer quaisquer e opes similares a alguma dessas, uma boa idia usar os nomes especicaco e e dos na codicao padro. Seu programa ir se comportar mais como outros ca a a programas e ir ser mais fcil para os usurios aprenderem. Voc pode visua a a e alizar o guia de Condicao GNU Padro para opes de linha de comando ca a co digitando o seguinte em um propt de comandos de um shell na maioria dos sistemas GNU/Linux systems2 : % info "(standards)User Interfaces"

2.1.3

Usando getopt long

A passagem de opes de linha de comando uma tarefa tediosa. Felizco e mente, a biblioteca C GNU fornece um funo que voc usar em prograca e mas em C e em programas em C++ para fazer esse trabalho de alguma forma um pouco mais fcil (embora ainda assim um pouco incmoda). Essa a o funo, getopt long, recebe ambas as formas curta e longa de passagem ca de parmetros. Se voc for usar essa funo, inclua o arquivo de cabealho a e ca c . Suponha, por exemplo, que voc est escrevendo um programa que para e a e aceitar as trs opes mostradas na tabela 2.1. e co Tabela 2.1: Opes do Programa Exemplo co Forma Curta Forma Longa Propsito o -h help Mostra sumrio de uso e sai a -o nomearquivo output nomearquivo Especica o nome do arquivo de sa da -v verbose Mostra mensagens detalhadas

Adicionalmente, o programa deve aceitar zero ou mais argumentos de linha de comando, que so os nomes de arquivos de entrada. aNota do tradutor: o guia de Condicao GNU Padro tambm pode ser acesca a e sado via http://www.gnu.org/prep/standards/html node/User-Interfaces.html# User-Interfaces2

32

Para usar a funo getopt long, voc deve fornecer duas estruturas de dados. ca e A primeira uma seqncia de caracteres contendo as opes vlidas em sua e ue co a forma curta, cada letra simples. Uma opo que necessite de um argumento ca seguida de dois pontos. Para o seu programa, a seqncia de caracteres e ue ho:vindica que as opes vlidas so -h, -o, e -v, com a segunda dessas trs co a a e opes devendo ser seguida por um argumento. co Para especicar as opes longas dispon co veis, voc constri um vetor de elee o mentos de estruturas de opes. Cada elemento corespondendo a uma opo co ca longa e tendo quatro campos. Em circunstncias normais, o primeiro campo a o nome da opo longa (na forma de uma seqncia de caracteres, sem e ca ue os dois h fens); o segundo campo 1 se a opo precisa de argumento, ou e ca 0 em caso contrrio; o terceiro campo NULL; e o quarto um caractere a e e constante especicando a forma curta que sinnimo da referida opo de e o ca forma longa. O ultimo elemento do vetor deve ter todos os campos zerados como adiante. Voc pode construir o vetor como segue: e const struct option long_options[] = { { "help", 0, NULL, h }, { "output", 1, NULL, o }, { "verbose", 0, NULL, v }, { NULL,0, NULL, 0} }; Voc chama a funo getopt long, passando a ela os argumentos argc e e ca argv que so passados ` funo main, a seqncia de caracteres descrevendo a a ca ue as opes curtas, e o vetor de elementos de estruturas de opes descrevendo co co as opes longas. co Cada vez que voc chamar getopt long, a funo getopt long passa uma e ca opo simples, retornando a letra da forma curta para aquela opo ou ca ca -1 se nenhuma opo for encontrada. ca Tipicamente, voc ir chamar getopt long dentro de um lao, para proe a c cessar todas as opes que o usurio tiver especicado, e voc ir maco a e a nusear as opes espec co cas usando o comando switch. Se a funo getopt long encontra uma opo invlida (uma opo que ca ca a ca voc no especicou como uma opo curta vlida ou como uma opo e a ca a ca longa vlida), a funo getopt long imprime uma mensagem de erro e a ca retorna o caractere ? (um ponto de interrogao). A grande maioria ca dos programas ir encerrar a execuo em resposta a isso, possivelmente a ca aps mostrar informaes de utilizao. o co ca 33

Quando se estiver manuseando uma opo que precisa de um arguca mento, a varvel global optarg aponta para o texto daquele argumento. a Aps getopt long terminar de manusear todas as opes, a varivel o co a global optind conter o a ndice (dentro de argv) do primeiro argumento no classicado como vlido. a a A listagem 2.2 mostra um exemplo de como voc pode usar getopt long e para processar seus argumentos. Listagem 2.2: (getopt long.c) Usando a funo getopt long ca#include #include #include < s t d l i b . h> / The name o f t h i s p r o g r a m . const char program name ; /

/ P r i n t s u s a g e i n f o r m a t i o n f o r t h i s p r o g r a m t o STREAM ( t y p i c a l l y Does n o t s t d o u t o r s t d e r r ) , and e x i t t h e p r o g r a m w i t h EXIT CODE . return . / void p r i n t u s a g e { f p r i n t f ( stream f p r i n t f ( stream h o v exit ( exit code } ( FILE stream , int exit code )

, Usage : %s o p t i o n s [ i n p u t f i l e . . . ] \ n , program name ) ; , h e l p D i s p l a y t h i s u s a g e i n f o r m a t i o n . \ n o u t p u t f i l e n a m e Write o u t p u t t o f i l e . \ n v e r b o s e P r i n t v e r b o s e m e s s a g e s . \ n ) ; );

/ Main p r o g r a m e n t r y p o i n t . ARGC c o n a i n s number o f a r g u m e n t e l e m e n t s ; ARGV i s an a r r a y o f p o i n t e r s t o them . / i n t main ( i n t a r g c , char a r g v [ ] ) { int next option ; / A s t r i n g l i s t i n g v a l i d s h o r t o p t i o n s l e t t e r s . / const char const s h o r t o p t i o n s = ho : v ; / An a r r a y d e s c r i b i n g v a l i d l o n g o p t i o n s . / const s t r u c t o p t i o n l o n g o p t i o n s [ ] = { { help , 0 , NULL, h } , { output , 1 , NULL, o } , { verbose , 0 , NULL, v } , { NULL, 0 , NULL, 0 } / R e q u i r e d a t end o f }; / The name o f t h e f i l e t o r e c e i v e p r o g r a m o u t p u t , standard output . / const char o u t p u t f i l e n a m e = NULL ; / Whether t o d i s p l a y v e r b o s e m e s s a g e s . / int verbose = 0 ; / Remember t h e name o f t h e program , t o The name i s s t o r e d i n a r g v [ 0 ] . / program name = a r g v [ 0 ] ; do { next option = getopt long incorporate

list

array .

/

o r NULL f o r

in

messages .

( a r g c , argv , s h o r t o p t i o n s , l o n g o p t i o n s , NULL) ; switch ( n e x t o p t i o n ) { case h : / h o r h e l p / / U s e r h a s r e q u e s t e d u s a g e i n f o r m a t i o n . Print i t to standard o u t p u t , and e x i t w i t h e x i t c o d e z e r o ( n o r m a l t e r m i n a t i o n ) . / p r i n t u s a g e ( stdout , 0) ; case o : / o o r o u t p u t / / T h i s o p t i o n t a k e s an a r g u m e n t , output filename = optarg ; break ;

t h e name o f

the

output

file .

/

34

case v : / v o r v e r b o s e / verbose = 1; break ; case ? : / The u s e r s p e c i f i e d an i n v a l i d o p t i o n . / / P r i n t u s a g e i n f o r m a t i o n t o s t a n d a r d e r r o r , and e x i t w i t h c o d e one ( i n d i c a t i n g a b o n o r m a l t e r m i n a t i o n ) . / p r i n t u s a g e ( stderr , 1) ; case 1: break ; / Done w i t h options . /

exit

default : / S o m e t h i n g abort () ; } } while ( n e x t o p t i o n != 1) ;

else :

unexpected .

/

/ Done w i t h o p t i o n s . OPTIND p o i n t s t o f i r s t nono p t i o n For d e m o n s t r a t i o n p u r p o s e s , p r i n t them i f t h e v e r b o s e specified . / i f ( verbose ) { int i ; f o r ( i = o p t i n d ; i < a r g c ; ++i ) p r i n t f ( Argument : %s \n , a r g v [ i ] ) ; } / The main p r o g r a m g o e s return 0 ; } here . /

argument . o p t i o n was

O uso de getopt long pode ser visto como muito trabalho, mas escrevendo cdigo para passar as opes de linha de comando propriamente ditas pode o co ser mais trabalhoso ainda. A funo getopt long muito sosticada e permite ca e grande exibilidade na especicao de qual tipo de opo aceitar. Todavia, ca ca uma boa idia adiar a adoo de recursos mais avanados e segurar-se um e e ca c pouco na estrutura bsica de opo descrita acima. a ca

2.1.4

E/S Padro a

A biblioteca C padro fornece uxos de entrada e sa padro (stdin e sta da a dout, respectivamente). Tanto a entrada como a sa padro so usadas por da a a scanf, printf, e outras funes de biblioteca. Na tradio UNIX, o uso da co ca entrada e da sa padro comum e habitual para programas GNU/Linux. da a e Isso permite encadeamento de multiplos programas usando pipes de shell e redirecionamentos de entrada e sa da. (Veja a pgina de manual para o seu a shell preferido para aprender a sintaxe nesses caso de pipes e redirecionamentos.) A biblioteca C tambm fornece stderr, o uxo padro de erro. Programas e a podem mostrar mensagens de erro para a sa padro de erro em lugar da a de enviar para a sa padro. Esse tipo de comportamento permite aos da a usurios separarem a sa normal e mensagens de erro, por exemplo, atravs a da e do redirecionamento da sa padro para um arquivo enquanto permite a da a impresso da sa de erro para o console. A funo fprintf pode ser usada a da ca para imprimir para a sa padro de erro stderr, por exemplo: da a fprintf (stderr, (Error: ...")); 35

Esses trs uxos3 so tambm access e a e veis com os comandos bsicos UNIX a de E/S (read, write, e assim por diante) por meio dos trs descritores de are quivo usados em shell. Os descritores so 0 para stdin, 1 para stdout, e 2 a para stderr. Quando um programa for chamado, pode ser algumas vezes util redirecio nar ambas a sa padro e a sa de erro para um arquivo ou pipe. A da a da sintaxe para fazer isso varia nos diversos shells; para shells do estilo Bourne (incluindo o bash, o shell padro na maioria das distribuies GNU/Linux), a co dois exemplos so mostrados logo abaixo: a

% programa > arquivo_saida.txt 2>&1 % programa 2>&1 | filtro A sintaxe 2>&1 indica que o descritor 2 de arquivo (stderr ) deve ser entregue no descritor de arquivo 1 (stdout). Note que 2>&1 deve vir aps o um redirecionamento de arquivo (a primeira linha exemplo logo acima) mas deve vir antes de um redirecionamento por meio de pipe (a segunda linha exemplo logo acima). Note que stdout armazenada em uma rea temporria. Dados escritos e a a para stdout no so enviados para o console (ou para outro dispositivo caso a a haja redirecionamento) imediatamente. Dados escritos para stdout so ena viados para o console em trs situaes: quando a rea de armazenamento e co a temporrio esteja preenchida compleamente, quando o programa terminar a normalmente ou quando stdout for fechada. Voc pode explicitamente dese carregar a rea de armazenamento temporria atravs da seguinte chamada: a a e fflush (stdout); Por outro lado, stderr no armazenada em um local temporrio; dados a e a escritos para stderr vo diretamente para o console. 4 a Isso pode produzir alguns resultados surpreendentes. Por exemplo, esse lao c no mosta um ponto a cada segundo; em vez disso, os pontos so armazenados a a em uma rea temporria, e um grupo de pontos mostrado todos de uma a a e unica vez quando o limite de armazenamento da rea temporria alcanado. a a e cNota do tradutor:stdin, stdout e stderr. Em C++, a mesma distino se mantm para cout e para cerr, respectivamente. Note ca e que a marca endl descarrega um uxo adicionalmente ` impresso um caractere de nova a a linha; se voc no quiser descarregar um uxo (por razes de performace, por exemplo), e a o use em substituio a endl uma constante de nova linha, \n. ca4 3

36

while ( 1 ) { printf ( . ); sleep (1); } No lao adiante, todavia, o ponto aparece uma vez a cada segundo: c while ( 1 ) { f p r i n t f ( stderr , . ) ; sleep (1); }

2.1.5

Cdigos de Sa de Programa o da

Quando um programa termina, ele indica sua situao de sa com um ca da cdigo de sa o da. O cdigo de sa um inteiro pequeno; por conveno, um o da e ca cdigo de sa zero denota execuo feita com sucesso, enquanto um cdigo o da ca o de sa diferente de zero indica que um erro ocorreu. Alguns programas da usam diferentes valores de cdigos diferentes de zero para distingir erros o u espec cos. Com a maioria dos shells, poss e vel obter o cdigo de sa o da do programa executado mais recentemente usando a varivel especial $?. a Aqui est um exemplo no qual o comando ls chamado duas vezes e seu a e cdigo de sa mostrado a cada chamada. No primeiro caso, ls executa o da e corretametne e retorna o cdigo de sa zero. No segundo caso, ls encontrou o da um erro (porque o o nome de arquivo especicado na linha de comando no a existe) e dessa forma retorna um cdigo de sa diferente de zero: o da % ls / bincoda etc libmisc nfs proc sbinusr boot dev home lost+found mnt opt root tmp var % echo $? 0 % ls bogusfile ls: bogusfile: No such file or directory % echo $? 1 Um programa em C ou em C++ especica seu cdigo de sa atravs o da e do retorno do cdigo de sa devolvido pela funo main. Existem ouo da ca tros mtodos de fornecer cdigos de sa e o da, e cdigos de sa especial so o da a atribu dos a programas que terminam de forma diferente da esperada (por meio de um sinal). Isso ser discutido adicionalmente no Cap a tulo 3. 37

2.1.6

O Ambiente

GNU/Linux fornece a cada programa sendo executado um ambiente. O ambiente uma coleo de pares varivel/valor. Ambos nome de variveis e ca a a de ambiente e seus valores respectivos so sequncias de caracteres. Por a e conveno, nomes de variveis de ambiente so grafados com todas as letras ca a a em maiscula. u Voc provavelmente j est familiarizado com muitas variveis de ambiente e a a a mais comuns. Por exemplo: USER contm seu nome de usurio. e a HOME contm o caminho para seu diretrio de usurio. e o a PATH contm uma lista de itens separada por ponto e v e rgula dos diretrios os quais GNU/Linux busca pelo comando que voc chamar. o e DISPLAY contm o nome e o nmero do display do servidor sobre o e u qual janelas de programas grcos do X iro aparecer. a a Seu shell, como qualquer outro programa, tem um ambiente. Shells fornecem mtodos para examinar e modicar o ambiente diretamente. Para e mostrar o ambiente atual em seu shell, chame o programa printenv. Vrios a shells possuem diferentes sintaxes internas para a utilizao de variveis de ca a ambiente; o que mostrado adiante a sintaxe no estilo dos shells do tipo e e Bourne. O shell automaticamente cria uma varivel shell para cada varivel de a a ambiente que encontrar, de forma que voc acessar valores de variveis e a de ambiente usando a sintaxe $nomedevariavel. Por exemplo: % echo $USER samuel % echo $HOME /home/samuel Voc pode usar o comando export para exportar uma varivel shell dene a tro do ambiente. Por exemplo, para modicar a varivel de ambiente a EDITOR, voc pode usar o seguinte: e % EDITOR=emacs % export EDITOR 38

Ou, de forma curta e rpida: a % export EDITOR=emacs Em um programa, voc acessa uma varivel de ambiente com a funo e a ca getenv na . A funo getenv pega um nome de varivel e reca a torna o valor correspondente como uma seqncia de caracteres, ou NULL ue se a referida varivel no tiver sido denida no ambiente. Para modicar ou a a limpar variveis de ambiente, use as funes setenv e unsetenv, respectivaa co mente. Listar todas as variveis de um ambiente um pouco complicado. a e Para fazer isso, voc deve acessar uma varivel global especial chamada ene a viron, que denida na biblioteca C GNU. Essa varivel, do tipo char**, e a um vetor de apontadores terminado com o caractere NULL que apontam e para seqncias de caracteres. Cada seqncia de caracteres contendo uma ue ue varivel de ambiente, na forma VARIAVEL=valor. O programa na listagem a 2.3, por exemplo, simplesmente mostra na tela todas as variveis de ambiente a atravs de um lao ao longo do vetor de apontadores environ. e c Listagem 2.3: (print-env.c) Mostrando o Ambiente de Execuo ca#include / The ENVIRON v a r i a b l e extern char e n v i r o n ; contains the environment . /

i n t main ( ) { char v a r ; f o r ( v a r = e n v i r o n ; v a r != NULL ; ++v a r ) p r i n t f ( %s \n , v a r ) ; return 0 ; }

No modique o ambiente propriamente dito; use as funes setenv e a co unsetenv para fazer as modicaes que voc precisar. Comumente, quando co e um novo programa iniciado, ele herda uma cpia do ambiente do programa e o que o chamou (o programa de shell, se o referido programa tiver sido chamado de forma interativa). Dessa forma, por exemplo, programas que voc executa e a partir de um programa de shell pode examinar os valores das variveis de a ambiente que voc escolheu no shell que o chamou. e Variveis de ambiente so comumente usadas para indicar informaes de a a co congurao a programas. Suponha, por exemplo, que voc est escrevendo ca e a um programa que se conecta a um servidor de internet para obter alguma informao. voc pode ter escrito o programa de forma que o nome do ca e servidor especicado na linha de comando. Todavia, suponha que o nome e do servidor no seja alguma coisa que os usurios iro modicar muitas vezes. a a a Voc pode usar uma varivel especial de ambiente digamos SERVER NAME e a para especicar o nome do servidor; se SERVER NAME no existir, um a 39

valor padro usado. Parte do seu programa pode parecer como mostrado a e na listagem 2.4. Listagem 2.4: (client.c) Parte de um Programa Cliente de Rede#include #include < s t d l i b . h> i n t main ( ) { char s e r v e r n a m e = g e t e n v ( SERVER NAME ) ; i f ( s e r v e r n a m e == NULL) / The SERVER NAME e n v i r o n m e n t v a r i a b l e was n o t default . / s e r v e r n a m e = s e r v e r . my company . com ; p r i n t f ( a c c e s s i n g s e r v e r %s \n , s e r v e r n a m e ) ; / A c c e s s t h e s e r v e r h e r e . . . / return 0 ; }

set .

Use t h e

Suponhamos que o programa acima seja chamado de client. Assumindo que voc no tenha criado ou que no tenha sido criada anteriormente a e a a varivel SERVER NAME, o valor padro para o nome do servidor usado: a a e % client accessing server server.my-company.com Mas fcil especicar um servidor diferente: e a % export SERVER_NAME=backup-server.emalgumlugar.net % client accessing server backup-server.emalgumlugar.net

2.1.7

Usando Arquivos Temporrios a

Algumas vezes um programa necessita criar um arquivo temporrio, para a armazenar grandes dados por alguns instantes ou para entreg-los a outro a programa. Em sistemas GNU/Linux, arquivos temporrios so armazenados a a no diretrio /tmp. Quando zer uso de arquivos temporrios, voc deve estar o a e informado das seguintes armadilhas: Mais de uma instncia de seu programa pode estar sendo executada a simultneamente (pelo mesmo usurio ou por diferentes usurios). As a a a instncias devem usar diferentes nomes de arquivos temporrios de a a forma que eles no colidam. a As permisses dos arquivos temporrios devem ser ajustadas de tal o a forma que somente usurios autorizados possam alterar a execuo a ca do programa atravs de modicao ou substituio do arquivo teme ca ca porrio. a 40

Nomes de arquivos temporrios devem ser gerados de forma imprea vis externamente; de outra forma, um atacante pode usar a espera vel entre a vericao de que um nome de arquivo fornecido j est sendo ca a a usado e abrir um novo arquivo temporrio. a GNU/Linux fornece funes, mkstemp e tmple, que cuidam desses reco cursos para voc de forma adequada (e adicionalmente muitas funes que e co no cuidam). Qual voc ir usar depende de seu planejamento de manusear a e a o arquivo temporrio para outro programa, e de se voc deseja usar E/S a e UNIX (open, write, e assim por diante) ou as funes de controle de uxos co da biblioteca C (fopen, fprintf, e assim por diante). Usando mkstemp A funo mkstemp criar um nome de arquivo temca a porrio de forma unica a partir de um modelo de nome de arquivo, cria o a arquivo propriamente dito com permisses de forma que somente o usurio o a atual possa acess-lo, e abre o arquivo para leitura e escrita. O modelo de a nome de arquivo uma seqncia de caracteres terminando com XXXXXX e ue (seis letras X maisculas); mkstemp substitui as letras X por outros caracu teres de forma que o nome de arquivo seja unico. O valor de retorno e um descritor de arquivo; use a fam de funes aparentadas com a funo lia co ca write para escrever no arquivo temporrio. Arquivos temporrios criados a a com mkstemp no so apagados automaticamente. Compete a voc remoa a e ver o arquivo temporrio quando o referido arquivo temporrio no mais a a a for necessrio. (Programadores devem ser muito cuidadosos com a limpeza a de arquivos temporrios; de outra forma, o sistema de arquivos /tmp ir a a encher eventualmente, fazendo com que o sistema que inoperante.) Se o arquivo temporrio for de uso interno somente e no for manuseado por outro a a programa, uma boa idia chamar unlink sobre o arquivo temporrio imee e a diatamente. A funo unlink remove a entrada do diretrio correspondente ca o a um arquivo, mas pelo fato de arquivos em um sistema de arquivo serem contados-referenciados, o arquivos em si mesmos no so removidos at que a a e no hajam descritores de arquivo abertos para aquele arquivo. Dessa forma, a seu programa pode continuar usando o arquivo temporrio, e o arquivo evoa lui automaticamenete at que voc feche o descritor do arquivo. Pelo fato de e e GNU/Linux fechar os descritores de arquivo quando um programa termina, o arquivo temporrio ir ser removido mesmo se seu programa terminar de a a forma abrupta. O par de funes na listagem 2.5 demonstra mkstemp. Usadas juntas, esco sas duas funes tornam fcil escrever o contedo de uma rea temporria co a u a a de armazenamento na memria para um arquivo temporrio (de forma que o a 41

a memoria possa ser liberada ou reutilizada) e de forma que esse contedo u armazenado possa ser trazido de volta ` memria mais tarde. a o Listagem 2.5: (temp le.c) Usando mkstemp#include < s t d l i b . h> #include / A h a n d l e f o r a t e m p o r a r y f i l e c r e a t e d t h i s implementation , i t s j u s t a f i l e typedef i n t t e m p f i l e h a n d l e ; with w r i t e t e m p f i l e . descriptor . / In

/ W r i t e s LENGTH b y t e s f r o m BUFFER i n t o a t e m p o r a r y f i l e . The temporary f i l e i s immediately u n l i n k e d . Returns a handle to temporary f i l e . /

the

t e m p f i l e h a n d l e w r i t e t e m p f i l e ( char b u f f e r , s i z e t l e n g t h ) { / C r e a t e t h e f i l e n a m e and f i l e . The XXXXXX w i l l b e r e p l a c e d w i t h c h a r a c t e r s t h a t make t h e f i l e n a m e u n i q u e . / char t e m p f i l e n a m e [ ] = /tmp/ t e m p f i l e .XXXXXX ; i n t f d = mkstemp ( t e m p f i l e n a m e ) ; / U n l i n k t h e f i l e i m m e d i a t e l y , s o t h a t i t w i l l b e r e m o v e d when t h e f i l e descriptor is closed . / unlink ( temp filename ) ; / W r i t e t h e number o f b y t e s t o t h e f i l e f i r s t . / w r i t e ( f d , &l e n g t h , s i z e o f ( l e n g t h ) ) ; / Now w r i t e t h e d a t a i t s e l f . / w r i t e ( fd , b u f f e r , l e n g t h ) ; / Use t h e f i l e d e s c r i p t o r a s t h e h a n d l e f o r t h e t e m p o r a r y f i l e . / return f d ; } / Reads t h e c o n t e n t s o f a t e m p o r a r y f i l e TEMP FILE c r e a t e d w i t h write temp file . The r e t u r n v a l u e i s a n e w l y a l l o c a t e d b u f f e r t h o s e c o n t e n t s , w h i c h t h e c a l l e r must d e a l l o c a t e w i t h f r e e . LENGTH i s s e t t o t h e s i z e o f t h e c o n t e n t s , i n b y t e s . The temporary f i l e i s removed . /

of

char r e a d t e m p f i l e ( t e m p f i l e h a n d l e t e m p f i l e , s i z e t l e n g t h ) { char b u f f e r ; / The TEMP FILE h a n d l e i s a f i l e d e s c r i p t o r t o t h e t e m p o r a r y f i l e . / int fd = t e m p f i l e ; / Rewind t o t h e b e g i n n i n g o f t h e f i l e . / l s e e k ( f d , 0 , SEEK SET ) ; / Read t h e s i z e o f t h e d a t a i n t h e t e m p o r a r y f i l e . / read ( fd , leng th , s i z e o f ( l e n g t h ) ) ; / A l l o c a t e a b u f f e r and r e a d t h e d a t a . / b u f f e r = ( char ) m a l l o c ( l e n g t h ) ; read ( fd , b u f f e r , l e n g t h ) ; / C l o s e t h e f i l e d e s c r i p t o r , w h i c h w i l l c a u s e t h e t e m p o r a r y f i l e t o g o away . / c l o s e ( fd ) ; return b u f f e r ; }

Usando tmple Se voc est usando as funes de E/S da biblioteca C e a co e no precisa passar o arquivo temporrio para outro programa, voc pode a a e usar a funo tmple. Essa funo cria e abre um arquivo temporrio, e ca ca a retorna um apontador de arquivo para esse mesmo arquivo temporrio. O a arquivo temporrio j unlinked, como no exemplo anterior, de forma que a ae ser apagado automaticamente quando quando o apontador de arquivo for a fechado (com fclose) ou quando o programa terminar. GNU/Linux fornece muitas outras funes para a gerao de arquivos temco ca porarios e nomes de arquivos temporrios, incluindo mktemp, tmpnam, e a a tempnam. No use essas funes, apesar disso, pelo fato de elas possu a co rem problemas de conabilidade e segurana j mencionados anteriormente. c a 42

2.2

Fazendo Cdigo Defensivamente o

Escrevendo programas que executam atualmente sob uso normal trabae lhoso; escrever programas que comportam-se de forma elegante em situaes co de falha mais trabalhoso ainda. Essa seo demonstra algumas tcnicas de e ca e codicao para encontrar erros facilmente e para detectar e recuperar-se de ca problemas durante a execuo de um programa. ca As amostras de cdigo apresentadas mais adiante nesse livro omitem erros o extensivos de vericao e recuperao de cdigo pelo fato de isso eventualca ca o mente vir a obscurecer a funcionalidade bsica que se deseja apresentar aqu a . Todavia, O exemplo nal no cap tulo ??, Uma Amostra de uma Aplicao ca GNU/Linuxretorna ` demonstrao de como usar essas tcnicas para escrea ca e ver programas robustos.

2.2.1

Usando assert

Um bom objetivo para se ter em mente quando criamos um cdigo fonte o de uma aplicao que erros comuns ou mesmo erros inesperados podem ca e fazer com que o programa falhe de forma dramtica, to facilmente quanto a a poss vel. O uso de assert ir ajudar voc a encontrar erros facilmente no a e desenvolvimento e na fase de teste. Falhas que no se mostram de forma a evidente passam surpreendentemente e muitas vezes desapercebidas e no se a mostram at que a aplicao esteja nas mos do usurio. e ca a a Um dos mais simples mtodos de vericar condies inesperadas a macro e co e assert da biblioteca C padro. O argumento para essa macro uma expresso a e a Booleana. O programa terminado se a expresso Booleana avaliar para e a false, aps mostrar uma mensagem de erro contendo o cdigo fonte e o nmero o o u da linha e o texto da expresso. A macro assert muito util para uma larga a e variedade de vericaes de consistncias internas em um dado programa. co e Por exemplo, use assert para testar a validade de argumentos de funes, co para testar condies prvias e condies pstumas de chamadas a funes co e co o co (e chamadas a mtodos, em C++), e para testar valores de retorno. e Cada utilizao de assert serve no somente como uma vericao em tempo ca a ca de execuo de uma condio, mas tambm como documentao sobre a ca ca e ca operao do programa dentro do cdigo fonte. Se eu programa contiver um ca o assert (condio) que diz a algum para ler seu cdigo fonte pelo fato de a ca e o condio obrigatriamente ter de ser verdadeira naquele ponto do programa, ca o e se a condio no verdadeira, temos a um erro no programa. Para ca a e cdigo de desempenho cr o tico, vericaes tais como a utilizao de assert co ca pode impor uma perda muito grande de desempenho. Nesses casos,voc e pode compilar seu cdigo com a macro NDEBUG denida, atravs do uso o e 43

do sinalizador -DNDEBUG na sua linha de comando de compilao. Com ca NDEBUG denida, aparces da macro assert iro ser preprocessadamente o a descartadas. O preprocessamento dessa forma uma boa idia no sentido e e de permitir fazer o uso de assert somente quando necessrio por razes de a o performace, embora que, somente com arquivos fonte de desempenho cr tico. Pelo fato de ser poss vel o descarte preprocessadamente da macro assert, garanta que qualquer expresso que voc venha a usar com assert no tenha a e a efeitos colaterais. Especicamente, voc no deve chamar funes dentro e a co de expresses assert, no deve atribuir valores a variveis e no deve usar o a a a modicadores de operao tais como ++. ca Suponhamos, por exemplo, que voc chame uma funo, fazer algumacoisa, e ca repetidamente em um lao. A funo fazer algumacoisa retorna zero em c ca caso de sucesso e no zero em caso de falha, mas voc no espera que esse a e a comportamento venha a falhar em seu programa. Voc pode ter tentado e escrever: for (i = 0; i < 100; ++i) assert (fazer_algumacoisa () == 0); Todavia, voc pode encontrar que essa vericao em tempo de execuo e ca ca impe uma grande perda de desempenho e decide mais tarde recompilar com o NDEBUG denida. Isso ir remover a chamada a assert inteiramente, de a forma que a expresso nunca ir ser avaliada e fazer algumacoisa nunca ir a a a ser chamada. Voc pode, ao invs do cdigo anterior escrever o seguinte: e e o for (i = 0; i < 100; ++i) { int status = fazer_algumacoisa (); assert (status == 0); } Outra coisa para se ter em mente que voc no deve usar assert para e e a testar entradas invlidas de usurio. Usurios no gostam quando aplicaes a a a a co simplesmente terminam abruptamente com uma mensagem de erro criptografada, mesmo em resposta a uma entrada invlida. Voc deve sempre a e vericar entradas invlidas e produzir mensagens de erro coerentes e lgicas a o em resposta a uma tal entrada invlida. Use assert somente para vericaes a co internas em tempo de execuo. ca Alguns bons lugares para usar assert so esses: a Vericao contra apontadores nulos, por exemplo, como argumentos ca vlidos a funes. A mensagem de erro gerada por assert (pointer != a co NULL), 44

Assertion pointer != ((void *)0) failed. mais informativa que a mensgem de erro que pode resultar se seu e programa simplesmente tentar acessar um apontador nulo: Segmentation fault (core dumped) Verique condies sobre valores de parmetros passados a funes. co a co Por exemplo, se uma funo deve ser chamada somente com um valor ca positivo para o parmetro qualquercoisa, use o seguinte no comeo do a c corpo da funo: ca assert (qualquercoisa > 0); Isso ir ajudar voc a detectar uso inadequado da funo, e essa prtica a e ca a tambm faz com que esteja muito claro a algum que ao ler o cdigo e e o fonte da funo ver que existe uma restrio sobre valores do parmetro. ca a ca a Evolua; use assert de forma liberal em toda a extenso de seu cdigo. a o

2.2.2

Falhas em Chamadas de Sistema

A maioria de ns originalmente aprendeu como escrever programas que exeo cutam at o nal ao longo de um caminho bem denido. Dividimos o proe grama em tarefas e subtarefas, e cada funo completa uma tarefa atravs de ca e chamadas a outras funes para executar as subtarefas correspondentes. Forco necendo entradas apropriadas, esperamos que uma funo produza a sa ca da correta e os efeitos corretos. As realidades das peas do computador e dos c programas de computador intromete-se nesse sonho perfeito. Computadores possuem recursos limitados; peas falham; muitos programas funcionam ao c mesmo tempo; usurios e programas cometem erros. Isso muitas vezes no a limite entre a aplicao e o sistema operacional que essas realidades exibem ca por si mesmas. Portanto, quando formos usar chamadas de sistema para acessar recursos, para realizar operaes de E/S, ou para outro propsito, co o e importante entender no somente o que ocorre quando a chamada acontece, a mas tambm quando e como a chamada de sistema pode falhar. Chamadas e de sistema falham de muitas formas. Por exemplo: O sistema pode extrapolar os recursos dispon veis de hardware (ou o programa excede os limites de recursos impostos pelo sistema para um programa simples). Por exemplo, o programa pode tentar alocar muita memria, escrever muito no disco, ou abrir muitos arquivos ao mesmo o tempo. 45

GNU/Linux pode bloquear uma certa chamada de sistema quando um programa tenta executar uma operao para a qual no tiver permisso. ca a a Por exemplo, um programa pode tentar escrever em um arquivo marcado como para somente leitura, acessar a memria de outro processo, o ou encerrar outro programa de usurio. a Os argumentos a uma chamada de sistema podem ser invlidos, ou a devido ao usurio fornecer entradas invlidas ou devido a um erro no a a programa. Por exemplo, o programa pode passar a outro programa um endereo invlido de memria ou um descritor de arquivo invlido c a o a para uma chamada de sistema. Ou, um programa pode tentar abrir um diretrio como um arquivo comum, ou pode passar o nome de um o arquivo comum a uma chamada de sistema que espera um diretrio. o Uma chamada de sistema falha por razes externar a um programa. o Isso acontee na maioria das vezes quando uma chamada de sistema c acessa um dispositivo. O dispositivo pode estar danicado ou pode no suportar uma operao em particular, ou talvez um disco no est a ca a a inserido no dispositivo de leitura e escrita em disco. Uma chamada de sistema pode muitas vezes ser interrompida por um evento externo, tal como a entrega de um sinal. Isso no necessaria amente indica falha externa, mas isso em resposta ` chamada de um a programa para reiniciar a chamada de sistema, se for desejvel. a Em um programa bem escrito que faz uso extensivo de chamadas de sistema, a falha de chamada de sistema causa o aparecimento de mais cdigo o devotado a detectar e manusear erros e outras circunstncias excepcionais a que o cdigo espec o co dedicado ao trabalho principal do programa.

2.2.3

Cdigos de Erro de Chamadas de Sistema o

A maioria das chamadas de sistema retorna zero se a operao terminar corca retamente, ou um valor diferente de zero caso a operao resultar em falha. ca (Muitas outras chamadas, apesar disso, possuem diferentes convees de vaco lores de retorno; por exemplo, a chamada malloc retorna um apontador nulo para indicar falha. Sempre leia a pgina de manual cuidadosamente quando a for usar uma chamada de sistema.) Embora essa informao possar suciente ca para determinar se o programa deva continuar a execuo normalmente, a ca leitura da pgina de manual provavelmente no fornece informao suciente a a ca para um recuperao satisfatria de erros. ca o 46

A maioria das chamadas de sistema usam uma varivel especial chamada era rno para armazenar informaes adicionais em caso de falha. 5 Quando uma co chamada vier a falhar, o sistema ajusta errno para um valor indicando o que aconteceu de errado. Pelo fato de todas as chamadas de sistema usarem a mesma varivel errno para armazenar informaes de erro, voc deve copiar a co e o valor para outra varivel imediatamente aps ocorrer a falha na chamada. a o A errno ir ter seu valor atual apagado e preenchido com outros valores da a prxima vez que voc zer uma chamada de sistema. o e Valores de erro so inteiros; os valores poss a veis so fornecidos pelas maa cros de prprocessamento, por conveno nomeadas em letras maisculas e e ca u iniciando com E, por exemplo, EACCES e EINVAL. Sempre use essas macros para referir-se a valores de errno em lugar de valores inteiros. Inclua o cabealho se voc for usar valores de errno. c e GNU/Linux fornece uma funo conveniente, strerror, que retorna uma desca crio em forma de sequncia de caracteres de um cdigo de erro que se ca e o encontra armazenado em errno, adequada para usar em mensagens de erro. Inclua o arquivo de cabealho caso voc resolva usar a funo c e ca strerror. GNU/Linux tambm fornece perror, que mostra a descrio do erro diretae ca mente para o uxo stderr. Passe a perror uma sequncia de caracteres para e ser usada como prexo a ser mostrado antes da descrio de erro, que deve ca habitualmente incluir o nome da funo que falhou. Inclua o arquivo de ca cabealho caso voc resolva usar a funo perror. c e ca O fragmento de cdigo adiante tenta abrir um arquivo; se a abertura falhar, o o cdigo mostra uma mensagem de erro e encerra a execuo do programa. o ca Note que a chamada open retorna um descritor de arquivo aberto se o operador open obtiver sucesso em sua tarefa, ou -1 se a operao falhar. caf d = open ( a r q u i v o d e e n t r a d a . t x t , O RDONLY ) ; i f ( f d == 1) { / A a b e r t u r a f a l h o u . M o s t r a uma menssagem d e e r r o e s a i . / f p r i n t f ( s t d e r r , e r r o ao a b r i r o a r q u i v o : %s \n , s t r e r r o r ( e r r n o ) ) ; exit (1); }

dependendo de seu programa e da natureza da chamada de sistema, a ao ca apropriada ao caso de falha pode ser mostrar uma mensagem de erro para cancelar uma operao, abortar o programa, tentar novamente, ou mesmo ca para ignorar o erro. A meno desse comportamento importante pelo fato ca e de ser necessrio incluir cdigo que manuseie todos os poss a o veis modos de falha de uma forma ou de outra. Um poss cdigo de erro que voc deve car de olho, especialmente com vel o e funes de E/S, EINTR. Algumas funes, tais como read, select, e sleep, co e coAtualmente, por razes de trabalhar de forma segura, errno implementada como o e uma macro, mas usada como uma varivel global. e a5

47

podem precisar de um intervalo de tempo signicativo para executar. Essas so consideradas funes de bloqueio pelo fato de a execuo do programa a co ca ser bloqueada at que a chamada seja completada. Todavia, se o programa e recebe um sinal enquanto estiver bloqueado em uma dessas chamadas, a chamada ir retornar sem completar a operao. Nesse caso, errno ajustada a ca e para EINTR. Comumente, voc ir querer chamar novamente a chamada de e a sistema que foi interrompida pelo sinal nesse caso. Adiante encontra-se um fragmento de cdigo que utiliza a chamada chown o para mudar o dono de um arquivo fornecido pelo caminho para o usurio esa pecicado atravs de user id. Se a chamada vier a falhar, o programa executa e uma ao que depende do valor de errno. Note que quando detectamos o ca que provavelmente um erro no programa ns saimos usando abort ou mane o ter (assert), o que causa a gerao de um arquivo principal. Esse arquivo ca pode ser util para depurao aps o encerramento do programa. Para outros ca o erros irrecuperveis, tais como condies de tentativas de acesso a reas de a co a memria no alocadas pelo sistema operacional ao programa em questo, o a a saimos usando a opo sair e um valor de sa no nulo em lugar de arquivo ca da a principal (core) pelo fato de que um arquivo core no vir a ser muito util. a r v a l = chown ( path , u s e r i d , 1); i f ( r v a l != 0 ) { / Grava e r r n o p e l o f a t o d e p o d e r s e r s o b r e s c r i t o p e l a p r o x i m a chamada d e int e r r o r c o d e = errno ; / A o p e r a c a o f a l h a chown d e v e r e t o r n a r 1 em c a s o d e e r r o . / a s s e r t ( r v a l == 1); / V e r i f i c a o v a l o r d e e r r n o , e e x e c u t a a a c a o a p r o p r i a d a . / switch ( e r r o r c o d e ) { case EPERM: / P e r m i s s a o n e g a d a . / case EROFS : / PATH e s t a em um s i s t e m a d e a r q u i v o s o m e n t e l e i t u r a . / case ENAMETOOLONG: / PATH e m u i t o l o n g o . / case ENOENT: / PATH nao e x i t e . / case ENOTDIR : / Um c o m p o n e n t e d e PATH nao e h um d i r e t o r i o . / case EACCES : / Um c o m p o n e n t e d e PATH nao e s t a a c e s s i v e l . / / A l g o e s t a e r r a d o com o a r q u i v o . M o s t r e uma mensagem d e e r r o . / f p r i n t f ( s t d e r r , e r r o mudando o dono de %s : %s \n , path , s t r e r r o r ( e r r o r c o d e ) ) ; / Nao e n c e r r a o p r o g r a m a ; t a l v e z f o r n e c a o ao u s u a r i o uma c h a n c e p a r a e s c o l h e r o u t r o a r q u i v o . . . / break ; case EFAULT : / PATH contem um e n d e r e c o d e memoria abort ( ) ; s i s t e m a . /

invalido .

Isso

e h p r o v a v e l m e n t e um e r r o . /

case ENOMEM: / E x e c u t o u f o r a da memoria do k e r n e l . / f p r i n t f ( s t d e r r , %s \n , s t r e r r o r ( e r r o r c o d e ) ) ; exit (1); default : / Alguma o u t r a e r r o s de c o d i g o abort ( ) ; }; }

c o i s a , i n e s p e r a d o , c o d i g o d e e r r o . Tentamos manusear t o d o s p o s s i v e i s ; s e t i v e r m o s o m i t i d o algum , i s s o e h um e r r o ! /

os

Voc pode simplesmente usar o cdigo abaixo, que comporta-se da mesma e o forma se a chamada obtem sucesso:r v a l = chown ( path , a s s e r t ( r v a l == 0 ) ; user \ id , 1);

48

Mas se a chamada vier a falhar, a alternativa de cdigo acima no faz o a nenhum esforo para reportar, manusear, ou para se recuperar dos erros. c Se voc usa a primeira forma, a segunda forma, ou algum meio termo entre e as duas vai depender da necessidade de seu sistema no tocante a deteco e ca recuperao de erros. ca

2.2.4

Erros e Alocao de Recursos ca

Muitas vezes, quando uma chamada de sistema falha, mais apropriado cane celar a operao atual mas no terminar o programa porque o cancelamento ca a simples pode tornar poss recuperar-se do erro. Uma forma de fazer isso vel retornar da funo em que se est no momento em que ocorreu o erro, e ca a passando um cdigo de retorno para a funo chamadora indicando o erro. o ca Caso voc decida retornar a partir do meio de uma funo, importante e ca e garantir que quaisquer recursos que tenham sido alocados com sucesso previamente na funo seja primeiramente liberado. Esses recursos podem incluir ca memria, descritores de arquivo, apontadores para arquivo, arquivos temo porrios, objetos de sincronizao, e assim por diante. De outra forma, se a ca seu programa continuar sendo executado, os recursos alocados anteriormente a ` ocorrncia da falha iro ser retirados. e a Considere, por exemplo, uma funo que faa a leitura de um arquivo ca c em um espao temporrio de armazenamento. A funo pode seguir esses c a ca passos: 1. Alocar o espao temporrio de armazenamento. c a 2. Abrir o arquivo. 3. Ler a partir do arquivo na rea temporria de armazenamento. a a 4. Fechar o arquivo. 5. Devolver o espao emporrio de armazenamento. c a Se o arquivo no existir, o Passo 2 ir falhar. Um caminho de ao a a ca pode ser retornar um apontador a partir da funo. Todavia, se o espao ca c de armazenamento temporrio j tiver sido alocado no Passo 1, existe um a a risco de perder aquela memria. Voc deve lembrar de desalocar o espao o e c temporrio de armazenamento em algum lugar com o decorrer de qualquer a uxo de controle do qual voc no venha a retornar. Se o Passo 3 vier a falhar, e a voc no somente deve desalocar o espao temporrio de armazenamento e a c a antes de retornar, mas tambm deve fechar o arquivo. e A listagem 2.6 mostra um exemplo de como voc pode escrever essa funo. e ca 49

Listagem 2.6: (readle.c) Liberando Recursos em Condies Inesperadas co#include #include #include #include #include < f c n t l . h> < s t d l i b . h> size t length )

char r e a d f r o m f i l e ( const char f i l e n a m e , { char b u f f e r ; int fd ; s s i z e t bytes read ;

/ A l l o c a t e t h e b u f f e r . / b u f f e r = ( char ) m a l l o c ( l e n g t h ) ; i f ( b u f f e r == NULL) return NULL ; / Open t h e f i l e . / f d = open ( f i l e n a m e , O RDONLY) ; i f ( f d == 1) { / o p e n f a i l e d . Deallocate buffer before returning . / free ( buffer ) ; return NULL ; } / Read t h e d a t a . / b y t e s r e a d = read ( fd , b u f f e r , l e n g t h ) ; i f ( b y t e s r e a d != l e n g t h ) { / r e a d f a i l e d . D e a l l o c a t e b u f f e r and c l o s e f d b e f o r e r e t u r n i n g . free ( buffer ) ; c l o s e ( fd ) ; return NULL ; } / E v e r y t h i n g s f i n e . C l o s e t h e f i l e and r e t u r n t h e b u f f e r . / c l o s e ( fd ) ; return b u f f e r ; }

/

Gnu/Linux limpa a memria alocada, limpa os arquivos abertos, e libera o a maioria de outros recursos quando um programa encerra, de forma que no necessrio desalocar espaos temporrios