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.