Mostrando postagens com marcador Linux. Mostrar todas as postagens
Mostrando postagens com marcador Linux. Mostrar todas as postagens

quarta-feira, 13 de fevereiro de 2008

Linux - Programação C - Comunicação entre Processos

Comunicação entre processos

(IPC - Interprocess Communication)

Por Ricardo Lima Caratti

Implementando o Jogo da Velha utilizando Semáforo e Memória Compartilhada


É muito comum no mundo do desenvolvimento de aplicações, deparar-se com a necessidade de dois ou mais processos se comunicam entre si. Isso ocorre quando se deseja principalmente: Compartilhar informações pertinentes a dois ou mais processos, dividir sistemas complexos em módulos menores e aumentar a velocidade de processamento utilizando-se arquiteturas de hardware paralelas.

O Linux, bem como em outros sistemas operacionais, fornecem mecanismos que facilitam a comunicação entre processos. São eles: Pipes, Filas, Semáforos e Memória Compartilhada. Dado a extensão desse assunto, será visto aqui somente a utilização de Semáforos e de Memória compartilhada. Para ilustrar o uso de comunicação entre processos, será implementado o jogo da velha, onde duas cópias do mesmo processo compartilharão uma mesma região de memória. Mais informações sobre o jogo serão descritas a seguir.

Comunicando Processos por meio de Semáforo e Memória Compartilhada

A figura 1 mostra o diagrama de comunicação entre dois processos usando memória compartilhada e semáforo. Note que a função do semáforo é controlar o acesso a memória de tal forma que somente um processo de cada vez tenha acesso.

Figura 1 – Os processos A e B compartilham informações contidas na memória. O semáforo controla acesso a memória.

Memória Compartilhada

Esse método consiste em fazer uso de uma área reservada de memória RAM onde os processos poderão trocar informações. Basicamente utiliza-se referências a uma região de memória em cada processo. Portanto, é a forma mais rápida de Comunicação entre Processos. A configuração para uso de memória compartilhada no Linux será vista mais adiante.

Semáforos

Na vida cotidiana, é possível observar a aplicação de semáforos nos cruzamentos de vias com alto fluxo de veículos. Nesse caso, o semáforo tem a função de controlar o fluxo de tal forma que todas a vias tenham condições de atender suas demandas. De forma similar em computação, semáforos são usados para gerenciar ações entres processos, reservando para cada um, o direito de ter acesso em um determinado momento a uma área de troca de informações.

Configurando o Linux para usar memória compartilhada para o programa Jogo da Velha.

Utilize um editor de texto e altere o arquivo lilo.conf existente no diretório /etc segundo mostrado a seguir em negrito:

image = /vmlinuz

append=”mem=XXm”

root=/dev/hda

label = linux

XX é a posição de memória que será reservada para compartilhamento, ou seja, se você tiver 32 MB de RAM XX deve ser igual a 31, indicado que o último MB de memória será reservado. Da mesma forma, se seu computador tiver 64 MB de RAM, utilize XX igual a 63.

Somente a linha em negrito deve ser incluída no arquivo. As demais dependerão da sua instalação, distribuição, versão do kernel e outras configurações. Portanto, não necessariamente serão iguais as apresentadas acima. Após a alteração do arquivo lilo.conf, execute o comando lilo a partir do prompt do shell. Em seguida o sistema deve ser reiniciado.

O programa Jogo da Velha

Para fazer demonstração do uso de Memória Compartilhada, foi desenvolvido o programa Jogo da Velha. Esse programa faz uso do segmento de memória compartilhada onde conterá as informações importantes do jogo. Você poderá jogar usando duas consoles ou terminais no KDE e executar o mesmo jogo em cada um. Idealmente, seria melhor executar o programa em máquinas distintas, ou seja, por meio de um telnet você poderia se conectar na máquina que contem o jogo e executar. O seu adversário poderia fazer o mesmo em um outro computador e também executar o mesmo jogo. Com isso, o programa automaticamente identificará que o jogo deu inicio e apresentará a seguinte tela:

Jogador1 x Jogador2

1 2 3

| |

1 | |

--------------

| |

2 | |

--------------

| |

3 | |

Entre com a Linha e a Coluna:

Para fazer uma jogada válida, você deve entrar com os valores de linha e coluna da seguinte forma:

11 se quiser marcar o quadro superior esquerdo, 22 se quiser marcar o quadro central. A figura abaixo mostra uma jogada onde o jogador marcou a linha 2 coluna 1.

1 2 3

| |

1 | |

--------------

X | |

2 X | |

--------------

| |

3 | |

Quando um jogador marcar uma linha ou uma coluna completa ou ainda preencher uma das diagonais, o jogo terminará dando a ele a vitória.

Fontes

O programa jogo da velha consiste em três arquivos fontes. semaforo.c, semaforo.h e JogoDaVelha.c. Semaforo.c é uma pequena biblioteca de funções para manipulação de semáforo. Os trechos considerados relevantes para compreensão do programa estão em negrito e serão comentados mais adiante. Em semaforo.h estão declaradas essas funções que serão usadas em JogoDaVelha.c.

Arquivo semaforo.h

int ObtemValor(int sid, int member);

int AbreSemaforo(int *sid, key_t key);

int CriaSemaforo(int *sid, key_t key, int members);

int LockSemaforo( int sid, int member );

int UnLockSemaforo( int sid, int member);

void RemoveSemaforo(int sid) ;

void RemoveSemaforo(int sid) ;

Arquivo semaforo.c

#include

union semun {

int val;

struct semid_ds *buf;

unsigned short int *array;

struct seminfo *__buf;

};

// Obtém o valor do semáforo

int ObtemValor(int sid, int member)

{

return ( semctl(sid,member, GETVAL,0) );

}

// Atribui um valor ao semáforo

int AtribuiValor(int sid, int member, int valor )

{

union semun semopts;

semopts.val = valor;

return (semctl(sid,member,SETVAL, semopts));

}

// Abre um semáforo; retorna -1 se não tiver sucesso.

int AbreSemaforo(int *sid, key_t key)

{

return ( (*sid = semget(key,0,0666)) == -1)? -1: *sid;

}

// Cria um semáforo; retorna -1 se não tiver sucesso.

int CriaSemaforo(int *sid, key_t key, int members)

{

int i;

union semun semopts;

if ((*sid=semget(key,members,IPC_CREAT|IPC_EXCL|0666))==-1)

return -1;

else {

semopts.val = 1;

for ( i= 0; i <>

semctl(*sid,i,SETVAL,semopts);

return *sid;

}

}

// Prende o semáforo ate a utilização de UnLockSemaforo, caso

// esteja em uso, espera ate ser liberado; retorna -1 se erro.

int LockSemaforo( int sid, int member )

{

struct sembuf sem_lock = {0,-1,0};

sem_lock.sem_num = member;

return ( (semop(sid,&sem_lock,1) ) == -1)? -1:(sid);

}

// Libera o semáforo permitido sua utilização; retorna -1 se erro

int UnLockSemaforo( int sid, int member)

{

struct sembuf sem_unlock = {member,1,0};

int semval;

sem_unlock.sem_num = member;

return ((semop(sid,&sem_unlock,1) ) == -1)? -1:(sid);

}

// Remove o semáforo

void RemoveSemaforo(int sid)

{

semctl(sid,0,IPC_RMID,0);

}

Arquivo JogoDaVelha.c

#include

#include

#include

#include "semáforo.h"

#define ENDERECO (63*0x100000)

struct jv {

int Jogador;

char Nome[2][20];

unsigned char Matr[3][3];

};

const char marca[] = { 'X','O'};

// Limpa a matriz do Jogo

void Limpa( unsigned char Matr[3][3] )

{

int i,j;

for ( i = 0; i <>

for (j = 0; j <>

Matr[i][j] = ' ';

}

// Verifica se existe alguma linha preenchida

int Linha(unsigned char Matr[3][3], int Jogador)

{

int i,j,k;

for ( i = 0; i <>

k = 0;

for ( j = 0; j <>

if (Matr[i][j]==marca[Jogador]) k++;

if ( k == 3 ) return 1; // Verdade

}

return 0; // Falso

}

// Verifica se existe alguma coluna preenchida

int Coluna( unsigned char Matr[3][3], int Jogador )

{

int i,j,k;

for ( i = 0; i <>

k = 0;

for ( j = 0; j <>

if (Matr[j][i]==marca[Jogador]) k++;

if ( k == 3 ) return 1; // Verdade

}

return 0; // Falso

}

// Verifica se Existe alguma diagonal preenchida

int Diagonal(unsigned char Matr[3][3],int Jogador)

{

return (Matr[0][0] == marca[Jogador] &&

Matr[1][1] == marca[Jogador] &&

Matr[2][2] == marca[Jogador]) ||

(Matr[0][2] == marca[Jogador] &&

Matr[1][1] == marca[Jogador] &&

Matr[2][0] == marca[Jogador]);

}

// Mostra a matriz do Jogo da Velha

void Mostra ( struct jv *jdv )

{

printf("\n%20s x %20s\n",jdv->Nome[0], jdv->Nome[1]);

printf("\n\n 1 2 3 ");

printf("\n1 %c | %c | %c ",jdv->Matr[0][0], jdv->Matr[0][1], jdv->Matr[0][2]);

printf("\n %c | %c | %c ",jdv->Matr[0][0], jdv->Matr[0][1], jdv->Matr[0][2]);

printf("\n -------------");

printf("\n2 %c | %c | %c ",jdv->Matr[1][0], jdv->Matr[1][1], jdv->Matr[1][2]);

printf("\n %c | %c | %c ",jdv->Matr[1][0], jdv->Matr[1][1], jdv->Matr[1][2]);

printf("\n -------------");

printf("\n3 %c | %c | %c ",jdv->Matr[2][0], jdv->Matr[2][1], jdv->Matr[2][2]);

printf("\n %c | %c | %c ",jdv->Matr[2][0], jdv->Matr[2][1], jdv->Matr[2][2]);

printf("\n");

}

// Programa Principal

int main()

{

int fd, id, Lin, Col, idx, Jogador;

char sCmd[4];

key_t chave_unica;

struct jv *JogoDaVelha;

// Cria ou abre um semáforo

chave_unica = ftok("/",'c');

if((id=AbreSemaforo(&id,chave_unica))==-1){

if((id=CriaSemaforo(&id,chave_unica,2)) ==-1){

AtribuiValor(id,0,1);

printf("\nNao foi possivel iniciar o jogo\n");

exit(2);

}

Jogador = 0;

printf("Você e o Jogador 1");

printf("\nAguardando outro jogador\n");

}

else {

Jogador = 1;

printf("\nVocê e o Jogador 2");

printf("\nJogo Iniciado\n");

}

// Abre device para manipular memória compartilhada

if ((fd = open("/dev/mem",O_RDWR)) <>

printf("\nNao foi possivel abrir mem. compartilhada!");

exit(1);

}

// Faz ponteiro da estrutura do jogo da velha apontar para memória comp.

JogoDaVelha = (struct jv *) mmap(0,sizeof(struct jv),PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, ENDERECO);

if ( Jogador == 0 )

JogoDaVelha->Jogador = 0;

printf("\nEntre com o seu nome: ");

fgets(JogoDaVelha->Nome[Jogador],20,stdin);

Limpa(JogoDaVelha->Matr);

while ( 1 )

{

LockSemaforo(id,1); // Segura a sua vez de jogar

if (ObtemValor(id,1) == -1) {

Mostra(JogoDaVelha);

printf("\nFim do Jogo.\nVoce Perdeu!\n");

return 0;

}

Mostra(JogoDaVelha);

// Jogada. Não permite uma jogada invalida

do {

printf("\nEntre com a Linha e a Coluna (exemplo: 11): ");

fgets(sCmd,3, stdin);

Lin = sCmd[0] - 48; // Converte valor da Linha para inteiro

Col = sCmd[1] - 48; // Converte valor da coluna para inteiro

if ( (Lin <> 3) || ( Col <>Col > 3 ) )

printf("\nEntrada Invalida!");

else {

Lin--;

Col--;

if ( JogoDaVelha->Matr[Lin][Col] != ' ' )

printf("\nJogada ha Efetuada. Tente Outra!");

else

break;

}

} while (1);

idx = JogoDaVelha->Jogador = Jogador; // Jogador da vez

JogoDaVelha->Matr[Lin][Col] = marca[idx]; // X ou O

// Verifica se houve vencedor

if(Linha(JogoDaVelha->Matr, JogoDaVelha->Jogador ) ||

Coluna(JogoDaVelha->Matr, JogoDaVelha->Jogador ) ||

Diagonal(JogoDaVelha->Matr, JogoDaVelha->Jogador) ) {

Mostra(JogoDaVelha);

printf("\nFim do Jogo\nVoce Venceu\n");

RemoveSemaforo(id);

return 0;

}

Mostra(JogoDaVelha);

UnLockSemaforo(id,1); // Libera para o outro jogar

sleep(2);

}

}

Comentários sobre os programas

As chamadas as funções do kernel bem como as funções padrões do C são ricamente comentadas nas documentações existentes no próprio Linux. Para saber sobre a função mmap por exemplo, basta executar o comando man mmap, da mesma forma, para saber mais sobre a função printf utilize o comando man printf. Portanto, essas funções não serão comentadas aqui.

Arquivo semaforo.c

union semun {

int val;

struct semid_ds *buf;

unsigned short int *array;

struct seminfo *__buf;

};

Essa estrutura é a utilizada pelas funções de chamadas ao kernel para manipular semáforos.

semctl

Essa função, dependendo dos seus argumentos, poderá ter várias utilidades. Aqui ela é usada praticamente para Ler um valor do semáforo ou para gravar um valor. Veja mais executando o comando man semctl.

semget

Essa função é usada para Criar ou abrir um semáforo dependendo dos seus argumentos. Veja mais executando o comando man semget.

semop

Essa função é utilizada para fazer operações com o semáforo. É utilizada para segurar ou liberar um recurso. Veja mais com man semop.

Arquivo JogoDaVelha.c

#define ENDERECO (63*0x100000)

define a posição de memória que será mapeada pela função mmap.

struct jv {

int Jogador;

char Nome[2][20];

unsigned char Matr[3][3];

};

Essa e a estrutura necessária para conter as informações do jogo. Jogador conterá 0 ou 1 dependendo de quem for a vez de jogar, Nome[2][20] conterá os nomes dos jogadores e Matr[3][3] conterá a matriz do jogo da velha.

const char marca[] = { 'X','O'};

Essa constante contem a marca de cada jogador, ou seja se for o jogador 0, será X, se for o jogador 1 será O.

chave_unica = ftok("/",'c');

A variável chave_unica conterá um identificador único obtido pela função ftok. Isso é necessário para não haver conflito com outros programas que usam memória compartilhada. Veja mais sobre ftok usando man ftok.

if((id=AbreSemaforo(&id,chave_unica))==-1){

if((id=CriaSemaforo(&id,chave_unica,2)) ==-1){

AtribuiValor(id,0,1);

printf("\nNao foi possivel iniciar o jogo\n");

exit(2);

}

Jogador = 0;

printf("Voce e o Jogador 1");

printf("\nAguardando outro jogador\n");

}

else

{

Jogador = 1;

printf("\nVoce e o Jogador 2");

printf("\nJogo Iniciado\n");

}

No trecho acima, a função AbreSemaforo tenta abrir o semáforo. Caso não consiga, ou seja retorne –1, então é assumido que o semáforo ainda não foi criado. Nesse caso a função CriaSemaforo entra em ação e cria um semáforo. Caso a função CriaSemaforo falhe, ou seja, retorne -1, o programa sair imediatamente informado que houve uma falha. Nesse caso o jogo não prossegue.

if ((fd = open("/dev/mem",O_RDWR)) <>

printf("\nNao foi possivel abrir mem. compartilhada!");

exit(1);

}

Nesse trecho, é usada a chamada ao kernel open para abrir um dispositivo de manipulação memória.

JogoDaVelha = (struct jv *) mmap(0,sizeof(struct jv), PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, ENDERECO);

O trecho anterior, usa a função mmap para manipular o segmento de memória compartilhada reservada pelo sistema. Note que ela usa a variável fp que recebeu um descritor por meio da função open. Veja mais sobre mmap usando man mmap.

LockSemaforo(id,1); // Segura a sua vez de jogar

O código acima, chama a função LockSemaforo implementada em semáforo.c e que na realidade é uma abstração para a função semop. Sua função é prender o recurso de tal forma que o outro jogador não possa jogar.

if(Linha(JogoDaVelha->Matr, JogoDaVelha->Jogador ) ||

Coluna(JogoDaVelha->Matr, JogoDaVelha->Jogador ) ||

Diagonal(JogoDaVelha->Matr, JogoDaVelha->Jogador) ) {

Mostra(JogoDaVelha);

printf("\nFim do Jogo\nVoce Venceu\n");

RemoveSemaforo(id);

return 0;

}

O trecho acima chamas as funções auxiliares Linha, Coluna e Diagonal, implementadas no arquivo principal, JogoDaVelha.c, para testar se o jogador conseguio preencher um linha, uma coluna ou uma diagonal. Caso afirmativo, o jogo e encerrado dando a vitória para o jogador da vez.

UnLockSemaforo(id,1); // Libera para o outro jogar

Ao contrário de LockSemaforo, a função UnLockSemaforo dá a oportunidade ao outro jogador. Ela também é uma abstração para a função semop.

Compilação do programa

Utilize seqüência de comandos a seguir para compilar e gerar o programa executável:

gcc –c semaforo.c

gcc –o jogodavelha jogodavelha.c semaforo.o

Conclusão

O Jogo da Velha foi só um exemplo de como usar memória compartilhada e semáforos no Linux. Essa técnica poderá trazer grandes benefícios em desenvolvimento de aplicações profissionais. Principalmente quando há a necessidade de criar módulos ou programas independentes que tenham a necessidade de interação. Dentre as formas de comunicação entre processo mencionadas no início deste artigo, Memória Compartilhada se destaca pela sua facilidade em usar e pela sua notável performance.


Referências Bibliográficas

· GOLDT, SEVEN.The Linux Programmer´s Guide,Version 0.4,1997(Existente no formato PDF e distribuído no Cd da RevistaDoLinux Nº 4).

· WALL, WATSON, AND WHITIS. Linux Programming. SAMS, 1999;

· TANENBAUM, ANDREW S. Operating Systems Design and Implementation. Prentice-Hall, 1987;

· STEVENS, W. RICHARD. Unix Network Programming. Volume 2, 2ª ed. , Prentice Hall, 1999;

· BACH, MAURICE J. The Design of the Unix Operating System. Prentice Hall, 1986;


sexta-feira, 8 de fevereiro de 2008

Linux - Programação C _ Bibliotecas Compartilhadas

PROGRAMAÇÃO

DESENVOLVENDO BIBLIOTECAS COMPARTILHADAS EM LINUX.

Por Ricardo Lima Caratti

Em programação, entende-se por bibliotecas, arquivos que contêm um conjunto de módulos ou membros de códigos pré-compilados reutilizáveis. Esses códigos podem ser usados por vários programas sem a necessidade de detalhes de sua implementação. A grande vantagem de usar uma biblioteca, é que uma vez fabricada, não será mais preciso compilar, bastando simplesmente liga-la ao programa desejado. Dessa forma, existe uma grande vantagem em usar bibliotecas, pois uma vez implementada ou adquirida de terceiros, o desenvolvedor pode se abstrair dos detalhes e concentrar-se somente no problema principal.

A figura abaixo esboça o uso de uma biblioteca




Note que “Meu Programa” necessitou usar uma função X e um procedimento Y. Entende-se por função, um procedimento ou uma rotina que retorna um valor. Sabendo que essas rotinas existem na biblioteca “Biblioteca”, para usa-las, basta fazer referência à biblioteca durante a compilação do programa principal.

Em Linux, pode-se desenvolver dois tipos de bibliotecas. A biblioteca de ligação estática e biblioteca de ligação dinâmica. As bibliotecas estáticas são ligadas ao programa e fazem parte do arquivo executável. Já as bibliotecas dinâmicas são ligadas em tempo de execução, ou seja, a ligação ocorre por demanda. Portanto, não fazem parte do programa principal, reduzindo assim, o tamanho do arquivo executável.

Optar pelo uso de bibliotecas estáticas ou dinâmicas depende muito do que se pretende. Em geral, pode-se obter o mesmo resultado usando uma ou outra técnica. Bibliotecas estáticas deixam o código executável mais livre da configuração do ambiente, ou seja, todo o código que o programa precisa para ser executado já se encontra no próprio executável. Ao contrário, quando se faz uso de bibliotecas compartilhadas, o programa é dividido em um módulo principal e em um ou mais módulos que serão ligados dinamicamente. Considerando que bibliotecas compartilhadas podem ser usadas por mais de um programa ao mesmo tempo, elas ocupam menos memória RAM, menos espaço em disco, menos recurso do sistema e finalmente fica mais simples fazer manutenção.

Resumindo, bibliotecas são arquivos que contêm módulos reutilizáveis pré-compilados que serão usados por desenvolvedores de aplicações. Elas podem ser classificadas em estáticas e compartilhadas. Ao optar por bibliotecas estáticas, ela passará a fazer parte do corpo do programa principal, liberando-o assim do ambiente de configuração do sistema. Ao contrário, bibliotecas compartilhadas ligam-se ao programa principal dinamicamente, ou seja, um módulo só será ligado ao programa principal se for solicitado. Com isso, bibliotecas compartilhadas consomem menos recursos do sistema operacional além de facilitarem a substituição de módulos defeituosos sem a necessidade de compilar novamente todos os outros módulos ou sistemas envolvidos.

Para ilustrar o desenvolvimento de bibliotecas compartilhadas, será utilizado o ambiente de desenvolvimento na linguagem C que vem com a própria distribuição Linux.

Será criado um procedimento que mostrará o resultado da soma de dois valores e uma função que dirá qual o maior valor.

Note pelo fonte a seguir, que não existe nada incomum no desenvolvimento das rotinas.

Fonte: MinhaLib.c


#include
#include
#include

/*

Dados dois argumentos inteiros, a e b, imprime a soma.

*/

void MinhaProcSoma( int a, int b)
{


int c;

c = a + b;

printf("\n a + b = %d \n", c);
}

/*

Dado dois argumentos inteiros, a e b, retorna o maior valor

*/

int MinhaFuncMaiorValor( int a, int b)

{

if ( a >= b)

return a;

else

return b;

}



É preciso agora tornar a função e o procedimento disponíveis para qualquer programa. Para tanto, é preciso compila-lo utilizando a seguinte linha de comando:

gcc -fPIC -c MinhaLib.c

Para obter mais detalhes sobre as opções de compilação do C da GNU utilize o comando man gcc. Um manual completo das opções do compilador será mostrado.


Após a compilação, um arquivo MinhaLib.o será gerado.

Até agora, tudo que se tem é um módulo compilado com um procedimento e uma função. Nessa forma, é possível utilizar MinhaLib.o como uma biblioteca estática. Bastando para isso, liga-la ao programa principal da seguinte forma:

gcc –o ProgPrincipal ProgPrincipal.c MinhaLib.o

Para fazer uso dos benefícios de bibliotecas compartilhadas, é preciso ir um pouco mais além. Para tanto, é preciso usar a seguinte linha de comando:

gcc -shared -Wl,-soname,libMinhaLib.so.1 -o libMinhaLib.so.1.0 MinhaLib.o

Apesar de não ser obrigatório iniciar o nome de uma biblioteca compartilhada com lib, é muito desejável, já que é o padrão de algumas ferramentas do Linux como veremos mais adiante. Pelo comando acima, será criada uma biblioteca com o nome de libMinhaLib.so.1.0. O sistema operacional irá reconhecê-la pelo nome de libMinhaLib.so.1. Isso também não é obrigatório, mas é bom usar esse padrão para ficar de acordo com as normas de desenvolvimento do Linux.

Na realidade o Linux usa o seguinte padrão para bibliotecas compartilhadas:

libaaa.so.b.c.ddd

onde:

libaaa.so é o nome da biblioteca;

b é um número que indicará a maior versão;

c é um número que indicará a menor versão;

ddd é um número que informa a “release”.

Uma vez desenvolvida as rotinas e criada a biblioteca compartilhada, é preciso publicá-la. Para tanto será preciso escolher um diretório onde suas bibliotecas ficarão. De preferência utilize /usr/local/lib. Utilizando um editor de texto de sua preferência, altere o arquivo /etc/ld.so.conf adicionando na última linha o diretório /usr/local/lib.

Mova a biblioteca compartilhada libMinhaLib.so.1.o para /usr/local/lib

mv libMinhaLib.so.1.o /usr/local/lib


em seguida execute o comando:


/sbin/ldconfig

o utilitário ldconf irá a todos os diretórios existentes referenciados em ld.so.conf e carregará todas as bibliotecas compartilhadas que iniciarem com lib, criando também um link que de acordo com o exemplo será libMinhaLib.so.1. Também será criada uma referência para cada biblioteca compartilhada em /etc/ld.so.cache. Isso permitirá que ela seja carregada toda vez que o sistema iniciar.

Após executar ldconfig, verifique no diretório /usr/local/lib os sequintes arquivos:


libMinhaLib.so.1 (Link criado pelo ldconfig);
libMinhaLib.so.1.o a biblioteca criada por você.


Testando a biblioteca:

Programa de Teste (TestaProc.c):

#include

#include

int main()
{
void *descritor;
void (*PonteiroParaMinhaProcSoma)( int a, int b);

int (*PonteiroParaMinhaFuncMaiorValor( int a, int b);

int x, y, MaiorValor;

x = 10; /* Somente para exemplo */
y = 11; /* Somente para exemplo */

/* Abre a biblioteca compartilhada */
if ( descritor = dlopen("MinhaLib.so.1.0",RTLD_LAZY))
{
PonteiroParaMinhaProcSoma = dlsym(descritor,"MinhaProcSomal");

PonteiroParaMinhaFuncMaiorValor = dlsym(descritor,”MinhaFuncMaiorValor”);

/* Chamada a MinhaProcSoma */

(*PonteiroParaMinhaProcSoma)( 2, 4 );
(*PonteiroParaMinhaProcSoma)( x, y );

/* Chamada a MinhaFuncMaiorValor */

MaiorValor = (*PonteiroParaMinhaFuncMaiorValor)(x,y);

printf(“\nO Maior valor é: %d”, MaiorValor);

}
else

{
printf("\nErro ao tentar usar a biblioteca dinâmica\n");

printf(“\n%s\n”,dlerror() );

return 0;
}

Observações:

#include

Declarações das funções para manipulação de bibliotecas dinâmicas

dlopen(const char*filename, int flag)

Carrega uma biblioteca Compartilhada

void * dlsym(void *handle, char *symbol)

Obtem o endereço do procedimento dentro
da biblioteca compartilhada

const char *dlerror()

Retorna uma string contendo o erro ou NULL se tudo OK

int dlclose(void *handle)

Fecha a biblioteca


Compile esse programa com:

gcc -o TesteProc TesteProc.c -ldl

Para executar faça:

./TestaProc

O exemplo mostrará o seguinte resultado:

a + b = 6
a + b = 21

O Maior valor é: 11

Para efeito prático, altere o procedimento MinhaProcSoma em MinhaLib.c da seguinte forma:

c = b - a;

printf("\n b - a = %d \n", c);

Execute os passos a seguir:

gcc -fPIC -c MinhaLib.c

gcc -shared -Wl,-soname,libMinhaLib.so.1 -o libMinhaLib.so.1.0 MinhaLib.o

mv libMinhaLib.so.1.o /usr/local/lib

Execute novamente o programa TestaProc da seguinte forma:

./TestaProc

Resultado:

b - a = 2
b - a = 1

O Maior valor é: 11

Note que não foi preciso compilar novamente o programa TestaProc. Nesse caso, a ligação à biblioteca alterada MinhaLib, foi efetuada em tempo de execução.

Concluindo, o Linux bem como outros ambientes Unix facilitam o uso e o desenvolvimento de bibliotecas compartilhadas. Dentre as vantagens em usar essas bibliotecas, destaca-se a facilidade de manutenção de sistemas, considerando é claro, que para tanto, basta trocar o módulo defeituoso sem a necessidade de re-compilar todos os outros módulos que o compõe.


Referências Bibliograficas:

WALL, WATSON, AND WHITIS. Linux Programming. SAMS, 1999

BARKAKATI, NABA. Red Hat Linux Secrets, Info World – IDG Books, 2nd Edition, 1999