Abaixo, você encontrá dicas para lhe ajudar a fazer o 1º
trabalho de SOI, que está descrito na página do curso.
Lembrando que essas dicas visam apenas o auxilio nas partes
mais problemáticas do trabalho e não pretendem substituir
outras fontes de consulta. Por isso, é extremamente
recomendado que os grupos consultem as páginas do manual
do linux (através do comando "Man") além de livros e sites
relacionados ao assunto.
Também é bom lembrar, que seu feedback será fundamental
para a inserção de novas dicas ou o melhoramentos das
existentes. Portanto, se você tiver alguma dúvida ou sugestão,
basta entrar em contato comigo.
•
Linux
Para conhecer a descrição de comando do linux, seus
parâmetros, valores de retorno e todas as informações
necessárias para utilizar as funções que usaremos no trabalho,
podemos usar o comando "man",que nos dá todas as
informações de qualquer comando válido do linux. Para usá-lo,
basta digitar man "nome do comando".
Para o primeiro trabalho, recomendo a leitura das seguintes
páginas do manual:
•
•
•
•
•
man
man
man
man
man
fork
pthread_create
pthread_join
wait
sleep
Estes são os principais comandos e conhecê-los é o primeiro
passo para desenvolver seu trabalho.
Para os que programam em C no windows e não tem
experiência em ambiente linux, é bom lembrar que biblioteca
conio.h não faz parte das bibliotecas padrão do C ANSI e por
isso não estão presentes no linux. Portanto, as funções
cprintf(), clrscr() e todos as outras desta biblioteca não estão
disponíveis. Para limpar a tela em seus programas, você pode
usar o comando "system("clear");" e, para usar cores, você
deverá usar as sequências de escape. Como se trata de algo
"extra" que não influencia no funcionamento do programa, irei
colocar apenas um link para um bom tutorial sobre o assunto:
tutorial.
Para ter acesso a uma lista com comandos básicos do linux,
basta clicar aqui.
•
Fork
Mesmo sendo um comando extremamente simples, o fork pode
ser perigoso, já que com um pequeno descuido o seu
programa pode criar mais processos do que deveria e causar
problemas. Para evitar isso, fique atento ao retorno da função
fork e só execute outro fork se tiver certeza que função está
sendo invocada no processo pai.
Para viabilizar este controle, a função fork() retorna 3 tipos de
valores distintos que devem ser testados em seu programa, os
valores são: 0 para o processo filho recém-criado, PID do filho
para o processo pai (inteiro e maior que zero ) e -1 em caso de
erro. Com isso, basta apenas um teste condicional para você
ter certeza sobre qual processo está sendo executado, como é
mostrado abaixo:
retorno= fork();
if(retorno ==0)
{
printf("Este é o processo filho");
}
else if(retorno>0)
{
printf("Este é o processo pai") ;
}
else
{
printf("Ocorreu um erro");
}
•
Threads
Ao contrário do fork(), os threads oferecem um meio mais
seguro para sua invocação, já que o método que será
executado no thread é um dos argumentos passados para
função o cria. Esta função se chama pthread_create e faz parte
da biblioteca "pthread.h", que como foi dito anteriormente, ela
está descrita no manual do linux.
Ao manusear esta função, é importante notar que os
parametros devem ser passados por referência e que deve der
feito um cast através da expressão: (void *). Como no
exemplo abaixo:
pthread_create(&tid_1, NULL, (void*) &thread_main, (void
*)argumento);
Neste trecho, tid_1 é uma variável do tipo "pthread_t",
"thread_main" é a função que será executada pela thread e
"argumento" é o argumento que será passado para a função,
mesmo ele sendo um inteiro ou qualquer outro tipo válido, o
cast deve ser sempre feito.
Além disso, para evitar erros de compilação, quando compilar o
arquivo com as threads, use o argumento "-lpthread" no
momento da compilação, como mostrado abaixo:
gcc -o trabalho.e principal.c -lpthread
•
Memória Compartilhada
Criação
Para criar o segmento de memória compartilhada, usaremos a
chamada shmget que tem a seguinte sintaxe:
int shmget(key_t key, int size, int shmflg);
Onde Key é um identificador que será usado para gerar o
shmid (shared memory id) que é um handler que será o
identificador deste segmento no SO. Size é o tamanho do
segmento que será compartilhado e shmflg um conjunto de
parametros que, de acordo com a utilização, fará com que se
crie um novo segmento de memória associado a key ou tentará
obter o handler de um segmento criado anteriormente.
Ex.:
memo_comp = shmget (chave_memo, sizeof (T_SHM), (0666
| IPC_CREAT | IPC_EXCL))
Na linha de códico acima, memo_comp será uma inteiro que
receberá o handler para manipular o segmento de memoria
associado a chave chave_memo. Logo depois, devemos
especificar o tamanho do segmento de memória que será
compartilhado. Em nosso exemplo será o tamanho da
estrutura(struct) T_SHM que é um struct contendo as variáveis
1 e 2.
Como podemos ver, temos 3 parametros em shmflg:
IPC_CREAT - Especifica que deve se criar um novo segmento
de memória.
IPC_EXCL - Usado para garantir que a função irá falhar caso o
segmento já esteja alocado.
0666 - indica as permisões do segmento de memória criado.
Neste exemplo, todos tem premisão de leitura e escrita no
segmento criado.
Existe um problema, pois não podemos ter 2 segmentos de
memória associados a uma mesma chave e por isso devemos
tomar cuidados para não utilizar valores óbvios como 123 ou
deixar a variável sem ser inicializada. Para resolver este
problema, existe uma função chamada ftok, cuja sintaxe é:
key_t ftok ( char *pathname, char proj )
O primeiro parametro é nome de um arquivo que deve estar
presente no disco e o segundo um caracter que identifica o
projeto onde será usado o segmento. Ex:
chave_memo_comp=ftok("/home/rodrigos/makefile",'a');
if(chave_memo_comp==-1)
perror("Ftok");
memo_comp = shmget(chave_memo_comp, sizeof(buffer),
(0666|IPC_CREAT|IPC_EXCL));
Associando o segmento a um ponteiro
Após a criação do segmento de memória, devemos anexar o
segmento a um ponteiro para viabilizar o acesso a esta
variável como se fosse uma variável local. Para isso, temos a
primitiva shmat (shared memory attach) que associa um
ponteiro a um segmento de memória compartilhada. A sintaxe
desta chamada é:
void *shmat(int shmid, const void *shmaddr, int shmflg);
A função anexa o ponteiro ao segemento de memória (
incrementando shm_nattach que é um dos flags internos do
segmento ) e retorna um ponteiro para o início do segmento
anexado. Exemplo:
shmem = shmat (memo_comp, NULL, 0666);
Onde shmem é um ponteiro para uma estrutura que será a
memória compartilhada.
Para acessar a memória compartilhada através do ponteiro
shmem, usaremos a mesma sintaxe de acesso a um struct
para uma estrutura local, por exemplo:
hmem->buffer[1];
No trecho acima estamos acessando a posição 1 do buffer
definido na memória compartilhada.
•
Makefile
Muito usado em ambientes Unix, o makefile é uma poderosa
ferramenta para a criação de grandes projetos de
programação. Através de um arquivo de texto, é possível
determinar um "roteiro" de compilação e link edição de vários
arquivos, facilitando muito o processo de desenvolvimento e
debug de grandes projetos. Em termos de implementação, o
makefile é bem simples para compilar poucos arquivos ( o caso
do nosso trabalho ) e existe muita documentação em
português sobre o assunto na internet. Lembrando que deve se
tomar cuidado com espaços e tabs, ele costuma ser muito
rigoroso neste aspecto e qualquer pequeno detalhe ira gerar
erros.
Download

Trabalho 1 - Dicas