Sinais

É o modo mais simples de comunicação entre processos. Funcionam como interrupções de software.

Cada sinal tem associado a si um handler - uma função que é executada quando um sinal é recebido por um processo. Esse handler pode ser alterado pelo utilizador. Os sinais são utilizados para o tratamento de excepções e de acontecimentos assíncronos.

 signal(SINAL,handler) - associa o handler ao SINAL. Alguns sinais como SIGKILL e SIGSTOP, não podem ser alterados.

kill(pid_do_processo, SINAL) - envia um sinal para um processo.

raise(sinal) - envia um sinal para o próprio processo (também se pode conseguir o mesmo objectivo utilizando a função kill).

pause() - suspende o processo corrente até à chegada de um sinal (assim não "gasta" tempo de CPU, por não estar escalonado).

alarm(n) - envia ao processo invocador um sinal SIGALRM dentro de n segundos, mesmo que este não esteja a ser executado.

 

Sinais mais Utilizados:

SIGSTOP - bloqueia um processo.

SIGCONT - desbloqueia um processo se estiver bloqueado.

SIGKILL - mata um processo.

SIGUSR1 - sinal para ser usado pelo utilizador.

SIGUSR2 - sinal para ser usado pelo utilizador.

SIGINT - interrupção do terminal (gerado pela tecla DEL ou Break)

SIGHUP - terminal desligado.

SIGQUIT - abortar a execução.

SIGILL - instrução ilegal.

SIGTERM - terminação ordenada do processo.

SIGCHLD - sinal que um processo recebe quando um filho morre (através do exit).

SIGALRM - manda o sinal SIGALRM (de alarme) a um processo.

 

Observações:

Ex: signal(SIGALRM,SIG_IGN).

Ex: signal(SIGALRM,SIG_DFL).

 

Exemplo

 

#include <unistd.h>

#include <sys/wait.h>

#include <sys/types.h>

#include <signal.h>

void trata(int sinal)

{

printf("Apanhei o sinal %d \n", sinal);

}

 

main()

{

pid_t pid;

int estado,i;

pid = fork();

if (pid < 0 )

{

printf("Erro!!!\n");

exit (-1);

}

else

if (pid == 0) /* FILHO */

{

printf("Estou ‘a espera de sinais\n");

signal(SIGINT, trata); /* se o filho receber o sinal SIGINT,

executa a funcao trata */

signal(SIGUSR2, trata);/* se o filho receber o sinal SIGUSR2,

executa a funcao trata */

for (i=0;i<3;i++)

pause(); /* o filho fica ‘a espera que cheguem

sinais  */

}

else /* PAI */

{

printf("Vou enviar sinais\n");

sleep(3); /* "Adormece" durante 3 segundos */

kill(pid, SIGUSR2); /* envia ao processo filho o sinal

SIGUSR2 */

sleep(3); /* "Adormece" durante 3 segundos */

kill(pid, SIGINT); /* envia ao processo filho o sinal

SIGINT */

sleep(3); /* "Adormece" durante 3 segundos */

kill(pid, SIGUSR2); /* envia ao processo filho o sinal

SIGUSR2 */

pid = wait(&estado); /* espera que o filho termine */

}

}

 

Exercícios:

1- Considere o seguinte programa:

...

 void funcao()

{

       execlp("prog", "prog", 0);

}

 

void main()

{

                pid_t pid;

                int i;

                pid = fork();

                if (pid == 0)

                               for(i=0; i<5; i++)

                               {

                                               kill(getppid(), SIGUSR1);

                                               sleep(2);

                               }

                else

if (pid >0)

                               {

                                               signal(SIGUSR1,funcao);

                                               for(; ;)

                                                               pause();

                               }

                 

}

 

Quantas vezes é executado o programa prog? Justifique.

 

2 – Implemente um programa que, após criar um novo processo, tem o seguinte comportamento cíclico:

- o processo pai aguarda 2 segundos e envia ao filho um sinal SIGUSR1;

- o processo filho, após a recepção do sinal, imprime "Apanhei o sinal SIGUSR1"
 

3 - Faça um programa que:

 

4 - Faça um programa que simule a função sleep(n).

 

5 - O acesso por parte de vários processos a um mesmo recurso exige sincronização. Implemente um programa que cria um novo processo e sincroniza o
acesso ao  ficheiro "dados.txt" através do uso de sinais.

O processo pai fica à espera que chegue um sinal SIGUSR1; depois de receber o sinal, deve ler do ficheiro "dados.txt" um número; depois de lido esse número, deve remover o conteúdo desse ficheiro e apresentar esse conteúdo no monitor; por fim envia ao filho um sinal SIGUSR1. O processo filho é responsável por escrever um novo número no ficheiro.

Nota: As operações de escrita e leitura devem ser feitas ciclicamente.

 

6 - Tendo um array de 1000 posições, faça um programa que crie 5 processos e:

Nota: O array não tem números repetidos.

 

 7 - Desenvolva um programa que faça o escalonamento de processos por ele criados (em cada instante só um dos programas está a ser executado) com a seguinte sintaxe:

    escalonar programa1 programa2 … programan

Utilize os sinais SIGSTOP e SIGCONT e assuma que cada programa é executado durante 5 segundos consecutivos, dando depois a oportunidade a outro programa de ser executado (ficando assim à espera da sua vez para continuar a execução). Assuma que os programas nunca terminam. Utilize as primitivas de processos, sinais e funções exec.

8 - Considere um array em que cada posição contém o nome de um comando e o tempo máximo de execução. Caso um comando esgote o seu tempo máximo de execução, deverá ser terminado. Implemente um programa que execute sequencialmente todos os comandos contidos no array tendo em conta as funcionalidades descritas anteriormente. Utilize as primitivas de processos, sinais e as funções exec. O array é constituído por estruturas do tipo abaixo:

        typedef struct {

                        char cmd[32];

                        int tempo;

        } comando;

 

 9  -   Uma empresa de software pretende testar a viabilidade da aplicação de um novo algoritmo no processamento de dados para descobrir determinado tipo de informação. Para isso, pretende utilizar esse novo algoritmo numa simulação que utiliza programação paralela/concorrente. A aplicação a desenvolver deve criar 50 processos e testar a funcionalidade do novo algoritmo da seguinte forma: 

·        Cada processo começa por executar a função simula1(). Essa função retorna Verdade se encontrou os dados e retorna Falso se não encontrou os dados. Quando termina de executar a função, cada processo deve enviar o sinal SIGUSR1 se a função teve sucesso (ou o sinal SIGUSR2 se não teve sucesso) para o processo que faz a gestão da simulação. Depois deve esperar que o processo gestor lhe envie um sinal para começar a função simula2().

·        O processo pai deverá gerir a simulação, efectuando o seguinte teste após
terem terminado 25 processos:

·        Se até esse momento nenhuma das pesquisas tiver sido efectuada com sucesso o processo pai deverá imprimir no ecrã "Algoritmo ineficiente!" e terminar os
restantes processos filho que ainda se encontram em execução.
 

·        Se até esse momento existir pelo menos um processo que tenha efectuado uma
pesquisa com sucesso, o processo pai deverá enviar um sinal para os processos
que ainda se encontram em execução, indicando que estes devem começar a
executar a função simula2(), interrompendo a execução da função simula1().

         Nota: utilize números aleatórios na implementação das funções simula1() e simula2().

 

10 - Para optimizar o tempo de resposta aos clientes de uma empresa de assistência automóvel, foi encarregado de desenvolver uma aplicação que utilize dez processos para pesquisar na base de dados a oficina da região onde um determinado cliente se encontra. A aplicação deve perguntar o nome da região onde o cliente se encontra. Quando um dos processos encontrar a região deve  escrever o nome da oficina e o respectivo número de telefone. Se a região já foi encontrada, o processo que está a gerir os outros 10 processos, deve terminar a execução dos que ainda não terminaram.

Nota: Considere que a base de dados contém 10000 posições e que cada posição é do tipo:

 

typedef struct {

            char nome_oficina[50];

            char regiao[50];

            int telefone;

} registo;