Está en la página 1de 167

Programao Linux Avanada ca c Autores:Mark Mitchell, Jerey Oldham e Alex Samuel http://www.advancedlinuxprogramming.com/ http://www.codesourcery.

com/

(...) 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.06 - 06/2011 a Qualquer dvida sobre a presente traduo primeiramente consulte o u ca original em ingls. e

Sumrio a
I Programao UNIX Avanada com Linux ca c
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

9
13 13 14 15 15 16 17 19 20 22 23 23 23 26 26 27 28 28 29 29 30 31 32 36 37 38 41 44 44

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 com GNU Make . . . . . . . . . . . . . . 1.4 Depurando com o Depurador GNU (GDB) . . . . . . . . 1.4.1 Depurando com GNU 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 1.5.4 Cdigo Fonte . . . . . . . . . . . . . . . . . . . . o 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 . . . . . . . . . . . 3

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

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

2.3

2.2.2 Falhas em Chamadas de Sistema . . . . . . 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 48 51 52 53 55 57 58 59 61 63 63 64 64 66 66 66 67 70 72 74 76 76 77 79 83 84 86 88 90 91 92 94 95 95 97 98 101 102

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 Processos . . . . . . . . . . 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 Linhas de Execuo ca 4.1 Criao de Linhas de Execuo . . . . . . . . . . . ca ca 4.1.1 Enviando Dados a uma Linha de Execuo . ca 4.1.2 Vinculando Linhas de Execuo . . . . . . . ca 4.1.3 Valores de Retorno de Linhas de Execuo . ca 4.1.4 Mais sobre IDs de Linhas de Execuo . . . ca 4.1.5 Atributos de Linha de Execuo . . . . . . . ca 4.2 Cancelar Linhas de Execuo . . . . . . . . . . . . ca 4.2.1 Linhas de Execuo Sincronas e Assincronas ca 4.2.2 Sees Cr co ticas Incancelveis . . . . . . . . . a 4.2.3 Quando Cancelar uma Linha de Execuo . ca 4.3 Area de Dados Espec cos de Linha de Execuo . ca 4.3.1 Manipuladores de Limpeza . . . . . . . . . . 4.3.2 Limpeza de Linha de Execuo em C++ . . ca

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

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

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

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

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

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

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

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

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

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

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

4.4

Sincronizao e Sees Cr ca co ticas . . . . . . . . . . . . . . . 4.4.1 Condies de Corrida . . . . . . . . . . . . . . . . . co 4.4.2 Mutexes . . . . . . . . . . . . . . . . . . . . . . . . 4.4.3 Travas Mortas de Mutex . . . . . . . . . . . . . . . 4.4.4 Testes de Mutex sem Bloqueio . . . . . . . . . . . . 4.4.5 Semforos para Linhas de Execuo . . . . . . . . . a ca 4.4.6 Variveis Condicionais . . . . . . . . . . . . . . . . a 4.4.7 Travas Mortas com Duas ou Mais Linhas de Execuo . . . . . . . . . . . . . . . . . . . . . . . ca 4.5 Implementao de uma Linha de Execuo em GNU/Linux ca ca 4.5.1 Manipulando Sinal . . . . . . . . . . . . . . . . . . 4.5.2 Chamada de Sistema clone . . . . . . . . . . . . . . 4.6 Processos Vs. Linhas de Execuo . . . . . . . . . . . . . . ca

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

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

103 104 106 109 111 111 115 121 122 123 124 124

5 Comunicao Entre Processos ca 5.1 Memria Compartilhada . . . . . . . . . . . . . . . . . . . . o 5.1.1 Comunicao Local Rpida . . . . . . . . . . . . . . ca a 5.1.2 O Modelo de Memria . . . . . . . . . . . . . . . . . o 5.1.3 Alocao . . . . . . . . . . . . . . . . . . . . . . . . . ca 5.1.4 Anexando e Desanexando . . . . . . . . . . . . . . . 5.1.5 Controlando e Desalocando Memria Compartilhada o 5.1.6 Um programa Exemplo . . . . . . . . . . . . . . . . . 5.1.7 Depurando . . . . . . . . . . . . . . . . . . . . . . . . 5.1.8 Prs e Contras . . . . . . . . . . . . . . . . . . . . . o 5.2 Semforos de Processos . . . . . . . . . . . . . . . . . . . . . a 5.2.1 Alocao e Desalocao . . . . . . . . . . . . . . . . ca ca 5.2.2 Inicializando Semforos . . . . . . . . . . . . . . . . . a 5.2.3 Operaes Wait e Post . . . . . . . . . . . . . . . . . co 5.2.4 Depurando Semforos . . . . . . . . . . . . . . . . . a 5.3 Arquivos Mapeados em Memria . . . . . . . . . . . . . . . o 5.3.1 Mapeando um Arquivo Comum . . . . . . . . . . . . 5.3.2 Programas Exemplo . . . . . . . . . . . . . . . . . . 5.3.3 Acesso Compartilhado a um Arquivo . . . . . . . . . 5.3.4 Mapeamentos Privados . . . . . . . . . . . . . . . . . 5.3.5 Outros Usos para Arquivos Mapeados em Mem-ria . o 5.4 Pipes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.4.1 Criando Pipes . . . . . . . . . . . . . . . . . . . . . . 5.4.2 Comunicao Entre Processos Pai e Filho . . . . . . . ca 5.4.3 Redirecionando a Entrada Padro, a Sa Pa-dro e a da a o Fluxo de Erro . . . . . . . . . . . . . . . . . . . . . 5.4.4 As Funes popen e pclose . . . . . . . . . . . . . . . co

127 . 128 . 129 . 129 . 130 . 131 . 132 . 133 . 133 . 134 . 134 . 135 . 136 . 136 . 138 . 138 . 139 . 140 . 142 . 143 . 143 . 144 . 144 . 145 . 147 . 148

5.4.5

5.5

FIFOs . . . . . . . . . . . . . . . . . . . . . . . . 5.4.5.1 Criando um FIFO . . . . . . . . . . . . 5.4.5.2 Accessando um FIFO . . . . . . . . . . Sockets . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.5.1 Conceitos de Socket . . . . . . . . . . . . . . . . . 5.5.2 Chamadas de Sistema . . . . . . . . . . . . . . . 5.5.3 Servidores . . . . . . . . . . . . . . . . . . . . . . 5.5.4 Sockets Locais . . . . . . . . . . . . . . . . . . . . 5.5.5 Um Exemplo Usando um Sockets de Escopo local 5.5.6 Sockets de Dom nio Internet . . . . . . . . . . . . 5.5.7 Sockets Casados . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

149 150 150 151 152 153 154 155 156 159 161 163

6 Licena de Livre Publicao c ca

Listagem Cdigos Fonte o


1.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 Cdigo fonte em C main.c . . . . . . . . . . . . . . 17 o Arquivo Cdigo fonte em C++ reciprocal.cpp . . . . . . . . 17 o Arquivo de cabealho reciprocal.hpp . . . . . . . . . . . . . 17 c (Arquivo arglist.c) Usando argc e argv. . . . . . . . . . . . . . 31 ca (getopt long.c) Usando a funo getopt long . . . . . . . . . . 35 (print-env.c) Mostrando o Ambiente de Execuo . . . . . . . 40 ca (client.c) Parte de um Programa Cliente de Rede . . . . . . . 41 (temp le.c) Usando mkstemp . . . . . . . . . . . . . . . . . . 43 (readle.c) Liberando Recursos em Condies Inesperadas . . 52 co (test.c) Area da Biblioteca . . . . . . . . . . . . . . . . . . . . 54 Um Programa Que Utiliza as Funes da Biblioteca Acima . . 54 co (titest.c) Usando a libti . . . . . . . . . . . . . . . . . . . . 58 ( print-pid.c) Mostrando o ID do Processo . . . . . . . . . . . 64 (system.c) Usando uma chamada ` funo system . . . . . . . 67 a ca ( fork.c) Usando fork para Duplicar o Processo de um Programa 68 ( fork-exec.c) Usando fork e exec Juntas . . . . . . . . . . . . 70 (sigusr1.c) Usando um Manipulador de Sinal . . . . . . . . . . 74 (zombie.c) Fazendo um Processo Zumbi . . . . . . . . . . . . . 78 (sigchld.c) Limpando Processos lhos pelo manuseio de SIGCHLD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 ( thread-create.c) Criando uma Linha de Execuo . . . . . . 86 ca ( thread-create2) Cria Duas Linhas de Execuo . . . . . . . . 87 ca Funo main revisada para thread-create2.c . . . . . . . . . . 89 ca ( primes.c) Calcula Nmeros Primos em uma Linha de Execuo 91 u ca (detached.c) Programa Esqueleto Que Cria uma Linha dde Execuo Desvinculada . . . . . . . . . . . . . . . . . . . . . . 93 ca (critical-section.c) Protege uma Transao Bancria com uma ca a Seo Cr ca tica . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 (tsd.c) Log Por Linhas de Execuo Implementado com Dados ca Espec cos de Linha de Execuo . . . . . . . . . . . . . . . . 100 ca 7

4.8 4.9 4.10 4.11 4.12 4.13 4.14 4.15 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 5.10 5.11 5.12

(cleanup.c) Fragmento de Programa Demonstrando um Manipulador de Limpeza de Linha de Execuo . . . . . . . . . . . 102 ca (cxx-exit.cpp) Implementando Sa Segura de uma Linha de da Execuo com Excees de C++ . . . . . . . . . . . . . . . . 103 ca co ( job-queue1.c) Funo de Linha de Execuo para Processar ca ca Trabalhos Enleirados . . . . . . . . . . . . . . . . . . . . . . 105 ( job-queue2.c) Funo de Tarefa da Fila de Trabalho, Proteca gida por um Mutex . . . . . . . . . . . . . . . . . . . . . . . . 108 ( job-queue3.c) Fila de Trabalhos Controlada por um Semforo 114 a (spin-condvar.c) Uma Implementao Simples de Varivel Conca a dicional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 (condvar.c) Controla uma Linha de Execuo Usando uma ca Varivel Condicional . . . . . . . . . . . . . . . . . . . . . . . 120 a (thread-pid) Imprime IDs de processos para Linhas de Execuo122 ca Exerc de Memria Compartilhada . . . . . . . . . . . . . . 133 cio o (sem all deall.c) Alocando e Desalocando um semforo Binrio 135 a a (sem init.c) Inicializando um Semforo Binrio . . . . . . . . . 136 a a (sem pv.c) Operaes Wait e Post para um Semforo Binrio 137 co a a (mmap-write.c) Escreve um Nmero Aleatrio para um Aru o quivo Mapeado em Memria . . . . . . . . . . . . . . . . . . . 140 o (mmap-read.c) L um Inteiro a partir de um Arquivo Mapeado e em Memria, e Dobra-o . . . . . . . . . . . . . . . . . . . . . 141 o (pipe.c) Usando um pipe para Comunicar-se com um Processo Filho . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146 (dup2.c) Redirecionar a Sa de um pipe com dup2 . . . . . . 148 da (popen.c) Exemplo Usando popen . . . . . . . . . . . . . . . . 149 (socket-server.c) Servidor de Socket de Escopo Local . . . . . 157 (socket-client.c) Cliente de Socket de Escopo Local . . . . . . 158 (socket-inet.c) L de um Servidor WWW . . . . . . . . . . . . 160 e

Parte I Programao UNIX Avanada ca c com Linux

1 Iniciando 2 O Sistema de Arquivos /proc 3 Processos 4 Linhas de Execuo ca 5 Comunicao Entre Processos ca

11

12

Cap tulo 1 Iniciando


ESSE CAP ITULO MOSTRA COMO EXECUTAR OS PASSOS bsicos rea queridos para criar um programa Linux usando a linguagem C ou a linguagem C++. Em particular, esse cap tulo mostra como criar e modicar cdigo o fonte C e C++, compilar esse cdigo modicado, e depurar o resultado. Se o voc tem experincia em programao em ambiente Linux, voc pode pue e ca e lar agora para o Cap tulo 2, Escrevendo Bom Software GNU/Linux prestando cuidadosa ateno ` seo 2.3, Escrevendo e Usando Bibliotecas para ca a ca informaes sobre linkagem/vinculao esttica versus linkagem/vinculao co ca a ca dinmica `s quais voc pode no conhecer ainda. a a 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 biblioteca ca co padro C. Os exemplos de cdigo fonte nesse livro esto em C, exceto quando a o a for necessrio demonstrar um recurso particular ou complicao de programa a 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 GNU/Linux terem iniciado programao no ambiente Windows, iremos ocasionalmente mostrar ca semelhanas e diferenas entre Windows e GNU/Linux. c c

1.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. e 13

Sobre 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 voc ainda no tem um editor Linux favorito, ento voc e 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 e pressionado a tecla Enter. Quando Emacs tiver iniciado, voc pode usar e os menus localizados na parte superior para criar um novo arquivo fonte. Clique no menu File, escolha Open File, ento digite o nome do arquivo a que voc deseja abrir no minibuer localizado na parte inferior da tela.1 e 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 e C++, use um nome de arquivo que termine em .cpp, .hpp, .cxx, .hxx, .C, ou .H. Quando o arquivo estiver aberto, voc pode digitar da mesma forma e que faria em qualquer programa processador de texto comum. Para gravar o arquivo, escolha a entrada Save no menu File. Quando voc tiver e encerrado a utilizao do Emacs, voc pode escolher a opo Exit Emacs ca e ca no menuFile. Se voc no gosta de apontar e clicar, voc pode usar teclas de atalho e a e de teclado 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 a C-s. Para sair do Emacs, apenas digite C-x C-c. Se voc desejar adquirir um e pouco mais de habilidade com Emacs, escolha a entrada Emacs Tutorial no menu Help.O tutorial abastece voc com uma quantidade grande de e dicas sobre como usar Emacs efetivamente.
Se voc no est executando em um sistema X Window, voc ter de pressionar F10 e a a e a para acessar os menus.
1

14

1.1.2

Formatando Automaticamente

Se voc est acostumado a programar em um Ambiente Integrado de Dee a senvolvimento (IDE)2 , voc consequentemente estar tambm acostumado a e 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 ( ) { p r i n t f ( Alo , mundo\n ) ; } Note como a linha foi apropriadamente indentada. ` A medida que seu uso do Emacs for acontecendo, voc ver como o Emacs 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
Do ingls Integrated Development Environment. Em nosso bom portugus seria e e AID 3 Tente executar o comando M-x dunnet se 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. a
2

15

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 a seguinte sequncia de caracteres: e

(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 e e voc inseriu dentro do seu .emacs semelhante a um cdigo da linguagem de e e o programao LISP.Isso ocorre pelo fato de ser um cdigo LISP! Muitas partes ca o de cdigo do Emacs so atualmente escritas em LISP. Voc pode adicionar o a 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 a 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 para 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.
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 reciprocal pode provavelmente ser chamado reciprocal.exe; a verso GNU/Linux somente reciprocal. a e
5 4

16

Listagem 1.1: Arquivo Cdigo fonte em C main.c o


1 2 3 4 5 6 7 8 9 10 11 12 #include <s t d i o . h> #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 Cdigo fonte em C++ reciprocal.cpp o


1 2 3 4 5 6 7 8 #include <c a s s e r t > #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
1 2 3 4 5 6 7 8 9 #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.

1.2.1

Compilando um Arquivo Simples de Cdigo Fonte o

O nome do compilador C gcc. Para compilar um cdigo fonte em C (gerar e o o arquivo objeto), voc usa a opo -c. Ento, por exemplo, inserindo o -c e ca a no prompt de comando compila o arquivo de cdigo fonte main.c: o % gcc -c main.c O arquivo objeto resultante chamado main.o. O compilador C++ e e chamado g++. Sua operao muito similar ao gcc; a compilao de recica e ca procal.cpp realizada atravs do seguinte comando: e e 17

% 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 ca produzir um executvel. Aps voc ter digitado esse comando, voc ir ter a o e e a um 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 ao a ca e GCC onde procurar por arquivos de cabealho. Por padro, GCC olha no c a diretrio atual e nos diretrios onde cabealhos para bibliotecas padro esto o o c a a instalados. Se voc precisar incluir arquivos de cabealho localizados em ale c gum outro lugar, voc ir precisar da opo -I. Por exemplo, suponhamos que e a ca seu projeto tenha um diretrio chamado src, para arquivos fonte, e outro o diretrio chamado include. Voc pode compilar o arquivo reciprocal.cpp o e como segue abaixo para indicar que g++ deve usar o diretrio ../include o adicionalmente para encontrar o arquivo de cabealho reciprocal.hpp: c % 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 isso requer modicao 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 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: 18

% 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 juntar e e a os cdigos objeto e gerar o executvel. Voc deve sempre usar o g++ para o a e linkar um programa que contm cdigo em C++, mesmo se esse cdigo C++ e o o tambm contenha cdigo em C. Se seu programa contiver somente cdigo em e o o C, voc deve usar o gcc no lugar do g++. Pelo fato de o g++ est apto a e a tratar ambos os arquivos em C e em C++, voc deve usar g++, como segue e adiante: % 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 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 usurio)6 , voc pode ter especicado a biblioteca com a opo -l. Em GNU/a e ca Linux, nomes de biblioteca quase sempre comeam com lib. Por exemplo, c a biblioteca Pluggable Authentication Module (PAM) chamada libe pam.a. Para linkar a libpam.a, voc usa um comando como o seguinte: e
6

Nota do tradutor: QT ou Gtk

19

% g++ -o reciprocal main.o reciprocal.o -lpam O compilador automaticamente adiciona o prexo lib e o suxo .a7 . Da mesma forma que para os arquivos de cabealho, o linkador procura por c bibliotecas em 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 a o e seguinte linha para instruir o linkador a encontrar a biblioteca test no diretrio atual: o % gcc -o app app.o -L. -ltest

1.3

Automatizando 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 e 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: reciproe o cal.o, main.o, e o reciprocal executvel propriamente dito. Voc j tinha a e a regras em mente para reconstruir esses alvos na forma da linha de comando fornecidas previamente. As dependncias requerem um pouco de racioc e nio.
Nota do tradutor: a biblioteca PAM pode ser encontrada em http://ftp.mgts.by/ pub/linux/libs/pam/library/.
7

20

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. Se acontece mais uma modicao em ca reciprocal.hpp isso tambm deve fazer com que ambos os arquivos objetos e sejam reconstru dos pelo fato de ambos os arquivos fontes incluirem o reciprocal.hpp. 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 comando e rm para remover os arquivos. Voc pode reunir toda essa informao para o make colocando a ine ca formao em um arquivo chamado Makele. Aqui est um exemplo de ca a contedo de Makele: u 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 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 21

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. Voc 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 -O2 foi 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 % info make Nas pginas info de manual, voc ir encontrar informaes sobre como a e a co fazer para manter um Makele simples, como reduzir o nmero de regras que u voc precisa escrever, e como automaticamente calcular dependncias. Voc e e e pode tambm encontrar mais informao no livro GNU Autoconf, Automake, e ca and Libtool escrito por Gary V.Vaughan, Ben Elliston,Tom Tromey, e Ian Lance Taylor (New Riders Publishing, 2000). 8
Nota do tradutor: A verso eletrnica do livro pode ser encontrada em http:// a o sources.redhat.com/autobook/download.html.
8

22

1.4.1

Depurando com GNU 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.

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 seu programa dentro do depurador. Apenas e insira o comando run e quaisquer argumentos do programa que voc est e a depurando. Tente executar o programa sem qualquer argumento, dessa forma 10 :
...a menos que seus programas sempre funcionem da primeira vez. 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
10 9

23

(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 programa 11 . 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 12 a pilha usando o comando where : (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 atoi com 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 e ca e do uso do comando up: (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
Em ingls: crash e 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
12 11

24

(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 step como segue: (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.
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

25

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 pages so 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 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 man e para acess-las. Para ver uma pgina de manual, simplesmente chame-a esa a crevendo 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: 26

% man 3 sleep Cada pgina de manual inclui um sumrio on-line do comando ou da a a funo. O comando whatis nome mostra 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 colocados na diretiva include se voc desejar chamar essa funo. e ca

1.5.2

Info

A documentao de sistema do tipo Info possuem documentao mais detaca ca lhada para muitos dos principais componentes do sistema GNU/Linux, alm e de muitos outros programas. Pginas Info so 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 info em uma janela de shell. Voc ir e a ser presenteado com um menu de documentos Info instalado em seu sistema. (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 a a esses: gcc O compilador gcc Libc A biblioteca C GNU, incluindo muitas chamadas de sistema Gdb O depurador GNU Emacs O editor de texto Emacs Info O sistema Info propriamente dito A maioria de todas as ferramentas padronizadas de programao em amca biente GNU/Linux (incluindo o ld, o linkador; as, o assemblador; e gprof, o proler ) so acompanhados com pginas Info bastante uteis. Voc pode ir a a e diretamente a uma documento Info em particular especicando o nome da pgina Info na linha de comando: a 27

% 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 info ou 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 importantes e centrais de como as chamadas de sistema trabalham esto reetidos nos arquivos de a cabealho nos diretrios /usr/include/bits, /usr/include/asm, e /usr/incluc o de/linux. Por exemplo, os valores numricos dos sinais (descritos na Seo e ca 3.3, Sinais no Cap tulo 3, Processos) so denidos em /usr/include/a bits/signum.h. Esses arquivos de cabealho so uma boa leitura para mentes c a inquiridoras. No inclua-os diretamente em seus programas; sempre use os a arquivos de cabealho em /usr/include ou como mencionado na pgina de c a manual para a funo que voc est usando. ca e a

1.5.4

Cdigo Fonte o

Isso cdigo aberto, certo? O rbitro nal de como o sistema trabalha e o a e o prprio cdigo fonte do sistema, e afortunadamente para programadores o o em ambiente GNU/Linux, para os quais o cdigo livremente dispon o e vel. Casualmente, sua distribuio inclue o cdigo fonte completo para o sistema ca o completo e todos os programas inclu dos nele; se no, voc est autorizado a e a nos termos da Licena Pblica Geral GNU a requisitar esse cdigo ao disc u o tribuidor. (O Cdigo Fonte pode no estar instalado no seu disco. Veja a o a documentao da sua distribuio para instrues de como instalar os cdigos ca ca co o fonte.) O cdigo fonte para o kernel do GNU/Linux est comumente armazenado o a no diretrio /usr/src/linux. Se esse livro deixa voc ansioso por detalher de o e como os processos, a memria compartilhada, e os dispositivos de sistema trao balham, voc sempre pode aprender um pouco mais a partir do cdigo fonte. e o A maioria das funes de sistema descritas nesse livro esto implementaco a das na biblioteca C GNU; verique na documentao de sua distribio pela ca ca localizao do cdigo fonte da biblioteca C. ca o 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 argc e argv, os quais recebem entradas para o seu programa. Voc aprendeu sobre stdout e stdin (ou sobre os uxos cout e e cin na 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, o tpico atual referente a o o e como a lista de argumentos do programa passada pelo fato de essa lista e no precisar ser originria de linha de comando de shell. No Cap a a tulo 3, Processos voc ir ver outro caminho para chamar um programa, no qual e a um programa pode especicar a lista de argumentos de outro programa diretamente. Quando um programa chamado a partir do shell, a lista de e argumentos contm a linha de comando completa, incluindo o nome do proe grama e quaisquer argumentos de linha de comando que possa ter sido fornecido. Suponhamos, por exemplo, que voc chame o comando ls em seu shell e para mostrar o contedo do diretrio ra e os correspondentes tamanhos dos u o z arquivos com essa linha de comando: % ls -s / A lista de argumentos que o programa ls acima consta de trs argumentos. e O primeiro deles o nome do programa propriamente dito, como especicado e na linha de comando, ls a saber. O segundo e o terceiro elementos da lista de argumentos so os dois argumentos de linha de comando, o -s e a /. a A funo main de seu programa pode acessar a lista de argumentos por ca meio dos parmetros da funo main argc e 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 exaca e a a minar os contedos de argc e argv. Se voc no estiver interessado no nome u e a do programa propriamente dito, lembre-se de ignorar o primeiro elemento. Logo abaixo temos a Listagem 2.1 demonstra como usar argc e argv.

Nota do tradutor: ver [K & R (1989)] p. 113.

30

Listagem 2.1: (Arquivo arglist.c) Usando argc e argv.


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <s t d i o . h> 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 simco ples (comumente em caixa baixa ou caixa alta). Opes curtas so co a rpidas de digitar. a Opes longas consistindo de dois h co fens, seguidos por um nome composto em caixa baixa e caixa alta e h fens. Opes longas so co a fceis de lembrar e fceis de ler (em scripts shell, por exemplo). a a Habitualmente, um programa fornece ambas as formas curta e longa para a maioria das opes que so suportadas pelo referido programa, a forma co a curta por uma questo de brevidades e a forma longa por uma questo de a a clareza. Por exemplo, a maioria dos programas entendem as opes -h e co --help, e as tratam de forma idntica. Normalmente, quando um programa e chamado em um shell sem nenhum argumento, quaisquer opes desejae co das seguem o nome do programa imediatamente. Algumas opes esperam co que um argumento as siga imediatamente. Muitos programas, por exemplo, interpretam a opo output qualquercoisa como especicando que ca a sa de um programa deva ser colocada em um arquivo chamado qualda quercoisa. Aps as opes, podem existir adiante delas outros argumentos o co de linha de comando, tipicamente arquivos de entrada ou dados de entrada. 31

Por exemplo, o comando ls -s / mostra o contedo do diretrio ra A u o z. Opo -s modica o comportamento padro do ls instruindo o ls a mostrar ca a o tamanho (em kilobytes) de cada entrada.O argumento / diz ao ls qual diretrio listar. A opo size sinnima da opo -s, de forma que o o ca e o ca mesmo comando pode poderia ter sido digitado como ls size /. A codicao GNU padro lista os nomes de algumas opes de linha ca a co de comando comumente usadas. Se voc planeja fornecer quaisquer opes e co similares a alguma dessas, uma boa idia usar os nomes especicados na e e codicao padro. Seu programa ir se comportar mais como outros proca a a gramas e ir ser mais fcil para os usurios aprenderem. Voc pode visualizar a a a e o guia de Condicao GNU Padro para opes de linha de comando digica a co tando o seguinte em um prompt de comandos de um shell na maioria dos sistemas GNU/Linux2 : % info "(standards)User Interfaces"

2.1.3

Usando getopt long

A passagem de opes de linha de comando uma tarefa tediosa. Felizmente, co e a biblioteca C GNU fornece um funo que voc usa em programas em C e em ca e programas em C++ para fazer esse trabalho de alguma forma um pouco mais fcil (embora ainda assim um pouco incmoda). Essa funo, getopt long, a o ca recebe ambas as formas curta e longa de passagem de parmetros. Se voc a e for usar essa funo, inclua o arquivo de cabealho <getopt.h>. 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. a
Nota 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-Interfaces
2

32

Para usar a funo getopt long, voc deve fornecer duas estruturas de ca e dados. A primeira uma sequncia de caracteres contendo as opes vlidas e e co a em sua forma curta, cada letra simples. Uma opo que necessite de um ca argumento seguida de dois pontos. Para o seu programa, a sequncia de e e caracteres ho:v indica que as opes vlidas so -h, -o, e -v, com a segunda co a a dessas trs opes devendo ser seguida por um argumento. e co Para especicar as opes longas dispon co veis, voc constri um vetor de e o elementos de estruturas de opes. Cada elemento corespondendo a uma co opo longa e tendo quatro campos. Em circunstncias normais, o primeiro ca a campo o nome da opo longa (na forma de uma seqncia de caracteres, e ca ue sem os dois h fens); o segundo campo 1 se a opo precisa de argumento, e ca ou 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 sequncia de caracteres descrevendo a a ca e as opes curtas, e o vetor de elementos de estruturas de opes descrevendo co co as opes longas. co 33

Cada vez que voc chamar getopt long, a funo getopt long informa e ca uma opo simples, retornando a letra da forma curta para aquela ca opo ou -1 se nenhuma opo for encontrada. ca ca Tipicamente, voc ir chamar getopt long dentro de um lao, para e a c processar todas as opes que o usurio tiver especicado, e voc co a e ir manusear as opes espec a co cas usando o comando switch. Se a funo getopt long encontra uma opo invlida (uma opo ca ca a ca que voc no especicou como uma opo curta vlida ou como uma e a ca a opo longa vlida), a funo getopt long imprime uma mensagem ca a ca de erro e retorna o caractere ? (um ponto de interrogao). A ca grande maioria dos programas ir encerrar a execuo em resposta a ca a isso, possivelmente aps mostrar informaes de utilizao. o co ca Quando se estiver manuseando uma opo que precisa de um arca gumento, a varvel global optarg aponta para o texto daquele ara gumento. co a Aps getopt long terminar de manusear todas as opes, a varivel o 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. 34

Listagem 2.2: (getopt long.c) Usando a funo getopt long ca


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 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 #include <g e t o p t . h> #include <s t d i o . h> #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 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 . Does n o t 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 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 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 ; case v : / v o r v e r b o s e / verbose = 1; break ;

i t to standard termination ) . /

t h e name o f

the

output

file .

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

argument . o p t i o n was

35

/ The main p r o g r a m g o e s return 0 ; }

here .

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 casos 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: ...")); 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 e arquivo 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 redireci onar 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
3

Nota do tradutor:stdin, stdout e stderr.

36

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 completamente, 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 no mosta um ponto a cada segundo; em vez disso, os pontos so armac a a zenados em uma rea temporria, e um grupo de pontos mostrado todos a a e de uma unica vez quando o limite de armazenamento da rea temporria a a e alcanado. c 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
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. ca
4

37

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 distinguir erros o espec cos. Com a maioria dos shells, poss obter o cdigo de sa do e vel o da programa executado mais recentemente usando a varivel especial $? (ponto a de interrogao). Aqui est um exemplo no qual o comando ls chamado ca a e duas vezes e seu cdigo de sa mostrado a cada chamada. No primeiro o da e caso, ls executa corretamente e retorna o cdigo de sa zero. No segundo o da caso, ls encontrou um erro (porque o nome de arquivo especicado na linha de comando no existe) e dessa forma retorna um cdigo de sa diferente a o da de zero:

% 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.

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 ambie a a a ente mais comuns. Por exemplo: 38

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 o e chamar. DISPLAY contm o nome e o nmero do display do servidor sobre e u o 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 a a de ambiente que encontrar, de forma que voc acessar valores de e variveis de ambiente usando a sintaxe $nomedevariavel. Por exema plo: % echo $USER samuel % echo $HOME /home/samuel Voc pode usar o comando export para exportar uma varivel shell e a dentro do ambiente. Por exemplo, para modicar a varivel de a ambiente EDITOR, voc pode usar o seguinte: e % EDITOR=emacs % export EDITOR 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 <stdlib.h>. A funo getenv pega um nome de varivel e reca a torna o valor correspondente como uma sequncia de caracteres, ou NULL e 39

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 sequncias de caracteres. Cada sequncia de caracteres contendo uma e e 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


1 2 3 4 5 6 7 8 9 10 11 12 #include <s t d i o . h> / 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 a a co de congurao a programas. Suponha, por exemplo, que voc est escreca e a vendo um programa que se conecta a um servidor internet para obter alguma informao. Voc pode ter escrito o programa de forma que o nome do serca e vidor seja especicado na linha de comando. Todavia, suponha que o nome do servidor no alguma coisa que os usurios iro modicar muitas vezes. a e 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 vaa lor padro usado. Parte do seu programa pode parecer como mostrado na a e Listagem 2.4. 40

Listagem 2.4: (client.c) Parte de um Programa Cliente de Rede


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <s t d i o . h> #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 a e varivel SERVER NAME, o valor padro para o nome do servidor usado: a

% 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: 41

Mais de uma instncia de seu programa pode estar sendo executada a simultneamente (pelo mesmo usurio ou por diferentes usurios). a a a As instncias devem usar diferentes nomes de arquivos temporrios a a de 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 e ca ca temporrio. a Nomes de arquivos temporrios devem ser gerados de forma ima previs externamente; de outra forma, um atacante pode usar a vel espera entre a vericao de que um nome de arquivo fornecido j ca a est sendo usado e abrir um novo arquivo temporrio. a 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)5 . 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 sequncia de caracteres terminando com XXXXXX e e (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
5

Nota do tradutor: no slackware tem a mktemp

42

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 automaticamente 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, co essas 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 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


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 < s t d l i b . h> #include <u n i s t d . h> / 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 ; }

43

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 co ca temporarios e nomes de arquivos temporrios, incluindo mktemp, tmpnam, a a e 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

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 ??, Um Modelo de Aplicao GNU/ca Linux retorna ` demonstrao de como usar essas tcnicas para escrever a ca e 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 nal. 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 44

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 ca a ca tempo de execuo de uma condio, mas tambm como documentao sobre ca ca e ca a operao do programa dentro do cdigo fonte. Se seu programa contiver ca o um 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 podem 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 do sinalizador -DNDEBUG na sua linha de comando de compilao. Com ca NDEBUG denida, aparies da macro assert iro ser preprocessadamente co 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 caso c ca de sucesso e no zero em caso de falha, mas voc no espera que esse compora e a tamento venha a falhar em seu programa. Voc pode ter tentado escrever: e 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. Essa recompilao com NDEBUG denida ir remover ca a a chamada a assert inteiramente, de forma que a expresso nunca ir ser a a avaliada e fazer algumacoisa nunca ir ser chamada. Voc pode, ao invs do a e e cdigo anterior escrever o seguinte: o for (i = 0; i < 100; ++i) { int status = fazer_algumacoisa (); assert (status == 0); } 45

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 argumenca tos vlidos a funes. A mensagem de erro gerada por assert (poina co ter != NULL), 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 ca valor positivo para o parmetro qualquercoisa, use o seguinte no a comeo do corpo da funo: c ca assert (qualquercoisa > 0); Isso ir ajudar voc a detectar uso inadequado da funo, e essa a e ca prtica tambm faz com que esteja muito claro a algum que ao ler a e e o cdigo fonte da funo ver que existe uma restrio sobre valores o ca a ca do parmetro. 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 sub-tarefas, e cada funo completa uma tarefa atravs ca e de chamadas a outras funes para executar as sub-tarefas correspondentes. co 46

Fornecendo 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:

47

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 o arquivos ao mesmo tempo. GNU/Linux pode bloquear uma certa chamada de sistema quando um programa tenta executar uma operao para a qual no tiver ca a permisso. Por exemplo, um programa pode tentar escrever em um a arquivo marcado como somente para leitura, acessar a memria de o outro processo, ou encerrar outro programa de usurio. a Os argumentos a uma chamada de sistema podem ser invlidos, a ou devido ao usurio fornecer entradas invlidas ou devido a um a a erro no programa. Por exemplo, o programa pode passar a outro programa um endereo invlido de memria ou um descritor de c a o arquivo invlido para uma chamada de sistema. Ou, um programa a pode tentar abrir um diretrio como um arquivo, ou pode passar o o nome de um arquivo 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 a ca a est inserido no dispositivo de leitura e escrita em disco. a Uma chamada de sistema pode muitas vezes ser interrompida por um evento externo, tal como a entrega de um sinal. Isso no nea cessariamente indica falha externa, mas ocorrer em resposta ` chaa mada de um 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 manipular erros e outras circunstncias excepcionais a que no o cdigo espec a 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 48

(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 A maioria das chamadas de sistema usam uma varivel especial chamada a errno para armazenar informaes adicionais em caso de falha. 6 Quando co uma 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 pr-processamento, por conveno nomeadas em letras maisculas e ca u e 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 <errno.h> se voc for usar valores de errno. c e GNU/Linux fornece uma funo conveniente, strerror, que retorna uma ca descrio 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 <string.h> caso voc resolva usar a funo c e ca strerror. GNU/Linux tambm fornece perror, que mostra a descrio do erro die ca retamente para o uxo stderr. Passe a perror uma sequncia de caracteres e para ser usada como prexo a ser mostrado antes da descrio de erro, que ca deve habitualmente incluir o nome da funo que falhou. Inclua o arquivo ca de cabealho <stdio.h> caso voc resolva usar a funo perror. c e ca O fragmento de cdigo adiante tenta abrir um arquivo; se a abertura o falhar, o cdigo mostra uma mensagem de erro e encerra a execuo do o ca programa. 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. ca
f 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


Atualmente, por razes de trabalhar de forma segura, errno implementada como o e uma macro, mas usada como uma varivel global. e a
6

49

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 co 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 pela varivel path para o usurio a a especicado atravs de user id. Se a chamada vier a falhar, o programa exee cuta uma ao que depende do valor de errno. Note que quando detectamos ca o que provavelmente um erro no programa ns saimos usando abort ou e o assert, o que causa a gerao de um arquivo core. Esse arquivo pode ser util ca para depurao aps o encerramento do programa. Para outros erros irrecuca o perveis, tais como condies de tentativas de acesso a reas de memria no a co a o a alocadas pelo sistema operacional ao programa em questo, saimos usando a exit e um valor de sa no nulo em lugar de arquivo core pelo fato de que da a um arquivo core pode 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

coisa ,

inesperado ,

codigo

de e r r o .

Tentamos manusear t o d o s

os

50

e r r o s de abort ( ) ; }; }

codigo

possiveis ;

se

tivermos

o m i t i d o algum ,

isso

e h um e r r o ! /

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

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 sejam primeiramente liberados. Esses recursos podem ca incluir memria, descritores de arquivo, apontadores para arquivo, arquivos o temporrios, 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 perdidos. 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 temporrio 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 51

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 e funo. ca Listagem 2.6: (readle.c) Liberando Recursos em Condies Inesperadas co
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 #include #include #include #include #include < f c n t l . h> < s t d l i b . h> <s y s / s t a t . h> <s y s / t y p e s . h> <u n i s t d . 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 de armazenamento e fechar a e a c a arquivos antes de chamar exit. Voc pode precisar liberar manualmente outros recursos compartilhados, e todavia, tais como arquivos temporrios e memria compartilhada, que poa o dem potencialmente sobreviver ao encerramento de um programa.

2.3

Escrevendo e Usando Bibliotecas

Virtualmente todos os programas so linkados usando uma ou mais bibliotea cas. Qualquer programa que usa uma funo C (tais como printf ou malloc) ca 52

ir ser linkado incluindo a biblioteca C de rotinas que atuam em tempo de a execuo. Se seu programa tem uma interface grca de usurio (GUI), seu ca a a programa ser linkado incluindo bibliotecas que fazem janelas. Se seu proa grama usa uma base de dados, o provedor da base de dados ir fornecer a a voc bibliotecas que voc pode usar para acessar a base de dados convenientee e mente. Em cada um desses casos, voc deve decidir se ir linkar a biblioteca e a estaticamente ou dinmicamente. Se voc escolher estaticamente, seu proa e grama ir ser maior e mais pesado na hora de atualizar, mas provavelmente a fcil de desenvolver. Se voc linkar dinmicamente, seu programa ir ser a e a a menor, fcil de atualizar, mas pesado para desenvolver. Essa seo explica a ca como linkar de ambas as formas estaticamente e dinmicamente, examinar os a reexos dessa escolha em mais detalhes, e fornecer algumas regras prticas a de manuseio para decidir que tipo de linkagem melhor para voc. e e

2.3.1

Agrupando Arquivos Objeto

Um agrupamento de arquivos objeto (ou biblioteca esttica) simplesmente a e vrios arquivos objeto armazenados como se fossem um arquivo unico. 7 a Quando voc fornece um agrupamento de arquivos objeto ao programa que e faz linkagem, ele procura no agrupamento de arquivos objeto pelo arquivo tipo objeto que ele precisa, extrai o referido arquivo, e anexa-o ao seu programa quase da mesma forma que seria se voc tivesse fornecido o referido e arquivo objeto diretamente. Voc pode criar uma biblioteca esttica usando o comando ar. Arquivos e a de biblioteca esttica tradicionalmente usam a extenso .a em lugar da exa a tenso .o usada por um arquivos objeto comuns. Aqui est como voc pode a a e combinar test1.o e test2.o em um arquivo simples libtest.a: % ar cr libtest.a test1.o test2.o Os sinalizadores cr dizem ao ar para criar a biblioteca esttica. 8 Agora a voc pode incluir essa biblioteca esttica em seu programa usando a opo e a ca -ltest com o gcc ou com o g++, como descrito na Seo 1.2.2, Linkando ca Arquivos Objeto no Cap tulo 1, Iniciando. Quando o programa de linkagem encontra uma biblioteca esttica na a linha de comando, ele procura na biblioteca esttica por todas as denies a co
Um agrupamento de arquivos objeto grosseiramente o equivalente ao arquivo .LIB e do Windows. 8 Voc pode usar outros sinalizadores para remover um arquivo de uma biblioteca e esttica ou executar outras operaes em uma bilioteca esttica. Essas operaes so a co a co a raramente usadas mas esto documentadas na pgina de manual do ar. a a
7

53

de s mbolo (funes ou variveis) que so referenciadas a partir dos arquivos co a a objeto que ele j tiver processado mas no ainda denido. Os arquivos objeto a a que denem aqueles s mbolos so extra a dos da biblioteca esttica e inclu a dos no executvel nal. Pelo fato de o programa linkador procurar na biblioteca a esttica ` medida que elas aparecem na linha de comando, faz sentido colocar a a a biblioteca esttica no nal da linha de comando. Por exemplo, suponhamos a que test.c contenha o cdigo na Listagem 2.7 e app.c contenha o cdigo na o o Listagem 2.8. Listagem 2.7: (test.c) Area da Biblioteca
1 int f ( ) 2 { 3 return 3 ; 4 }

Listagem 2.8: Um Programa Que Utiliza as Funes da Biblioteca Acima co


1 2 3 4 5 6 extern i n t f () ; i n t main ( ) { return f ( ) ; }

Agora suponhamos que test.o seja combinado com alguns outros arquivos objetos para produzir uma bilbioteca esttica libtest.a. A seguinte linha de a comando ir falhar: a % gcc -o app -L. -ltest app.o app.o: In function main: app.o(.text+0x4): undefined reference to f collect2: ld returned 1 exit status A mensagem de erro indica que mesmo que libtest.a contenha uma denio de f, o programa de linkagem no a encontra. Isso ocorre pelo fato ca a de que a libtest.a foi pesquisada quando em primeiro lugar e antes de app.o, e naquele ponto o programa de linkagem no viu nenhuma referncia a f. a e Por outro lado, se usarmos a linha abaixo, nenhuma mensagem de erro e mostrada: % gcc -o app app.o -L. -ltest A razo que a referncia a f em app.o faz com que o programa de a e e linkagem inclua o arquivo objeto test.o contido na biblioteca esttica libtest.a. a 54

2.3.2

Bibliotecas Compartilhadas

Uma biblioteca compartilhada (tambm conhecida como um objeto compare tilhado, ou como uma biblioteca linkada dinamicamente) similar a uma e biblioteca esttica no sentido de que uma biblioteca dinmica um agrupaa a e mento de arquivos objeto. Todavia, existem muitas diferenas importantes.A c diferena mais fundamental que quando uma biblioteca compartilhada for c e linkada em um programa, o executvel nal no conter o cdigo que est prea a a o a sente na biblioteca compartilhada. Ao invs disso, o executvel meramente e a contm uma referncia ` biblioteca compartilhada. Se muitos programas no e e a sistema forem linkados usando a mesma biblioteca compartilhada, eles iro a todos referencia a referida biblioteca compartilhada, mas nenhum deles ir a conter algum cdigo da biblioteca. Dessa forma, a biblioteca compartio e lhada por todos os programas que foram linkados fazendo referncia a ela. e Uma segunda diferena que uma biblioteca compartilhada no meramente c e a e uma coleo de arquivos objeto, entre os quais objetos o programa de linkaca gem escolhe aquele que necessrio para satisfazer refercias no denidas e a e a no cdigo principal do programa que est sendo linkado. Ao invs disso, os o a e arquivos objetos que compes a biblioteca compartilhada esto combinados o a dentro de um arquivo objeto simples de forma que um programa que tiver sido linkado referenciando uma biblioteca compartilhada sempre inclua todo o cdigo presente na biblioteca, em lugar de apenas aquelas pores que foo co rem necessrias. Para criar uma bibioteca compartilhada, voc deve compilar a e os objetos que iro compor a biblioteca usando a opo -fPIC no compilador, a ca da seguinte forma: % gcc -c -fPIC test1.c A opo -fPIC 9 diz ao compilador que voc estar usando test1.o como ca e a parte de um objeto compartilhado.
Cdigo Independente da Posio - (PIC) o ca PIC habilita o suporte a cdigo independente da posio. As funes em o ca co uma biblioteca compartilhada podem ser chamadas em diferentes endereos c em diferentes programas, de forma que o cdigo no objeto compartilhado no o a ca dependente do endereo (ou posio) a partir do qual chamado. Essa c ca e considerao no tem impacto sobre voc, como programador, exceto que voc ca a e e deve lembrar-se de usar o sinalizador -fPIC quando estiver compilando algum cdigo que ir ser usado em uma biblioteca compartilhada. o a

Ento voc combina os arquivos objetos dentro de uma biblioteca coma e partilhada, como segue:
9

Position-Independent Code

55

% gcc -shared -fPIC -o libtest.so test1.o test2.o A opo -shared diz ao programa de linkagem produzir uma biblioteca ca compartilhada em lugar de um arquivo executvel comum. As bibliotecas a compartilhadas usam a extenso .so, que usada para objeto compartilhado. a e Da mesma forma que nas bibliotecas estticas, o nome sempre comea com a c lib para indicar que o arquivo uma biblioteca. e A linkagem fazendo referncia a uma biblioteca compartilhada da mesma e e forma que a linkagem referenciando uma biblioteca esttica. Por exemplo, a a linha abaixo ir fazer a linkagem referenciando libtest.so se libtest.so esa tiver no diretrio atual, ou em um dos diretrios de busca de bibliotecas o o padronizados do sistema: % gcc -o app app.o -L. -ltest Suponhamos agora que ambas as biblioteca libtest.a e libtest.so estejam dispon veis. Ento o programa de linkagem deve uma das bibliotecas e no a a outras. O programa de linkagem busca cada diretrio (primeiramente aqueles o especicados com a opo -L, e ento aqueles nos diretrios pardronizados ca a o de bibliotecas do sistema). Quando o programa de linkagem encontra um diretrio que contenha qualquer uma ou libtest.a ou libtest.so, o programa o de linkagem para a busca nos diretrios. Se somente uma das duas variantes o estiver presente no diretrio, o programa de linkagem escolhe aquela vario ante que foi encontrada em primeiro lugar. De outra forma, o programa de linkagem escolhe a verso compartilhada, a menos que voc explicitamente a e instrua ao programa de linkagem para proceder de outra forma. Voc pode e usar a opo -static para exigir bibliotecas estticas. Por exemplo, a linha de ca a comando adiante ir usar a biblioteca esttica libtest.a, mesmo se a biblioteca a a compartilhada libtest.so estiver tambm presente: e % gcc -static -o app app.o -L. -ltest O comando ldd mostra as bibliotecas compartilhadas que so referencia adas dentro de um executvel. Essas bibliotecas precisam estar dispon a veis quando o executvel for chamado. Note que o comando ldd ir listar uma a a biblioteca adicional chamada ld-linux.so, que uma parte do mecanismo de e linkagem dinmica do GNU/Linux. a Usando a Varivel de Ambiente LD LIBRARY PATH Quando voc a e zer a linkagem de um programa referenciando uma biblioteca compartilhada, o programa de linkagem no coloca o caminho completo da locaa lizao da biblioteca compartilhada no executvel resultante. Ao invs disso, ca a e 56

o programa de linkagem coloca apenas o nome da biblioteca compartilhada. Quando o programa for executado, o sistema busca pela biblioteca compartilhada e a torna dispon para ser usada pelo programa que precisa dela. vel O sistema busca somente no /lib e no /usr/lib por padro. Se uma biblioa teca compartilhada que for referenciada por seu programa executvel estiver a instalada fora daqueles diretrios, essa biblioteca compartilhada no ir ser o a a encontrada, e o sistema ir se recusar a executar o programa. a Uma soluo para esse problema usar a opo -Wl,-rpath ao usar o ca e ca programa de linkagem. Suponhamos que voc use o seguinte: e % gcc -o app app.o -L. -ltest -Wl,-rpath,/usr/local/lib

Ento, quando o programa app estiver executando, o sistema ir buscar a a em /usr/local/lib por qualquer biblioteca compartilhada requerida. Outra soluo para esse problema ajustar a varivel de ambiente LD LIca e a ca BRARY PATH na hora da execuo do programa de linkagem. Da mesma forma que a varivel de ambiente PATH, LD LIBRARY PATH uma lista de a e diretrios separados por ponto e v o rgula. Por exemplo, se LD LIBRARY PATH for /usr/local/lib:/opt/lib, ento /usr/local/lib e /opt/lib sero buscaa a dos antes dos diretrios padro /lib e /usr/lib. Voc deve tambm notar que o a e e se voc tiver LD LIBRARY PATH, o programa de linkagem ir buscar os e a diretrios fornecidos l adicionalmente aos diretrios fornecidos com a opo o a o ca -L quando estiver construindo um executvel.10 a

2.3.3

Bibliotecas Padronizadas

Mesmo se voc no especicar qualquer bibliotecas durante a fase de line a kagem, o seu programa certamente usa uma biblioteca compartilhada. Isso acontece pelo fato de GCC automaticamente fazer a linkagem usando a biblioteca C padro, a libc, mesmo sem voc pedir. As funes matemticas a e co a da biblioteca C padro no esto inclu a a a das na libc; ao invs disso, as funes e co matemticas constituem uma biblioteca separada, a libm, a qual voc precisa a e especicar explicitamente. Por exemplo, para compilar e fazer a linkagem do programa compute.c que utiliza funes trigonomtricas tais como sin e cos, co e voc deve chamar o seguinte cdigo: e o % gcc -o compute compute.c -lm
Voc pode ver uma referncia a LD RUN PATH em alguma documentao na intere e ca net. No acredite no que voc l; essa varivel atualmente no faz nada em GNU/Linux. a e e a a
10

57

Se escrever um programa em C++ e zer a linkagem dele usando os comandos c++ ou g++, voc ir tambm usar a biblioteca padro C++, e a e a libstdc++, automaticamente.

2.3.4

Dependncia de uma Biblioteca e

Uma biblioteca ir muitas vezes depender de outra biblioteca . Por exemplo, a muitos sistemas GNU/Linux incluem a libti, uma biblioteca que contm e funes para leitura e escrita de arquivos de imagem no formato TIFF. Essa co biblioteca, por sua vez, utiliza as bibliotecas libjpeg (rotinas de imagens no formato JPEG) e libz (rotinas de compresso). A Listagem 2.9 mostra um a pequeno programa que usa a biblioteca libti para abrir um arquivo de imagem no formato TIFF. Listagem 2.9: (titest.c) Usando a libti
1 2 3 4 5 6 7 8 9 10 #include <s t d i o . h> #include < t i f f i o . h> i n t main ( i n t a r g c , char a r g v ) { TIFF t i f f ; t i f f = TIFFOpen ( a r g v [ 1 ] , r ) ; TIFFClose ( t i f f ) ; return 0 ; }

Grave esse arquivo fonte como titest.c. Para compilar esse programa e fazer a linkagem referenciando a libti, especique a opo -lti na sua linha ca de linkagem: % gcc -o tifftest tifftest.c -ltiff Por padro, o comando acima ir selecionar a biblioteca compartilhada a a pela verso da libti, encontrada em /usr/lib/libti.so. Pelo fato de libti a utilizar libjpeg e libz, uma verso de biblioteca compartilhada dessas duas a e tambm puxada (uma biblioteca compartilhada pode tambm apontar para e e outra biblioteca compartilhada da qual depende). Para vericar isso, use o comando ldd : % ldd tifftest linux-gate.so.1 => (0xffffe000) /lib/libsafe.so.2 (0xb7f58000) libtiff.so.3 => /usr/lib/libtiff.so.3 (0xb7ee6000) libc.so.6 => /lib/libc.so.6 (0xb7d9a000) libdl.so.2 => /lib/libdl.so.2 (0xb7d96000) 58

libjpeg.so.62 => /usr/lib/libjpeg.so.62 (0xb7d76000) libz.so.1 => /usr/lib/libz.so.1 (0xb7d62000) libm.so.6 => /lib/libm.so.6 (0xb7d3c000) /lib/ld-linux.so.2 (0xb7f5f000) Bibliotecas estticas, por outro lado, no podem apontar para outras a a biblioteca. Se voc decidir fazer a linkagem com a verso esttica da libti e a a especicando a opo -static na sua linha de comando, voc ir encontrar ca e a s mbolos no resolvidos: a
% gcc -static -o tifftest tifftest.c -ltiff /usr/lib/.../libtiff.a(tif_aux.o): In function TIFFVGetFieldDefaulted: (.text+0x621): undefined reference to pow /usr/lib/.../libtiff.a(tif_jpeg.o): In function TIFFjpeg_data_src: (.text+0x189): undefined reference to jpeg_resync_to_restart /usr/lib/.../libtiff.a(tif_jpeg.o): In function TIFFjpeg_destroy: ...

Para fazer a linkagem desse programa estaticamente, voc deve especicar e as outras duas bibliotecas explicitamente: % gcc -static -o tifftest tifftest.c -ltiff -ljpeg -lz Ocasionalmente, duas bibliotecas iro ser mutuamente dependentes. Em a outras palavras, a primeira biblioteca esttica ir referenciar s a a mbolos na segunda biblioteca esttica, e vice versa. Essa situao geralmente provea ca e niente de um planejamento falho, mas aparece ocasionalmente. Nesses casos, voc pode repetir uma biblioteca multiplas vezes na linha de comando. O e programa de linkagem ir refazer a procura na biblioteca cada vez que isso a ocorrer. Por exemplo, a linha adiante ir fazer com que libqqcoisa.a seja a procurada multiplas vezes: % gcc -o app app.o -lqqcoisa -loutracoisa -lqqcoisa De forma que, mesmo se libqqcoisa.a referencie s mbolos em liboutracoisa.a, e vice versa, o programa ir ser linkado com sucesso. a

2.3.5

Prs e Contras o

Agora que voc sabe tudo sobre bibliotecas estticas e bibliotecas compare a tilhadas, voc est provavelmente se perguntando qual usar. Existe umas e e poucas consideraoes maiores para ter em mente. c Uma grande vantagem de uma biblioteca compartilhada que essa biblie oteca compartilhada economiza espao no sistema onde o programa estiver c instalado. Se voc estiver instalando 10 programas, e eles todos fazem uso e 59

da mesma biblioteca compartilhada, ento voc libera uma grande quantia e dade de espao usando uma biblioteca compartilhada. Se voc tiver usado c e biblioteca esttica em substituio ` compatilhada, a biblioteca est inclu a ca a a da em todos os 10 programas repetidamente. Ento, usando bibliotecas coma partilhadas libera espao em disco. As bibliotecas compartilhadas tambm c e reduzem tempos cpia e libera recursos de coneco se seu programa est o ca a sendo copiado a partir da web. Uma vantagem relacionada `s bibliotecas a compartilhadas que o usurios podem escolher entre atualizar as biblioe a tecas com ou sem atualizar todos os programas que dependem delas. Por exemplo, suponha que voc produza uma biblioteca compartilhada que gee rencia coneces HTTP. Muitos programas podem depender dessa biblioteca. co Se voc encontrar um erro nessa biblioteca, voc pode atualizar a biblioteca. e e instantaneamente, todos os programas que dependerem da biblioteca iro ser a corrigidos; voc no ter que refazer a linkagem de todos os programas que e a a seria o caminho adotado caso se estivesse usando a linkagem esttica. As vana tagem acima fariam voc pensar em usar sempre a biblioteca compartilhada. e Todavia, razes substanciais existem para o uso da biblioteca esttica em o a lugar da compartilhada. O fato que uma atualizao com o uso de uma bica blioteca compartilhada afeta todos os programas que dependem dela pode ser uma desvantagem. Por exemplo, se voc estiver desenvolvendo um programa e de alta disponibilidade, voc pode preferir fazer a linkagem referenciando e uma biblioteca esttica de forma que uma atualizao de bibliotecas coma ca partilhadas no sistema no afete seu programa. (De outra forma, usurios a a podem atualizar a biblioteca compartilhada, afetando seu programa que foi compilado referenciando bibliotecas compartilhadas e causarem uma parada no programa, e ento chamar sua linha de suporte ao usurio, censurando a a voc!) Se voc est indo pelo caminho de no instalar suas biblioteca no /lib e e a a ou no /usr/lib, voc deve denitivamente pensar duas vezes sobre usar uma e biblioteca compartilhada. (Voc no espera instalar suas bibliotecas naquee a les diretrios se voc no esperar que usurios que iro instalar seu software o e a a a possuam privilgio de administrador.) Particularmente, a opo/artif de e ca cio compilao -Wl,-rpath no ir servir de nada se voc no sabe onde as biblica a a e a otecas esto indo parar. E pedindo a seus usurios para ajustar a varivel a a a de ambiente LD LIBRARY PATH signica uma tarefa extra para eles. Pelo fato de cada usurio ter de fazer isso individualmente, isso uma substancial a e e adicional carga de responsabilidade. Voc ir ter que pesar essas vantagens e a e desvantagens para cada programa que voc vier a distribuir. e

60

2.3.6

Carregamento e Descarregamento Dinmico a

Algumas vezes voc pode desejar carregar algum cdigo em tempo de execue o ca o sem explicitamente fazer a linkagem daquele cdigo. Por exemplo, cono sidere uma aplicao que suporta mdulos do tipo plug-in, tal como um ca o navegador internet. O navegador permite a desenvolvedores externos ao projeto criar acessrios para fornecer ao navegador funcionalidades adicionais. o Os desenvolvedores externos criam bibliotecas compartilhadas e as colocam em uma localizao conhecida pelo navegador. O navegador ento autoca a maticamente carrega o cdigo nessas bibliotecas. Essa funcionalidade est o a dispon em ambiente GNU/Linux atravs do uso da funo dlopen. Voc j vel e ca e a pode ter aberto uma biblioteca compartilhada chamada libtest.so chamando a funo dlopen da forma abaixo: ca dlopen ("libtest.so", RTLD_LAZY) (O segundo parmetro um sinalizador que indica como associar s a e mbolos na biblioteca compartilhada. Voc pode consultar as pginas de manual e a instaladas no seu sistema sobre dlopen se voc desejar mais informao, mas e ca RTLD LAZY comumente a opo que voc deseja.) Para usar funes de e ca e co carregamento dinmico, inclua o arquivo de cabealho <dlfcn.h> e faa a a c c linkagem com a opo -ldl para selecionar a biblioteca libdl. ca O valor de retorno dessa funo um void * que usado como um manica e e pulador para a biblioteca compartilhada. Voc pode passar esse valor para a e funo dlsym para obter o endereo de uma funo que tiver sido chamada ca c ca com a biblioteca compartilhada. Por exemplo, se libtest.so dene uma funo ca e chamada minha funcao, voc pode ter chamado a minha funcao como segue: void* manipulador = dlopen ("libtest.so", RTLD_LAZY); void (*test)() = dlsym (manipulador, "minha_funcao"); (*test)(); dlclose (manipulador); A funo dlsym pode tambm ser usada para obter um apontador para ca e uma varivel esttica na biblioteca compartilhada. a a Ambas as funes dlopen e dlsym retornam NULL se no obtiverem suco a cesso. no evento descrito acima, voc pode chamar a funo dlerror (sem e ca parmetros) para obter uma mensagem de erro em formato leg aos hua vel manos descrevendo o problema. A funo dlclose descarrega a biblioteca compartilhada. Tecnicamente, ca a funo dlopen carrega a biblioteca somente se a referida biblioteca j no ca a a tiver sido chamada anteriormente. Se a biblioteca j tiver sido chamada, a 61

dlopen simplesmente incrementa o contador de referncia da biblioteca. Sie milarmente, a funo dlclose decrementa o contador de referncia e ento ca e a descarrega a biblioteca somente se o contador de referncia tiver alcanado e c o valor zero. Se voc est escrevendo um cdigo em sua biblioteca compartilhada em e a o C++, voc ir provavelmente desejar declarar aquelas funes e variveis que e a co a voc planeja acessar a partir de algum lugar com o especicador de linkagem e extern C. Por exemplos, se a funo C++ minha funcao estiver em uma ca biblioteca compartilhada e voc desejar acessar essa funo com a funo e ca ca dlsym, voc deve declarar a minha funcao como segue: e extern "C" void minha_funcao (); Isso evita que o compilador C++ desgure o nome da funo, pelo fato ca de o compilador C++ poder mudar o nome da funo de minha funo para ca ca um diferente, um nome mais engraado ao olhar que expresse informaes c co extras sobre a funo. Um compilador C no ir desgurar nomes; os nomes ca a a iro ser usados qualquer que seja o nome que voc fornea para sua funo a e c ca ou varivel. a

62

Cap tulo 3 Processos


UMA INSTANCIA EXECUTANDO UM PROGRAMA CHAMA-SE UM PROCESSO. Se voc tem duas janelas de terminal exibindo informaes em e co sua tela, ento voc est provavelmente executando o mesmo programa de a e a terminal duas vezes voc tem dois processos de terminal. Cada janela de e terminal est provavelmente executando um shell ; cada shell sendo executado a um outro processo. Quando voc chama um comando em um shell, o e e programa correspondente executado em um novo processo; o processo de e shell continua quando o processo do comando chamado se completar. Programadores avanados muitas vezes utilizam muitos processos em coc operao em uma aplicao simples para habilitar a capacidade da aplicao ca ca ca de executar mais de uma coisa ao mesmo tempo, para incrementar robustez da aplicao, e para fazer uso dos programas j existentes. ca a A maioria das funes de manipulao de processos descritas nesse cap co ca tulo so similares a aquelas em outros sistemas UNIX. A maioria declarada no a e arquivo de cabealho <unistd.h>; verique a pgina de manual de cada c a funo para ter certeza. ca

3.1

Visualizando Processos

Sempre que voc senta em seu computador para us-lo, exitem processos em e a atividade. Todos os programas sendo executados usam um ou mais processos. Vamos iniciar dando uma olhada nos processos j existentes em seu a computador. 63

3.1.1

Identicadores de Processos

Cada processo em um sistema GNU/Linux identicado por seu unico e nmero de identicao, algumas vezes referenciado como pid. Identicadou ca res de Processos so nmeros inteiros de 16-bit que so atribuidos sequnciala u a e mente pelo kernel GNU/Linux a cada vez que um novo processo criado. e Todo processo tem um processo pai (exceto o processo init, descrito na Seo 3.3.4, Processos do Tipo Zumbi). Dessa forma, voc pode pensar de ca e processos em um sistema GNU/Linux como organizados em uma rvore, com a o processo init sendo a ra principal que originou toda a rvore. A identiz a cao do processo pai, ou ppid, simplesmente o nmero de identicao ca e u ca do processo pai. Quando zermos referncia ao nmero de identicao de e u ca um processo em um programa em C ou em C++, sempre usa-se a denio ca de tipo pid t, que feita em <sys/types.h>. Um programa pode obter o e nmero de identicao do processo que o est executando com a chamada u ca a de sistema getpid(), e o programa tambm pode obter o nmero de identie u cao de processo do processo que o originou com a chamada de sistema ca getppid(). Por exemplo, o programa na Listagem 3.1 mostra o o nmero de u identicao do processo que o est executando e o nmero de identicao ca a u ca do processo que o originou. Listagem 3.1: ( print-pid.c) Mostrando o ID do Processo
1 2 3 4 5 6 7 8 9 #include <s t d i o . h> #include <u n i s t d . h> i n t main { printf printf return } () ( The p r o c e s s i d i s %d\n , ( i n t ) g e t p i d ( ) ) ; ( The p a r e n t p r o c e s s i d i s %d\n , ( i n t ) g e t p p i d 0;

() ) ;

Observe que se voc chamar esse programa muitas vezes, um ID diferente e de processo ser reportado a cada vez que voc chamar o programa pelo a e fato de cada chamada estar em um novo processo. Todavia, se voc chamar e o programa vrias vezes a partir da mesma janela de shell, o nmero de a u identicao do processo que o originou (isto , a nmero de identicao do ca e u ca processo do shell ) o mesmo. e

3.1.2

Visualizando os Processos Ativos

O comando ps mostra os processos que estiverem sendo executados sobre seu sistema. A verso GNU/Linux do ps tem muitas opes pelo fato de tentar a co ser compat com as verses do ps de muitas outras variantes UNIXs. Essas vel o 64

opes controlam quais processos so listados e qual informao sobre cada co a ca processo dever ser mostrada. a Por padro, chamando ps mostra os processos controlados pelo terminal a ou janela de terminal na qual o comando ps for chamado. Por exemplo: % ps PID TTY 21693 pts/8 21694 pts/8

TIME CMD 00:00:00 bash 00:00:00 ps

Essa chamada de ps mostra dois processos. O primeiro, o bash, um shell e executando sobre o referido terminal. O segundo a instncia de execuo e a ca do programa ps propriamente dito. A primeira coluna, rotulada PID, mostra o nmero de identicao de cada processo listado na sa do comando. u ca da Para uma olhada mais detalhada no que est sendo executado no seu a sistema GNU/Linux, use o seguinte: % ps -e -o pid,ppid,command A opo -e instrui o ps a mostrar todos os processos sendo executados no ca sistema. A opo -o pid,ppid,command diz ao ps qual informao mostrar ca ca sobre cada processo no caso acima, o ID do processo, o ID do processo pai, e o comando sendo executado no referido processo.
Formatos de Sa do ps da Com a opo -o fornecida ao comando ps, voc especica a informao soca e ca bre o processo que voc deseja na sa no formato de uma lista separada e da por v rgulas. por exemplo, ps -o pid,user, start time,command mostra o ID do processo, o nome do usurio dono do processo, o tempo decorrido desde a quando o processo comeou, e o comando que est executando o processo. c a Veja a pgina de manual do comando ps para a lista completa dos cdigos a o de campo. Voc pode usar as opes -f (lista completa), -l (lista longa), ou e co -j (lista de tarefas) ao invs da opo -o acima e usar esses trs diferentes e ca e formatos predenidos de listagem (completa, longa ou de tarefas).

Aqui est algumas linhas iniciais e nais de sa do comando ps em meu a da sistema. Voc pode ver diferentes sa e das, dependendo do que estiver sendo executado em seu sistema. % ps -e -o pid,ppid,command PID PPID COMMAND 1 0 init [5] 2 1 [kflushd] 65

1 [kupdate]

... 21725 21693 xterm 21727 21725 bash 21728 21727 ps -e -o pid,ppid,command Note que o ID do processo pai do comando ps, 21727, o ID do bash, o e shell a partir do qual chamou-se o ps. O processo pai do bash por sua vez o e de nmero 21725, o ID do processo do programa xterm no qual o shell est u a sendo executado.

3.1.3

Encerrando um Processo

Voc pode encerrar um processo que est sendo executado com o comando e a kill. Simplesmente especicando na linha de comando o ID do processo a ser encerrado. O comando kill trabalha enviando ao processo um SIGTERM, ou sinal de encerramento.1 Isso faz com que o processo encerre, a menos que o programa em execuo explicitamente manipule ou mascare o sinal SIGTERM. Sinais ca so descritos na Seo 3.3, Sinais. a ca

3.2

Criando Processos

Duas tcnicas so usadas para criar um novo processo. A primeira relae a e tivamente simples mas deve ser usada de forma bem comedida e econmica o pelo fato de ser ineciente e de ter considerveis riscos de segurana. A sea c gunda tcnica mais complexa mas fornece grande exibilidade, rapidez, e e e segurana. c

3.2.1

Usando system

A funo system na biblioteca C padro fornece um caminho fcil para execa a a cutar um comando dentro de um programa, principalmente se o comando tiver sido digitado dentro de um shell. De fato, a funo system cria um ca sub-processo que executa o shell Bourne padro (/bin/sh) e repassa o coa mando `quele shell para execuo. Por exemplo, o programa na Listagem a ca 3.2 chama o comando ls para mostrar o contedo do diretrio ra como se u o z, voc digitasse ls -l / dentro de um shell. e
Voc pode tambm usar o comando kill para enviar outros sinais a um processo. Isso e e descrito na Seo 3.3.1, Encerramento de Processos. e ca
1

66

Listagem 3.2: (system.c) Usando uma chamada ` funo system a ca


1 2 3 4 5 6 7 8 #include < s t d l i b . h> i n t main ( ) { int r e t u r n v a l u e ; r e t u r n v a l u e = system ( l s l / ) ; return r e t u r n v a l u e ; }

A funo system retorna a condio de sa do comando do shell. Se o ca ca da shell propriamente dito no puder ser executado, a funo system retorna o a ca cdigo 127; se outro erro ocorrer, a funo system retorna -1. o ca Pelo fato de a funo system usar um shell para chamar seu comando, ela ca d margens a recursos, limitaes, e a falhas de segurana do shell de seu sisa co c tema. Voc no pode saber seguramente sobre a disponibilidade de qualquer e a verso em particular do shell Bourne. Em muitos sistemas UNIX, /bin/sh a e uma ligao simblica para outro shell. Por exemplo, na maioria dos sisteca o mas GNU/Linux, o /bin/sh aponta para o bash (o Bourne-Again SHell ), e diferentes distribuies GNU/Linux utilizam diferentes verses do bash. Chaco o mando um programa com privilgios de administrador com a funo system, e ca pode exemplo, pode ter diferentes resultados sob diferentes sistemas GNU/Linux. Devido ao que foi aqui exposto, prefer usar o mtodo fork and e vel e exec (bifurcar e executar) para criar processos.

3.2.2

Usando bifurcar e executar

A API 2 do DOS e do Windows possuem a fam spawn de funes. Essas lia co funes recebem como argumento o nome de um programa para executar e co criam uma nova intncia de processo daquele programa. O GNU/Linux no a a contm uma funo que faz tudo isso de uma vez s. Ao invs disso, fornece e ca o e uma funo, a funo fork, que cria um processo lho que uma cpia exata ca ca e o de seu processo pai. GNU/Linux fornece outro conjunto de funes, a fam co lia das funes exec, que faz com que um processo em particular no mais seja co a uma instncia de um programa e ao invs disso torne-se uma instncia de a e a outro programa. Para criar um novo processo, voc primeiramente deve usar e a funo fork para fazer uma cpia do processo atual que est executando ca o a seu programa. A seguir voc usa a funo exec para transformar um desses e ca dois processos iguais em uma instncia do programa que voc deseja criar. a e Chamando a funo fork Quando um programa chama a funo fork, ca ca um processo clone do processo que fez a chamada, chamado processo lho, criado. O processo pai continua executando o programa na instruo e ca
2

Nota do tradutor: Application Programming Interface

67

imediatamente aps a instruo que chamou a funo fork. O processo lho, o ca ca tambm, executa o mesmo programa a partir da mesma posio de instruo. e ca ca Como fazer para os dois processos diferirem? Primeiramente, o processo lho um novo processo e portanto tem um novo ID de processo, distinto e do ID de seu processo pai. Um caminho para um programa distinguir se ele mesmo est em um processo pai ou em um processo lho chamar a funo a e ca getpid da biblioteca C padro. Todavia, a funo fork fornece diferentes a ca valores de retorno quando chamada a partir de um processo pai ou a partir de um processo lho um processo entra na chamada a fork, dois processos saem com diferentes valores de retorno. O valor de retorno no processo pai o ID de processo do processo lho. O valor de retorno no processo lho e zero. Pelo fato de nenhum processo mesmo ter um ID de processo com o e valor zero, isso torna fcil para o programa distinguir se est sendo executado a a como o processo pai ou processo lho. A Listagem 3.3 um exemplo de utilizao da funo fork para duplicar e ca ca o processo de um programa. Note que o primeiro bloco da declarao if ca e executado somente no processo pai, enquando a clusula else executada no a e processo lho. Listagem 3.3: ( fork.c) Usando fork para Duplicar o Processo de um Programa
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <s t d i o . h> #include <s y s / t y p e s . h> #include <u n i s t d . h> i n t main ( ) { pid t child pid ; printf ( t h e main program p r o c e s s id i s %d\n , ( i n t ) getpid () ) ;

child pid = fork () ; i f ( c h i l d p i d != 0 ) { p r i n t f ( t h i s i s t h e p a r e n t p r o c e s s , w i t h i d %d\n , ( i n t ) g e t p i d ( ) ) ; p r i n t f ( t h e c h i l d s p r o c e s s i d i s %d\n , ( i n t ) c h i l d p i d ) ; } else p r i n t f ( t h i s i s t h e c h i l d p r o c e s s , w i t h i d %d\n , ( i n t ) g e t p i d ( ) ) ; return 0 ; }

Usando a Fam exec As funes exec substituem o programa que est lia co a sendo executado em um processo por outro programa. Quando um programa chama uma funo exec, o processo que abriga a chamada feita ` funo exec ca a ca imediatamente cessa de executar o programa atual e inicia a execuo de um ca novo programa a partir do in cio desse mesmo novo programa, assumindo que a chamada ` funo exec tenha sido executada com sucesso. a ca Dentro da fam de funes exec, existem funes que variam de forma lia co co muito pequena na parte que se refere a compatibilidade e no que se refere ` a 68

maneira de serem chamadas. Funes que possuem a letra p em seus nomes (execvp e execlp) co aceitam um nome de programa e procuram por um programa que tenha o nome recebido no atual caminho de execuo; funes que ca co no contiverem o p no nome devem receber o caminho completo a de localizao do programa a ser executado. ca Funes que possuem a letra v em seus nome (execv, execvp, e co execve) aceitam a lista de argumentos para o novo programa como um vetor terminado pelo caractere NULL de apontadores para sequncias de caractere. Funes que contiverem a letra l(execl, e co execlp, e execle) aceitam a lista de argumentos usando o mecanismo varargs da linguagem C. a As funes que possuem a letra e em seus nomes (execve e co execle) aceitam um argumento adicional, um vetor de variveis a de ambiente. O argumento deve ser um vetor de apontadores para sequncia de caracteres terminado pelo caractere NULL. Cada e sequncias de caractere deve ser da forma VARIAVEL=valor. e
Nota do tradutor: Veja http://www.cs.utah.edu/dept/old/texinfo/glibcmanual-0.02/library toc.html #SEC472 e tambm http://gcc.gnu.org/onlinedocs/gccint/Varargs.html. e
a

Pelo fato de a funo exec substituir o programa chamado por outro, ela ca nunca retorna a menos que um erro ocorra. A lista de argumentos passada ao programa anloga aos argumentos de e a linha comando que voc especica a um programa quando voc o executa e e a partir de um shell. Eles esto disponiveis atravs dos parmetros argc a e a e de argv passados ` funo main. Lembre-se, quando um programa for a ca chamado a partir de um shell, o shell ajusta o primeiro elemento da lista de argumentos (argv[0] ) para o nome do programa, o segundo elemento da lista de argumentos (argv[1] ) para o primeiro argumento da linha de comando, e assim por diante. Quando voc usar uma funo exec em seu programa, e ca voc, tambm, deve passar o nome da funo como o primeiro elemento da e e ca lista de argumentos. Usando fork e exec Juntas Um modelo comum para executar um subprograma dentro de um programa primeiramente bifurcar o processo e ento e a executar o sub-programa. Isso permite que o programa que fez a chamada continue a execuo no processo pai enquanto o mesmo programa que fez a ca chamada substitu pelo subprograma no processo lho. e do 69

O programa na Listagem 3.4, da mesma forma que a Listagem 3.2, mostra o contedo do diretrio ra usando o comando ls. Diferindo do exemplo u o z anterior, de outra forma, a Listagem 3.4 chama o comando ls diretamente, passando ao ls os argumentos de linha de comando -l e / ao invs de e chamar o ls a partir de um shell. Listagem 3.4: ( fork-exec.c) Usando fork e exec Juntas
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 #include #include #include #include <s t d i o . h> < s t d l i b . h> <s y s / t y p e s . h> <u n i s t d . h>

/ Spawn a c h i l d p r o c e s s r u n n i n g a new p r o g r a m . PROGRAM i s t h e name o f t h e program t o run ; t h e p a t h w i l l be s e a r c h e d f o r t h i s program . ARG LIST i s a NULL e r m i n a t e d l i s t o f c h a r a c t e r s t r i n g s t o b e t p a s s e d as t h e program s argument l i s t . Returns the pr ocess id of t h e spawned p r o c e s s . / i n t spawn ( char program , char { pid t child pid ; arg list )

/ D u p l i c a t e t h i s p r o c e s s . / child pid = fork () ; i f ( c h i l d p i d != 0 ) / T h i s i s t h e p a r e n t p r o c e s s . / return c h i l d p i d ; else { / Now e x e c u t e PROGRAM, s e a r c h i n g f o r i t i n t h e p a t h . / e x e c v p ( program , a r g l i s t ) ; / The e x e c v p f u n c t i o n r e t u r n s o n l y i f an e r r o r o c c u r s . / f p r i n t f ( s t d e r r , an e r r o r o c c u r r e d i n e x e c v p \n ) ; abort () ; } } i n t main ( ) { / The a r g u m e n t l i s t t o p a s s t o t h e l s command . / char a r g l i s t [ ] = { ls , / a r g v [ 0 ] , t h e name o f t h e p r o g r a m . / l , / , NULL / The a r g u m e n t l i s t must end w i t h a NULL . }; / Spawn a c h i l d p r o c e s s r u n n i n g t h e l s command . returned child process id . / spawn ( l s , a r g l i s t ) ; printf ( done w i t h main program \n ) ;

Ignore

the

return 0 ; }

3.2.3

Agendamento de Processo

GNU/Linux faz o agendamento dos processos pai e processos lho independentemente; no existe garantias de qual dos dois ir ser executado em pria a meiro lugar, ou quanto tempo de execuo previamente ir decorrer antes de ca a GNU/Linux interromp-lo e liberar o ciclo de processamento para o outro e processo (ou para algum outro processo do sistema que no os processos pai a e lho aqui citados) ser executado. Em particular, nenhuma parte, alguma parte, ou todo o processo do comando ls pode executar em um processo lho 70

antes de o processo pai que o criou ser encerrado.3 GNU/Linux promete que cada processo ir ser executado em algum momento nenhum processo ir a a ser totalmente discriminado na distribuio dos recursos de execuo.4 ca ca Voc pode especicar que um processo menos importante e deve ree e ceber uma prioridades mais baixa atribuindo a esse processo um valor alto de gentileza. Por padro, todo processo recebe um valor de gentileza zero. a Um valor de gentileza mais alto signica que o processo recebe uma menor prioridade de execuo; de modo contrrio, um processo com um baixo (isto ca a , negativo) valor de gentileza recebe mais tempo de execuo. e ca Para executar um programa com um valor de gentileza no nulo, use o a comando nice, especicando o valor de gentileza com a opo -n. Por exemca plo, adiante mostra-se como voc pode chamar o comando sort entrada.txt e > saida.txt, que corresponde a uma longa operao de ordenao, como ca ca reduzida prioridade de forma que essa operao de ordenao no torne o ca ca a sistema muito lento: % nice -n 10 sort input.txt > output.txt Voc pode usar o comando renice para modicar o n de gentileza de e vel um processo sendo executado a partir da linha de comando. Para modicar o n de gentileza de um processo que est em execuo a vel a ca partir de outro programa, use a funo nice. O argumento dessa funo um ca ca e valor de incremento, que adicionado ao n de gentileza do processo est e vel a executando o programa cujo n de gentileza se deseja mudar. Lembre-se vel que um valor positivo aumenta o valor de gentileza e dessa forma reduz a prioridade de execuo de um processo. ca Note que somente um processo com privilgios de usurio root pode exee a cutar um ou outro processo com um valor de gentileza negativo ou reduzir o valor de gentileza de um processo que est sendo executado. Isso signica a que voc pode especicar valores negativos para os comando nice e renice soe mente quando est acessando o computador como super-usurio, e somente a a um processo executando com privilgios de super-usurio pode enviar um e a valor negativo para a funo nice da glibc. Esse comportamento previne que ca usurios comuns consigam prioriade de execuo em nome de outros usurios a ca a que no o seu prprio usando o sistema. a o
Um mtodo para denir a ordem de execuo de dois processos apresentado na e ca e seo 3.3.2, Esperando pelo Encerramento de um Processo. ca 4 Nota do tradutor:O autor refere-se aos algor tmos de escalonamento. Veja tambm e http://www.kernel.org/doc/#5.1
3

71

3.3

Sinais

Sinais so mecanismos usados como forma de comunicao e manipulao a ca ca de processos em GNU/Linux. O tpico que fala de sinais muito extenso; o e aqui falaremos sobre alguns sinais mais importantes e tcnicas que so usadas e a para controlar processos. Um sinal uma mensagem especial enviada a um processo. Sinais so e a ass ncronos; quando um processo recebe um sinal, o referido processo manipula o sinal imediatamente, sem encerrar a funo que est processando no ca a momento ou mesmo sem encerrar a linha de cdigo que ele est executando o a no momento. Existem muitas dzias de diferentes sinais, cada um com um u signicado diferente. Cada tipo de sinal especicado atravs de seu nmero e e u de sinal, mas em programas, voc comumente se refere a um sinal atravs de e e seu nome. Em GNU/Linux, os sinais so denidos em /usr/include/bits/a signum.h. (Voc no deve incluir esse arquivo de cabealho diretamente em e a c seu programa; ao invs disso, use <signal.h>.) e Quando um processo recebe um sinal, esse mesmo processo pode ter uma entre muitas respostas/comportamentos, dependendo do comportamento do sinal recebido. Para cada sinal, existe um comportamento padro, que detera mina o que acontece ao processo se o programa executado no processo no a especica algum outro comportamento. Para a maioria dos tipos de sinal, um programa especica algum comportamento ou ignora o sinal ou chama uma funo especial manipuladora de sinal para responder ao sinal. Se uma ca funo manipuladora de sinal for usada, o programa atualmente em execuo ca ca colocado em estado de espera, a funo manipuladora de sinal executada, e ca e e, quando a funo manipuladora de sinal retornar, o programa que estava ca sendo executado na hora da chegada do sinal retomado pelo processo e e continua do ponto onde parou. O sistema GNU/Linux envia sinais a processos em resposta a condies co espec cas. Por exemplo, os sinais SIGBUS (erro de bus), SIGSEGV (violao de segmento de memria), e SIGFPE (exceo de ponto utuante) ca o ca podem ser enviados a um processo que tenta executar uma operao ilegal. ca O comportamento padro para esses sinais encerrar o processo e produzir a e um arquivo core. Um processo pode tambm enviar um sinal a outro processo. Um uso e comum desse mecanismo encerrar outro processo enviando um sinal SIGe TERM ou um sinal SIGKILL. 5
Qual a diferena? O sinal SIGTERM pergunta a um processo se ele pode terminar; o c processo pode ignorar a requisio por mascaramento ou ignorar o sinal. O sinal SIGKILL ca sempre encerra o processo imediatamente pelo fato de o processo no poder mascarar ou a ignorar o sinal SIGKILL.
5

72

Outro uso comum enviar um comando a um programa que est sendo e a executado. Dois sinais denidos pelo usurio so reservados com esse oba a jetivo: SIGUSR1 e SIGUSR2. O sinal SIGHUP algumas vezes usado para e esse propsito tambm, comumente para acordar um programa que est coo e a chilando ou fazer com que um programa releia seus arquivos de congurao. ca A funo sigaction pode ser usada para congurar um comportamento ca de sinal. O primeiro parmetro o nmero do sinal. Os dois parmetros a e u a imediatamente a seguir so apontadores para estruturas da funo sigaction; a ca o primeiro dos dois contm o comportamento desejado para aquele nmero e u de sinal, enquanto o segundo recebe o comportamento atualmente existente. O campo mais importante tanto na primeira como na segunda estrutura apontadas da funo sigaction sa handler. O sa handler pode receber um ca e dos trs valores abaixo: e SIG DFL, que especica o comportamento padro para o sinal. a SIG IGN, que especica a possibilidade de o sinal pode ser ignorado. Um apontador para uma funo manipuladora de sinal. A funo ca ca deve receber um parmetro, o nmero do sinal, e retornar void a . a u
a

Nota do tradutor:Vazio

Pelo fato de sinais serem ass ncronos, o programa principal pode estar em um estado muito frgil quando um sinal processado e dessa forma a e tambm enquanto uma funo manipuladora de sinal est sendo executada. e ca a Portanto, voc deve evitar executar quaisquer operaes de E/S ou chamar a e co maior parte das funes de biblioteca e de sistema a partir de manipuladores co de sinal. Um manipulador de sinal executa o trabalho m nimo necessrio para resa ponder ao sinal, e ento retornar o controle ao programa principal (ou ena cerrar o programa). Na maioria dos casos, a tarefa do manipulador de sinal consiste simplesmente em gravar o fato de que um sinal ocorreu. O programa principal ento verica periodicamente se um sinal ocorreu e reage conforme a o sinal ocorrido ou no ocorrido. a poss que uma funo manipuladora de sinal seja interrompida por E vel ca meio da entrega de outro sinal. Embora isso seja uma ocorrncia rara, se e vier a ocorrer, ir ser muito dif diagnosticar e depurar o problema. (Isso a cil um exemplo de uma condio de corrida, discutida no Cap e ca tulo 4, Linhas de Execuo Seo 4.4, Sincronizao e Sees Cr ca ca ca co ticas.) Portanto, voc deve ser muito cuidadoso sobre o que seu programa faz em uma funo e ca manipuladora de sinal. 73

Mesmo a atribuio de um valor a uma varivel global pode ser perigosa ca a pelo fato de que a atribuio poder ser atualmente realizada em duas ou mais ca instrues de mquina, e um segundo sinal pode ocorrer entre essas duas co a instrues de mquina, abandonando a varivel em um estado corrompido. co a a Se voc vier a usar uma varivel global para marcar um sinal a partir de e a uma funo manipuladora de sinal, essa varivel deve ser do tipo especial ca a sig atomic t. GNU/Linux garante que atribuies a variveis desse tipo so co a a realizadas em uma instruo simples e portanto no pode ser interrompida ca a no meio do caminho. Em GNU/Linux, sig atomic t um int comum; de e fato, atribuies a tipos inteiros do tamanho de int ou de menor tamanho, co ou para apontadores, so atmicos. Se voc deseja escrever um programa a o e que seja portvel para qualquer sistema UNIX padronizado, apesar do que a foi aqui escrito, use o tipo sig atomic t para variveis globais. a O esqueleto de programa na Listagem 3.5 por exemplo, utiliza uma funo ca manipuladora de sinal para contar o nmero de vezes que o programa recebe u SIGUSR1, um dos sinais reservados para uso por aplicao. ca Listagem 3.5: (sigusr1.c) Usando um Manipulador de Sinal
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include #include #include #include #include < s i g n a l . h> <s t d i o . h> < s t r i n g . h> <s y s / t y p e s . h> <u n i s t d . h> sigusr1 count = 0;

sig atomic t

void h a n d l e r ( i n t s i g n a l n u m b e r ) { ++s i g u s r 1 c o u n t ; } i n t main ( ) { struct s i g a c t i o n sa ; memset (& sa , 0 , s i z e o f ( s a ) ) ; s a . s a h a n d l e r = &h a n d l e r ; s i g a c t i o n ( SIGUSR1 , &sa , NULL) ; / Do some l e n g t h y / . . . / stuff here . /

p r i n t f ( SIGUSR1 was r a i s e d %d t i m e s \n , return 0 ; }

sigusr1 count ) ;

3.3.1

Encerramento de Processos

Normalmente, um processo encerra atravs de um entre dois caminhos. Ou e o programa que est sendo executado chama a funo exit, ou a fuo main a ca ca do programa retorna. Cada processo tem um cdigo de sa o da: um nmero u que o processo retorna a seu processo pai. O cdigo de sa o argumento o da e passado ` funo exit, ou o valor retornado a partir da funo main. a ca ca Um processo pode tambm terminar de forma abrupta, em resposta a um e sinal. Por exemplo, os sinais SIGBUS, SIGSEGV, e SIGFPE mencionados 74

anteriormente fazem com que o processo encerre. Outros sinais so usados a para encerrar um processo explicitamente. O sinal SIGINT enviado a e um processo quando o usurio tenta encerr-lo digitando Ctrl+C em seu a a terminal. O sinal SIGTERM enviado pelo comando kill. A disposio e ca padro em ambos os casos encerrar o processo. Por meio de chamada ` a e a funo abort, um processo envia a si mesmo o sinal SIGABRT, que encerra o ca processo e produz um arquivo core. O mais poderoso sinal para encerrar um processo SIGKILL, que encerra um processo imediatamente e no pode ser e a bloqueado ou manuseado por um programa. Qualquer desses sinais pode ser enviado usando o comando kill por meio da especicao de um sinalizador extra de linha de comando; por exemplo, ca para encerrar um processo perturbador por meio do envio de a esse processo de um SIGKILL, use o seguinte comando, onde pid o nmero de identie u cao do seu processo perturbador: ca % kill -KILL pid Para enviar um sinal a partir de um programa, use a funo kill. O ca primeiro parmetro o ID do processo alvo. O segundo parmetro o nmero a e a e u do sinal; use SIGTERM para simular o comportamento padro do comando a kill. Por exemplo, sendo child pid o ID de processo do processo lho, voc e pode usar a funo kill para encerrar um processo lho a partir do processo ca pai por meio de um chamado ` funo kill como o seguinte: a ca kill (child_pid, SIGTERM); Inclua cabealhos <sys/types.h> e <signal.h> caso voc resolva usar a c e funo kill. ca Por conveno, o cdigo de sa usado para indicar se o programa foi ca o da e executado corretamente. Um cdigo de sa com valor zero indica execuo o da ca correta, enquanto um cdigo de sa no nulo indica que um erro ocorreu. o da a No caso de ocorrncia de erro, o valor particular retornado pode fornecer e alguma indicao da natureza do erro. E uma boa idia apegar-se a essa ca e conveno em seus programas pelo fato de outros componentes do sistema ca GNU/Linux assumirem esse comportamento. Por exemplo, programas de shells assumem essa conveno quando voc conecta multiplos programas ca e com os operadores && (sinal lgico e) e || (sinal lgico para ou). o o Portanto, voc deve explicitamente retornar zero a partir de sua funo main, e ca a menos que um erro acontea. c Com a maioria dos shells, poss obter o cdigo de sa da maioria dos e vel o da programas para o mais recentemente programa executado usando a varivel a 75

especial $?. Segue um exemplo no qual o comando ls chamado duas vezes e e seu cdigo de sa mostrado aps cada chamada. no primeiro caso, o da e o o comando ls executa corretamente e retorna o cdigo de sa zero. No o da segundo caso, ls encontra um erro (pelo fato de o nomedearquivo especicado na linha de comando no existir) e dessa forma retorna um cdigo de sa a o da no nulo. a
% ls / bin coda etc lib misc nfs proc sbin usr boot dev home lost+found mnt opt root tmp var % echo $? 0 % ls nomedearquivo ls: impossivel acessar nomedearquivo: Arquivo ou diretorio nao encontrado % echo $? 1

Note que apesar de o tipo de dado do parmetro da funo exit ser int a ca e a funo main retornar um tipo de dado int, GNU/Linux no preserva ca a os 32 bits completos do cdigo de retorno. De fato, voc deve usar cdigos o e o de sa somente entre zero e 127. Cdigos de sa acima de 128 possuem da o da um signicado especial quando um processo for encerrado por meio de um sinal, seus cdigos de sa so 128 mais o nmero do sinal. o da a u

3.3.2

Esperando pelo Encerramento de um Processo

Se voc tiver digitado e executado o exemplo de fork e exec na Listagem e 3.4, voc pode ter notado que a sa fornecida pelo programa ls muitas e da vezes aparece aps o programa principal ter sido completado. Isso ocorre o pelo fato de o processo lho, no qual ls estava sendo executado, agendado e independentemente do processo pai. Pelo fato de GNU/Linux ser um sistema operacional multi-tarefa, ambos os processos parecem ser executados simultneamente, e voc no pode prever se o programa ls ir ter uma chance a e a a de ser executado antes ou depois de o seu processo pai ser executado. Em algumas situaes, apesar disso, desejvel que o processo pai espere co e a at que um ou mais prodessos lhos se completem. Isso pode ser realizado e com a fam wait de chamadas de sistema. Essas funes permitem a voc lia co e esperar que um processo termine sua execuo, e habilite o processo pai ca recuperar informao sobre o encerramento de seu processo lho. Existem ca quatro diferentes chamadas de sistema na fam wait; voc pode escolher lia e pegar pouca ou muita informao sobre o processo encerrado, e voc pode ca e escolher se preocupar acerca de qual processo lho encerrou.

3.3.3

As Chamadas de Sistema da Fam wait lia

A funo mais simples da fam chamada apenas wait. Essa funo bloca lia e ca queia o processo que est fazendo a chamada at que um de seus processos a e 76

lhos encerre (ou ocorra um erro). A funo wait retorna um cdigo que ca o reete a situao atual por meio de um argumento apontador inteiro, do ca qual voc pode extrair informao sobre como o porcesso lho terminou. Por e ca exemplo, a macro WEXITSTATUS extrai o cdigo de sa do processo lho. o da Voc pode usar a macro WIFEXITED para determinar a partir da situao e ca de sa de um processo lho se o referido processo terminou normalmente da (por meio da funo exit ou retornando a partir da funo main) ou foi enca ca cerrado por meio de um sinal que no pode ser manipulado. Nesse ultimo a caso, use a macro WTERMSIG para extrair a partir de sua situao de sa ca da o nmero do sinal atravs do qual o processo em questo foi encerrado. Aqui u e a est a funo main de um exemplo com fork e com exec novamente. Dessa a ca vez, o processo pai chama wait para esperar at que o processo lho, no qual e o comando ls est sendo executado, termine. a
i n t main ( ) { int c h i l d s t a t u s ; / The a r g u m e n t l i s t t o p a s s t o t h e l s command . / char a r g l i s t [ ] = { ls , / a r g v [ 0 ] , t h e name o f t h e p r o g r a m . / l , / , NULL / The a r g u m e n t l i s t must end w i t h a NULL . }; / Spawn a c h i l d p r o c e s s r u n n i n g t h e l s command . r e t u r n e d c h i l d p r o c e s s ID . / spawn ( l s , a r g l i s t ) ;

Ignore

the

/ Wait f o r t h e c h i l d p r o c e s s t o c o m p l e t e . / w a i t (& c h i l d s t a t u s ) ; i f (WIFEXITED ( c h i l d s t a t u s ) ) p r i n t f ( the c h i l d p r o c e s s e x i t e d normally , with e x i t WEXITSTATUS ( c h i l d s t a t u s ) ) ; else p r i n t f ( t h e c h i l d p r o c e s s e x i t e d a b n o r m a l l y \n ) ; return 0 ; }

c o d e %d\n ,

Muitas chamadas de sistema similares esto dispon a veis em GNU/Linux, que so mais ex a veis ou fornecem mais informao sobre a sa de um ca da processo lho. A funo waitpid pode ser usada para esperar pela sa ca da de um processo lho espec co em lugar de esperar pelo trmino de algum e processo no espec a co. A funo wait3 retorna estat ca sticas de uso de CPU sobre o processo lho que est encerrando, e a funo wait4 permite a voc a ca e especicar opes adicionais sobre quais processos aguardar. co

3.3.4

Processos do Tipo Zumbi

Se um processo lho termina enquanto seu pai est chamando uma funo a ca wait, o processo lho desaparece e sua situao de encerramento informada ca e a seu processo pai por meio da chamada wait. Mas o que acontece quando um processo lho termina e o processo pai no est chamando a funo wait? a a ca 77

O processo lho simplesmente desaparece? No, porque a informao sobre a ca seu encerramento - informao tal como se ele terminou normalmente ou no, ca a e se tiver terminado normalmente, o que sua situao de sa mostra agora ca da - pode ser perdida. Quando um processo lho termina e o processo pai no a est chamando a funo wait, ele torna-se um processo zumbi. a ca Um processo zumbi um processo que tenha terminado mas no tenha e a da responsabilidade do processo pai limpar o sistema sido limpo ainda. E de sua criana zumbi. As funes wait fazem isso, tambm, de forma que c co e no seja necessrio rastrear se seu processo lho est ainda executando antes a a a de esperar por ele. Suponhamos, por exemplo, que um programa faa um c fork criando um processo lho, execute alguma outra computao, e ento ca a chame a funo wait. Se o processo lho no tiver terminado nesse ponto, o ca a processo pai ir bloquear na chamada a wait at que o processo lho encerre. a e Se o processo lho encerrar antes que o processo pai chame wait, o processo lho torna-se um zumbi. Quando o processo pai chama wait, a situao atual ca de encerramento do lho zumbi extra e da, o processo lho apagado, e a e chamada a wait retorna imediatamente. O que acontece se o processo pai no limpa seus lhos? Eles permanecem a soltos no sistemas, como processos zumbis. O programa na Listagem 3.6 cria um processo lho atravs de fork, que se encerra imediatamente e ento o e a mesmo programa que criou o processo lho vai cochilar por um minuto, sem mesmo limpar o processo lho. Listagem 3.6: (zombie.c) Fazendo um Processo Zumbi
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include < s t d l i b . h> #include <s y s / t y p e s . h> #include <u n i s t d . h> i n t main ( ) { pid t child pid ; / C r e a t e a c h i l d p r o c e s s . / child pid = fork () ; i f ( c h i l d p i d > 0) { / T h i s i s t h e p a r e n t p r o c e s s . sleep (60) ; } else { / T h i s i s t h e c h i l d p r o c e s s . exit (0) ; } return 0 ; }

Sleep

f o r a minute .

Exit

immediately .

Tente compilar esse arquivo em um executvel chamado fazer-zumbi. a Rode esse executvel, e enquanto ele ainda estiver sendo executado, liste a os processos no sistema usando o seguinte comando em outra janela: % ps -e -o pid,ppid,stat,cmd 78

O comando acima lista o ID de processo, ID do processo pai, situao ca atual do processo, e linha de comando do processo. Observe que, adicionalmente ao processo pai do processo fazer-zumbi, existe outro processo fazerzumbi listado. Esse o processo lho; note que seu ID de processo pai est ao e a lado do ID de processo do processo fazer-zumbi principal. O processo lho e marcado como <defunct>, e seu cdigo de situao atual Z, de zumbi.6 o ca e O que acontece quando o programa principal fazer-zumbi termina quando o processo pai sai, sem ter chamado a funo wait? Fica o processo zumbi ca continua vagando por a No tente executar o comando ps novamente, e ? a notar que ambos os processos pai e lho fazer-zumbi se foram. Quando um programa sai, seus lhos so herdados por um processo especial, o programa a init, o qual sempre executa com o ID de processo como sendo 1 ( o primeiro e processo iniciado quando GNU/Linux passa pelo processo de inicializao). ca O processo init automaticamente limpa qualquer processo lho zumbi que ele herda.

3.3.5

Limpando Filhos de Forma No Sincronizada a

Caso voc esteja usando um processo lho simplesmente para executar outro e programa, funciona de forma satisfatria chamar a funo wait imediatao ca mente no processo pai, que ir bloquear at que o processo lho seja complea e tado. Mas muitas vezes, voc ir desejar que o processo pai continue sendo e a executado, como um ou mais processos lhos executando de forma sincronizada. Como pode voc garantir que limpou processos lhos que j tenham e a completado sua tarefa de forma que voc no esquea por a pelo sistema e a c processo zumbis, os quais consomem recursos de sistema, com informaes co falsas por a ? Uma abordagem pode ser a chamada pelo processo pai das funes wait3 co ou wait4 periodicamente, para limpar lhos zumbis. Chamando a funo ca wait com esse objetivo no funciona bem pelo fato de que, se nenhum proa cesso lho terminar, a chamada a wait ir bloquear o processo pai at que a e algum processo lho encerre. Todavia, as funes wait3 e wait4 recebem co um parmetro sinalizador adicional, para o qual voc pode passar o valor a e sinalizador WNOHANG. Com esse sinalizador, a funo chamada executa ca em modo no bloqueador de processo pai ir limpar um processo lho que a a terminou se existir algum, ou simplesmente retornar se no houver nenhum a
Nota do tradutor: em um slackware 12.2 a sa da, mostrando somente as duas linhas que interessam, foi a seguinte: PID PPID STAT CMD 9152 9133 S+ ./fazer-zumbi 9153 9152 Z+ [fazer-zumbi] <defunct>
6

79

processo lho executando. O valor de retorno da chamada o ID do proe cesso do lho encerrado, ou zero no caso de no haver nenhum processo sendo a executado. Uma soluo mais elegante noticar o processo pai quando um lho conca e clui seu trabalho. Existem muitas formas de fazer isso usando os mtodos e discutidos no Cap tulo 5, Comunicao Entre Processosmas afortunadaca mente GNU/Linux faz isso para voc, usando sinais. Quando um processo e lho cumpre sua tarefa, GNU/Linux envia ao processo pai o sinal SIGCHLD. A disposio padro desse sinal no fazer nada, coisa que talvez voc possa ca a e a e no ter notado antes. a Dessa forma, um caminho fcil para limpar processos lhos pelo maa e nuseio de SIGCHLD. Certamente, durante a limpeza de processos lhos, e importante guardar sua situao atual de encerramento se essa informao ca ca for necessria, pelo fato de uma vez que o processo for limpo usando wait, a a sua informao de encerramento no mais estar dispon ca a a vel. A Listagem 3.7 mostra um exemplo de programa que usa uma funo manipuladora de ca SIGCHLD para limpar seus processos lhos. 7 Listagem 3.7: (sigchld.c) Limpando Processos lhos pelo manuseio de SIGCHLD
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 #include #include #include < s i g n a l . h> < s t r i n g . h> <s y s / t y p e s . h> <s y s / w a i t . h> child exit status ;

sig atomic t

void c l e a n u p c h i l d p r o c e s s ( i n t s i g n a l n u m b e r ) { / C l e a n up t h e c h i l d p r o c e s s . / int s t a t u s ; w a i t (& s t a t u s ) ; / S t o r e i t s e x i t s t a t u s i n a g l o b a l v a r i a b l e . child exit status = status ; } i n t main ( ) { / H a n d l e SIGCHLD b y c a l l i n g c l e a n struct s i g a c t i o n s i g c h l d a c t i o n ; memset (& s i g c h l d a c t i o n , 0 , s i z e o f s i g c h l d a c t i o n . s a h a n d l e r = &c l e a n s i g a c t i o n (SIGCHLD , &s i g c h l d a c t i o n / Now do t h i n g s , / . . . / return 0 ; } including

up child process . ( sigchld action ) ) ; up child process ; , NULL) ; child process .

forking a

O cdigo em clean up child process pode no trabalhar corretamente se houver mais o a que um processo lho. O kernel do GNU/Linux ir somente chamar o manipulador de sinal a uma vez se dois ou mais processos lhos encerrarem quase ao mesmo tempo. Portanto, caso haja mais de um processo lho, o manipulador de sinal deve repetidamente chamar por waitpid (ou uma das outras funes relacionada) com a opo WNOHANG at que co ca e waitpid retorne.

80

Note como o manipulador de sinal armazena a situao de sa do proca da cesso lho em uma varivel global, da qual o programa principal pode acessa a la. Pelo fato de a varivel se atribu em um manipulador de sinal, ela (a a da varivel global) do tipo sig atomic t. a e

81

82

Cap tulo 4 Linhas de Execuo ca


LINHAS DE EXECUCAO1 ,COMO PROCESSOS, SAO UM MECANISMO PARA PERMITIR A UM PROGRAMA fazer mais de uma coisa ao mesmo tempo. Da mesma forma que acontece com processos, linhas de execuo paca recem executar concorrentemente; o kernel GNU/Linux agenda-as de forma no sincronizada, interrompendo cada uma dessas linhas de execuo de tema ca pos em tempos para fornecer a outros uma chance para executar. Conceitualmente, uma linha de execuo existe dentro de um processo. ca Linhas de execuo so menores unidades de execuo que processos. Quando ca a ca voc chama um programa, GNU/Linux cria um novo processo e esse processo e cria uma linha de execuo simples, que executa o programa sequencialmente. ca Essa linha de execuo pode criar linhas de execuo adicionais; todas esca ca sas linhas de execuo executam o mesmo programa no mesmo processo, ca mas cada linha de execuo pode estar executando uma parte diferente do ca programa em qualquer tempo fornecido. Ns vimos como um programa pode atravs de um fork criar um processo o e lho. O processo lho inicialmente executa seu programa pai, na memria o virtual do processo pai, com descritores de arquivo do processo pai e assim por diante copiado tudo do processo pai. O processo lho pode modicar sua memria fechar descritores de arquivo, e coisas parecidas sem afetar seu o processo pai, e vice-versa.2 Quando um programa no processo lho cria outra linha de execuo, apesar disso, nada copiado. A linha de execuo criadora ca e ca e a linha de execuo criatura compartilham o mesmo espao de memria, os ca c o mesmos descritores de arquivo, e outros recursos de sistema como o original. Se uma linha de execuo muda o valor de uma varivel, por exemplo, a outra ca a linha de execuo sequencialmente ir ver o valor modicado. Similarmente, ca a
1 2

Nota do tradutor: Threads Nota do tradutor: o processo pai pode fazer vrios procedimentos sem afetar o lho. a

83

se uma linha de execuo fecha um descritor de arquivo, outra linha de ca execuo pode no ler aquele descritor ou no escrever para aquele descritor. ca a a Pelo fato de um processo e todas as suas linhas de execuo poderem executar ca somente um programa de cada vez, se alguma linha de execuo dentro de um ca 3 processo chama uma das funes exec , todas as outras linhas de execuo co ca so nalizadas (o novo programa pode, certamente, criar novas linhas de a execuo). ca GNU/Linux implementa o padro POSIX para Interface de Programao a ca 4 de Aplicao (API) de linha de execuo (conhecido como pthreads) . Todas ca ca funes de linha de execuo e tipos de dado so declarados no arquivo de co ca a cabealho <pthread.h>. As funes POSIX de linha de execuo no esto c co ca a a inclu das na biblioteca C padro. Ao invs disso, elas esto na libpthread, a e a ento voc deve adicionar -lpthread ` linha de comando quando voc zer a a e a e linkagem de seu programa.

4.1

Criao de Linhas de Execuo ca ca

Cada linha de execuo identicada por um ID (identicador) de linha de ca e execuo. Quando for se referir a IDs de linha de execuo em programas ca ca feitos em C ou em C++, use o tipo pthread t. Sobre criao, cada linha de execuo executa uma funo de linha de ca ca ca execuo. Essa funo de linha de execuo apenas uma funo comum e ca ca ca e ca contm o cdigo que a linha de execuo deve executar. Quando a funo e o ca ca retorna, a linha de execuo encerra. Em ambiente GNU/Linux, funes de ca co linha de execuo recebem um parmetro simples, do tipo void*, e possuem o ca a tipo de dado retornado tambm void*. O parmetro o argumento da linha e a e de execuo: GNU/Linux passa o valor conforme a linha de execuo sem ca ca olhar para o contedo. Seu programa pode usar esse parmetro para passar u a dados para uma nova linha de execuo. Reciprocamente, seu programa pode ca usar o valor de retorno para passar dados a partir de uma linha de execuo ca existente de volta ao criador da linha de execuo. ca ca e A funo pthread create cria uma nova linha de execuo. Voc alimenta ca a pthread create com o seguinte:
Nota do tradutor: relembrando que a fam de funes exec substituem o programa lia co que est sendo executado por outro. a 4 Npta do tradutor: p-threads ou POSIX-threads ou ainda threads POSIX
3

84

1. Um apontador para uma varivel do tipo pthread t, na qual o ID a de linha de execuo da nova linha de execuo est armazenado. ca ca a 2. Um apontador para um objeto de atributo de linha de execuo. ca Esse apontador controla detalhes de como a linha de execuo inca terage com o restante do programa. Se voc passa um dado NULL e como atributo de linha de execuo, uma linha de execuo ir ser ca ca a criada com os atributos padronizados de linha de execuo. Atribuca tos de linha de execuo so discutidos na Seo 4.1.5, Atributos ca a ca de Linhas de Execuo. ca 3. Um apontador para a funo de linha de execuo. Esse apontador ca ca um apontador de funo comum, do seguinte tipo: e ca void* (*) (void*) 4. Um valor de argumento de linha de execuo do tipo void*. Todo ca o resto que voc enviar simplesmente passado como argumento e e para a funo de linha de execuo quando a linha de execuo ca ca ca inicia sua execuo. ca

Uma chamada a pthread create retorna imediatamente, e a linha de execuca o original continua executando as instrues imediatamente aps a chaco o mada. Enquanto isso, a nova linha de execuo inicia-se executando a funo ca ca de linha de execuo. GNU/Linux agenda ambas as linhas de execuo de ca ca forma no sincronizada, e seu programa continua independentemente da ora dem relativa na qual instrues so executadas em duas linhas de execuo. co a ca O programa na Listagem 4.1 cria uma linha de execuo que imprime xs ca continuamente para a sa de erro. Aps chamar pthread create, a linha de da o execuo principal imprime os continuamente para a sa de erro. ca da 85

Listagem 4.1: ( thread-create.c) Criando uma Linha de Execuo ca


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 #include <p t h r e a d . h> #include <s t d i o . h> / P r i n t s x s to stderr . The p a r a m e t e r is unused . Does n o t return . /

void p r i n t x s ( void unused ) { while ( 1 ) fputc ( x , stderr ) ; return NULL ; } / The main p r o g r a m . /

i n t main ( ) { pthread t thread id ; / C r e a t e a new t h r e a d . The new t h r e a d w i l l r u n t h e function . / p t h r e a d c r e a t e (& t h r e a d i d , NULL, &p r i n t x s , NULL) ; / P r i n t o s c o n t i n u o u s l y t o s t d e r r . / while ( 1 ) fputc ( o , stderr ) ; return 0 ; }

print xs

Compile e faa a linkagem desse programa usando o seguinte cdigo: c o \% cc -o thread-create thread-create.c -lpthread Tente execut-lo para ver o que ocorre. Preste atenao ao padro ima c a previs de xs e os devido ` alternncia de agendamentos do Linux com vel a a relao `s duas linhas de execuo. ca a ca Sob circunstncias normais, uma linha de execuo encerra-se por meio a ca de uma entre duas formas. Uma forma, como ilustrado previamente, por e meio do retorno da funo de linha de execuo. O valor de retorno da ca ca funo de linha de execuo usado para ser o valor de retorno da linha de ca ca e execuo. Alternativamente, uma linha de execuo pode sair explicitamente ca ca por meio de uma chamada a pthread exit. Essa funo pode ser chamada de ca dentro da funo de linha de execuo ou a partir de alguma outra funo ca ca ca chamada diretamente ou indiretamente pela funo de linha de execuo. O ca ca e ca argumento para pthread exit o valor de retorno da linha de execuo.

4.1.1

Enviando Dados a uma Linha de Execuo ca

O argumento de linha de execuo fornece um mtodo conveniente de enviar ca e dados a linhas de execuo. Pelo fato de o tipo de dado do argumento ca ser void*, apesar disso, voc no pode enviar grande quantidade de dados e a diretamente atravs do argumento. Ao invs disso, use o argumento de linha e e de execuo para enviar um apontador para alguma estrutura ou vetor de ca dados. Uma tcnica comumente usada denir uma estrutura para cada e e funo de linha de execuo, a qual contm os parmetros esperados pela ca ca e a funo de linha de execuo. ca ca 86

Usando o argumento de linha de execuo, torna-se fcil reutilizar a ca a mesma funo de linha de execuo para muitas linhas de execuo. Toca ca ca das essas linhas de execuo executam o mesmo cdigo, mas sobre diferentes ca o dados. O programa na Listagem 4.2 similar ao exemplo anterior. O referido proe grama cria duas novas linhas de execuo, um para imprimir xs e o outro para ca imprimir os. Ao invs de imprimir innitamente, apesar disso, cada linha e de execuo imprime um nmero xo de caracteres e ento encerra-se retorca u a nando ` funo de linha de execuo. A mesma funo de linha de execuo, a ca ca ca ca char print, usada em ambas as linhas de execuo, mas cada linha de e ca execuo congurada diferentemente usando a estrutura char print parms. ca e Listagem 4.2: ( thread-create2) Cria Duas Linhas de Execuo ca
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 #include <p t h r e a d . h> #include <s t d i o . h> / P a r a m e t e r s to print function . /

struct c h a r p r i n t p a r m s { / The c h a r a c t e r t o p r i n t . / char c h a r a c t e r ; / The number o f t i m e s t o p r i n t int count ; }; / P r i n t s a number o f which i s a p o i n t e r

it .

c h a r a c t e r s t o s t d e r r , a s g i v e n b y PARAMETERS, t o a s t r u c t c h a r p r i n t p a r m s . /

void c h a r p r i n t ( void p a r a m e t e r s ) { / C a s t t h e c o o k i e p o i n t e r t o t h e r i g h t t y p e . / struct c h a r p r i n t p a r m s p = ( struct c h a r p r i n t p a r m s ) parameters ; int i ; f o r ( i = 0 ; i < p o u n t ; ++i ) >c f p u t c ( p h a r a c t e r , s t d e r r ) ; >c return NULL ; } / The main p r o g r a m . i n t main ( ) { pthread t thread1 id ; pthread t thread2 id ; struct c h a r p r i n t p a r m s struct c h a r p r i n t p a r m s /

thread1 args ; thread2 args ;

/ C r e a t e a new t h r e a d t o p r i n t 3 0 , 0 0 0 x s . / thread1 args . character = x ; t h r e a d 1 a r g s . count = 30000; p t h r e a d c r e a t e (& t h r e a d 1 i d , NULL, &c h a r p r i n t , &t h r e a d 1 a r g s ) ; / C r e a t e a new t h r e a d t o p r i n t 2 0 , 0 0 0 o s . / thread2 args . character = o ; t h r e a d 2 a r g s . count = 20000; p t h r e a d c r e a t e (& t h r e a d 2 i d , NULL, &c h a r p r i n t , &t h r e a d 2 a r g s ) ; return 0 ; }

Mas Espere! O programa na Listagem 4.2 tem um erro srio nele. A lie nha de execuo principal (que executa a funo main) cria as estruturas do ca ca parmetro de linha de execuo (thread1 args e thread2 args) como variveis a ca a 87

locais, e ento passa apontadores para essas estruturas destinados `s linhas a a de execuo que cria. O que fazer para prevenir o Linux do agendamento das ca trs linhas de execuo de tal forma que a linha de execuo principal tere ca ca mine antes de qualquer das duas outras linhas de execuo terem terminado? ca Nada! Mas caso isso ocorra, a memria contendo as estruturas do parmetro o a da linha de execuo ter sido desalocada enquanto as outras duas linhas de ca a execuo estiverem ainda acessando-a. ca

4.1.2

Vinculando Linhas de Execuo ca

Uma soluo forar main a esperar at que as outras duas linhas de execuo ca e c e ca tenham terminado. O que precisamos de uma funo similar ` funo wait e ca a ca que espere pelo m de uma linha de execuo ao invs de esperar pelo m de ca e um processo. A funo desejada pthread join, que recebe dois argumentos: ca e o ID de linha de execuo da linha de execuo pelo qual vai esperar, e um ca ca apontador para uma var avel do tipo void* que ir receber o valor de retorno a da linha de execuo terminada. Se voc no quiser preocupar-se com o valor ca e a de retorno, informe NULL como o segundo argumento.

A Listagem 4.3 mostra a funo main corrigida para o exemplo de falha ca na listagem 4.2. Nessa verso, main no encerra at que ambas as linhas de a a e execuo imprimindo xs e os tenham sido completadas, ento elas no mais ca a a utilizam as estruturas de argumento. 88

Listagem 4.3: Funo main revisada para thread-create2.c ca


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 54 #include <p t h r e a d . h> #include <s t d i o . h> / P a r a m e t e r s to print function . /

struct c h a r p r i n t p a r m s { / The c h a r a c t e r t o p r i n t . / char c h a r a c t e r ; / The number o f t i m e s t o p r i n t int count ; }; / P r i n t s a number o f which i s a p o i n t e r

it .

c h a r a c t e r s t o s t d e r r , a s g i v e n b y PARAMETERS, to a s t r u c t char print parms . /

void c h a r p r i n t ( void p a r a m e t e r s ) { / C a s t t h e c o o k i e p o i n t e r t o t h e r i g h t t y p e . / struct c h a r p r i n t p a r m s p = ( struct c h a r p r i n t p a r m s ) parameters ; int i ; f o r ( i = 0 ; i < p o u n t ; ++i ) >c f p u t c ( p h a r a c t e r , s t d e r r ) ; >c return NULL ; } / The main p r o g r a m . /

i n t main ( ) { pthread t thread1 id ; pthread t thread2 id ; struct c h a r p r i n t p a r m s struct c h a r p r i n t p a r m s

thread1 args ; thread2 args ;

/ C r e a t e a new t h r e a d t o p r i n t 3 0 0 0 0 x s . / thread1 args . character = x ; t h r e a d 1 a r g s . count = 30000; p t h r e a d c r e a t e (& t h r e a d 1 i d , NULL, &c h a r p r i n t , &t h r e a d 1 a r g s ) ; / C r e a t e a new t h r e a d t o p r i n t 2 0 0 0 0 o s . / thread2 args . character = o ; t h r e a d 2 a r g s . count = 20000; p t h r e a d c r e a t e (& t h r e a d 2 i d , NULL, &c h a r p r i n t , &t h r e a d 2 a r g s ) ; / Make pthread / Make pthread sure join sure join the f i r s t thread has f i n i s h e d . / ( t h r e a d 1 i d , NULL) ; the second thread has f i n i s h e d . / ( t h r e a d 2 i d , NULL) ; safely return . /

/ Now we can return 0 ; }

A moral da estria: garanta que qualquer dado que seja passado a uma o linha de execuo por referncia seja mantido na memria, mesmo que por ca e o uma linha de execuo diferente, at que voc tenha certeza que a linha de ca e e execuo tenha terminado com esse dado. Essa garantia verdadeira em ca e ambos os casos tanto para variveis locais, que so removidas quando as a a linhas de execuo saem do ambiente no qual foram denidas, quanto para ca variveis alocadas em grupo/pilha, que voc libera atravs de um chamado a e e a free (ou usando delete em C++). 89

4.1.3

Valores de Retorno de Linhas de Execuo ca

Se o segundo argumento que voc passar a pthread join for no nulo, o valor e a de retorno da linha de execuo ser colocado na localizao apontada por ca a ca aquele argumento. O valor de retorno da linha de execuo,da mesma forma ca que o argumento de linha de execuo, do tipo void*. Se voc desejar devolca e e ver um dado do tipo int simples ou outro nmero pequeno, voc pode fazer u e isso facilmente convertendo o valor para void* e ento convertendo de volta a para o tipo apropriado aps chamar pthread join. 5 O programa na Listagem o 4.4 calcula o ensimo nmero primo em uma linha de execuo isolada. O e u ca valor de retorno dessa linha de execuo isolada o nmero primo desejado. ca e u A linha de execuo principal, enquanto isso, est livre para executar outro ca a cdigo. Note que o algor o tmo de divises sucessivas usado em compute prime o completamente ineciente; consulte um livro sobre algor e tmos numricos se e voc precisar calcular muitos primos em seus programas. e

Note que esse procedimento perde a portabilidade, e cabe a voc garantir que seu e valor pode ser convertido seguramente para void* e ser convertido de volta sem perder bits.

90

Listagem 4.4: ( primes.c) Calcula Nmeros Primos em uma Linha de u Execuo ca


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 #include <p t h r e a d . h> #include <s t d i o . h> / Compute s u c c e s s i v e p r i m e n u m b e r s ( v e r y Nth p r i m e number , w h e r e N i s t h e v a l u e void c o m p u t e p r i m e ( void a r g ) { int candidate = 2 ; int n = ( ( int ) arg ) ; while ( 1 ) { int f a c t o r ; int i s p r i m e = 1 ; / T e s t p r i m a l i t y b y s u c c e s s i v e d i v i s i o n . / f o r ( f a c t o r = 2 ; f a c t o r < c a n d i d a t e ; ++f a c t o r ) i f ( c a n d i d a t e % f a c t o r == 0 ) { is prime = 0; break ; } / I s t h i s t h e p r i m e number we r e l o o k i n g f o r ? / if ( is prime ) { i f (n == 0 ) / R e t u r n t h e d e s i r e d p r i m e number a s t h e t h r e a d return ( void ) c a n d i d a t e ; } ++c a n d i d a t e ; } return NULL ; } i n t main ( ) { pthread t thread ; int which prime = 5000; int prime ; / S t a r t t h e c o m p u t i n g t h r e a d , up t o t h e 5 0 0 0 t h p r i m e number . / p t h r e a d c r e a t e (& t h r e a d , NULL, &c ompute prime , &w h i c h p r i m e ) ; / Do some o t h e r w o r k h e r e . . . / / Wait f o r t h e p r i m e number t h r e a d t o c o m p l e t e , and g e t t h e r e s u l t . p t h r e a d j o i n ( t h r e a d , ( void ) &p r i m e ) ; / P r i n t t h e l a r g e s t p r i m e i t c o m p u t e d . / p r i n t f ( The %dth p r i m e number i s %d . \ n , w h i c h p r i m e , p r i m e ) ; return 0 ; } inefficiently ). Return t h e p o i n t e d t o b y ARG. /

return

value .

4.1.4

Mais sobre IDs de Linhas de Execuo ca

Ocasionalmente, util para uma sequncia de cdigo determinar qual linha e e o de execuo a est executando. A funo pthread self retorna o ID da linha ca a ca de execuo que a chamou. Esse ID de linha de execuo pode ser comparado ca ca com outro ID de linha de execuo usando a funo pthread equal. ca ca Essas funes podem ser uteis para determinar se um ID de linha de co execuo em particular corresponde ao ID da linha de execuo atual. Por ca ca exemplo, um erro para uma linha de execuo chamar pthread join para e ca vincular-se a si mesma. (Nesse caso, pthread join ir retornar o cdigo de a o erro EDEADLK.) Para vericar isso antecipadamente, voc pode usar um e cdigo como o que segue: o
if ( ! pthread equal ( pthread self () , p t h r e a d j o i n ( o t h e r t h r e a d , NULL ) ; other thread ))

91

4.1.5

Atributos de Linha de Execuo ca

Atributos de linha de execuo fornecem um mecanismo para ajuste preciso ca do comportamento de linhas de execuo individuais. Lembrando que pthre ca ad create aceita um argumento que um apontador para um objeto de atrie buto de linha de execuo. Se voc informar um apontador nulo, os atributos ca e de ancadeamento padronizados so usados para congurar a nova linha de a execuo. Todavia, voc pode criar e personalizar um objeto de atributo de ca e linha de execuo para especicar outros valores para os atributos. 6 ca Para especicar atributos personalizados de linhas de execuo, voc deve ca e seguir esses passos: 1. Crie um objeto pthread attr t. O caminho mais fcil de fazer isso a e simplesmente declarar uma varivel automtica desse tipo. a a 2. Chame pthread attr init, informando um apontador para esse objeto. Esse procedimento inicializa os atributos com seus valores padronizados. 3. Modique o objeto de atributo de forma que contenha os valores de atributo desejados. 4. Informe um apontador para o objeto de atributo ao chamar pthread create. 5. Chame pthread attr destroy para liberar o objeto de atributo. A varivel pthread attr t propriamente dita no desalocada. A a a e varivel pthread attr t pode ser reinicializada com pthread attr init. a Um objeto de atributo de linha de execuo simples pode ser usado para ca muitas linhas de execuo. No necessrio manter o objeto de atributo de ca a e a linha de execuo por ai aps as linhas de execuo terem sido criadas. ca o ca Para a maioria das linha de execuo de programao para criao de ca ca ca aplicativos em GNU/Linux, somente um atributo de linha de execuo ca e tipicamente de interesse (os outros atributos dispon veis so primariamente a para especicidades de programao em tempo real). Esse atributo o estado ca e de desvinculao da linha de execuo. Uma linha de execuo pode ser ca ca ca criada como uma linha de execuo vinculvel (o padro) ou como uma ca a a linha de execuo desvinculada. Uma linha de execuo vinculvel, como um ca ca a processo, no tem seus recursos de sistema liberados automaticamente pelo a GNU/Linux quando termina sua execuo. Ao invs disso, o estado de sa ca e da
Nota do tradutor: para mais detalhes sobre threads/linhas de execuo veja http: ca //www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html
6

92

da linha de execuo vagueia sem destino no sistema (semelhantemente a um ca processo zumbi) at que outra linha de execuo chame pthread join para e ca obter seu valor de retorno. Somente ento so seus recursos liberados. Uma a a Linha de execuo desvinculada, ao cantrrio, tem seus recursos de sistema ca a automaticamete liberados quando termina sua execuo. Pelo fato de uma ca linha de execuo desvinculada ter seus recursos liberados automaticamente, ca outra linha de execuo pode no conseguir informaes sobre sua concluso ca a co a atravs do uso de pthread join ou obter seu valor de retorno. e Para atribuir o estado desvinculado a um objeto de atributo de linha de execuo, use a funo pthread attr setdetachstate. O primeiro argumento ca ca e um apontador para o objeto de atributo de linha de execuo, e o segundo o ca e estado desvinculado desejado. Pelo fato de o estado vinculvel ser o padro, a a e necessrio chamar a funo pthread attr setdetachstate somente para criar lia ca nhas de execuo desvinculadas; informe PTHREAD CREATE DETACHED ca como o segundo argumento. O cdigo na Listagem 4.5 cria uma linha de execuo desvinculada usando o ca o atributo de linha de execuo desvinculada para a linha de execuo. ca ca

Listagem 4.5: (detached.c) Programa Esqueleto Que Cria uma Linha dde Execuo Desvinculada ca
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <p t h r e a d . h> void t h r e a d f u n c t i o n ( void t h r e a d a r g ) { / Do w o r k h e r e . . . / return NULL ; } i n t main ( ) { pthread attr t attr ; pthread t thread ; pthread pthread pthread pthread a t t r i n i t (& a t t r ) ; a t t r s e t d e t a c h s t a t e (& a t t r , PTHREAD CREATE DETACHED) ; c r e a t e (& t h r e a d , &a t t r , &t h r e a d f u n c t i o n , NULL) ; a t t r d e s t r o y (& a t t r ) ; / the second thread . /

/ Do w o r k h e r e . . . / No n e e d return 0 ; } to join

Mesmo se uma linha de execuo for criada com o estado vinculvel, ele ca a pode ser transformado em uma linha de execuo desvinculada. Para fazer ca isso, chame pthread detach. Uma vez que seja desvinculada, ela no pode se a tornar vinculvel novamente. a 93

4.2

Cancelar Linhas de Execuo ca

Sob circunstncias normais, uma linha de execuo encerra-se quando seu a ca estado de sa normal, ou pelo retorno de seu valor de retorno ou por da e uma chamada ` funo pthread exit. Todavia, poss para uma linha de a ca e vel execuo requisitar que outra linha de execuo termine. Isso chamado ca ca e cancelar uma linha de execuo. ca Para cancelar uma linha de execuo, chame a funo pthread cancel, inca ca formando o ID de linha de execuo da linha de execuo a ser cancelada. ca ca Uma linha de execuo cancelada pode mais tarde ser vinculada; de fato, voc ca e pode vincular uma linha de execuo cancelada para liberar seus recursos, a ca menos que a linha de execuo seja desvinculada (veja a Seo 4.1.5, Atrica ca butos de Linha de Execuo). O valor de retorno de uma linha de execuo ca ca cancelada o valor especial fornecido por PTHREAD CANCELED. e Muitas vezes uma linha de execuo pode ter alguma parte de seu cdigo ca o que deva ser executada em um estilo tudo ou nada. Por exemplo, a linha de execuo pode alocar alguns recursos, us-los, e ento liberar esses mesmos ca a a recursos em seguida. Se a linha de execuo for cancelada no meio do cdigo, ca o pode no ter a oportunidade de liberar os recursos como era esperado, e dessa a forma os recursos iro ser perdidos. Para contar com essa possibilidade, a poss e vel para uma linha de execuo controlar se e quando ela pode ser ca cancelada. Uma linha de execuo pode estar em um dos trs estados abaixo com ca e relao a cancelar linhas de execuo. ca ca A linha de execuo pode ser cancelvel de forma no sincronica a a zada. Isso que dizer que a linha de execuo pode ser cancelada em ca qualquer ponto de sua execuo. ca A linha de execuo pode ser cancelvel sincronizadamente. A lica a nha de execuo pode ser cancelada, mas no em algum ponto ca a determinado de sua execuo. Ou ao contrrio, requisies de canca a co celamento so colocadas em uma regio temporria de armazenaa a a mento, e a linha de execuo cancelada somente quando forem ca e alcanados pontos espec c cos em sua execuo. ca Uma linha de execuo pode ser incancelvel. Tentativas de canca a celar a linha de execuo so silenciosamente ignoradas. ca a Quando criada inicialmente, uma linha de execuo cancelvel sincroca e a nizadamente. 94

4.2.1

Linhas de Execuo Sincronas e Assincronas ca

Uma linha de execuo cancelvel assincronizadamente pode ser cancelado ca a em qualquer ponto de sua execuo. Uma linha de execuo cancelvel sincroca ca a nizadamente, ao contrrio, pode ser cancelado somente em lugares determia nados de sua execuo. Esses lugares so chamados pontos de cancelamento. ca a A linha de execuo ir armazenar uma requisio de cancelamento at que ca a ca e o ponto de cancelamento seguinte seja alcanado. c Para fazer uma linha de execuo assincronizadamente cancelvel, use ca a pthread setcanceltype. A funo pthread setcanceltype afeta linha de execuo ca ca que fez o chamado. O primeiro argumento deve ser PTHREAD CANCEL A SYNCHRONOUS para tornar a linha de execuo assincronizadamente canca celvel, ou PTHREAD CANCEL DEFERRED para retornar a linha de execua ca o ao estado de sincronizadamente cancelvel. O segundo argumento, se no a a for nulo, um apontador para uma varivel que ir receber o tipo de cancee a a lamento anterior para a linha de execuo. A chamada abaixo, por exemplo, ca transforma a linha de execuo que est fazendo a chamada em assincronica a zadamente cancelvel. a
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

O que constitui um ponto de cancelamento, e onde deve ele ser colocado? O caminho mais direto para criar um ponto de cancelamento chamar a e funo pthread testcancel. Essa chamada faz unicamente atender um pedido ca de cancelamento que se encontra pendente em uma linha de execuo sincroca nizadamente cancelvel. Voc deve chamar a funo pthread testcancel perioa e ca dicamente durante computaes longas em uma funo de linha de execuo, co ca ca em pontos onde a linha de execuo pode ser cancelada sem desperdiar ca c quaisquer recursos ou produzir outros efeitos igualmente danosos. Certas outras funes trazem implicitamente pontos de cancelamento co tambm. So elas listadas na pgina de manual da funo pthread cancel e a a ca 7 . Note que outras funes podem usar essas funes internamente e dessa co co forma serem pontos de cancelamento.

4.2.2

Sees Cr co ticas Incancelveis a

Uma linha de execuo pode desabilitar o cancelamento de si mesma comca pletamente com a funo pthread setcancelstate. Da mesma forma que pthca read setcanceltype, a funo pthread setcancelstate afeta a linha de execuo ca ca
Nota do Tradutor:se for usado o comando man pthread cancel e no se encontrar a a a referida pgina de manual instalada no ubuntu 10.10 default mas na internet existem a pelo menos duas verses de man page para pthread cancel. o
7

95

que zer a chamada. O primeiro argumento PTHREAD CANCEL DISAB e LE para disabilitar a cancelabilidade, ou PTHREAD CANCEL ENABLE para reabilitar a cancelabilidade. O segundo argumento, se no for NULL, a aponta para uma varivel que ir receber o estado de cancelamento anterior. a a A chamada a seguir, por exemplo, desabilita a cancelabilidade da linha de execuo na linha de execuo que zer a referida chamada. ca ca

pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL);

Usando a funo pthread setcancelstate habilita voc a implementar sees ca e co cr ticas. Uma seo cr ca tica uma sequncia de cdigo que deve ser executado e e o ou em sua totalidade ou parcialmente; em outras palavras, se uma linha de execuo inicia-se executando uma seo cr ca ca tica, essa linha de execuo deve ca continuar at o nal da seo cr e ca tica sem ser cancelada. Por exemplo, suponhamos que voc est escrevendo uma rotina para um e a programa bancrio que transfere dinheiro de uma conta para outra. Para a fazer isso voc deve adicionar valor ao saldo em uma conta e abater o mesmo e valor do saldo de outra conta. Se a linha de execuo que estiver executando ca sua rotina for cancelada exatamente no pssimo momento entre essas duas e operaes, o programa pode ter um aumento esprio do depsito total cauco u o sado pela falha na concluso da transao. Para previnir essa possibilidade, a ca coloque as duas operaes dentro de uma seo cr co ca tica. Voc pode implementar a transferncia com uma funo tal como a pro e e ca cess transaction, mostrada na Listagem 4.6. Essa funo desabilita o canca celamento da linha de execuo para iniciar uma seo cr ca ca tica antes que a funo modique ou um ou outro balano de conta. ca c 96

Listagem 4.6: (critical-section.c) Protege uma Transao Bancria com ca a uma Seo Cr ca tica
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 #include <p t h r e a d . h> #include <s t d i o . h> #include < s t r i n g . h> / An a r r a y of balances in accounts , indexed b y a c c o u n t number . /

float account balances ; / T r a n s f e r DOLLARS f r o m a c c o u n t FROM ACCT t o a c c o u n t TO ACCT . Return 0 i f t h e t r a n s a c t i o n s u c c e e d e d , o r 1 i f t h e b a l a n c e FROM ACCT i s too small . / int p r o c e s s t r a n s a c t i o n { int o l d c a n c e l s t a t e ; ( int from acct , int to acct , float dollars )

/ Check t h e b a l a n c e i n FROM ACCT . / i f ( account balances [ from acct ] < d o l l a r s ) return 1 ; / B e g i n c r i t i c a l s e c t i o n . / p t h r e a d s e t c a n c e l s t a t e (PTHREAD CANCEL DISABLE, &o l d c a n c e l s t a t e ) ; / Move t h e money . / a c c o u n t b a l a n c e s [ t o a c c t ] += d o l l a r s ; a c c o u n t b a l a n c e s [ f r o m a c c t ] = d o l l a r s ; / End c r i t i c a l s e c t i o n . / p t h r e a d s e t c a n c e l s t a t e ( o l d c a n c e l s t a t e , NULL) ; return 0 ; }

Note que importante restaurar o estado de cancelamento antigo no nal e da seo cr ca tica em lugar de escolher incondicionalmente o estado PTHREA e D CANCEL ENABLE. restaurando o estado antigo ao invs de usar incondicionalmente PTHREAD CANCEL ENABLE habilita voc a chamar e a funo process transaction seguramente de dentro de outra seo cr ca ca tica como no caso mostrado acima, permitindo que o estado de cancelamento seja colocado da mesma forma que se encontrava antes da sua interveno. ca

4.2.3

Quando Cancelar uma Linha de Execuo ca

Em geral, uma boa idia no cancelar linhas de execuo para encerrar a e e a ca execuo de uma linha de execuo, exceto em circunstncias raras. Durante ca ca a operaes normais, a melhor estratgia indicar ` linha de execuo que ela co e e a ca deve encerrar, e ento esperar o trmino da linha de execuo por seu estilo a e ca prprio e ordeiro. Iremos discutir tcnicas para comunicao com linhas de o e ca execuo mais tarde no atual cap ca tulo, e no Cap tulo 5, Comunicao Entre ca Processos. 97

4.3

Area de Dados Espec cos de Linha de Execuo ca

Ao contrrio dos processos, todas as linhas de execuo em um programa a ca simples compartilham o mesmo espao de endereamento. Isso signica que c c se uma linha de execuo modica uma localizao na memria (por exemplo, ca ca o uma varivel global), a mudana vis a c e vel para todas as outras linhas de execuo. Isso permite que multiplas linhas de execuo operem sobre os ca ca mesmos dados sem o uso de mecanismos de comunicao entre processos ca (que so descritos no Cap a tulo 5). Cada linha de execuo tem dua prpria pilha de chamadas, apesar do exca o posto acima. Isso permite a cada linha de execuo executar cdigo diferente ca o e chamar e retornar de sub-rotinas no caminho usual. Como no programa de linha de execuo simples, cada chamada a uma sub-rotina em cada linha de ca execuo tem seu prprio conjunto de variveis locais, que armazenada na ca o a e pilha para aquela linha de execuo. ca Algumas vezes, todavia, desejvel duplicar uma certa varivel de forma e a a que cada linha de execuo tenha uma cpia separada. GNU/Linux suporta ca o isso fornecendo cada linha de execuo com uma rea de dados espec ca a cos de linha de execuo. As variveis armazenadas nessa rea so duplicadas para ca a a a cada linha de execuo, e cada linha de execuo pode modicar sua cpia ca ca o da varivel sem afetar outras linhas de execuo. Devido ao fato de todas a ca as linhas de execuo compartilharem o mesmo espao de memria, dados ca c o espec cos de linha de execuo no podem ser acessados usando referncias ca a e normais de variveis. GNU/Linux fornece funes especiais para modicar e a co recuperar valores da rea de dados espec a cos de linha de execuo. ca Voc pode criar tantos dados espec e cos de linha de execuo quantos ca voc quiser, cada um do tipo void*. Cada item referenciado por uma e e chave. Para criar uma nova chave, e dessa forma um novo item de dado para cada linha de execuo, use a funo pthread key create. O primeiro arguca ca mento um apontador para uma varivel do tipo denido em pthread key t. e a Esse valor de chave pode ser usado por cada linha de execuo para acessar ca sua prpria cpia do correspondente item dos dados. O segundo argumento o o a pthread key create uma funo de limpeza. Se voc informar um apone ca e tador de funo aqui, GNU/Linux automaticamente chama aquela funo ca ca indicada pelo apontador informado quando cada linha de execuo terminar ca sua execuo, informando o valor espec ca co da linha de execuo que corresca ponde quela chave. Isso particularmente adequado pelo fato de a funo de a e ca limpeza ser chamada mesmo se a linha de execuo for cancelada em algum ca ponto arbitrrio em sua execuo. Se o valor espec a ca co da linha de execuo ca 98

for NULL, a funo de limpeza da linha de execuo no chamada. Se voc ca ca a e e no precisa de uma funo de limpeza, voc pode informar null ao invs de a ca e e um apontador de funo. ca

Aps voc ter criado uma chave, cada linha de execuo pode modicar o e ca seu valor espec co correspondente para aquela chave chamando a funo ca pthread setspecic. O Primeiro argumento a chave, e o segundo do tipo e e void* e corresponde ao valor espec co da linha de execuo a ser armazeca nado. Para recuperar algum item de dados espec cos da linha de execuo, ca chame a funo pthread getspecic, informando a chave como seu argumento. ca

Suponhamos, por exemplo, que sua aplicao distribua um trabalho entre ca diversas linhas de execuo. Para propsitos de auditoria, cada linha de ca o execuo tem um arquivo de log separado, no qual mensagens de progresso, ca para os trabalhos executados por aquela linha de execuo, so gravadas. A ca a a rea especica de dados um lugar conveniente para armazenar o apontador e para o arquivo de log de cada linha de execuo. ca

A Listagem 4.7 mostra como voc pode implementar isso. A funo prine ca cipal nesse programa exemplo cria uma chave para armazenar o apontador ao arquivo espec co da linha de execuo e ento armazenar as informaes em ca a co thread log key. Pelo fato de thread log key ser uma varivel global, ela coma e partilhada por todas as linhas de execuo. Quando cada linha de execuo ca ca inicia executando sua funo de linha de execuo, a linha de execuo abre ca ca ca um arquivo de log e armazena o apontador de arquivo sob aquela chave. Mais tarde, qualquer dessas linhas de execuo pode chamar write to thread log ca para escrever uma mensagem para o arquivo de log espec co de linha de execuo. A funo write to thread log recupera o apontador de arquivo ca ca para o arquivo de log da linha de execuo para dados espec ca cos de linha de execuo e escreve a mensagem. ca 99

Listagem 4.7: (tsd.c) Log Por Linhas de Execuo Implementado com ca Dados Espec cos de Linha de Execuo ca
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 54 55 56 57 #include <m a l l o c . h> #include <p t h r e a d . h> #include <s t d i o . h> / The k e y u s e d t o a s s o c a t e a l o g f i l e static pthread key t thread log key ; / W r i t e MESSAGE t o the log file for pointer with each thread . /

the

current

thread .

void w r i t e t o t h r e a d l o g ( const char m e s s a g e ) { FILE t h r e a d l o g = ( FILE ) p t h r e a d g e t s p e c i f i c f p r i n t f ( t h r e a d l o g , %s \n , m e s s a g e ) ; } / C l o s e the log file p o i n t e r THREAD LOG . /

( thread log key ) ;

void c l o s e t h r e a d l o g ( void t h r e a d l o g ) { f c l o s e ( ( FILE ) t h r e a d l o g ) ; } void t h r e a d f u n c t i o n ( void a r g s ) { char t h r e a d l o g f i l e n a m e [ 2 0 ] ; FILE t h r e a d l o g ; / G e n e r a t e t h e f i l e n a m e f o r t h i s t h r e a d s l o g f i l e . / s p r i n t f ( t h r e a d l o g f i l e n a m e , t h r e a d%d . l o g , ( i n t ) p t h r e a d s e l f ( ) ) ; / Open t h e l o g f i l e . / t h r e a d l o g = f o p e n ( t h r e a d l o g f i l e n a m e , w ) ; / S t o r e t h e f i l e p o i n t e r i n t h r e a d s p e c i f i c d a t a u n d e r t h r e a d l o g k e y . pthread setspecific ( thread log key , thread log ) ; w r i t e t o t h r e a d l o g ( Thread s t a r t i n g . ) ; / Do w o r k h e r e . . . / return NULL ; } i n t main ( ) { int i ; pthread t

threads [ 5 ] ;

/ C r e a t e a k e y t o a s s o c i a t e t h r e a d l o g f i l e p o i n t e r s i n thread s p e c i f i c data . Use c l o s e t h r e a d l o g t o c l e a n up t h e pointers . / p t h r e a d k e y c r e a t e (& t h r e a d l o g k e y , c l o s e t h r e a d l o g ) ; / C r e a t e t h r e a d s t o do t h e w o r k . / f o r ( i = 0 ; i < 5 ; ++i ) p t h r e a d c r e a t e (&( t h r e a d s [ i ] ) , NULL, t h r e a d f u n c t i o n , NULL) ; / Wait f o r a l l t h r e a d s t o f i n i s h . / f o r ( i = 0 ; i < 5 ; ++i ) p t h r e a d j o i n ( t h r e a d s [ i ] , NULL) ; return 0 ; }

file

Observe que thread function no precisa fechar o arquivo de log. Isso a ocorre pelo fato de que ao ser o arquivo de log criado, close thread log foi especicada como a funo de limpeza para aquela chave. Sempre que uma ca linha de execuo encerra, GNU/Linux chama close thread log, informando ca o valor espec co de linha de execuo para a chave do log espec ca co da linha de execuo. Essa funo toma o cuidado de fechar o arquivo de log. ca ca 100

4.3.1

Manipuladores de Limpeza

As funes de limpeza para dados espec co cos de linha de execuo so neca a cessrias para garantir que recursos no sejam perdidos quando a linha de a a execuo encerrar ou for cancelada. Algumas vezes, ao longo de todo um ca projeto de software, util estar apto a especicar funes de limpeza sem e co criar novos itens de dados espec cos de linha de execuo que duplicado ca e para cada linha de execuo. GNU/Linux fornece cabealhos de limpeza ca c para esse propsito. o Um manipulador de limpeza simplesmente uma funo que deve ser e ca chamada quando a linha de execuo termina. O manipulador recebe um ca parmetro simples do tipo void*, e seu valor de argumento fornecido quando a e o manipulador registrado isso facilita o uso da mesma funo manipulae ca dora para liberar recursos em multiplas instncias. a Um manipulador um procedimento temporrio, usado para liberar um e a recurso somente se a linha de execuo encerrar ou for cancelada ao invs de ca e terminar a execuo de uma regio particular de cdigo. Sob circunstncias ca a o a normais, quando a linha de execuo no encerra e no cancelada, o reca a a e curso deve ser liberado explicitamente e o manipulador de limpeza deve ser removido. Para registrar um manipulador de limpeza, chame a funo pthread clean ca up push, informando um apontador para a funo de limpeza e o valor do seu ca argumento void*. A chamada a pthread cleanup push deve ser equilibrada por uma correspondente chamada a pthread cleanup pop, que remove o registro do maniplulador de limpeza. Por convenincia, pthread cleanup pop recebe e um argumento sinalizador do tipo int; se o sinalizador for diferente de zero, a ao de limpeza executada imediatamente e seu registro removido. ca e e O fragmento de programa na Listagem 4.8 mostra como voc pode pose sivelmente usar um manipulador de limpeza para garantir que um espao c temporrio de armazenamento alocado dinamicamente seja limpo se a linha a de execuo terminar. ca 101

Listagem 4.8: (cleanup.c) Fragmento de Programa Demonstrando um Manipulador de Limpeza de Linha de Execuo ca
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 #include <m a l l o c . h> #include <p t h r e a d . h> / A l l o c a t e a t e m p o r a r y buffer . /

void a l l o c a t e b u f f e r ( s i z e t { return m a l l o c ( s i z e ) ; } / D e a l l o c a t e a t e m p o r a r y void d e a l l o c a t e b u f f e r { free ( buffer ) ; }

size )

buffer .

( void b u f f e r )

void d o s o m e w o r k ( ) { / A l l o c a t e a t e m p o r a r y b u f f e r . / void t e m p b u f f e r = a l l o c a t e b u f f e r ( 1 0 2 4 ) ; / R e g i s t e r a c l e a n u p h a n d l e r f o r t h i s b u f f e r , t o d e a l l o c a t e case t h e t h r e a d e x i t s or i s c a n c e l l e d . / pthread cleanup push ( deallocate buffer , temp buffer ) ; / Do some w o r k h e r e cancelled . . . / that might call pthread exit or might be

it

in

/ U n r e g i s t e r t h e c l e a n u p h a n d l e r . S i n c e we p a s s a nonz e r o t h i s a c t u a l l y performs t h e c l e a n u p by c a l l i n g deallocate buffer . / pthread cleanup pop (1) ; }

value ,

Pelo fato de o argumento a pthread cleanup pop ser diferene de zero nesse e caso, a funo de limpeza deallocate buer chamada automaticamente aqui ca e no precisa ser chamada explicitamente. Nesse simples caso, pudemos ter a a funo da biblioteca padro liberando diretamente como nosso manipulador ca a de limpeza ao invs de deallocate buer. e

4.3.2

Limpeza de Linha de Execuo em C++ ca

Programadores em C++ esto acostumados limpar livremente empacotando a aes de limpeza em objetos destrutores. Quando os objetos saem fora do co escopo, ou por que um bloco executado para completar alguma coisa ou e pelo fato de uma exceo ser esquecida, C++ garante que destrutores seca jam chamados para aquelas variveis automticas que tiverem as referidas a a excees e blocos. Esse comportamento de C++ fornece um mecanismo maco nipulador para garantir que cdigo de limpeza seja chamado sem importar o como o bloco terminou. a Se uma linha de execuo chama a funo pthread exit, C++ no garante ca ca que destrutores sejam chamados para todas as variveis automticas na pilha a a da linha de execuo. Uma maneira inteligente de recuperar essa funcionaca lidade invocar a funo pthread exit no n mais alto da funo de linha e ca vel ca de execuo abandonando alguma exceo especial. ca ca 102

O programa na Listagem 4.9 demonstra isso. Usando essa tcnica, uma e funo indica sua inteno de encerrar a linha de execuo abandonando uma ca ca ca ThreadExitException ao invs de chamar pthread exit diretamente. Pelo fato e de a exceo ter sido detectada na funo de linha de execuo de n ca ca ca vel mais alto, todas as variveis locais sobre a pilha da linha de execuo sero a ca a destru das como se a exceo limpasse a si mesma. ca Listagem 4.9: (cxx-exit.cpp) Implementando Sa Segura de uma Linha da de Execuo com Excees de C++ ca co
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 #include <p t h r e a d . h> extern b o o l should exit thread immediately () ;

c l a s s ThreadExitException { public : / C r e a t e an e x c e p t i o n s i g n a l l i n g t h r e a d T h r e a d E x i t E x c e p t i o n ( void r e t u r n v a l u e ) : thread return value ( return value ) { } / A c t u a l l y e x i t t h e t h r e a d , u s i n g t h e constructor . / void DoThreadExit ( ) { pthread exit ( thread return value ) ; } private : / The r e t u r n v a l u e t h a t w i l l void t h r e a d r e t u r n v a l u e ; }; void d o s o m e w o r k ( ) { while ( 1 ) { / Do some u s e f u l if } }

exit

w i t h RETURN VALUE .

return

value

provided

in

the

b e u s e d when

exiting

the

thread .

things

here . . .

( should exit thread immediately () ) throw T h r e a d E x i t E x c e p t i o n ( / t h r e a d s

return

v a l u e = / NULL) ;

void t h r e a d f u n c t i o n ( void ) { try { do some work ( ) ; } c a t c h ( T h r e a d E x i t E x c e p t i o n ex ) { / Some f u n c t i o n i n d i c a t e d t h a t we s h o u l d ex . DoThreadExit ( ) ; } return NULL ; }

exit

the

thread .

4.4

Sincronizao e Seoes Cr ca c ticas

Programar com linhas de execuo muito complicado pelo fato de que a ca e maioria dos programas feitos usando linhas de execuo serem programas ca que competem uns com os outros. Em particular, no existe caminho para a saber quando o sistema ir agendar uma linha de execuo para ser execua ca tada e quando o sistema ir executar outra linha de execuo. Uma linha a ca 103

de execuo pode ser executada pelo sistema por tempo muito longo, ou o ca sistema pode alternar entre diversas linhas de execuo muito rapidamente. ca Em um sistema com mltiplos processadores, o sistema pode mesmo agendar u multiplas linhas de execuo para serem executadas literalmente ao mesmo ca tempo. Depurar um programa que usa linha de execuo dif pelo fato de ca e cil voc no poder sempre e facilmente reproduzir o comportamento que causa e a o problema. Voc pode executar o programa e ter tudo trabalhando perfeitae mente; a prxima vez que voc executar o programa, ele pode cair. No existe o e a caminho para fazer o sistema agendar as linhas de execuo exatamente da ca mesma maneira que foi feito anteriormente. A mais recente causa da maioria dos erros envolvendo linhas de execuo ca que as linhas de execuo diferentes acessando a mesma informao na e ca ca memria. Como mencionado anteriormente, esse comportamento de divero sas linhas de execuo acessaem a mesma informao um dos poderosos ca ca e aspctos de uma linha de execuo, mas esse comportamento tambm pode e ca e ser perigoso. Se uma linha de execuo atualiza parcialmente uma estrutura ca de dados quando outra linha de execuo acessa a mesma estrutura de daca dos, vai provavelmente acontecer uma confuso. Muitas vezes, programas a que usam linha de execuo e possuem erros carregam um cdigo que ir traca o a balhar somente se uma linha de execuo recebe agendamento muitas vezes ca mais ou mais cedo que outra linha de execuo. Esses erros so chamaca a dos condies de corrida; as linhas de execuo esto competindo uma com co ca a a outra para modicar a mesma estrutura de dados.

4.4.1

Condies de Corrida co

Suponhamos que seu programa tenha uma srie de trabalhos enleirados e que so processados por muitas linhas de execuo concorrentes. A la de a ca trabalhos representada por uma lista linkada de objetos de estrutura de e trabalho. Aps cada linha de execuo terminar uma operao, ela verica o ca ca a la para ver se um trabalho adicional est dispon a vel. Se job queue for diferente de NULL, a linha de execuo remove o trabalho do topo da lista ca linkada e posiciona job queue no prximo trabalho da lista. A funo de linha o ca de execuo que processa trabalhos na la pode parecer-se com a Listagem ca 4.10. 104

Listagem 4.10: ( job-queue1.c) Funo de Linha de Execuo para Proca ca cessar Trabalhos Enleirados
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 #include <m a l l o c . h> struct job { / L i n k f i e l d f o r struct job next ; / O t h e r }; / A l i n k e d l i s t o f p e n d i n g struct job job queue ; extern void p r o c e s s j o b / P r o c e s s queued jobs jobs . / fields linked list . /

describing

work t o

be done . . .

( struct job ) ; until the queue is empty . /

void t h r e a d f u n c t i o n ( void a r g ) { while ( j o b q u e u e != NULL) { / Get t h e n e x t a v a i l a b l e j o b . / struct job n e x t j o b = job queue ; / Remove t h i s j o b f r o m t h e l i s t . / job queue = job queue >n e x t ; / C a r r y o u t t h e w o r k . / process job ( next job ) ; / C l e a n up . / free ( next job ) ; } return NULL ; }

Agora suponhamos que duas linhas de execuo encerrem um trabalho ca aproximadamente ao mesmo tempo, mas somente um trabalho reste na la. A primeira linha de execuo verica se job queue NULL; encontrando que ca e no , a linha de execuo entra no lao e armazena o apontador para o a e ca c objeto de trabalho em next job. Nesse ponto, o sistema GNU/Linux interrompe a primeira linha de execuo e agenda a segunda. A segunda linha ca de execuo tambm verica se job queue NULL; e encontrando que no ca e e a , tambm atribui o mesmo apontador de trabalho para next job. Por desae e fortunada coincidncia, temos agora duas linhas de execuo executando o e ca mesmo trabalho. Para piorar a situao, uma linha de execuo ir deslinkar o objeto ca ca a de trabalho da lista, permitindo que job queue contenha NULL. Quando a outra linha de execuo avaliar job queue->next, uma falha de segmentao ca ca ir aparecer. a Esse um exemplo de condio de corrida. Sob afortunadascircunstne ca a cias, esse particular agendamento de duas linhas de execuo podem nunca ca ocorrer, e a condio de corrida pode nunca mostrar-se. Somente em circa cunstncias diferenciadas, talvez ao executar sobre um sistema muito pesado a (ou sobre um novo servidor multi-processado de um importante usurio!) a pode o erro mostrar-se. Para eliminar condies de corrida, voc precisa de um caminho para co e fazer operaes atmicas. Uma operao atmica indivis e no pode ser co o ca o e vel a 105

interrompida; uma vez que a operao for iniciada, no ir ser pausada ou ca a a interrompida at que se complete, e nenhuma outra operao ir tomar o seu e ca a lugar enquanto isso. Nesse exemplo em particular, voc ir querer vericar e a job queue; se no estivar vazia, remover o primeiro trabalho, tudo isso junto a como uma operao atmica simples. ca o

4.4.2

Mutexes

A soluo para o problema da condio de corrida da la de trabalho ca ca e permitir que somente uma linha de execuo por vez acesse a la de linhas de ca execuo. Assim que uma linha de execuo inicia olhando na la, nenhuma ca ca outra linha de execuo deve estar apta a acessar a la at que a primeira ca e linha de execuo tenha decidido se realiza um trabalho e, se zer isso , tiver ca removido o trabalho da lista. A implementao disso requer suporte por parte do sistema operacional. ca GNU/Linux fornece mutexes, abreviatura de trava de excluso mtua 8 . Um a u mutex uma trava especial que somente uma linha de execuo pode travar e ca a cada vez. Se uma linha de execuo trava um mutex e ento uma segunda ca a linha de execuo tambm tenta travar o mesmo mutex, a segunda linha de ca e execuo bloqueada, ou colocada em espera. somente quando a primeira ca e linha de execuo destrava o mutex a segunda linha de execuo desbloca e ca queada permitindo sua execuo. GNU/Linux garante que condies de ca co corrida no ocorram em meio a linhas de execuo que tentem travar um a ca mutex ; somente uma linha de execuo ir mesmo pegar a trava, e todas as ca a outras linhas de execuo iro ser bloqueadas. ca a Pensando em um mutex como a trava de uma porta de banheiro. Quem chegar primeiro entra no banheiro e trava a porta. Se alguma outra pessoa tenta entrar no banheiro enquanto ele estiver ocupado, aquela pessoa encontra a porta fechada e ir ser forada a esperar do lado de fora at que o a c e ocupante aparea. c Para criar um mutex, crie uma varivel do tipo pthread mutex t e informe a um apontador para essa varivel criada para a funo pthread mutex init. O a ca segundo argumento de pthread mutex init um apontador para um objeto de e atributo de mutex, que especica os atributos de um mutex. Da mesma forma que ocorre com a funo pthread create, se o apontador de atributo for nulo, ca atributos padronizados so assumidos. A Varivel mutex deve ser inicializada a a somente uma unica vez. Esse fragmento de cdigo adiante demonstra a o declarao e a inicializao de uma varivel mutex. ca ca a p t h r e a d m u t e x t mutex ;
8

Nota do tradutor:MUTual EXclusion

106

p t h r e a d m u t e x i n i t (&mutex , NULL ) ;

Outra maneira mais simples de criar um mutex com atributos padronizados inicializar o referido mutex com o valor especial PTHREAD MUTEX e INITIALIZER. Nenhuma chamada adicional a pthread mutex init necessria. e a Essa forma particularmente conveniente para variveis globais (e, em C++, e a membros de dados estticos). O fragmento de cdigo acima poderia equivaa o lentemente ter sido escrito como segue:

p t h r e a d m u t e x t mutex = PTHREAD MUTEX INITIALIZER ;

Uma linha de execuo pode tentar travar um mutex por meio de uma ca chamada a pthread mutex lock referindo-se ao dito mutex. Se o mutex estiver desbloqueado, ele torna-se travado e a funo retorna imediatamente. Se o ca mutex estiver travado por outra linha de execuo, pthread mutex lock bloca queia a execuo e retorna somente quando o mutex for desbloqueado pela ca outra linha de execuo. Diversas linhas de execuo ao mesmo tempo poca ca dem ser bloqueadas ao tentarem usar um mutex travado. Quando o mutex for desbloqueado, somente uma das linhas de execuo bloqueadas (escolhida ca de forma imprevis vel) desbloqueada e permitido que a referida linha de e e execuo trave o mutex ; as outras linhas de execuo continuam bloqueadas. ca ca Uma chamada a pthread mutex unlock desbloqueia um mutex. Essa funo ca deve sempre ser chamada a partir da mesma linha de execuo que travou o ca mutex. A listagem 4.11 mostra outra verso do exemplo de la de trabalhos. a Agora a la protegida por um mutex. Antes de acessar a la (ou para e leitura ou para escrita), cada linha de execuo trava um mutex primeiraca mente. Somente quando a completa sequncia de vericar a la e remover e um trabalho for completada o mutex destravado. Isso evita a condio de e ca corrida previamente descrita. 107

Listagem 4.11: ( job-queue2.c) Funo de Tarefa da Fila de Trabalho, ca Protegida por um Mutex
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 <m a l l o c . h> #include <p t h r e a d . h> struct job { / L i n k f i e l d f o r struct job next ; / O t h e r }; / A l i n k e d l i s t o f p e n d i n g struct job job queue ; extern void p r o c e s s j o b jobs . / fields

linked

list .

describing

work t o

be done . . .

( struct job ) ;

/ A mutex p r o t e c t i n g j o b q u e u e . / p t h r e a d m u t e x t j o b q u e u e m u t e x = PTHREAD MUTEX INITIALIZER ; / P r o c e s s queued jobs until the queue is empty . /

void t h r e a d f u n c t i o n ( void a r g ) { while ( 1 ) { struct job n e x t j o b ; / L o c k t h e mutex on t h e j o b q u e u e . / p t h r e a d m u t e x l o c k (& j o b q u e u e m u t e x ) ; / Now i t s s a f e t o c h e c k i f t h e q u e u e i s empty . / i f ( j o b q u e u e == NULL) n e x t j o b = NULL ; else { / Get t h e n e x t a v a i l a b l e j o b . / next job = job queue ; / Remove t h i s j o b f r o m t h e l i s t . / job queue = job queue >n e x t ; } / U n l o c k t h e mutex on t h e j o b q u e u e , s i n c e we r e d o n e w i t h q u e u e f o r now . / p t h r e a d m u t e x u n l o c k (& j o b q u e u e m u t e x ) ; / Was t h e q u e u e empty ? i f ( n e x t j o b == NULL) break ; If so , end t h e thread . /

the

/ C a r r y o u t t h e w o r k . / process job ( next job ) ; / C l e a n up . / free ( next job ) ; } return NULL ; }

Todo o acesso a job queue, o apontador de dados compartilhados, vem entre a chamada a pthread mutex lock e a chamada a pthread mutex unlock. Um objeto de trabalho, armazenado em next job, acessado de fora dessa e regio somente aps aquele objeto de trabalho ter sido removido da la e a o estar, dessa forma, inacess a outras linhas de execuo. vel ca Note que se a la estiver vazia (isto , job queue for NULL), ns no e o a sa mos fora do lao imediatamente pelo fato de termos que manter o mutex c permanentemente travado e devemos prevenir que qualquer outra linha de execuo acesse a la de trabalhos novamente pois ela est vazia. Ao invs ca a e disso, lembramos esse fato escolhendo next job para NULL e saimos fora do lao somente aps desbloquear o mutex. c o O uso de mutex para travar job queue no automtico; cabe a voc a e a e 108

adicionar o cdigo para travar o mutex antes de acessar job queue e tambm o e o cdigo para destravar job queue posteriormente. Por exemplo, uma funo o ca para adicionar um trabalho ` la de trabalhos pode parecer-se com isso: a

void e n q u e u e j o b ( struct j o b new job ) { p t h r e a d m u t e x l o c k (& j ob qu eu e mu te x ) ; new job>next = j o b q u e u e ; j o b q u e u e = new job ; p t h r e a d m u t e x u n l o c k (& j ob qu eu e mu te x ) ; }

4.4.3

Travas Mortas de Mutex

Mutexes fornecem um mecanismo para permitir que uma linha de execuo ca bloquei a execuo de outra. Esse procedimento abre a possibilidade de uma ca nova classe de falhas, chamadas travas mortas. Uma trava morta ocorre quando uma ou mais linhas de execuo esto presas esperando por alguma ca a coisa que nunca ir ocorrer. a Um tipo simples de trava morta ocorre quando a mesma linha de execuo ca tenta bloquear um mutex duas vezes em uma linha. O comportamento nesse caso depende de qual tipo de mutex est sendo usado. Existem trs tipos de a e mutex : 109

rpido - travando um mutex rpido (o tipo padro) far com que a a a a ocorra uma trava morta. Como foi dito anteriormente, uma tentativa trava os blocos mutex at que o mutex seja desbloqueado. Mas e pelo fato de a linha de execuo que travou o mutex estar bloqueada ca nesse mesmo mutex, a trava no pode nunca ser liberada. a recursivo - travando um mutex recursivo no causa uma trava a morta. Um mutex recursivo pode seguramente ser travado vrias a vezes pela mesma linha de execuo. O mutex recursivo lembra ca quantas vezes pthread mutex lock foi chamada sobre o mesmo mutex pela linha de execuo que segura a trava; a linha de execuo ca ca que segura a trava deve fazer o mesmo nmero de chamadas a pthu read mutex unlock antes do mutex atual ser desbloqueado e outra linha de execuo conseguir travar o mutex liberado. ca vericao de erro - GNU/Linux ir detectar e sinalizar uma trava ca a dupla sobre um mutex de vericao de erro que poderia de outra ca forma causar uma trava morta. A segunda chamada consecutiva a pthread mutex lock retorna o cdigo de falha EDEADLK. o Por padro, um mutex GNU/Linux do tipo rpido. Para criar um a e a mutex de um dos outros dois tipos, primeiro crie um objeto de atributo de mutex declarando uma varivel do tipo pthread mutexattr t e chamando ptha a read mutexattr init sobre um apontador para a varivel do tipo pthread mutex attr t. A seguir ajuste o tipo do mutex chamando pthread mutexattr setkind np; o primeiro argumento um apontador para o objeto de atributo de mue tex, e o segundo PTHREAD MUTEX RECURSIVE NP para um mutex e recursivo, ou PTHREAD MUTEX ERRORCHECK NP para um mutex de vericao de erro. Informe um apontador para esse atributo de objeto na ca ca funo pthread mutex init para criar um mutex do tipo de vericao de erro, ca e ento destrua o objeto de atributo com a funo pthread mutexattr destroy. a ca A sequncia de cdigo abaixo ilustra a criao de ummutex de vericao e o ca ca de erro, por exemplo:
pthread pthread pthread pthread pthread pthread mutexattr t attr ; m u t e x t mutex ; m u t e x a t t r i n i t (\& a t t r ) ; m u t e x a t t r s e t k i n d n p (\& a t t r , PTHREAD MUTEX ERRORCHECK NP ) ; m u t e x i n i t (\&mutex , \& a t t r ) ; m u t e x a t t r d e s t r o y (\& a t t r ) ;

Como sugerido pelo suxo np, os mutexes do tipo recursivo e de vericao de erro so espec ca a cos do GNU/Linux e no so portveis. Todavia, a a a no geralmente aconselhado usar esses dois tipos de mutexes em programas. a e (Mutexes de vericao de erro podem ser uteis quando se faz depuraes, ca co apesar disso.) 110

4.4.4

Testes de Mutex sem Bloqueio

Ocasionalmente, util testar se um mutex est travado sem sofrer bloqueio e a algum relativamente a esse mutex. Por exemplo, uma linha de execuo pode ca precisar travar um mutex mas pode ter outro trabalho para fazer ao invs ser e bloqueada se o mutex j estiver travado. Pelo fato de que pthread mutex lock a no ir retornar at que o mutex se torne desbloqueado, alguma outra funo a a e ca necessria. e a GNU/Linux fornece pthread mutex trylock para esse propsito. Se voc o e chamar pthread mutex trylock sobre um mutex destravado, voc ir travar o e a mutex como se voc tivesse chamado called pthread mutex lock, e pthread mut e ex trylock ir retornar zero. Todavia, se o mutex j estiver bloqueado por a a outra linha de execuo, pthread mutex trylock no ir bloquear a linha de ca a a a execuo atual. Ao invs disso, pthread mutex trylock ir retornar imediataca e mente com o cdigo de erro EBUSY. A trava de mutex mantida pela outra o linha de execuo no afetada. Voc pode tentar mais tarde travar o mutex. ca a e e

4.4.5

Semforos para Linhas de Execuo a ca

No exemplo precedente, no qual muitas linhas de execuo processam trabaca lhos a partir de um la, a funo de linha de execuo principal das linhas de ca ca execuo realiza o prximo trabalho at que nenhum trabalho seja esquecido ca o e e ento termina a linha de execuo. Esse esquema funciona se todos os a ca trabalhos forem enleirados previamente ou se novos trabalhos forem enleirados to rapidamente quanto as linhas de execuo os processam. Todavia, a ca se as linhas de execuo trabalham muito rapidamente, a la de trabalhos ir ca a esvaziar e as linhas de execuo encerraram. Se novos trabalhos forem mais ca tarde enleirados, nenhuma linha de execuo pode restar para process-los. ca a O que podemos apreciar ao invs do exposto acima um mecanismo para e e bloquear as linhas de execuo quando a la esvaziar at que novos trabalhos ca e estejam dispon veis. Um semforo fornece um mtodo conveniente para fazer isso. Um semforo a e a um contador que pode ser usado para sincronizar multiplas linhas de e execuo. Da mesma forma que com o mutex, GNU/Linux garante que a ca vericao ou a modicao do valor de um semforo pode ser feito de forma ca ca a segura, sem criar condies de corrida. co Cada semforo tem um valor de contagem, que um inteiro no negativo. a e a Um semforo suporta duas operaes bsicas: a co a 111

Uma operao wait decrementa o semforo de 1. Se o valor j ca a a for zero, a operao bloqueia at que o valor do semforo torneca e a se positivo (devido a ao de alguma outra linha de execuo). ca ca Quando o valor do semforo torna-se positivo, ele decrementado a e de 1 e a operao de espera retorna. ca Uma operao post incrementa o valor do semforo de 1. Se o ca a semforo era anteriormente zero e outras linhas de execuo esto a ca a bloqueadas em uma operao wait sobre o atual semforo, uma ca a daquelas linhas de execuo desbloqueada e sua operao wait ca e ca realiza-se (o que acarreta o retorno do valor do semforo a zero). a Note que GNU/Linux fornece duas implementaes de semforos ligeiraco a mente diferentes. A primeira que descrevemos aqui a implementao de e ca semforos POSIX padro. Use os semforos POSIX quando comunicando-se a a a entre linhas de execuo. A outra implementao, usada para comunicao ca ca ca entre processos, descrita na Seo 5.2, Semforos de Processos. Se voc e ca a e usa semforos, inclua <semaphore.h>. a Um semforo representado por uma varvel sem t. Antes de usar a a e a varivel, voc deve inicializ-la usando a funo sem init, informando um a e a ca apontador para a varivel sem t. O segundo parmetro deve ser zero 9 , e o a a terceiro parmetro o valor inicial do semforo. Se voc no mais precisar a e a e a de um semforo, bom liberar seus recursos com sem destroy. a e Para operaes do tipo wait, use sem wait. Para operaes do tipo post, co co ca a use sem post. Uma funo que no faz bloqueio do tipo wait, chamada e e ca e sem trywait, tambm fornecida. A funo sem trywait semelhante a pthread mutex trylock se a operao do tipo wait puder ser bloqueada pelo ca fato de o valor do semforo ser zero, a funo retorna imediatamente, com o a ca valor de erro EAGAIN, ao invs de efetuar o bloqueio. e GNU/Linux tambm fornece uma funo para recuperar o valor atual de e ca um semforo, sem getvalue, a qual coloca o valor em um apontador para uma a varivel do tipo int por meio de seu segundo argumento. Voc no deve usar a e a o valor do semforo que voc pegou dessa funo para decidir fazer ou um a e ca wait ou um post sobre o semforo, apesar disso. Usar o valor do semforo a a pode levar a uma condio de corrida: Outra linha de execuo pode mudar ca ca o valor do semforo entre a chamada a sem getvalue e a chamada a outra a funo de semforo. Use as funes atmicas post e wait ao invs de usar o ca a co o e valor do semforo. a Retomando para nosso exemplo de la de trabalho, podemos usar um semforo para contar o nmero de trabalhos esperando na la. A Listagem a u
Um valor diferente de zero pode indicar a semforo que pode ser compartilhado por a vrios processos, o que no suportado pelo GNU/Linux para esse tipo de semforo. a a e a
9

112

4.12 controla a la com um semforo. A funo enqueue job adiciona um a ca novo trabalho ` la. a

113

Listagem 4.12: ( job-queue3.c) Fila de Trabalhos Controlada por um Semforo a


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 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 #include <m a l l o c . h> #include <p t h r e a d . h> #include <semaphore . h> struct job { / L i n k f i e l d f o r struct job next ; / O t h e r }; / A l i n k e d l i s t o f p e n d i n g struct job job queue ; extern void p r o c e s s j o b jobs . / fields

linked

list .

describing

work t o

be done . . .

( struct job ) ;

/ A mutex p r o t e c t i n g j o b q u e u e . / p t h r e a d m u t e x t j o b q u e u e m u t e x = PTHREAD MUTEX INITIALIZER ; / A s e m a p h o r e c o u n t i n g sem t job queue count ; / P e r f o r m onet i m e t h e number o f jobs in the queue . /

initialization

of

the

job

queue .

void i n i t i a l i z e j o b q u e u e ( ) { / The q u e u e i s i n i t i a l l y empty . / j o b q u e u e = NULL ; / I n i t i a l i z e t h e s e m a p h o r e w h i c h c o u n t s i n i t i a l v a l u e s h o u l d be zero . / s e m i n i t (& j o b q u e u e c o u n t , 0 , 0 ) ; } / P r o c e s s queued jobs until the queue is

jobs

in

the

queue .

Its

empty .

void t h r e a d f u n c t i o n ( void a r g ) { while ( 1 ) { struct job n e x t j o b ; / Wait on t h e j o b q u e u e s e m a p h o r e . If i t s value is positive , i n d i c a t i n g t h a t t h e q u e u e i s n o t empty , d e c r e m e n t t h e c o u n t b y one . I f t h e q u e u e i s empty , b l o c k u n t i l a new j o b i s e n q u e u e d . s e m w a i t (& j o b q u e u e c o u n t ) ; / L o c k t h e mutex on t h e j o b q u e u e . / p t h r e a d m u t e x l o c k (& j o b q u e u e m u t e x ) ; / B e c a u s e o f t h e s e m a p h o r e , we know t h e q u e u e i s n o t empty . Get the next a v a i l a b l e job . / next job = job queue ; / Remove t h i s j o b f r o m t h e l i s t . / job queue = job queue >n e x t ; / U n l o c k t h e mutex on t h e j o b q u e u e , s i n c e we r e d o n e w i t h t h e q u e u e f o r now . / p t h r e a d m u t e x u n l o c k (& j o b q u e u e m u t e x ) ; / C a r r y o u t t h e w o r k . / process job ( next job ) ; / C l e a n up . / free ( next job ) ; } return NULL ; } / Add a new j o b to the front of the job queue . / / )

void e n q u e u e j o b ( / P a s s j o b s p e c i f i c { struct job new job ;

data

here . . .

/ A l l o c a t e a new j o b o b j e c t . / new job = ( struct job ) malloc ( s i z e o f ( struct job ) ) ; / S e t t h e o t h e r f i e l d s o f t h e j o b s t r u c t h e r e . . . / / L o c k t h e mutex on t h e j o b q u e u e b e f o r e a c c e s s i n g p t h r e a d m u t e x l o c k (& j o b q u e u e m u t e x ) ; / P l a c e t h e new j o b a t t h e h e a d o f t h e q u e u e . / >n e x t = j o b q u e u e ; n e w j o b job queue = new job ; it . /

/ P o s t t o t h e s e m a p h o r e t o i n d i c a t e a n o t h e r j o b i s a v a i l a b l e . If t h r e a d s a r e b l o c k e d , w a i t i n g on t h e s e m a p h o r e , one w i l l become u n b l o c k e d s o i t can p r o c e s s t h e j o b . / s e m p o s t (& j o b q u e u e c o u n t ) ;

114

/ U n l o c k t h e j o b q u e u e mutex . / p t h r e a d m u t e x u n l o c k (& j o b q u e u e m u t e x ) ; }

Antes de pegar um trabalho da primeira posio da la, cada linha de ca execuo ir primeiramente realizar uma operao wait sobre o semforo. ca a ca a Se o valor do semforo for zero, indicando que a la est vazia, a linha de a a execuo ser simplesmente bloqueada at que o valor do semforo torne-se ca a e a positivo, indicando que um trabalho foi adicionado ` la. a A funo enqueue job adiciona um trabalho ` la. Da mesma forma que ca a ca thread function, a funo enqueue job precisa travar o mutex da la antes de modicar a la. Aps adicionar um trabalho ` la, a funo enqueue job efeo a ca tua uma operao do tipo post no semforo, indicando que um novo trabalho ca a est dispon a vel. Na verso mostrada na Listagem 4.12, as linhas de execuo a ca que atuam sobre os trabalhos nunca terminam; se no houverem trabalhos a dispon veis em algum momento, todas as linhas de execuo simplesmente ca bloqueiam em sem wait.

4.4.6

Variveis Condicionais a

Mostramos como usar um mutex para proteger uma varivel contra acessos a simultneos de duas linhas de execuo e como usar semforos para implea ca a mentar um contador compartilhado. Uma varivel condicional uma terceiro a e dispositivo de sincronizao que GNU/Linux fornece; com variveis condicioca a nais, voc pode implementar condicionais mais complexas sob as quais linhas e de execuo realizam trabalhos. ca Suponhamos que voc escreva uma funo que executa um lao innitae ca c mente, fazendo algum trabalho a cada iterao. O lao da linha de execuo ca c ca , todavia, precisa ser controlado por um sinalizador: o lao executa somente c quando o sinalizador est ativo; quando o sinalizador est desativado, o lao a a c para. A Listagem 4.13 mostra como voc pode implementar a funo suposta e ca acima girando em um lao. Durante cada iterao do lao, a funo de linha c ca c ca de execuo verica se o sinalizador est ativo. Pelo fato de o sinalizador ca a ser acessado por vrias linhas de execuo, ele protegido por um mutex. a ca e Essa implementao pode ser correta, mas no eciente. A funo de ca a e ca linha de execuo ir gastar recursos de CPU sempre que sinalizador estiver ca a dasativado, at que alguma circunstncia possa fazer com que o sinalizador e a torne-se ativado. 115

Listagem 4.13: (spin-condvar.c) Uma Implementao Simples de Varivel ca a Condicional


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 <p t h r e a d . h> extern void do wor k () ;

int t h r e a d f l a g ; pthread mutex t thread flag mutex ; void i n i t i a l i z e f l a g ( ) { p t h r e a d m u t e x i n i t (& t h r e a d f l a g m u t e x , NULL) ; thread flag = 0; } / C a l l s d o w o r k spins . / repeatedly while the thread flag is set ; otherwise

void t h r e a d f u n c t i o n { while ( 1 ) { int f l a g i s s e t ;

( void t h r e a d a r g )

/ P r o t e c t t h e f l a g w i t h a mutex l o c k . / p t h r e a d m u t e x l o c k (& t h r e a d f l a g m u t e x ) ; f l a g i s s e t = thread flag ; p t h r e a d m u t e x u n l o c k (& t h r e a d f l a g m u t e x ) ; if ( flag is set ) do wor k ( ) ; / E l s e don t do a n y t h i n g . } return NULL ; } / S e t s the value of the thread flag t o FLAG VALUE . /

Just

loop

again .

void s e t t h r e a d f l a g ( i n t f l a g v a l u e ) { / P r o t e c t t h e f l a g w i t h a mutex l o c k . / p t h r e a d m u t e x l o c k (& t h r e a d f l a g m u t e x ) ; thread flag = flag value ; p t h r e a d m u t e x u n l o c k (& t h r e a d f l a g m u t e x ) ; }

Uma varivel condicional capacita voc a implementar uma condio sob a e ca a qual uma linha de execuo realiza algum trabalho e, inversamente, a ca condio sob a qual a linha de execuo bloqueada. Enquanto toda linha ca ca e de execuo que potencialmente modica o senso da condio usa a varivel ca ca a condicional propriamente, GNU/Linux garante que linhas de execuo bloca queadas na condio iro ser desbloqueadas quando a condio mudar. ca a ca Da mesma forma que com um semforo, uma linha de execuo pode a ca esperar por uma varivel condicional. Se linha de execuo A espera por a ca uma varivel condicional, a linha de execuo A bloqueada at que alguma a ca e e outra linha de execuo, uma linha de execuo B, sinalize a mesma varivel ca ca a condicional. Diferentemente do semforo, uma varivel condicional no tem a a a contador ou memria; a linha de execuo A deve esperar pela varivel condio ca a cional antes da linha de execuo B sinalize essa mesma varivel condicional ca a novamente. Se a linha de execuo B sinaliza a varivel condicional antes ca a que a linha de execuo A espere pela mesma varivel condicional, o sinal ca a e perdido, e a linha de execuo A ca bloqueada at que alguma outra linha ca e de execuo sinalize a varivel condicional novamente. ca a 116

Adiante mostra-se como voc poderia usar uma varivel condicional para e a fazer a linha de execuo acima de forma mais eciente: ca O lao em thread function verica o sinalizador. Se o sinalizador c est desativado, a linha de execuo espera pela varivel condicioa ca a nal. A funo set thread ag sinaliza a varivel condicional aps moca a o dicar o valor do sinalizador. Por esse caminho, se o lao estiver c bloqueado na varivel condicional, ir ser desbloqueado e vericar a a a a condicional novamente. Existe um problema com isso: h uma condio de corrida entre vericar o a ca valor do sinalizador e modicar seu valor ou esperar pela varivel condicional. a Suponhamos que thread function vericou o sinalizador e encontrou-a desabilitada. Naquele momento, o GNU/Linux agendou uma pausa para aquela linha de execuo e retomou a linha de execuo principal. Por alguma coinca ca cidncia, a linha de execuo principal est em na funo set thread ag. A e ca a ca funo set thread ag ajusta o sinalizador e sinaliza a varivel condicional. ca a Pelo fato de nenhuma linha de execuo estar esperando pela varivel conca a dicional naquele momento (lembre que thread function estava pausada antes de poder esperar pela varivel condicional), o sinal perdido. Agora, quando a e GNU/Linux reagenda a outra linha de execuo, ela inicia esperando pela ca varivel condicional e pode acabar bloqueada para sempre. a Para resolver esse problema, precisamos de um caminho para travar o sinalizador e a varivel condicional juntos com um mutex simples. Afortua nadamente, GNU/Linux fornece exatamente esse mecanismo. Cada varivel a condicional deve ser usada conjuntamente com um mutex, para prevenir esse tipo de condio de corrida. Usando esse esquema, a funo de linha de ca ca execuo segue os passos abaixo: ca 1. O lao em thread function trava o mutex e l o valor do sinalizador. c e 2. Se o sinalizador estiver ativado, o sinalizador ativado causa o desbloqueio do mutex e a execuo da funo de trabalho. ca ca 3. Se o sinalizador estiver desativado, o sinalizador desativado causa o desbloqueio atomicamente do mutex e a espera pela varivel cona dicional. A funcionalidade cr tica aqui est no passo 3, no qual GNU/Linux permite a a voc destravar o mutex e esperar pela varivel condicional atomicamente, e a sem a possibilidade de outra linha de execuo interferir. Isso elimina a ca 117

possibilidade que outra linha de execuo possa modicar o valor da varivel ca a condicional entre o teste de thread function do valor do sinalizador e a espera pela varivel condicional. a Uma varivel condicional representada por uma instncia de pthread con a e a d t. Lembrando que cada varivel condicional deve ser acompanhada de um a mutex. Abaixo temos as funes que manipulam variveis condicional: co a pthread cond init inicializa uma varivel condicional. O primeiro a argumento um apontador para a instncia pthread cond t. O see a gundo argumento, um apontador para uma objeto de atributo de varivel condicional , o qual ignorado em GNU/Linux. O mutex a e deve ser inicializado separadamente, como descrito na Seo 4.4.2, ca Mutexes. pthread cond signal sinaliza uma varivel condicional. Uma linha a de execuo simples, que bloqueada conforme o estado da varivel ca e a condicional, ir ser desbloqueada. Se nenhuma outra linha de a execuo estiver bloqueada conforme a varivel de condio, o sica a ca nal ignorado. O argumento um apontador para a instncia e e a pthread cond t. Uma chamada similar, pthread cond broadcast, desbloqueia todos as linhas de execuo que estiverem bloqueadas conforme a varivel ca a condicional, ao invs de apenas uma. e ca a pthread cond wait bloqueia a linha de execuo que a est chamado at que a varivel de condio for sinalizada. O argumento e a ca um apontador par a instncia pthread cond t. O segundo argue a mento um apontador para instncia de mutex pthread mutex t. e a Quando pthread cond wait for chamada, o mutex deve j estar traa vado por meio da linha de execuo que o chamou. A funo pthca ca read cond wait atomicamente desbloqueia o mutex e bloqueia sob a varivel de condio. Quando a varivel de condio seja sinalizada a ca a ca e a linha de execuo que chamou desbloquear, pthread cond wait ca automaticamente readquire uma trava sob o mutex. Sempre que seu programa executar uma ao que pode modicar o senso ca da condio voc est protegendo com a varivel condicional, seu programa ca e a a deve executar os passos adiante. (No nosso exemplo, a condio o estado ca e do sinalizador da linha de execuo, de forma que esses passos devem ser ca executados sempre que o sinalizador for modicado.) 118

1. Travar o mutex que acompanha a varivel condicional. a 2. Executar a ao que pode mudar o senso da condio (no nosso ca ca exemplo, ajustar o sinalizador). 3. Sinalizar ou transmitir a varivel condicional, dependendo do coma portamento desejado. 4. Desbloquear o mutex acompanhando a varivel condicional. a

A Listagem 4.14 mostra o exemplo anterior novamente, agora usando uma varivel condicional para proteger o sinalizador da linha de execuo. a ca Note que na funo thread function, uma trava sob o mutex mantida antes ca e de vericar o valor de thread ag. Aquela trava automaticamente liberada e por pthread cond wait antes de bloquear e automaticamente readquirida e posteriormente. Tambm note que set thread ag trava o mutex antes de e ajustar o valor de thread ag e sinalizar o mutex. 119

Listagem 4.14: (condvar.c) Controla uma Linha de Execuo Usando uma ca Varivel Condicional a
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include <p t h r e a d . h> extern void do wor k () ;

int t h r e a d f l a g ; pthread cond t thread flag cv ; pthread mutex t thread flag mutex ; void i n i t i a l i z e f l a g ( ) { / I n i t i a l i z e t h e mutex and c o n d i t i o n v a r i a b l e . p t h r e a d m u t e x i n i t (& t h r e a d f l a g m u t e x , NULL) ; p t h r e a d c o n d i n i t (& t h r e a d f l a g c v , NULL) ; / I n i t i a l i z e t h e f l a g v a l u e . / thread flag = 0; } / C a l l s d o w o r k r e p e a t e d l y the f l a g is clear . / while the thread flag

is

set ;

blocks

if

void t h r e a d f u n c t i o n ( void t h r e a d a r g ) { / Loop i n f i n i t e l y . / while ( 1 ) { / L o c k t h e mutex b e f o r e a c c e s s i n g t h e f l a g v a l u e . / p t h r e a d m u t e x l o c k (& t h r e a d f l a g m u t e x ) ; while ( ! t h r e a d f l a g ) / The f l a g i s c l e a r . Wait f o r a s i g n a l on t h e c o n d i t i o n v a r i a b l e , i n d i c a t i n g the f l a g v a l u e has changed . When t h e s i g n a l a r r i v e s and t h i s t h r e a d u n b l o c k s , l o o p and c h e c k t h e f l a g again . / p t h r e a d c o n d w a i t (& t h r e a d f l a g c v , &t h r e a d f l a g m u t e x ) ; / When we v e g o t t e n h e r e , we know t h e f l a g must b e s e t . Unlock t h e mutex . / p t h r e a d m u t e x u n l o c k (& t h r e a d f l a g m u t e x ) ; / Do some w o r k . / do wor k ( ) ; } return NULL ; } / S e t s the value of the thread flag t o FLAG VALUE . /

void s e t t h r e a d f l a g ( i n t f l a g v a l u e ) { / L o c k t h e mutex b e f o r e a c c e s s i n g t h e f l a g v a l u e . / p t h r e a d m u t e x l o c k (& t h r e a d f l a g m u t e x ) ; / S e t t h e f l a g v a l u e , and t h e n s i g n a l i n c a s e t h r e a d f u n c t i o n i s b l o c k e d , w a i t i n g f o r t h e f l a g t o become s e t . However , t h r e a d f u n c t i o n can t a c t u a l l y c h e c k t h e f l a g u n t i l t h e mutex i s unlocked . / thread flag = flag value ; p t h r e a d c o n d s i g n a l (& t h r e a d f l a g c v ) ; / U n l o c k t h e mutex . / p t h r e a d m u t e x u n l o c k (& t h r e a d f l a g m u t e x ) ; }

A condio protegida pela varivel condicional pode ser arbitrariamente ca a complexa. Todavia, antes de executar qualquer operao que possa mudar ca o senso da condio, uma trava de mutex deve ser requerida, e a varivel ca a condicional deve ser sinalizada depois. Uma varivel condicional pode tambm ser usada sem uma condio, a e ca simplesmente como um mecanismo para bloquear uma linha de execuo at ca e que outra linha de execuo acorde-a. Um sinalizador pode tambm ser ca e usado para aquele propsito. A principal diferena que um sinalizador o c e lembra o chamada para acordar mesmo se nenhuma linha de execuo ca tiver bloqueada sobre ele naquela ocasio, enquanto uma varivel condicional a a 120

discarta a chamada para acordar a menos que alguma linha de execuo esteja ca atualmente bloqueada sob essa mesam varivel condicional naquela ocasio. a a Tambm, um sinalizador entrega somente um simples acorde por post; com e pthread cond broadcast, um nmero arbitrrio e desconhecido de linhas de u a execuo bloqueadas pode ser acordado na mesma ocasio. ca a

4.4.7

Travas Mortas com Duas ou Mais Linhas de Execuo ca

Travas mortas podem ocorrer quando duas (ou mais) linhas de execuo ca estiverem bloqueadas, esperando que uma condio ocorra e que somente ca outra das duas (ou mais) pode fazer acontecer. Por exemplo, se uma linha de execuo A est bloqueada sob uma varivel condicional esperando pela linha ca a a de execuo B sinalize a varivel condicional, e a linha de execuo B est ca a ca a bloqueada sob uma varivel de condio esperando que a linha de execuo a ca ca A sinalize essa mesma varivel de condio, uma trava morta ocorreu pelo a ca fato de que nenhuma das linhas de execuo envolvidas ir sinalizar para a ca a outrar. Voc deve evitar a todo custo a possibilidade de tais stuaes pelo e co fato de elas serem bastante dif ceis de detectar. Um erro comum que causa uma trava morta envolve um problema no qual mais de uma linha de execuo est tentando travar o mesmo conjunto de ca a objetos. Por exemplo, considere um programa no qual duas diferentes linhas de execuo, executando duas diferentes funes de linha de execuo, precica co ca sam travar os mesmos dois mutexes. Suponhamos que a linha de execuo A ca trave o mutex 1 e a seguir o mutex 2, e a linha de execuo B precise travar ca o mutex 2 antes do mutex 1. Em um sucientemente desafortunado cenrio a de agendamento, GNU/Linux pode agendar a linha de execuo A por um ca tempo suciente para travar o mutex 1, e ento agende a linha de execuo a ca B, que prontamente trava mutex 2. Agora nenhuma linha de execuo pode ca progredir pelo fato de cada uma estar bloqueada sob um mutex que a outra linha de execuo mantm bloqueada. ca e Acima temos um exemplo de um problema genrico de trava morta, que e pode envolver no somente sincronizao de objetos tais como mutexes, mas a ca tambm outros recursos, tais como travas sob arquivos ou dispositivos. O e problema ocorre quando multiplas linhas de execuo tentam travar o mesmo ca conjunto de recursos em diferentes ordens. A soluo garantir que todas as ca e linhas de execuo que travam mais de um recurso faam tambm o travaca c e mento desses recursos na mesma ordem. 121

4.5

Implementao de uma Linha de Execuo ca ca em GNU/Linux

A implementao de linhas de execuo POSIX em GNU/Linux difere da ca ca implementao de linha de execuo de muitos outros sistemas semelhantes ca ca ao UNIX em um importante caminho: no GNU/Linux, linhas de execuo ca so implementadas como processos. Sempre que voc chamar pthread create a e para criar uma nova linha de execuo, GNU/Linux cria um novo processo ca que executa aquela linha de execuo. Todavia, esse processo no o mesmo ca a e que o processo criado com fork ; particularmente, o processo criado com pthread create compartilha o mesmo espao de endereo e recursos que o proc c cesso original em lugar de receber cpias. o O programa thread-pid mostrado na Listagem 4.15 demonstra isso. O programa cria uma linha de execuo; ambas a nova linha de execuo e a ca ca original chamam a funo getpid e imprimem seus respectivos IDs de processo ca e ento giram innitamente. a Listagem 4.15: (thread-pid) Imprime IDs de processos para Linhas de Execuo ca
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <p t h r e a d . h> #include <s t d i o . h> #include <u n i s t d . h> void t h r e a d f u n c t i o n ( void a r g ) { f p r i n t f ( stderr , c h i l d thread pid / S p i n f o r e v e r . / while ( 1 ) ; return NULL ; }

i s %d\n , ( i n t )

getpid

() ) ;

i n t main ( ) { pthread t thread ; f p r i n t f ( s t d e r r , main t h r e a d p i d i s %d\n , ( i n t ) g e t p i d p t h r e a d c r e a t e (& t h r e a d , NULL, &t h r e a d f u n c t i o n , NULL) ; / S p i n f o r e v e r . / while ( 1 ) ; return 0 ; }

() ) ;

Execute o programa em segundo plano, e ento chame ps x para mostrar a seus processos executando. Lembre-se de matar o programa thread-pid depois o mesmo consome muito da CPU sem fazer absolutamente nada. Aqui est a como a sa do ps x pode parecer: da % cc thread-pid.c -o thread-pid -lpthread % ./thread-pid \& [1] 14608 main thread pid is 14608 child thread pid is 14610 122

\% ps x PID TTY STAT 14042 pts/9 S 14608 pts/9 R 14609 pts/9 S 14610 pts/9 R 14611 pts/9 R \% kill 14608 [1]+ Terminated

TIME COMMAND 0:00 bash 0:01 ./thread-pid 0:00 ./thread-pid 0:01 ./thread-pid 0:00 ps x ./thread-pid

Noticao de Controle de Trabalho no Shell ca As linhas iniciam-se com [1] so do shell. Quando voc executa um programa a e em segundo plano, o shell atribui um nmero de trabalho para ele nesse caso, u 1 e imprime o pid do programa. Se o trabalho em segundo plano encerra-se, o shell mostra esse fato da prxima vez que voc chamar um comando. o e

Chamo a ateno para o fato de que existem trs processos executando ca e o programa thread-pid. O primeiro desses, com o pid 14608, a linha de e execuo principal no programa; o terceiro, com pid 14610, a linha de ca e execuo que criamos para executar thread function. ca O que dizer da segunda linha de execuo, com pid 14609? Essa a linha ca e de execuo gerente que parte da implementao interna de linhas de ca e ca execuo em GNU/Linux. A linha de execuo gerente criada na primeira ca ca e vez que um programa chama pthread create para criar uma nova linha de execuo. ca

4.5.1

Manipulando Sinal

Suponhamos que um programa com vrias linhas de execuo receba um sia ca nal. Em qual linha de execuo das linhas de execuo multiplas deve ser ca ca chamado o manipulador para esse sinal? O comportamento da interao enca tre sinais e linhas de execuo varia de entre os diversos sistemas operacionais ca semelhantes ao UNIX. Em GNU/Linux, o comportamento ditado pelo fato e de que as linhas de execuo so implementadas como processos. ca a Pelo fato de cada linha de execuo ser um processo separado, e pelo ca fato de um sinal ser entregue para um processo em particular, no existe a ambiguidade sobre qual linha de execuo recebe o sinal. Tipicamente, sinais ca enviados de fora do programa so enviados para o processo correspondente a a ` linha de execuo principal do programa. Por exemplo, se um programa ca executa forks e o processo lho faz execs sobre um programa com vrias a linhas de execuo, o processo pai ir manter o ID de processo da linha de ca a execuo principal do programa do processo lho e ir usar aquele ID de ca a 123

processo para enviar sinais para seu lho. Esse comportamento geralmente e uma boa conveno a seguir por voc mesmo quando enviar sinais para um ca e programa com vrias linhas de execuo. a ca Note que esse aspecto da implementao em GNU/Linux das linhas de ca execuo uma varincia da linha de execuo POSIX padro. No cone ca e a ca a a nesse comportamento em programas que so signicativamente para serem a portveis. a Dentro de um programa com vrias linhas de execuo, poss para a ca e vel uma linha de execuo enviar um sinal especicamente para outra linha de ca execuo. Use a funo pthread kill para fazer isso. O primeiro parmetro ca ca a e um ID de linha de execuo, e seu segundo parmetro um nmero de sinal. ca a e u

4.5.2

Chamada de Sistema clone

Embora linhas de execuo em GNU/Linux criadas em um mesmo proca grama sejam implementadas como processos separados, eles compartilham seu espao virtual de memria e outros recursos. Um processo lho criado c o com uma operao fork, todavia, recebe cpias desses itens. Como personaca o lizar o processo criado? A chamada de sistema GNU/Linux clone uma forma generalizada de e a fork e de pthread create que permite a quem est chamando especicar quais recursos so compartilhados entre o processo que est chamando e o processo a a criado recentemente. Tambm, clone requer que voc especique a regio e e a de memria para a pilha de execuo que o novo processo ir usar. Embora o ca a mencionemos clone aqui para satisfazer a curiosidade do leitor, essa chamada de sistema no deve frequentemente ser usada em programas. a Use fork para criar novos processos ou pthread create para criar linhas de execuo. ca

4.6

Processos Vs. Linhas de Execuo ca

Para alguns programas que se beneciam da concorrncia, a deciso entre e a usar processos ou linhas de execuo pode ser dif ca cil. Aqui esto algumas a linhas guias para ajudar voc a decidir qual modelo de concorrncia melhor e e se ajusta ao seu programa: 124

Todas as linhas de execuo em um programa devem rodar o mesmo ca executvel. Um processo lho, por outro lado, pode rodar um a executvel diferente atravs da funo exec. a e ca Uma linha de execuo errante pode prejudicar outras linhas de ca execuo no mesmo processo pelo fato de linhas de execuo comca ca partilharem o mesmo espao de memria virtual e outros recursos. c o Por exemplo, uma brbara escrita na memria por meio de um pona o teiro no inicializado em uma linha de execuo pode corromper a a ca memria vis para outra linha de execuo. Um processo errante, o vel ca por outro lado, no pode fazer isso pelo fato de cada processo ter a uma cpia do espao de memria do programa. o c o A cpia de memria para um novo processo cria um trabalho adio o cional diminuindo a performace em comparao ` criao de uma ca a ca nova linha de execuo. Todavia, a cpia executada somente ca o e quando a memria modicada, de forma que o penalti minimo o e e se o processo lho somente l a memria. e o Linhas de Execuo podem ser usadas por programas que precisam ca de paralelismo no e granulado. Por exemplo, se um problema pode ser quebrado em multiplos trabalhos aproximamente identicos, linhas de execuo podem ser uma boa escolha. Processos podem ca ser usados por programas que precisam de paralelismo rude. Compartilhando dados em torno de linhas de execuo trivial ca e pelo fato de linhas de execuo compartilharem a mesma memria ca o (Todavia, grande cuidado deve ser tomado para evitar condies co de corrida, como descrito anteriormente). Compartilhando dados em torno de processos requer o uso de mecanismos IPC a , como descrito no Cap tulo 5. Compartilhar dados em torno de processos pode ser incmodo mas faz multiplos processos parecer menos com o navegar em erros de concorrncia. e
a

Nota do tradutor:Comunicao Entre Processos ca

125

126

Cap tulo 5 Comunicao Entre Processos ca


NO CAP ITULO 3,PROCESSOS FOI DISCUTIDO A CRIACAO DE PROCESSOS e mostrado como um processo pode obter a situao de sa ca da de um processo lho. Essa a forma mais simples de comunicao entre e ca dois processos, mas isso no signica que seja o mais poderoso. Os mecaa nismos do Cap tulo 3 no fornecem nenhum caminhos para que o processo a pai comunique-se com o processo lho a no ser atravs de argumentos de a e linha de comando e de variveis de ambiente, nem fornece tambm qualquer a e caminho para o processo lho comunicar-se com o processo pai a no ser a atravs da situao de sa do processo lho. Nenhum desses mecanismos e ca da fornece quaisquer meios para comunicao com o processo lho enquanto ele ca estiver executando, nem faz esses mecanismos permitir comunicao com um ca processo fora do relacionamento pai-lho. Esse cap tulo descreve meios para comunicao entre processos que conca tornam as limitaes descritas acima. Apresentaremos vrios caminho para co a comunicao entre pais e lhos, entre processos desaparentados, e mesmo ca entre processos em diferentes mquinas. a Comunicao entre processos (IPC)1 a transferncia de dados em meio ca e e a processos. Por exemplo, um navegador Web pode requisitar uma pgina a Web de um servidor Web, que ento envia dados no formato HTML. Essa a transferncia de dados comumente usa sockets em uma coneco semelhante e ca a `s coneces telefnicas. Em outro exemplo, voc pode desejar imprimir os co o e nomesmde arquivos em um diretrio usando um comando tal como ls | lpr. o O shell cria um processo ls e um processo lpr separado, conectando os dois com um pipe, representado pelo s mbolo |. Um pipe permite comunicao ca de mo unica entre dois processos relacionados. O processo ls envia dados a para o pipe, e o processo lpr l dados a partir do pipe. e
1

Nota do tradutor:a traduo da sigla no adequada nesse caso - CEP. ca a e

127

No presente cap tulo, discutiremos cinco tipos de comunicao entre proca cessos: Memria compartilhada - permite que processos comuniquem-se o simplesmente lendo e escrevendo para uma localizao de memria ca o especicada. Memria mapeada - similar ` memria compartilhada, execeto o e a o que a memria mapeada est associada com um arquivo no sistema o a de arquivos. Pipes - permite comunicao sequncial de um processo para um ca e outro processo seu parente. FIFOs - so similares a pipes, exceto que processos no aparentados a a podem comunicar-se pelo fato de ao pipe ser fornecido um nome no sistema de arquivo. Sockets - suporta comunicao entre processos no aparentados ca a mesmo em computadores diferentes. Esses tipos de IPC diferem pelos seguintes critrios: e Se a comunicao restrita de processos aparentados (processos ca e com um ancestral comum) com processos no aparentados compara tilhando o mesmo sistema de arquivos ou com qualquer computador conectado a uma rede Se um processo de comunicao limitado a somente escrita ou ca e somente leitura de dados O nmero de processo permitidos para comunicar-se u Se os processos de comunicao so sincronizados atravs de IPC ca a e por exemplo, um processo de leitura pra at que dados estejam a e dispon veis para leitura Nesse cap tulo, omitiremos consideraes acerca de IPC permitindo comuco nicaes somente por um limitado nmero de vezes, tais como comunicao co u ca atravs de um valor de sa de processo lho. e da

5.1

Memria Compartilhada o

Um dos mais simples mtodos de comunicao entre processos o uso de e ca e memria compartilhada. Memria compartilhada permite a dois ou mais o o 128

processos acessarem a mesma memria como se todos eles tivessem chao mado malloc e tivessem obtido, como valor de retorno, apontadores para a mesma rea de memria em uso atualmente. Quando um processo modica a o a memria, todos os outros processos veem a modicao. o ca

5.1.1

Comunicao Local Rpida ca a

Memria compartilhada a forma mais rpida de comunicao entre proo e a ca cessos pelo fato de todos os processos compartilharem a mesma pea de c memria. O acesso a essa memria compartilhada to rpido quanto o o o e a a acesso a memria no compartilhada de processos, e no requer uma chao a a mada de sistema ou entrada para o kernel. A comunicao usando memria ca o compartilhada tambm evita cpias desnecessrias de informaes. e o a co Pelo fato de o kernel no sincronizar acessos ` memria compartilhada, a a o voc deve fornecer sua prpria sincronizao. Por exemplo, um processo no e o ca a deve ler a memria somente aps dados serem escritos nela, e dois processos o o no devem escrever na mesma localizao de memria ao mesmo tempo. Uma a ca o estratgia comum para evitar essas condies de corrida usar-se semforos, e co e a que sero discutidos na prxima seo. Nossos programas ilustrativos, apesar a o ca disso, mostram apenas um processo simples acessando a memria, para evio denciar o mecanismo de memria compartilhada e para evitar um amontoado o a amostra de cdigo com sincronizao lgica. o ca o

5.1.2

O Modelo de Memria o

Para usar um segmento de memria compartilhada, um processo deve alocar o o segmento. Ento cada processo desejando acessar o segmento deve anexar a esse mesmo segmento. Aps terminar seu uso do segmento, cada processo o desanexa o segmento. Em algum ponto, um processo deve desalocar o segmento. Entendendo o modelo de memria do GNU/Linux ajuda a explicao do o ca mecanismo de alocao e anexao. Sob GNU/Linux, cada memria virtual ca ca o usada por um processo quebrada em pginas. Cada processo mantm um e a e mapeamento de seus endereos de memria para essas pginas de memria c o a o virtual, as quais carregam os dados atuais. Alm disso cada processo tem e seus prprio endereos, mapeamentos de multiplos processos podem apontar o c para a mesma pgina, permitindo compartilhameto de memria. Pginas de a o a memria so adicionalmente discutidas na Seo ??,A Fam mlock : Trao a ca lia vando Memria F o sica do Cap tulo ??,Chamadas de Sistema do GNU/Linux. A alocao de um novo segmento de memria compartilhada faz com que ca o pginas de memria virtual sejam criadas. Pelo fato de todos os procesa o 129

sos desejarem acessar o mesmo segmento compartilhado, somente um processo deve alocar um novo segmento compartilhado. A alocao de um segca mento existente no cria novas pginas, mas ir retornar um identicador a a a para as pginas existentes. Para permitir a um processo usar o segmento a de memria compartilhado, um processo anexa-o, o que adiciona entradas o mapeando de sua memria virtual para as pginas compartilhadas do sego a mento. Quando termina com o segmento, essas entradas de mapeamento so removidas. Quando nenhum processo deseja acessar esses segmentos de a memria compartilhada, exatamente um processo deve desalocar as pginas o a de memria virtual. o Todos segmentos de memria compartilhada so alocados como multiplos o a inteiros do tamanho de pgina do sistema, que o nmero de ocupado por a e u uma pgina de memria. Sob sistemas GNU/Linux, o tamanho da pgina a o a e 4KB, mas voc pode obter esse valor chamando a funo getpagesize. e ca

5.1.3

Alocao ca

Um processo aloca um segmento de memria compartilhada usando shmget o (SHared Memory GET ). O primeiro parmetro a shmget uma chave a e inteira que especica qual o segmento a ser criado. Processos no aparentados a podem acessar o mesmo segmento compartilhado especicando o mesmo valor de chave inteira. Desafortunadamente, outros processos podem ter tambm e escolhido a mesma chave xada, o que pode levar a conitos. Usando a constante especial IPC PRIVATE como local de armazenamento da chave garante que um segmento de memria marcado como novo seja criado. o O segundo parmetro a shmget especica o nmero de bytes no segmento. a u Pelo fato de segmentos serem alocados usando pginas, o nmero de bytes a u alocados atualmente arredondado para cima para um inteiro multiplo do e tamanho da pgina. a O terceiro parmetro a shmget o conjunto de valores de bits ou de a e sinalizadores que especicam opes a shmget. co Os valores de sinalizadores incluem os seguintes: 130

IPC CREAT Esse sinalizador indica que um novo segmeto deve ser criado. Permite a criao de um novo segmento na mesma hora ca em que especica um valor de chave. IPC EXCL Esse sinalizador, que sempre usado com e IPC CREAT, faz com que shmget falhe se uma chave de segmento que j exista for especicada. Portanto, IPC EXCL possibilita ao a processo que est chamando ter um segmento exclusivo. Se esse a sinalizador no for fornecido e a chave de um segmento existente a for usada, shmget retorna o segmento existente ao invs de criar e um novo. Sinalizadores de modo Esse valor composto de 9 bits indicando e permisses garantidas ao dono, grupo e o restante do mundo para o controlar o acesso ao segmento. Bits de execuo so ignorados. ca a Um caminho fcil para especicar permisses usar constantes dea o e nidas no arquivo de cabealho <sys/stat.h> e documentadas na c seo 2 da pgina de manual de stat a . Por exemplo, S IRUSR e ca a o S IWUSR especicam permisses de leitura e escrita para o dono do segmento de memria compartilhada, e S IROTH e S IWOTH o especicam permisses de leitura e escrita para outros. o
Esses bits de permisso so os mesmos aqueles usados para arquivos. Eles so a a a descritos na Seo ??, Permisses do Sistema de Arquivos. ca o
a

Por exemplo, a chamada adiante a shmget cria um novo segmento de a memria compartilhada (ou acessa um que j existe, se shm key j estio a ver sendo usada) que pode ser lido e escrito pelo dono mas no por outros a usurios. a
int segment\_id = shmget (shm\_key, getpagesize (), IPC\_CREAT | S\_IRUSR | S\_IWUSR);

Se a chamada obtiver sucesso,shmget retorna um identicador de segmento. Se o segmento de memria compartilhada j existir, as permisses de o a o acesso so vericadas e uma conrmao feita para garantir que o segmento a ca e no seja marcado para destruio. a ca

5.1.4

Anexando e Desanexando

Para tornar o segmento de memria compartilhada dispon o vel, um processo deve usar shmat, SHared Memory ATtach. Informe a shmat o identicador de segmento de memria compartilhada SHMID retornado por shmget. O o segundo argumento um apontador que especica onde no seu espao de e c endereamento de processo voc deseja mapear a memria compartilhada; se c e o 131

voc especicar NULL, GNU/Linux ir escolher um endereo dispon e a c vel. O terceiro argumento um sinalizador, que pode incluir o seguinte: e SHM RND indica que o endereo especicado para o segundo c parmetro deve ser arredondado por baixo para um multiplo do a tamanho da pgina de memria. Se voc no especicar esse sinaa o e a lizador, voc deve ajustar conforme o tamanho da pgina o segundo e a argumento para shmat por si mesmo. SHM RDONLY indica que o segmento ir ser somente para leitura, a no para escrita. a Se a chamada obtiver sucesso, a chamada ir retornar o endereo do a c segmento compartilhado anexado. Processos lhos criados por chamadas a fork herdaro os segmentos de memria compartilhada anexados; eles podem a o desanexar os segmentos de memria anexados, se assim o desejarem. o Quando voc tiver terminado com um segmento de memria compartie o lhada, o segmento deve ser liberado usando shmdt (SHared Memory DeTach). Informe a shmdt o endereo retornado por shmat. Se o segmento c tiver sido desalocado e o processo atual for o ultimo processo usando o seg mento de memria em questo, esse segmento removido. Chamadas a exit e o a e a qualquer chamada da fam exec automaticamente desanexam segmentos. lia

5.1.5

Controlando e Desalocando Memria Compartio lhada

A chamada shmctl (SHared Memory ConTroL) retorna informaes sobre co um segmento de memria compartilhada e pode modicar o referido sego mento. O primeiro parmetro um identicador de segmento de memria a e o compartilhada. Para obter informaes sobreu um segmento de memria compartilhada, co o informe IPC STAT como o segundo argumento e um apontador para uma varivel do tipo struct chamada shmid ds. a Para remover um segmento, informe IPC RMID como o segundo argumento, e informe NULL como o terceiro argumento. O segmento removido e quando o ultimo processo que o tiver anexado nalmente o desanexe. Cada segmento de memria compartilhada deve ser explicitamente desao locado usando shmctl quando voc tiver acabado com esse mesmo segmento, e para evitar violao um limite de tamanho interno ao GNU/Linux 2 com ca
Nota do tradutor:system-wide limit conjunto de limites respeitado pelo kernel para proteger o sistema. Os limites so aplicados na quantidade de arquivos aberto por processo, a
2

132

relao ao nmero total de segmentos de memria compartilhada. Chamaca u o das a exit e exec desanexam segmentos de memria mas no os desalocam. o a Veja a pgina de manual para shmctl para uma descrio de outras a ca operaes que voc pode executar sobre segmentos de memria compartico e o lhada.

5.1.6

Um programa Exemplo

O programa na Listagem 5.1 ilustra o uso de memria compartilhada. o Listagem 5.1: Exerc de Memria Compartilhada cio o
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 #include <s t d i o . h> #include <s y s /shm . h> #include <s y s / s t a t . h> i n t main ( ) { int segment id ; char shared memory ; struct shmid ds shmbuffer ; int s e g m e n t s i z e ; const i n t s h a r e d s e g m e n t s i z e = 0 x6400 ; / A l l o c a t e a s h a r e d memory s e g m e n t . / s e g m e n t i d = shmget ( IPC PRIVATE , s h a r e d s e g m e n t s i z e , IPC CREAT | IPC EXCL | S IRUSR | S IWUSR ) ; / A t t a c h t h e s h a r e d memory s e g m e n t . / shared memory = ( char ) shmat ( s e g m e n t i d , 0 , 0 ) ; p r i n t f ( s h a r e d memory a t t a c h e d a t a d d r e s s %p\n , shared memory ) ; / D e t e r m i n e t h e s e g m e n t s s i z e . / s h m c t l ( s e g m e n t i d , IPC STAT , &s h m b u f f e r ) ; segment size = shmbuffer . shm segsz ; p r i n t f ( segment s i z e : %d\n , s e g m e n t s i z e ) ; / W r i t e a s t r i n g t o t h e s h a r e d memory s e g m e n t . / s p r i n t f ( shared memory , H e l l o , w o r l d . ) ; / D e a t c h t h e s h a r e d memory s e g m e n t . / shmdt ( shared memory ) ; / R e a t t a c h t h e s h a r e d memory s e g m e n t , a t a d i f f e r e n t a d d r e s s . / shared memory = ( char ) shmat ( s e g m e n t i d , ( void ) 0 x5000000 , 0 ) ; p r i n t f ( s h a r e d memory r e a t t a c h e d a t a d d r e s s %p\n , shared memory ) ; / P r i n t o u t t h e s t r i n g f r o m s h a r e d memory . / p r i n t f ( %s \n , shared memory ) ; / D e t a c h t h e s h a r e d memory s e g m e n t . / shmdt ( shared memory ) ; / D e a l l o c a t e t h e s h a r e d memory s e g m e n t . s h m c t l ( s e g m e n t i d , IPC RMID , 0 ) ; return 0 ; } /

5.1.7

Depurando

Os comandos ipc fornecem informao sobre as facilidade da comunicao ca ca entre processos, incluindo segmentos compartilhados. Use o sinalizador -m para obter informao sobre memria compartilhada. Por exemplo, o cdigo ca o o
no tamanho de alguma mensagem do sistema, na quantidade de arquivos em uma la, etc. So obtidos com o comando sysctl -a em um slackware por exemplo. a

133

a seguir ilustra que um segmento de memria compartilhada, cujo nmero o u e 1627649, est em uso: a
% ipcs -m ------ Shared Memory Segments -------key shmid owner perms 0x00000000 1627649 user 640

bytes 25600

nattch 0

status

Se esse segmento de memria tiver sido errneamente deixado para trs o o a por um programa, voc pode usar o comando ipcrm para remov-lo. e e % ipcrm shm 1627649

5.1.8

Prs e Contras o

Segmentos de memria compartilhada permitem comunicao bidirecional o ca rpida envolvendo qualquer nmero de processos. Cada usurio pode tanto a u a ler quanto escrever, mas um programa deve estabelecer e seguir algum protocolo para prevenir condies de corrida tais como sobrescrever informao anco ca tes que essa mesma informao seja lida. Desafortunadamente, GNU/Linux ca no garante estritamente acesso exclusivo mesmo se voc criar um novo a e segmnto compartilhado com IPC PRIVATE. Tambm, para multiplos processos usarem um segmento compartilhado, e eles devem fazer arranjos para usar a mesma chave.

5.2

Semforos de Processos a

Como se nota na seo anterior, processos devem ter acesso coordenado a ca memria compartilhada. Como discutimos na Seo 4.4.5, Semforos para o ca a Linhas de Execuo no Cap ca tulo 4, Linhas de Execuo semforos so ca a a contadores que permitem sincronizar multiplas linhas de execuo. GNU/Linux ca fornece uma implementao alternativa diferente de semforos que pode ser ca a usada para sincronizar processos (chamada semforos de processo ou algua mas vezes semforos System V ). Se mforos de processo so alocados, usaa a a dos, e desalocados como segmentos de memria compartilhada. Embora o um semforo simples seja suciente para a maioria dos usos, semforos de a a processo veem em conjuntos. ao longo de toda essa seo, apresentamos chaca madas de sistema para semforos de processo, mostrando como implementar a semforos binrios simples usando essas chamadas de sistema. a a 134

5.2.1

Alocao e Desalocao ca ca

As chamadas semget e semctl alocam e desalocam semforos, ambas anlogas a a a shmget e shmctl para memria compartilhada. Chame semget com uma o chave especicando um conjunto de semforo, o nmero de semforos no a u a conjunto, e sinalizadores de permisso da mesma forma que para shmget; a o valor de retorno um identicador do conjunto de semforo. Voc pode e a e obter o identicador de um conjunto de semforo existente especicando o a valor da chave respectiva; nesse caso, o nmero de semforos pode ser zero. u a Semforos continuam a existir mesmo aps todos os processos que os a o tiverem usado tenham terminado. O ultimo processo a usar um conjunto de semforo deve explicitamente remover o conjunto de forma a garantir a que o sistema operacional no desperdisse semforos. Para fazer isso, chame a a semctl com o identicador de semforo, o nmero de semforos no conjunto, a u a IPC RMID como o terceiro argumento, e qualquer valor de union semun 3 como o quarto argumento (que ignorado). O identicador efetivo do usurio e a do processo que est chamando deve coincidir com o do alocador do semforo a a (ou o chamador deve ser o super usurio). Ao contrrio do que ocorre com a a segmentos de memria compartilhada, a remoo de um conjunto de semforo o ca a faz com que GNU/Linux o desaloque imediatamente. A Listagem 5.2 mostra funes para alocar e desalocar um semforo co a binrio. a a Listagem 5.2: (sem all deall.c) Alocando e Desalocando um semforo Binrio 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 #include <s y s / i p c . h> #include <s y s / sem . h> #include <s y s / t y p e s . h> / We must define u n i o n semun o u r s e l v e s . /

union semun { int val ; struct semid ds buf ; unsigned short i n t a r r a y ; struct seminfo b u f ; }; / O b t a i n a b i n a r y s e m a p h o r e s ID , allocating if necessary . sem flags ) /

i n t b i n a r y s e m a p h o r e a l l o c a t i o n ( k e y t key , { return s e m g e t ( key , 1 , s e m f l a g s ) ; } / D e a l l o c a t e a b i n a r y s e m a p h o r e . All use . R e t u r n s 1 on f a i l u r e . /

int

u s e r s must h a v e

finished

their

i n t b i n a r y s e m a p h o r e d e a l l o c a t e ( i n t s e m id ) { union semun i g n o r e d a r g u m e n t ; return s e m c t l ( semid , 1 , IPC RMID , i g n o r e d a r g u m e n t ) ; }

Nota do tradutor: denido em sem.h.

135

5.2.2

Inicializando Semforos a

Alocao e inicializao so duas operaes distintas. Para inicializar um ca ca a co semforo, use semctl com zero como o segundo argumento e SETALL como a o terceiro argumento. Para quarto argumento, voc deve criar um objeto e union semun e apontar seu campo vetor para um vetor de valores inteiros curtos. Cada valor usado para inicializar um semforo no conjunto. e a A Listagem 5.3 mostra uma funo que inicializa um semforo binrio. ca a a

Listagem 5.3: (sem init.c) Inicializando um Semforo Binrio a a


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <s y s / t y p e s . h> #include <s y s / i p c . h> #include <s y s / sem . h> / We must define u n i o n semun o u r s e l v e s . /

union semun { int val ; struct semid ds buf ; unsigned short i n t a r r a y ; struct seminfo b u f ; }; / Initialize a binary semaphore with a value o f one . /

i n t b i n a r y s e m a p h o r e i n i t i a l i z e ( i n t s e m id ) { union semun argument ; unsigned short v a l u e s [ 1 ] ; values [ 0 ] = 1; argument . a r r a y = v a l u e s ; return s e m c t l ( semid , 0 , SETALL, argument ) ; }

5.2.3

Operaes Wait e Post co

Cada semforo tem um valor no negativo e suporta operaes wait e post. a a co A chamada de sistema semop implementa ambas as operaes. Seu primeiro co parmetro especica um identicador de conjunto de semforo. Seu segundo a a parmetro um vetor de elementos do tipo struct sembuf, que especica as a e operaes que voc deseja executar. O terceiro parmetro o comprimento co e a e do vetor. Os campos de struct sembuf so listados aqui: a 136

sem num o nmero do semforo no conjunto de semforo sobre e u a a o qual a operao executada. ca e sem op um inteiro que especica a operao do semforo. e ca a Se sem op for um nmero positivo, esse nmero positivo adiciou u e nado ao valor do semforo Imediatamente. a Se sem op for um nmero negativo, o valor absoluto do nmero u u negativo subtra do valor do semforo. Se isso zer com que o e do a valor de semforo torne-se negativo, a chamada bloqueia at que o a e valor de semforo torne-se to grande quanto o valor absoluto de a a sem op (pelo fato de algum outro processo incrementar esse valor). Se sem op for zero, a operao bloqueia at que o valor do semforo ca e a torne-se zero. sem g um valor de sinalizador. Especique IPC NOWAIT para e prevenir a operao de bloquear; se a operao puder ter bloca ca queio, a chamada a semop falha ao invs disso. Se voc especicar e e SEM UNDO, GNU/Linux automaticamente desmancha a operao ca sobre o semforo quando o processo encerra. a A Listagem 5.4 ilustra operaes wait e post para um semforo binrio. co a a Listagem 5.4: (sem pv.c) Operaes Wait e Post para um Semforo co a Binrio 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 #include <s y s / t y p e s . h> #include <s y s / i p c . h> #include <s y s / sem . h> / Wait on a b i n a r y s e m a p h o r e . p o s i t i v e , then decrement i t Block u n t i l b y one . / the semaphore value is

i n t b i n a r y s e m a p h o r e w a i t ( i n t s e m id ) { s t r u c t sembuf o p e r a t i o n s [ 1 ] ; / Use t h e f i r s t ( and o n l y ) s e m a p h o r e . o p e r a t i o n s [ 0 ] . sem num = 0 ; / D e c r e m e n t b y 1 . / o p e r a t i o n s [ 0 ] . sem op = 1; / P e r m i t undo i n g . / o p e r a t i o n s [ 0 ] . s e m f l g = SEM UNDO ; return semop ( semid , } / P o s t t o a b i n a r y s e m a p h o r e : returns immediately . / increment operations , 1) ;

its

value

b y one .

This

i n t b i n a r y s e m a p h o r e p o s t ( i n t s e m id ) { s t r u c t sembuf o p e r a t i o n s [ 1 ] ; / Use t h e f i r s t ( and o n l y ) s e m a p h o r e . o p e r a t i o n s [ 0 ] . sem num = 0 ; / I n c r e m e n t b y 1 . / o p e r a t i o n s [ 0 ] . sem op = 1 ; / P e r m i t undo i n g . / o p e r a t i o n s [ 0 ] . s e m f l g = SEM UNDO ; return semop ( semid , } operations , 1) ;

137

Especicando o sinalizador SEM UNDO permite lidar com o problema de terminar um processo enquanto esse mesmo processo tem recursos alocados atravs de um semforo. Quando um processo encerra, ou voluntariamente e a ou involuntriamente, o valores do semforo so automaticamente ajustados a a a para desfazer os efeitos do processo sobre o semforo. Por exemplo, se um a processo que tiver decrementado um semforo for morto, o valor do semforo a a incrementado. e

5.2.4

Depurando Semforos a

Use o comando ipcs -s para mostrar informao sobre conjuntos de semforo ca a existentes. Use o comando ipcrm sem para remover um conjunto de semaforo a partir da linha de comando. Por exemplo, para remover o conjunto de semforo com o identicador 5790517, use essa linha: a \% ipcrm sem 5790517

5.3

Arquivos Mapeados em Memria o

Memria mapeada permite a diferentes processos comunicarem-se por meio o de um arquivo compartilhado. Embora voc possa entender memria mae o peada como sendo um segmento de memria compartilhada com um nome, o voc deve ser informado que exitem diferenas tcnicas. Memria mapeada e c e o pode ser usada para comunicao entre processos ou como um caminho fcil ca a para acessar o contedo de um arquivo. u Memria mapeada forma uma associao entre um arquivo e a memria o ca o de um processo. GNU/Linux quebra o arquivo em pedaos do tamanho de c pginas de memria e ento copia esses pedaos para dentro das pginas de a o a c a memria virtual de forma que os pedaos possam se tornar dispon o c veis no espao de endereamento de um processo. Dessa forma, o processo pode ler c c o contedo do arquivo com acesso de memria comum. O processo pode u o tambm modicar o contedo do arquivo escrevendo para a memria. Esse e u o processo de leitura e escrita para a memria permite acesso rpido a arquivos. o a Voc pode entender a memria mapeada como alocao de um espao e o ca c temporrio de armazenamento para manter o contedo total de um arquivo, a u e ento lendo o arquivo na rea temporria de armazenamento e (se a rea a a a a temporria de armazenamento for modicada) escrevendo a rea temporria a a a de armazenamento de volta para o arquivo posteriormente. GNU/Linux manipula as operaes de leitura e escrita para voc. co e 138

Existem outros usos para arquivos mapeados em memria alm do uso o e para comunicao entre processos. Alguns desses outros usos so discutidos ca a na Seo 5.3.5, Outros Usos para Arquivos Mapeados em Memria. ca o

5.3.1

Mapeando um Arquivo Comum

Para mapear um arquivo comum para a memria de um processo, use a o chamada de sistema mmap (Memory MAPped pronuncia-se em-map). O primeiro argumento o endereo no qual voc gostaria que GNU/Linux e c e mapeasse o arquivo dentro do espao de endereamento do processo; o valor c c NULL permite ao GNU/Linux escolher um endereo inicial dispon c vel. O segundo argumento o comprimento do mapa em bytes. O terceiro argue mento especica a proteo sobre o intervalo de endereamento mapeado. A ca c proteo consiste de um ou bit a bit de PROT READ, PROT WRITE, ca a ca e PROT EXEC, correspondendo a permisso de leitura, escrita, e execuo, respectivamente. O quarto argumento um valor de sinalizador que especie ca opes adicionais. O quinto argumento um descritor de arquivo aberto co e para o arquivo a ser mapeado. O ultimo argumento o oset a partir do e in do arquivo do qual inicia-se o mapa. Voc pode mapear todo ou parte cio e do arquivo para dentro da memria escolhendo o oset de in e o comprio cio mento apropriadamente. O valor do sinalizador um ou bit a bit restrito aos seguintes: e MAP FIXED Caso especique esse sinalizador, GNU/Linux usa o endereo de sua requisio para mapear o arquivo em lugar de c ca tratar esse endereo como uma sugesto. Esse endereo deve ser c a c ajustado ` pgina de memria. a a o MAP PRIVATE Escritas para o intervalo de memria mapeado o no devem ser escritos de volta ao arquivo mapeado, mas para uma a cpia privada do arquivo mapeado. Nenhum outro processo v essas o e escritas. Esse modo no pode ser usado com MAP SHARED. a MAP SHARED Escritas so imediatamente reetidas no ara quivo correspondente ao invs de serem guardadas em uma rea e a temporria na memria. Use esse modo quando estiver usando a o memria mapeada em IPCa . Esse modo no pode ser usado com o a MAP PRIVATE.
a

Nota do tradutor:Inter Process Communication

Se a chamada de sistema mmap obtiver sucesso, ir retornar um apona tador para o in da memria mapeada. Em caso de falha, a chamada de cio o sistema mmap retorna MAP FAILED. 139

Quando voc tiver terminado com a memria mapeada, libere-a usando e o munmap. Informe a munmap o endereo inicial e o comprimento da regio de c a memria mapeada. GNU/Linux automaticamente desmancha o mapeamento o das regies de memria mapeada quando um processo terminar. o o

5.3.2

Programas Exemplo

Vamos olhar em dois programas para ilustrar a utilizao de regies de ca o memria mapeada para ler e escrever em arquivos. O primeiro programa, o Listagem 5.5, gera um nmero aleatrio e escreve-o em um arquivo mapeado u o em memria. O segundo programa, Listagem 5.6, l o nmero, mostra-o, e o e u substitui seu valor no arquivo de memria mapeada com o valor dobrado. o Ambos recebem um argumento de linha de comando do arquivo a ser mapeado. Listagem 5.5: (mmap-write.c) Escreve um Nmero Aleatrio para um u o Arquivo Mapeado em Memria o
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 #include < s t d l i b . h> #include <s t d i o . h> #include < f c n t l . h> #include <s y s /mman . h> #include <s y s / s t a t . h> #include <t i m e . h> #include <u n i s t d . h> #d e f i n e FILE LENGTH 0 x100 / R e t u r n a u n i f o r m l y random number i n the range [ low , h i g h ] . /

i n t r a n d o m r a n g e ( unsigned const low , unsigned const h i g h ) { unsigned const r a n g e = h i g h low + 1 ; return low + ( i n t ) ( ( ( double ) r a n g e ) rand ( ) / (RAND MAX + 1 . 0 ) ) ; } i n t main ( i n t a r g c , char const a r g v [ ] ) { int fd ; void f i l e m e m o r y ; / S e e d t h e random number g e n e r a t o r . s r a n d ( t i m e (NULL) ) ; /

/ P r e p a r e a f i l e l a r g e e n o u g h t o h o l d an u n s i g n e d i n t e g e r . f d = open ( a r g v [ 1 ] , O RDWR | O CREAT, S IRUSR | S IWUSR ) ; l s e e k ( f d , FILE LENGTH+1 , SEEK SET ) ; w r i t e ( fd , , 1) ; l s e e k ( f d , 0 , SEEK SET ) ;

/ C r e a t e t h e memorymapping . / f i l e m e m o r y = mmap ( 0 , FILE LENGTH , PROT WRITE, MAP SHARED, f d , 0 ) ; c l o s e ( fd ) ; / W r i t e a random i n t e g e r t o memorymapped a r e a . / s p r i n t f ( ( char ) f i l e m e m o r y , %d\n , r a n d o m r a n g e ( 100 , 1 0 0 ) ) ; / R e l e a s e t h e memory ( u n n e c e s s a r y s i n c e t h e p r o g r a m e x i t s ) . / munmap ( f i l e m e m o r y , FILE LENGTH) ; return 0 ; }

O programa mmap-write abre o arquivo, criando-o se ele j no existir a a previamente. O terceiro argumento a open especica que o arquivo deve ser 140

aberto para leitura e escrita. Pelo fato de no sabermos o comprimento do a arquivo, usamos lseek para garantir que o arquivo seja grande o suciente para armazenar um inteiro e ento mover de volta a posio do arquivo para a ca seu in cio. O programa mapeia o arquivo e ento fecha o descritor de arquivo pelo a fato de esse descritor no ser mais necessrio. O programa ento escreve a a a um inteiro aleatrio para a memria mapeada, e dessa forma para o arquivo, o o e desmapeia a memria. A chamada de sistema munmap desnecessria o e a pelo fato de que GNU/Linux deve automaticamente desmapear o arquivo ao trmino do programa. e Listagem 5.6: (mmap-read.c) L um Inteiro a partir de um Arquivo Mae peado em Memria, e Dobra-o o
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 #include < s t d l i b . h> #include <s t d i o . h> #include < f c n t l . h> #include <s y s /mman . h> #include <s y s / s t a t . h> #include <u n i s t d . h> #d e f i n e FILE LENGTH 0 x100 i n t main ( i n t a r g c , char const a r g v [ ] ) { int fd ; void f i l e m e m o r y ; int i n t e g e r ; / Open t h e f i l e . / f d = open ( a r g v [ 1 ] , O RDWR, S IRUSR | S IWUSR ) ; / C r e a t e t h e memorymapping . / f i l e m e m o r y = mmap ( 0 , FILE LENGTH , PROT READ | PROT WRITE, MAP SHARED, f d , 0 ) ; c l o s e ( fd ) ; / Read t h e i n t e g e r , p r i n t i t o u t , and d o u b l e i t . / s s c a n f ( f i l e m e m o r y , %d , &i n t e g e r ) ; p r i n t f ( v a l u e : %d\n , i n t e g e r ) ; s p r i n t f ( ( char ) f i l e m e m o r y , %d\n , 2 i n t e g e r ) ; / R e l e a s e t h e memory ( u n n e c e s s a r y s i n c e t h e p r o g r a m e x i t s ) . munmap ( f i l e m e m o r y , FILE LENGTH) ; return 0 ; }

O programa mmap-read l o nmero para fora do arquivo e ento escreve e u a o valor dobrado para o arquivo. Primeiramente, mmap-read abre o arquivo e mapeia-o para leitura e escrita. Pelo fato de podermos assumir que o arquivo grande o suciente para armazenar um inteiro sem sinal, no precisamos e a usar lseek, como no programa anterior. O programa l e informa o valor para e fora da memria usando sscanf e ento formata e escreve o valor dobrado o a usando sprintf. Aqui est um exemplo de execuo desses dois programas exemplo. Os a ca dois mapeiam o arquivo /tmp/integer-le. \% ./mmap-write /tmp/integer-file \% cat /tmp/integer-file 141

42 \% ./mmap-read /tmp/integer-file value: 42 \% cat /tmp/integer-file Observe que o texto 42 foi escrito para o arquivo de disco sem mesmo haver uma chamada ` funo write, e foi lido de volta novamente sem haver a ca uma chamada ` funo read. Note que esses programas amostra escrevem e a ca leem ponteiro como uma sequncia de caracteres (usando sprintf e sscanf ) e com propsitos didticos somente no existe necessidade de o contedo o a a u de um arquivo mapeado em memria ser texto. Voc pode armazenar e o e recuperar binrios arbitrrios em um arquivo mapeado em memria. a a o

5.3.3

Acesso Compartilhado a um Arquivo

Diferentes processos podem comunicar-se usando regies mapeadas em memria o o associadas ao mesmo arquivo. Especicamente o sinalizador MAP SHARED permite que qualquer escrita a essa regies sejam imediatamente transferidas o ao correspondente arquivo mapeado em memria e tornados vis o veis a outros processos. Se voc no especicar esse sinalizador, GNU/Linux pode colocar e a as operaes de escrita em reas temporrias de armazenamento antes de co a a transfer -las ao arquivo mapeado. Alternativamente, voc pode forar o GNU/Linux a esvaziar as reas e c a temporrias de armazenamento para o arquivo em disco chamando msync. a Os primeiros dois parmetros a msync especicam uma regio de memria a a o mapeada, da mesma forma que para munmap. O terceiro parmetro pode os a os seguintes valores de sinalizador: MS ASYNC A atualizao agendada mas no necessriamente ca e a a efetuada antes de a chamada retornar. MS SYNC A atualizao imediata; a chamada a msync bloca e queia at que a atualizao tenha sido nalizada. MS SYNC e e ca MS ASYNC no podem ambas serem usadas simultneamente. a a MS INVALIDATE Todos os outros mapeamentos so invalidados a de forma que eles possam ver os valores atualizados. Por exemplo, para descarregar a rea de armazenamento temporrio de a a um arquivo compartilhado mapeado no endereo mem addr de comprimento c mem length bytes, chame o seguinte: msync (mem_addr, mem_length, MS_SYNC | MS_INVALIDATE); 142

Da mesma forma que com segmentos de memria compartilhada, os o usurios de regies de memria mapeada devem estabelecer e seguir um proa o o tocolo para evitar condies de corrida. Por exemplo, um semforo pode ser co a usado para garantir que somente um processo acesse a regio de memria a o mapeada de cada vez. Alternativamente, voc pode usar fcntl para coloe car uma trava de leitura ou escrita no arquivo, como descrito na Seo ??, ca A Chamada de Sistema fcntl : Travas e Outras Operaes em Arquivosno co Cap tulo ??.

5.3.4

Mapeamentos Privados

A especicao de MAP PRIVATE a mmap cria uma regio copie-na-escrita. ca a Qualquer escrita para a regio reetida somente nessa memria do processo; a e o outros processos que mapeiam o mesmo arquivo no iro ver as modicaes. a a co Ao invs de escrever diretamente para uma pgina compartilhada por todos e a os processos, o processo escreve para uma cpia privada dessa pgina. Todas o a as leituras e escritas subsequentes feitas pelo processo usaram essa cpia o privada.

5.3.5

Outros Usos para Arquivos Mapeados em Memo ria

A chamada mmap pode ser usada para outros propsitos alm da comuo e nicao entre processos. Um uso comum uma substituio para leitura e ca e ca escrita. Por exemplo, ao invs de explicitamente ler um contedo de arquivo e u dentro da memria, um programa pode mapear o arquivo na memria e ver o o seu contedo atravs de leituras de memria. Para alguns programas, isso u e o e mais conveniente e pode tambm executar mais rapidamente que operaes e co expl citas de entrada e sa em arquivos. da Uma tcnica avanada e poderosa usada por alguns programas conse c e truir estruturas de dados (comumente instncias de estruturas, por exemplo) a em um arquivo mapeado em memria. Em uma chamada subsequnte, o o e programa mapeia aquele arquivo de volta na memria, e as estruturas de o dados so restabelecidas em seu estado anterior. Note que, apesar disso, que a apontadores nessas estruturas de dados iro ser invlidos a menos que eles a a todos apontem para dentro da mesma regio mapeada de memria e a menos a o que cuidados sejam tomados para mapear o arquivo de volta para dentro do mesma regio de endereamento que o arquivo ocupava originalmente. a c Outra tcnica usada mapear o arquivo especial de dispositivo /dev/e e zero para a memria. O arquivo /dev/zero, que descrito na Seo ??, O o e ca 143

Dispositivo /dev/zero do Cap tulo ??, Dispositivoscomporta-se como se fosse um arquivo innitamente longo preenchido com 0 bytes. Um programa que precisa uma fonte de 0 bytes pode mmap o arquivo /dev/zero. Escritas para /dev/zero so descartadas, de forma que a memria mapeada possa ser a o usada para qualquer propsito. Alocaes de memria personalizadas muitas o co o vezes mapeiam /dev/zero para obter pedaos de memria pr-inicializados. c o e

5.4

Pipes

A pipe um dispositivo de comunicao que permite comunicao unidirecie ca ca onal. Dados escritos para a escrita nal do pipe lido de volta a partir da e leitura nal. Os Pipes so dispositivos seriais; os dados so sempre lidos a a a partir do pipe na mesma ordem em que foram escritos. Tipicamente, um pipe usado para comunicao entre duas linhas de execuo em um processo e ca ca simples ou entre processos pai e lho. Em um shell, o s mbolo | cria um pipe. Por exemplo, o comando shell adiante faz com que o shell produza dois processos lhos, um para o comando ls e outros para o comando less: \% ls | less O shell tambm cria um pipe conectando a sa padro do subprocesso e da a ls com a entrada padro do processo less. Os nomes de arquivos listados a pelo ls so enviados para o less na exatamente mesma ordem como se a eles tivessem sido enviados diretamente para o terminal. A capacidade de dados do pipe limitada. Se o processo escritor escreve e mais rapidamente que o processo leitor pode consumir os dados, e se o pipe no puder armazenar mais dados, o processo escritor blioqueia at que mais a e capacidade torne-se dispon vel. Se o leitor tenta ler mas nenhum dado a ser lido est dispon a vel, o processo leitor bloqueia at que dados tornem-se dise pon veis. Dessa forma, o pipe automaticamente sincroniza os dois processos.

5.4.1

Criando Pipes

Para criar um pipe, chame o comando pipe. Fornea um vetor de inteiros de c tamanho 2. A chamada a pipe armazena o descritor do arquivo de leitura na posio 0 do vetor e o descritor do arquivo de escrita na posio 1. Por ca ca exemplo, considere o cdigo abaixo: o int pipe_fds[2]; int read_fd; 144

int write_fd; pipe (pipe_fds); read_fd = pipe_fds[0]; write_fd = pipe_fds[1];

Dados escritos para o descritor de arquivo write fd podem ser lidos de volta a partir de read fd.

5.4.2

Comunicao Entre Processos Pai e Filho ca

Uma chamada a pipe cria descritores de arquivo, os quais so vlidos somente a a dentro do referido processo e seus lhos. Descritores de arquivo de processo no podem ser informados a processos no aparentados; todavia, quando o a a processo chama fork, descritores de arquivo so copiados para o novo processo a lho. Dessa forma, pipes podem conectar somente com processos parentes. No programa na Listagem 5.7, um fork semeia um processo lho. O lho herda os descritores de arquivo do pipe. O pai escreve uma sequncia de e caracteres para o pipe, e o lho l a sequncia de caracteres. O programa de e e amostra converte esses descritores de arquivo em uxos FILE* usando fdop en. Pelo fato de usarmos uxos ao invs de descritores de arquivo, podemos e usar funes de entrada e sa da biblioteca C padro de n mais alto tais co da a vel como printf e fgets. 145

Listagem 5.7: (pipe.c) Usando um pipe para Comunicar-se com um Processo Filho
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 54 55 56 57 58 59 60 61 62 63 64 65 66 #include < s t d l i b . h> #include <s t d i o . h> #include <u n i s t d . h> / W r i t e COUNT c o p i e s between each . / o f MESSAGE t o STREAM, pausing f o r a second

void w r i t e r ( const char message , i n t count , FILE s t r e a m ) { f o r ( ; c o u n t > 0 ; c o u n t ) { / W r i t e t h e m e s s a g e t o t h e s t r e a m , and s e n d i t o f f i m m e d i a t e l y . f p r i n t f ( stream , %s \n , m e s s a g e ) ; f f l u s h ( stream ) ; / S n o o z e a w h i l e . / sleep (1) ; } } / Read random strings from t h e stream as long as possible . /

void r e a d e r ( FILE s t r e a m ) { char b u f f e r [ 1 0 2 4 ] ; / Read u n t i l we h i t t h e end o f t h e s t r e a m . f g e t s reads u n t i l e i t h e r a n e w l i n e o r t h e endo f f i l e . / while ( ! f e o f ( s t r e a m ) && ! f e r r o r ( s t r e a m ) && f g e t s ( b u f f e r , s i z e o f ( b u f f e r ) , s t r e a m ) != NULL) fputs ( buffer , stdout ) ; } i n t main ( ) { int f d s [ 2 ] ; p i d t pid ; / C r e a t e a p i p e . F i l e d e s c r i p t o r s f o r t h e two ends o f t h e p i p e a r e placed in fds . / pipe ( fds ) ; / F o r k a c h i l d p r o c e s s . / pid = f o r k ( ) ; i f ( p i d == ( p i d t ) 0 ) { FILE s t r e a m ; / T h i s i s t h e c h i l d p r o c e s s . C l o s e o u r c o p y o f t h e w r i t e end o f the f i l e descriptor . / close ( fds [ 1 ] ) ; / C o n v e r t t h e r e a d f i l e d e s c r i p t o r t o a FILE o b j e c t , and r e a d from i t . / stream = fdopen ( f d s [ 0 ] , r ) ; r e a d e r ( stream ) ; close ( fds [ 0 ] ) ; } else { / T h i s i s t h e p a r e n t p r o c e s s . / FILE s t r e a m ; / C l o s e o u r c o p y o f t h e r e a d end o f t h e f i l e d e s c r i p t o r . / close ( fds [ 0 ] ) ; / C o n v e r t t h e w r i t e f i l e d e s c r i p t o r t o a FILE o b j e c t , and w r i t e to i t . / s t r e a m = f d o p e n ( f d s [ 1 ] , w ) ; w r i t e r ( Hello , world . , 5 , stream ) ; close ( fds [ 1 ] ) ; } return 0 ; }

No in da main, a varivel fds declarada como sendo do tipo vetor cio a e inteiro de tamanho 2. A chamada pipe cria um pipe e coloca os descritores de arquivo de leitura e de escrita naquele vetor. O programa ento faz um a fork no processo lho. Aps o fechamento da leitura nal do pipe, o processo o pai inicia escrevendo sequncias de caractere para o pipe. Aps o fechamento e o 146

da escrita nal do pipe, o lho l sequncias de caractere a partir do pipe. e e Note que aps a escrita na funo escritora, o pai esvazia o pipe atravs o ca e de chamada a ush. De outra forma, a sequncia de caracteres pode no ter e a sido enviada imediatamente atravs do pipe. e Quando voc chama o comando ls | less, dois forks ocorrem: um para e o processo lho ls e um para processo lho less. Ambos esses processos herdam o descritores de arquivo do pipe de forma que eles podem comunicarse usando um pipe. Para ter processos no aparentados comunicando-se use a um FIFO ao invs de pipe, como discutido na Seo 5.4.5, FIFOs. e ca

5.4.3

Redirecionando a Entrada Padro, a Sa a da Padro e o Fluxo de Erro a

Frequentemente, voc no ir querer criar um processo lho e escolher o nal e a a de um pipe bem como suas entrada padro e sua sa padro. Usando a a da a chamada dup2, voc pode equiparar um descritor de arquivo a outro. Por e exemplo, para redirecionar a sa padro de um processo para um descritor da a de arquivo fd, use a seguinte linha:

dup2 (fd, STDIN\_FILENO);

A constante simblica STDIN FILENO representa o descritor para a eno trada padro, cujo valor 0. A chamada fecha a entrada padro e ento a e a a reabre-a com uma duplicata de fd de forma que os dois caminhos possam ser usados alternadamente. Descritores de arquivos equiparados compartilham a mesma posio de arquivo e o mesmo conjunto de sinalizadores de situao ca ca atual do arquivo. Dessa forma, caracteres lidos a partir de fd no so lidos a a novamente a partir da entrada padro. a O programa na Listagem 5.8 usa dup2 para enviar a sa de um pipe da 4 para o comando sort . Aps criar um pipe, o programa efetua um fork. O o processo pai imprime algumas sequncias de caractere para o pipe. O processo e lho anexa o descritor de arquivo de leitura do pipe para sua entrada padro a usando dup2. O processo lho ento executa o programa sort. a
O comando sort l linhas de texto a partir da entrada padro, ordena-as em ordem e a alfabtica, e imprime-as para a sa padro. e da a
4

147

Listagem 5.8: (dup2.c) Redirecionar a Sa de um pipe com dup2 da


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 #include #include #include #include <s t d i o . h> <s y s / t y p e s . h> <s y s / w a i t . h> <u n i s t d . h>

i n t main ( ) { int f d s [ 2 ] ; p i d t pid ; / C r e a t e a p i p e . F i l e d e s c r i p t o r s f o r t h e two ends o f t h e p i p e a r e placed in fds . / pipe ( fds ) ; / F o r k a c h i l d p r o c e s s . / pid = f o r k ( ) ; i f ( p i d == ( p i d t ) 0 ) { / T h i s i s t h e c h i l d p r o c e s s . C l o s e o u r c o p y o f t h e w r i t e end o f the f i l e descriptor . / close ( fds [ 1 ] ) ; / C o n n e c t t h e r e a d end o f t h e p i p e t o s t a n d a r d i n p u t . / dup2 ( f d s [ 0 ] , STDIN FILENO ) ; / R e p l a c e t h e c h i l d p r o c e s s w i t h t h e s o r t p r o g r a m . / execlp ( s o r t , s o r t , 0) ; } else { / T h i s i s t h e p a r e n t p r o c e s s . / FILE s t r e a m ; / C l o s e o u r c o p y o f t h e r e a d end o f t h e f i l e d e s c r i p t o r . / close ( fds [ 0 ] ) ; / C o n v e r t t h e w r i t e f i l e d e s c r i p t o r t o a FILE o b j e c t , and w r i t e to i t . / s t r e a m = f d o p e n ( f d s [ 1 ] , w ) ; f p r i n t f ( stream , T h i s i s a t e s t . \ n ) ; f p r i n t f ( stream , H e l l o , w o r l d . \ n ) ; f p r i n t f ( stream , My dog h a s f l e a s . \ n ) ; f p r i n t f ( stream , T h i s program i s g r e a t . \ n ) ; f p r i n t f ( stream , One f i s h , two f i s h . \ n ) ; f f l u s h ( stream ) ; close ( fds [ 1 ] ) ; / Wait f o r t h e c h i l d p r o c e s s t o f i n i s h . / w a i t p i d ( pid , NULL, 0 ) ; } return 0 ; }

5.4.4

As Funes popen e pclose co

Um uso comum de pipes enviar dados para ou receber dados de um proe grama sendo executado em um sub-processo. As funes popen e pclose co facilitam esse paradigma por meio da eliminao da necessidade de chamar ca pipe, fork, dup2, exec, e fdopen. Compare a Listagem 5.9, que utiliza popen e pclose, com o exemplo anterior (a Listagem 5.8). 148

Listagem 5.9: (popen.c) Exemplo Usando popen


1 2 3 4 5 6 7 8 9 10 11 12 13 #include <s t d i o . h> #include <u n i s t d . h> i n t main ( ) { FILE s t r e a m = popen ( s o r t , w ) ; f p r i n t f ( stream , T h i s i s a t e s t . \ n ) ; f p r i n t f ( stream , H e l l o , w o r l d . \ n ) ; f p r i n t f ( stream , My dog h a s f l e a s . \ n ) ; f p r i n t f ( stream , T h i s program i s g r e a t . \ n ) ; f p r i n t f ( stream , One f i s h , two f i s h . \ n ) ; return p c l o s e ( s t r e a m ) ; }

A chamada a popen cria um processo lho executando o comando sort, substituindo chamadas a pipe, fork, dup2, e execlp. O segundo argumento, w, indica que o processo que fez a chamada a popen espera escrever para o processo lho. O valor de retorno de popen um m de pipe; o outro nal e e conectado ` entrada padro do processo lho. Aps a escrita terminar, pclose a a o fecha o uxo do processo lho, espera que o processo encerre, e retorna valor de situao atual. ca O primeiro argumento a popen executado como um comando shell em e um sub-processo executando /bin/sh. O shell busca pela varivel de ambia ente PATH pelo caminho usual para encontrar programas executveis. Se a o segundo argumento for r, a funo retorna o uxo de sa padro do ca da a processo lho de forma que o processo pai possa ler a sa da. Se o segundo argumento for w, a funo retorna o uxo de entrada padro do processo ca a lho de forma que o processo pai possa enviar dados. Se um erro ocorrer, popen retorna um apontador nulo. Chama pclose para fechar um uxo retornado por popen. Aps fechar o o uxo especicado, pclose espera pelo m do processo lho.

5.4.5

FIFOs

Um arquivo rst-in, rst-out (FIFO)5 um pipe que tem um nome no sistema e de arquivos. Qualquer processo pode abrir ou fechar o FIFO; os processo em cada lado do pipe precisam ser aparentados uns aos outos. FIFOs so a tambm chamados pipes com nomes. e Voc cria um FIFO usando o comando mkfo. Especique o caminho do e FIFO na linha de comando. Por exemplo, para criar um FIFO em /tmp/fo voc deve fazer o seguinte: e
\% mkfifo /tmp/fifo \% ls -l /tmp/fifo prw-rw-rw1 samuel

users

0 Jan 16 14:04 /tmp/fifo

Nota do tradutor:Quem entrar primeiro sai tambm primeiro e

149

O primeiro caractere da sa do comando ls uma letra p, indicando da e que esse arquivo atualmente um FIFO (pipe com nome). Em uma janela, e leia a partir do FIFO usando o seguinte: \% cat < /tmp/fifo Em uma segunda janela, escreva para o FIFO fazendo o seguinte: \% cat > /tmp/fifo Ento digite algumas linhas de texto. A cada vez que voc pressionar a e Enter, a linha de texto enviada atravs do FIFO e aparece na primeira e e janela. Feche o FIFO pressionando Ctrl+D na segunda janela. Remova o FIFO com a seguinte linha: \% rm /tmp/fifo 5.4.5.1 Criando um FIFO

Criar um FIFO a partir de um programa em linguagem C use a funo mkca 6 fo . O primeiro argumento a localizao na qual criar o FIFO; o segundo e ca parmetro especica o dono do pipe, o grupo ao qual pertence o group, e as a permisses para o resto do mundo, como discutido no Cap o tulo ??, Segurana na Seo ??, Permisses do Sistema de Arquivo. Pelo fato de um c ca o pipe possuir obrigatriamente um leitor e um escritor, as permisses devem o o incluir ambas tanto para leitura quanto para escrita. Se o pipe no puder a ser criado (por exemplo, se um arquivo com o nome escolhido para o pipe j a exista), mkfo retorna -1. Inclua os arquivos de cabealho <sys/types.h> e c <sys/stat.h> se voc chamar a funo mkfo. e ca 5.4.5.2 Accessando um FIFO

Acesse um FIFO da mesma forma que feita com arquivos comuns. Para e comunicar-se atravs de um FIFO, um programa deve abr para escrita, e -lo e outro programa deve abr para leitura. Ou ainda usando as funes de -lo co entra e sa de baixo n da vel (open, write, read, close, e assim por diante, como listado no apndice B, E/S de Baixo N e vel) ou as funes de E/S co da bilioteca C (fopen, fprintf, fscanf, fclose, e assim por diante) podem ser usadas. Por exemplo, para escrever uma rea temporria de armazenamento de a a dados para um FIFO usando rotinas de E/S de baixo n vel, voc pode usar e o cdigo abaixo: o
6

Nota do tradutor:para mais informaes use o comando shell man 3 mkfo co

150

int f d = open ( f i f o p a t h , O WRONLY) ; w r i t e ( fd , data , d a t a l e n g t h ) ; c l o s e ( fd ) ; Para ler uma sequncia de caracteres a partir do FIFO usando as funes e co de E/S da biblioteca C, voc pode usar o cdigo abaixo: e o FILE f i f o = f o p e n ( f i f o p a t h , r ) ; f s c a n f ( f i f o , %s , b u f f e r ) ; fclose ( fifo ); Um FIFO pode ter multiplos leitores ou multiplos escritores. Os Bytes de cada escritor so escritos automaticamente at alcanar o mximo tamanho a e c a de PIPE BUF (4KB no GNU/Linux). Pedaos de escritas sumultneas pode c a ser intercalados. Regras similares aplicam-se a leituras simultnea. a Dierenas de Pipes nomeados do Windows c Pipes no sistemas operacionais Win32 so muito similares a pipes em a GNU/Linux. (Reporte-se ` documentao de biblioteca do Win32 para dea ca talhes tcnicos sobre isso.) As principais diferenas referem-se a pipes nomee c ados, os quais, para Win32, funcionam mais como sockets. Pipes nomeados em Win32 podem conectar processos em cmputadores separados conectados via rede. Em GNU/Linux, sockets so usados para esse propsito. Tambm, a o e Win32 permite multiplas coneces de leitura e escrita por meio de pipe co nomeado sem intercalao de dados, e pipes podem ser usados para comuca nicao em mo dupla.7 ca a

5.5

Sockets

Um socket um dispositivo de coneco bidirecional que pode ser usado para e ca comunicar-se com outro processo na mesma mquina ou com um processo a em outras mquinas. Sockets so o unico tipo de comunicao entre processo a a ca que discutiremos nesse cap tulo que permite comunicao entre processos em ca dirferentes computadores . Programas de Internet tais como Telnet, rlogin, FTP, talk, e a World Wide Web usam sockets. Por exemplo, voc pode obter a pgina WWW de um servidor Web e a usando o programa Telnet pelo fato de eles ambos (servidor WWW e Telnet do cliente) usarem sockets para comunicaes em rede.8 Para abrir uma co
Note que somente Windows NT pode criar um pipe nomeado; programas Windows 9x pode formar somente coneces como cliente. co 8 Comumente, poderia usar telnet para conectar um servidor Telnet para acesso remoto. Mas voc pode tambm usar o telnet para conectar um servidor de um tipo diferente e e e ento digitar comentrios diretamete no prprio telnet. a a o
7

151

coneco com um servidor WWW localizado em www.codesourcery.com, use ca telnet www.codesourcery.com 80. A constante mgica 80 especica uma coa neco para o programa de servidor Web executando www.codesourcery.com ca ao invs de algum outro processo. Tente digitar GET / aps a coneco e o ca ser estabelecida. O comando GET / envia uma mensagem atravs do e socket para o servidro Web, o qual responde enviando o cdigo fonte em na o linguagem HTML da pgina inicial fechando a coneco em seguida: a ca
\% telnet www.codesourcery.com 80 Trying 206.168.99.1... Connected to merlin.codesourcery.com (206.168.99.1). Escape character is ^]. GET / <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> ...

5.5.1

Conceitos de Socket

Quando voc cria um socket, voc deve especicar trs parmetros: o estilo e e e a da comunicao,o escopo, e o protocolo. ca Um estilo de comunicao controla como o socket trata dados transmitica dos e especica o nmero de parceiros de comunicao. Quando dados so u ca a enviados atravs de um socket, esses dados so empacotados em partes menoe a res chamadas pacotes. O estilo de comunicao determina como esses pacotes ca so manuseados e como eles so endereados do emissor para o receptor. a a c Estilos de coneco garantem a entrega de todos os pacotes na orca dem que eles foram enviados. Se pacotes forem perdidos ou reordenados por problemas na rede, o receptor automaticamente requisita a retransmisso desses pacotes perdidos/reordenados ao emissor. a Um socket de estilo do tipo coneco como uma chamada teca e lefnica: O endereo do emissor e do receptor so xados no in o c a cio da comunicao quando a coneco estabelecida. ca ca e Um socket de estilo do tipo datagrama no garante a entrega ou a a ordem de chegada. Pacotes podem ser perdidos ou reordenados no caminho devido a erros de rede ou outras condies. Cada paco cote deve ser rotulado com seu destino e no garantido que seja a e entregue. O sistema garante somente o melhor esforo de forma c que pacotes podem desaparecer ou chegar em uma ordem diferente daquela que foi transportado. Um estilo de transmisso do tipo a datagram socket comporta-se mais como vrias cartas colocadas a na agncia de correio. O emissor especica o endereo do receptor e c para cada carta individualmente. 152

Um escopo de socket especica como endereos de socket so escritos. Um c a endereo de socket identica a outra extremidade de uma coneco de socket. c ca Por exemplo, endereos de socket no espao de endereamento localso c c c a comumente nomes de arquivo comuns. No escopo de Internet um endereo c de socket composto do endereo Internet (tambm conhecido como um e c e endereo de protocolo de Internet ou endereo IP) de uma mquina anexada c c a a ` rede e um nmero de porta. O nmero de porta faz distino no conjunto u u ca de multiplos sockets na mesma mquina. a Um protocolo especica como dados so transmitidos. Alguns protocolos a so TCP/IP, os protocolos primrios usados pela Internet; o protocolo de a a rede AppleTalk ; e o protocolo de comunicao local UNIX. Algumas comca binaes de estilos, escopo, e protocolos no so suportadas. co a a

5.5.2

Chamadas de Sistema

Os Sockets so mais ex a veis que as tcnicas de comunicao discutidas ane ca teriormente. Adiante temos as chamadas de sistema relacionadas a sockets 9 : socket Cria um socket close Destri um socket o connect Cria uma coneco entre dois sockets ca bind Rotula um socket de servidor com um endereo c listen Congura um socket para aceitar condies co accept Aceita uma coneco e cria um novo socket para a coneco ca ca Sockets so representados por descritores de arquivo. a Criando e Destruindo Sockets As funes socket e close criam e destroem sockets, respectivamente. co Quando voc cria um socket, especica as trs escolhas de socket: escopo, e e estilo de comunicao, e protocolo. Para o parmetro de escopo, use consca a tantes iniciando por PF (abreviatura de protocol families). Por exemplo, PF LOCAL ou PF UNIX especicam o escopo local, e PF INET especicam escopos de Internet. Para o parmetro de estilo de comunicao, use a ca constantes iniciando com SOCK . Use SOCK STREAM para um socket de
Nota do tradutor: no slackware 13.1 padro o comando man 2 socketcall retorna, a entre outras coisas: accept(2), bind (2), connect(2), getpeername(2), getsockname(2), getsockopt(2), listen(2), recv (2), recvfrom(2), recvmsg(2), send (2), sendmsg(2), sendto(2), setsockopt(2), shutdown(2), socket(2), socketpair (2).
9

153

estilo do tipo coneco, ou use SOCK DGRAM para um socket de estilo do ca tipo datagrama. O terceiro parmetro, o protocolo, especica o mecanismo de baixo n a vel para transmitir e receber dados. Cada protocolo vlido para uma come a binao particular de estilo e escopo. Pelo fato de existir habitualmente um ca melhor protocolo para cada tal par de estilo e espao de endereamento, espec c cicar 0 (zero) comumente o protocolo correto. Se o socket obtiver sucesso, e ele retornar um descritor de arquivo para o socket. Voc pode ler de ou esa e crever para o socket usando read, write, e assim por diante, como com outro descritor de arquivo. Quando voc tiver terminado com um socket, chame e close para remov-lo. e Chamando connect Para criar uma coneco entre dois sockets, o cliente chama connect, especa cicando o endereo de um socket de servidor para conectar-se. Um cliente c e o processo que inicia a coneco, e um servidor um processo esperando para ca e aceitar coneces. O cliente chama connect para iniciar uma coneco de um co ca socket local para o socket de servidor especicado pelo segundo argumento. O terceiro argumento o comprimento, em bytes, da estrutura de endereo e c apontada pelo segundo argumento. O formato de endereo de socket difere c conforme o escopo do socket. Enviando Informaes co Qualquer tcnica para escrever para um descritor de arquivos pode ser e usada para para escrever para um socket. Veja o Apndice ?? para uma dise curso sobre funo de E/S de baixo n do GNU/Linux e algumas questes a ca vel o envolvendo seu uso. A funo send, que espec ca e ca para descritores de arquivo de socket, fornece uma alternativa pra escrever com poucas escolhas adicionais; veja a pgina de manual de send para mais informaes10 . a co

5.5.3

Servidores

Um ciclo de vida de um servidor consiste da criao de um socket de estilo ca do tipo coneco, associao de um endereo a esse socket, colocao de uma ca ca c ca chamada pra escutar e que habilita coneces para o socket, colocao de chaco ca madas para aceitar coneces de entrada, e nalmente fechamento do socket. co Dados no so lidos e escritos diretamente via socket do servidor; ao invs a a e disso, a cada vez que um programa aceita uma nova coneco, GNU/Linux ca cria um socket em separado para usar na transferncia de dados sobre aquela e conneco. Nessa seo, introduziremos as chamadas de sistema bind, listen, ca ca e accept.
10

Nota do tradutor: man 2 send

154

Um endereo deve ser associado ao socket do servidor usando bind se for c para um cliente encontr-lo. O primeiro argumento de bind o descritor de a e arquivo do socket. O segundo argumento de bind um apontador para uma e estrutura de endereo de socket; o formato desse segundo argumento depende c da fam de endereo do socket. o terceiro argumento o comprimento da lia c e estrutura de endereo, em bytes. Quando um endereo associado a um c c e socket de estido do tipo coneco, esse socket de estido do tipo coneco ca ca deve chamar listen para indicar que esse socket de estido do tipo coneco ca um servidor. O primeiro argumento ` chamada listen o descritor de e a e arquivo do socket. O segundo argumento a listen especica quantas coneces co pendentes so enleiradas. Se a la estiver cheia, coneces adicionais iro ser a co a rejeitadas. Essa rejeio de coneces no limita o nmero total de coneces ca co a u co que um servidor pode manipular; Essa rejeio de coneces limita o nmero ca co u de clientes tentando conectar que no tiveram ainda aceitao. a ca Um servidor aceita uma requisio de coneco de um cliente por meio de ca ca uma chamada ` chamada de sistema accept. O primeiro argumento a accept a e o descritor de arquivo do socket. O segundo argumento a accept aponta para uma estrutura de endereo de socket, que preenchida com o endereo de c e c socket do cliente. O terceiro argumento a accept o comprimento, em bites, e de uma estrutura de endereo de socket. O servidor pode usar o endereo do c c cliente para determinar se o socket servidor realmente deseja comunicar-se com o cliente. A chamada a accept cria um novo socket para comunicao ca com o cliente e retorna o correspondente descritor de arquivos. O socket servidor original continua a accept novas coneces de outros clientes. Para co ler dados de um socket sem remover esse socket da la de entrada, use recv. A chamada recv recebe os mesmos argumentos que a chamada read, mas adicionalmente o argumento FLAGS. Um sinalizador do tipo MSG PEEK faz com que dados sejam lidos mas no removidos da la de entrada. a

5.5.4

Sockets Locais

Sockets conectando processos no mesmo computador podem usar o escopo local representado pelos sinnimos PF LOCAL e PF UNIX. Sockets coneco tando processos no mesmo computador so chamados sockets locais ou soca kets de dom UNIX. Seus endereos de socket, especicados por nomes de nio c arquivo, so usados somente quando se cria coneces. a co O nome de socket especicado em struct sockaddr un. Voc deve escoe e lher o campo sun family para AF LOCAL, indicando que o nome do socket s vlido no escopo local. O campo sun path especica o nome de arquivo oe a que vai ser usado e pode ser, no mximo, do comprimento de 108 bytes. O a comprimento atual de struct sockaddr un deve ser calculado usando a ma155

cro SUN LEN. Qualquer nome de arquivo pode ser usado, mas o processo deve ter permisso de escrita no diretrio, o que permite a adio de arquia o ca vos ao diretrio. Para conectar um socket, um processo deve ter permisso o a de leitura para o arquivo. Mesmo atravs de diferentes computadores come partilhando o mesmo sistema de arquivos, somente processos executando no mesmo computador podem comunicar-se com sockets de escopo local. O unico protocolo permitido para o escopo local 0 (zero). e Pelo fato de residir no sistema de arquivos, um socket local listado como e um arquivo. Por exemplo, preste ateno o s inicial: ca

\% ls -l /tmp/socket srwxrwx--x 1 user

group

0 Nov 13 19:18 /tmp/socket

Chame unlink para remover um socket local quando voc tiver encerrado e com o referido socket local.

5.5.5

Um Exemplo Usando um Sockets de Escopo local

Ilustraremos sockets com dois programas. O programa do servidor, na Listagem 5.10, cria um socket de escopo local e escuta ` espera de coneces a a co esse socket de escopo local. Quando esse socket de escopo local recebe uma coneco, ele l mensagens de texto a partir da coneco e mostra-as at que ca e ca e a coneco feche. Se uma das mensagens recebidas pelo socket do servidor ca for quit o programa do servidor remove o socket e termina. O programa socket-server recebe o caminho para o socket como seu argumetnode linha de comando. 156

Listagem 5.10: (socket-server.c) Servidor de Socket de Escopo Local


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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 #include #include #include #include #include #include <s t d i o . h> < s t d l i b . h> < s t r i n g . h> <s y s / s o c k e t . h> <s y s /un . h> <u n i s t d . h> out . Continue u n t i l the c l i e n t sent a quit

/ Read t e x t f r o m t h e s o c k e t and p r i n t i t socket closes . R e t u r n nonz e r o i f t h e message , z e r o o t h e r w i s e . / int s e r v e r ( int { while ( 1 ) { int length ; char t e x t ; client socket )

/ F i r s t , r e a d t h e l e n g t h o f t h e t e x t m e s s a g e f r o m t h e s o c k e t . read r e t u r n s zero , the c l i e n t c l o s e d the connection . / i f ( r e a d ( c l i e n t s o c k e t , &l e n g t h , s i z e o f ( l e n g t h ) ) == 0 ) return 0 ; / A l l o c a t e a b u f f e r t o h o l d t h e t e x t . / t e x t = ( char ) m a l l o c ( l e n g t h ) ; / Read t h e t e x t i t s e l f , and p r i n t i t . / read ( c l i e n t s o c k e t , text , length ) ; p r i n t f ( %s \n , t e x t ) ; / F r e e t h e b u f f e r . / free ( text ) ; / I f t h e c l i e n t s e n t t h e m e s s a g e q u i t , we r e a l l d o n e . / i f ( ! strcmp ( text , q u i t ) ) return 1 ; } } i n t main ( i n t a r g c , char const a r g v [ ] ) { const char const s o c k e t n a m e = a r g v [ 1 ] ; int s o c k e t f d ; s t r u c t s o c k a d d r u n name ; int c l i e n t s e n t q u i t m e s s a g e ; / C r e a t e t h e s o c k e t . / s o c k e t f d = s o c k e t (PF LOCAL , SOCK STREAM, / I n d i c a t e t h i s i s a s e r v e r . / name . s u n f a m i l y = AF LOCAL ; s t r c p y ( name . s u n p a t h , s o c k e t n a m e ) ; b i n d ( s o c k e t f d , &name , SUN LEN (&name ) ) ; / L i s t e n f o r c o n n e c t i o n s . / l i s t e n ( soc ke t fd , 5) ;

If

0) ;

/ R e p e a t e d l y a c c e p t c o n n e c t i o n s , s p i n n i n g o f f one s e r v e r ( ) t o d e a l with each c l i e n t . Continue u n t i l a c l i e n t sends a q u i t message . do { struct sockaddr un c l i e n t n a m e ; socklen t client name len ; int c l i e n t s o c k e t f d ;

/ A c c e p t a c o n n e c t i o n . / c l i e n t s o c k e t f d = a c c e p t ( s o c k e t f d , &c l i e n t n a m e , &c l i e n t n a m e l e n ) ; / H a n d l e t h e c o n n e c t i o n . / client sent quit message = server ( client socket fd ) ; / C l o s e o u r end o f t h e c o n n e c t i o n . / close ( client socket fd ) ; } while (! client sent quit message ) ; /

/ Remove t h e s o c k e t f i l e . close ( socket fd ) ; unlink ( socket name ) ; return 0 ; }

O programa cliente, na Listagem 5.11, conecta a umsocket de escopo local e envia uma mensagem. O nome path para o socket e a mensagem so a especicados na linha de comando. 157

Listagem 5.11: (socket-client.c) Cliente de Socket de Escopo Local


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 #include #include #include #include #include <s t d i o . h> < s t r i n g . h> <s y s / s o c k e t . h> <s y s /un . h> <u n i s t d . h> the socket given by file d e s c r i p t o r SOCKET FD . /

/ W r i t e TEXT t o

void w r i t e t e x t ( i n t s o c k e t f d , const char t e x t ) { / W r i t e t h e number o f b y t e s i n t h e s t r i n g , i n c l u d i n g NUL e r m i n a t i o n . t / int length = s t r l e n ( text ) + 1 ; w r i t e ( s o c k e t f d , &l e n g t h , s i z e o f ( l e n g t h ) ) ; / W r i t e t h e s t r i n g . / write ( socket fd , text , length ) ; } i n t main ( i n t a r g c , char const a r g v [ ] ) { const char const s o c k e t n a m e = a r g v [ 1 ] ; const char const m e s s a g e = a r g v [ 2 ] ; int s o c k e t f d ; s t r u c t s o c k a d d r u n name ; / C r e a t e t h e s o c k e t . / s o c k e t f d = s o c k e t (PF LOCAL , SOCK STREAM, 0 ) ; / S t o r e t h e s e r v e r s name i n t h e s o c k e t a d d r e s s . / name . s u n f a m i l y = AF LOCAL ; s t r c p y ( name . s u n p a t h , s o c k e t n a m e ) ; / C o n n e c t t h e s o c k e t . / c o n n e c t ( s o c k e t f d , &name , SUN LEN (&name ) ) ; / W r i t e t h e t e x t on t h e command l i n e t o t h e s o c k e t . / w r i t e t e x t ( s o c k e t f d , message ) ; close ( socket fd ) ; return 0 ; }

Antes de o cliente enviar uma mensagem de texto, ele envia o comprimento do texto que pretende enviar mandando bytes da varivel inteira a length. Da mesma forma, o servidor l o comprimento do texto a partir e do socket de dentro da varivel inteira. Isso permite ao servidor alocar uma a a rea temporria de armazenamento de tamanho apropriado para manter a a mensagem de texto antes de l-la a partir do socket. e Para tentar esse exemplo, inicie o programa servidor em uma janela. Especique um caminho para o socket por exemplo, /tmp/socket. \% ./socket-server /tmp/socket Em outra janela, execute o cliente umas poucas vezes, especicando o mesmo caminho de socket adicionando mensagens para enviar para o servidor: \% ./socket-client /tmp/socket Hello, world." \% ./socket-client /tmp/socket This is a test." O programa servidor recebe e imprime as mensagens acima. Para fechar o servidor, envie a menssagem quit a partir de um cliente: \% ./socket-client /tmp/socket quit" O programa servidor termina. 158

5.5.6

Sockets de Dom nio Internet

Sockets de dom UNIX podem ser usados somente para comunicao entre nio ca dois processos no mesmo computador. Sockets de dom nio Internet, por outro lado, podem ser usados para conectar processos em diferentes mquinas a conectadas por uma rede. Sockets conectando processos atravs da Internet e usam o escopo de Internet representado por PF INET. Os protocolos mais comuns so TCP/IP. O protocolo Internet (IP), um protocolo de baixo n a vel, move pacotes atravs da Internet, quebrando em pedaos e remontando os e c pedaos, se necessrio. O IP garante somente melhor esforo de entrega, c a c de forma que pacotes podem desaparece ou serem reordenados durante o transporte. Todo computador participante especicando usando um unico e nmero IP. O Protocolo de Controle de Transmisso (TCP), formando uma u a camada sobre o IP, fornece transporte convel no que se refere a ordenao a ca na coneco. Os dois protocolos juntos tornam possivel que coneces semeca co lhantes `s telefnicas sejam estabelecidas entre computadores e garante que a o dados se entregues de forma convel e em ordem. a
Nomes de DNS Pelo fato de ser mais fcil lembrar nome que nmeros, o Servio de Nomes de a u c Dom nio (DNS) associa nomes tais como www.codesourcery.com a nmeros IP u unicos de computadores. DNS implementado por meio de uma hierarquia e mundial de servidores de nome, mas voc no precisa entender o protocolo DNS e a para usar nomes de computador conectado ` rede Internet em seus programas. a

Endereos de socket localizados na Internet possuem duas partes: uma c mquina e um nmero de porta. Essa informao armazenada na varivel a u ca e a struct sockaddr in. Escolha o campo sin family para AF INET de forma a indicar que struct sockaddr in um endereo de escopo Internet. O campo e c sin addr armazena o endereo Internet da mquina desejada como um nmero c a u de IP inteiro de 32-bit. Um nmero de porta distingue entre diferentes socu kets em uma mesma mquina. Pelo fato de diferentes mquinas armazenarem a a valores multibyte em ordem de bytes diferentes, use o comando htons para converter o nmero da porta para ordem de byte de rede. Veja a pgina de u a manual para o comando ip para maiores informaes.11 co Para converter para converter nomes de computador conectado ` rede a leg veis a humanos, ou em nmeros na notao de ponto padronizada (tais u ca como 10.0.0.1) ou em nomes de DNS12 (tais como www.codesourcery.com) em
11 12

Nota do tradutor:temos ip tanto na seo 7 como na seo 8 das pginas de manual. ca ca a Nota do tradutor:Domain Name service

159

nmeros IP de 32-bit, voc pode usar gethostbyname. A funo gethostbyu e ca name retorna um apontador para a estrutura struct hostent; o campo h addr contm o nmero IP do computador conectado ` rede. Veja o programa e u a amostra na Listagem 5.12. A Listagem 5.12 ilustra o uso de sockets de dom Internet. O programa nio obtm o pgina inicial do servidor Web cujo nome do computador conectado e a a ` rede especicado na linha de comando. e Listagem 5.12: (socket-inet.c) L de um Servidor WWW e
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 54 55 56 57 58 59 #include #include #include #include #include #include #include < s t d l i b . h> <s t d i o . h> <n e t i n e t / i n . h> <n e t d b . h> <s y s / s o c k e t . h> <u n i s t d . h> < s t r i n g . h> the server s socket .

/ P r i n t t h e c o n t e n t s o f t h e home p a g e f o r R e t u r n an i n d i c a t i o n o f s u c c e s s . / void g e t h o m e p a g e ( i n t s o c k e t f d ) { char b u f f e r [ 1 0 0 0 0 ] ; s s i z e t number characters read ;

/ Send t h e HTTP GET command f o r t h e home p a g e . / s p r i n t f ( b u f f e r , GET /\ n ) ; write ( socket fd , buffer , strlen ( buffer ) ) ; / Read f r o m t h e s o c k e t . r e a d may n o t r e t u r n a l l t h e d a t a a t one t i m e , s o k e e p t r y i n g u n t i l we r un o u t . / while ( 1 ) { number characters read = read ( s o c k e t f d , buffer , 10000) ; i f ( n u m b e r c h a r a c t e r s r e a d == 0 ) return ; / W r i t e t h e d a t a t o s t a n d a r d o u t p u t . / f w r i t e ( b u f f e r , s i z e o f ( char ) , n u m b e r c h a r a c t e r s r e a d , s t d o u t ) ; } } i n t main ( i n t a r g c , char const a r g v [ ] ) { int s o c k e t f d ; s t r u c t s o c k a d d r i n name ; struct hostent h o s t i n f o ; / C r e a t e t h e s o c k e t . / s o c k e t f d = s o c k e t ( PF INET , SOCK STREAM, 0 ) ; / S t o r e t h e s e r v e r s name i n t h e s o c k e t a d d r e s s . / name . s i n f a m i l y = AF INET ; / C o n v e r t f r o m s t r i n g s t o n um b e r s . / h o s t i n f o = gethostbyname ( argv [ 1 ] ) ; i f ( h o s t i n f o == NULL) return 1 ; else name . s i n a d d r = ( ( s t r u c t i n a d d r ) h o s t i n f o >h a d d r ) ; / Web s e r v e r s u s e p o r t 8 0 . / name . s i n p o r t = h t o n s ( 8 0 ) ; / C o n n e c t t o t h e web s e r v e r / i f ( c o n n e c t ( s o c k e t f d , &name , s i z e o f ( s t r u c t perror ( connect ) ; return 1 ; } / R e t r i e v e t h e s e r v e r s home p a g e . / get home page ( s o c k e t f d ) ; return 0 ; }

s o c k a d d r i n ) ) == 1) {

Esse programa recebe o nome do computador conectado ` rede do servia dor Web na linha de comando (no uma URL isto , recebe a informao a e ca 160

sem o http://). O programa chama a funo gethostbyname para traduca zir o nome do computador conectado ` rede em um endereo IP numrico a c e e ento conectar um uxo (TCP) socket na porta 80 daquele computador a conectado ` rede. Servidores Web falam o Protocolo de Transporte de Hia pertexto (HTTP), de forma que o programa emita o comando HTTP GET e o servidor responda enviando o texto da pgina inicial. a
N meros de Porta Padronizados u Por conveno, servidores Web esperam por coneces na porta 80. A maioria ca co dos servios de rede Internet so associados a nmeros de prota padronizac a u dos. Por exemplo, servidores Web que usam SSL esperam por coneces na co porta 443, e servidores de e-mail (que usam o protocolo SMTP) esperam por coneces na porta 25. Em sistemas GNU/Linux, a associao entre nomes de co ca protocolos, nomes de servios e nmeros de porta padronizados est listada no c u a arquivo /etc/services. A primeira coluna o protocolo ou nome de servio. A e c segunda coluna lista o nmero da porta e o tipo de coneco: tcp para servios u ca c orientados ` coneco, ou udp para datagramas. Se voc implementar algum a ca e servio personalizado de rede usando sockets de dom Internet, use nmeros c no u de porta maiores que 1024.

Por exemplo, para recuperar a pgina inicial do s Web www.codesour a tio cery.com, chame o seguinte:
\% ./socket-inet www.codesourcery.com <html> <meta http-equiv=Content-Type" content="text/html; charset=iso-8859-1"> ...

5.5.7

Sockets Casados

Como vimos anteriormente, a funo pipe cria dois descritores de arquivo ca para o in e o m de um pipe. Pipes so limitados pelo fato de os descricio a tores de arquivo deverem ser usados por processos aparentados e pelo fato de a comunicao ser unidirecional. A funo socketpair cria dois descritoca ca res de arquivo para dois sockets conectados no mesmo computador. Esses descritpres de arquivo permitem comunicao de mo dupla entre processos ca a aparentados. Seus primeiros trs parmetros so os mesmo que aqueles da chamada de e a a sistema socket: eles especicam o dom nio, estilo de coneco, e o protocolo. O ultimo parmetro um vetor de dois inteiros, os quais so preenchidos com a e a as descries de arquivo dos dois sockets, de maneira similar a pipe. Quando co voc chama socketpair, voc deve especicar PF LOCAL como o dom e e nio.

161

162

Cap tulo 6 Licena de Livre Publicao c ca

1. Licena de Livre Publicao Esta uma traduo no-ocial da Open Puc ca e ca a blication Licence verso 1.0, de 8 de junho de 1999, e no substituto legal a a e para a Licena original, dispon em http://www.opencontent.org/openpub. c vel Entretanto, esta traduo poder auxiliar pessoas que falem Portugus a enca a e tender melhor a licena Openpub. c permitido a qualquer pessoa copiar e distribuir cpias desse documento E o de licena, desde que sem a implementao de qualquer mudana. c ca c OPEN PUBLIC LICENCE Draft v1.0, 8 june 1999 1.1. Requisitos comuns `s verses no-modicada a o a e modicada Os trabalhos protegidos pela Licena de Livre Publicao (Open c ca Publication Licence) podem ser reproduzidos e distribu dos no todo ou em parte, em qualquer meio f sico ou eletrnico, desde que os termos desta licena o c estejam inclu dos, e que esta licena ou uma incorporao dela por referncia c ca e (com quaisquer das opes escolhidas pelo autor ou editor) estejam presentes co na reproduo. ca A forma apropriada para uma incorporao por referncia : ca e e Note: Copyright(c) (ano) (nome do autor ou proprietrio da obra). Este a material somente poder ser distribu se sujeito aos termos e condies a do co rmados na Licena de Livre Publicao (Open Publication Licence), verso c ca a X.Y ou superior (a verso mais atual encontra-se dispon em http://www.opencontent.org/openpub/ a vel Esta referncia, devidamente preenchida com os dados da publicao, e ca deve ser seguida imediatamente com quaisquer opes escolhidas pelos autoco res ou editor do documento. E permitida a redistribuio comercial de material licenciado pela Licena ca c de Livre Publicao (Open Publication Licence). ca Qualquer publicao no formato livro padro (papel) requer obrigatorica a amente a citao dos autores e editor originais. Os nomes dos autores e do ca editor devem aparecer em todas as superf cies externas do livro. Em todas 163

as faces externas do livro, o nome do editor original deve estar impresso em tamanho to grande quanto o t a tulo do trabalho, e citado como proprietrio a em relao `quele t ca a tulo. Copyright O copyright de todo trabalho protegido pela Licena de Livre Publicao c ca (Open Publication Licence) pertence aos autores ou proprietrios. a Escopo desta licena c Os termos de licena a seguir aplicam-se a todos os trabalhos protegidos c pela Licena de Livre Publicao (Open Publication Licence), a no ser que c ca a explicitamente indicado no trabalho. A mera adio de trabalhos protegidos pela Licena de Livre Publicao ca c ca (Open Publication Licence) ou partes de trabalhos protegidos pela Licena c de Livre Publicao (Open Publication Licence) em uma mesma m ca dia que contenha outros trabalhos ou programas no protegidos por essa licena no a c a decorre em aplicao da Licena de Livre Publicao (Open Publication Lica c ca cence) para esses outros trabalhos. O trabalho resultante deve explicitamente conter uma nota especicando a incluso do material protegido pela Licena a c de Livre Publicao (Open Publication Licence) e o aviso de copyright aproca priado. APLICABILIDADE. Se alguma parte desta licena no puder ser aplic a cada em alguma jurisdio, as partes restantes deste documento continuam ca sendo aplicadas. AUSENCIA DE GARANTIA. Os trabalhos protegidos pela Licena de c Livre Publicao (Open Publication Licence) so fornecidos como esto, ca a a sem garantias de qualquer tipo, expl cita ou impl cita, incluindo, mas no a limitado a, as garantias impl citas de comercializao e convenincia para ca e um propsito particular, ou garantia de no-infrao. o a ca Requisitos para trabalhos modicados Todas as verses modicadas de documentos cobertos por esta licena, o c incluindo tradues, antologias, compilaes e documentao parcial, deve co co ca seguir os requisitos abaixo: A verso modicada deve ser indicada como tal. a As pessoas que zerem as modicaes e as datas de modicao devem co ca ser identicadas. O reconhecimento dos autores e editor originais (se aplicvel) deve ser a mantido de acordo com as prticas acadmicas usuais de citao. a e ca O local da verso no-modicada do documento deve ser indicado. a a Os nomes originais dos autores no devem ser utilizados para indicar ou a garantir seu endosso ao documento resultante sem a autorizao expressa dos ca autores. Prticas recomendadas a 164

Em adio aos requisitos desta licena, solicitado e extremamente recoca c e mendado aos redistribuidores que: Se os trabalhos protegidos pela Licena de Livre Publicao (Open Puc ca blication Licence) estiverem sendo distribu dos em impressos ou CD-ROM, os autores sejam informados por email, ao menos trinta dias antes, para que os autores tenham tempo de providenciar documentao atualizada. Esta ca noticao deve descrever as modicaoes introduzidas no documento, se ca c existirem. Todas as modicaes substanciais (incluindo excluses) devem ser marco o cadas claramente no documento, ou ento descritas em um anexo ao docua mento. Finalmente, mesmo no sendo obrigatrio sob esta licena, considerado a o c e de bom tom oferecer uma cpia sem nus de todo o material modicado o o (impresso e CD-ROM) para os autores originais. Termos opcionais Os autores e editores de documentos protegidos pela Licena de Livre c Publicao (Open Publication Licence) podem escolher certas opes de lica co cena simplesmente incluindo alguns pargrafos aps a cpia da licena ou c a o o c sua referncia. Estas opes so consideradas parte da licena e devem ser e co a c inclu das com ela (ou com a referncia a ela) nos trabalhos derivados. e As opes que se aplicam a este trabalho so: co a vedada a distribuio de verses com modicaes substanciais deste A: E ca o co documento sem a expressa permisso dos proprietrios do direito autoral. a a B: E vedada a distribuio deste trabalho ou qualquer derivado seu em ca qualquer formato de livro padro (papel) sem a prvia autorizao dos proa e ca prietrios do direito autoral. a Pol ticas de Publicaes Livres co (O texto a seguir no considerado parte da licena.) a e c Os trabalhos protegidos pela Licena de Livre Publicao (Open Public ca cation Licence) esto dispon a veis na home page da Open Publication. Os autores de trabalhos protegidos pela Licena de Livre Publicao c ca (Open Publication Licence) podem incluir suas prprias licenas nesses trao c balhos, desde que os termos dessa licena no sejam mais restritrivos que os c a da Licena de Livre Publicao (Open Publication Licence). c ca Em caso de dvidas sobre a Licena de Livre Publicao (Open Publiu c ca cation Licence), contactar David Wiley ou a lista de autores de publicaes co livres via email. Para se inscrever na lista de autores de publicaes livres (Open Publico cation Authors List), mande um email para opal-request@opencontent.org com a palavra subscribe no corpo da mensagem. 165

Para enviar mensagens para a lista de autores de publicaes livres (Open co Publication Authors List), mande um email para opal@opencontent.org ou simplesmente responda a uma mensagem postada. Para se desinscrever na lista de autores de publicaes livres (Open Publico cation Authors List), mande um email para opal-request@opencontent.org com a palavra unsubscribe no corpo da mensagem.

166

Referncias Bibliogrcas e a
[K & R (1989)] KERNIGHAN, B. W. & RITCHIE, D. M.; Traduo de Daca niel Vieira. C, A Linguagem De Programao: Padrao ANSI. Rio de ca Janeiro: Editora Campus,1989.

167