SO1 – modelo de programação unix, processos, sinais, redireccionamento

[so1t]

modelo de programação unix, processos, sinais, redireccionamento

Fundamentos de sistemas operativos, 3º edição, capítulos 6 e 11
Begining Linux programming, capítulos 10, 11 e 12

um programa:
é um conjunto de instruções, que descrevem como o computador deve proceder para cumprir um determinado algoritmo
um programa pode ser executado em diferentes processos e em simultâneo

um processo (figSO1_10):


é um ambiente de trabalho com recursos para que um programa seja executado
um processo pode faz parte sempre de um programa, mas pode mudar para outro programa

um processo tem como recursos:
atributos
espaço de memória organizada, tem o heap (variáveis e blocos de memoria dinâmica), tem a pilha (variáveis locais e os endereços de retorno das funções)
tem uma identificação (PID)
recursos, como uma tabela de ficheiros abertos

um processo é criado:
através de um um fork, e o fork só é possivel de ser executado porque já existe um processo
após o fork, o novo processo fica com uma cópia do processo anterior

um novo programa é executado:
com o mecanismo exec (execl, execlp, execle, execv, execvp, execve)
com o exec, o novo processo executa o novo programa, perdendo-se o anterior
se existir a necessidade de não perder o anterior, o novo processo tem que executar nele o programa pretendido (fork+exec)

o fork:
devolve -1, se existir erro
0, estou no contexto do filho
outro valor, estou no contexto do pai

comandos importantes
getpid, obtém o id do processo
getppid, obtém o id do processo pai

como proceder à sincronização simples de processos:
comando wait, espera que o processo filho termine
comando waitpid, espera que o processo filho especifico termine

o waitpid:
-1, pelo primeiro filho que terminar
>0, espera pelo determinado pid que foi o indicado

#sinais
os sinais são um mecanismo de notificação, mecanismo de comunicação assíncrono
os sinais desbloqueiam e execução do processos que os recebem (scanf, pause, sleep,..)

exemplo do tratamento de sinais:

#include <stdio.h>;
#include <stdlib.h>;
#include <signal.h>;

int a =0;
void trata_sinal(int s){
a++;
printf("ouch %d ", a);
if(a==5){
printf("ate logo..");
exit(0);
}
}

int main(){
setbuf(stdout, NULL);
signal(SIGINT, trata_sinal);
while(1){
pause();
}
retrun 0;
}

exemplo de interrupção com um sinal:

#include <stdio.h>; 
#include <stdlib.h>; 
#include <signal.h>;

int recebeuSinal;

void atendeSinal(int snum){
printf("\nSinal recebido..");
printf("\nProcessar acontecimento, ler named pipes, etc \n");
recebeuSinal=1;
}

int main(){
char buffer[50];
int res;
printf("\n\nPID=%d\n, getpid());
recebeuSinal=0;
signal(SIGUSR1, atendeSinal);

while(1){
printf("Favor de escrever algo (\"Fim\2 para sair)\n---> ");
res=scanf("%s", buffer);
printf("scanf leu isto: %s\n", buffer);
printf("resultado dos scanf = %d\n", res);

if(recebeuSinal==1){
recebeuSinal=0;
printf("Parece que entretanto foi atendido ");
printf("um sinal qualquer \n");
printf("Lamentamos a eventual cofusao na ");
printf("interface com o utilizador\n");
}
if(strcmp(buffer,"fim")==0){
break;
}
}
printf("ok\n\n");
return 0;
}

Existe outro mecanismo, uma segunda versão, dentro dos sinais que acrescentam funcionalidades adicionais:
sigaction, define o comportamento dos sinais
sigqueue, envia sinais

o sigaction:
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
signum, sinal a ser tratato
struct sigaction *act, ponteiro para a estrutura que descrever o que fazer com o sinal
struct sigaction *oldact, ponteiro para a estrutura preenchida com a descrição de como era feito o tratamento do sinal

o sigqueue:
int sigqueue(pid_t pid, int sig, const union sigval value);
pid, é o pid do processo alvo
sig, sinal a enviar
value, valor a passar ao processo alvo juntamente com o sinal

funções relacionadas com mecanismos de sinais:
int pause, aguarda que o processo receba um sinal
in alarm( int seconds), faz uso do sinal SIGALRM, passados x segundos
int sleep( int seconds), faz com que um processo adormeça até que chegue um sinal, ou que passe o numero de segundos

#redireccionamento
o redireccionamento está relacionado com ficheiros unix, funções de entrada e sa+ida, tratamento de input/output e pipes anónimos.

a tabela de ficheiro aberta, tem na sua ordem:
posicao 0, operações de read, stdin
posicao 1, operações de write, stdou
posicao 2, operações de stderr

exemplo, na sheel, de redireccionamento de stdin:

close(0); //libertar o stdin
open("ficheiro.txt", O_RDONLY); //abre o ficheiro, que fica na posição zero
execlp("sort","sort", NULL); //executa o programa

exemplo, na sheel, de redireccionamento de stdout:

close(1); //libertar o stdout
open("ficheiro.txt", O_WDONLY); //abre o ficheiro, que fica na posição um
execlp("ls","ls", NULL); //executa o programa

exemplo, de redireccionamento entre programas usando pipes anónimos:

antes de criar os filhos
int mat[2]; // mat[0], descritor do lado da leitura, mat[1] descritor do lado da escrita
pipe(mat);// criar o pipe

fork... // os filhos vão herdar a tabela de descritores
//no filho que escreve:
close(1); //liberta o stdout
dup(mat[1]); // duplica a extremidade do pipe para escrita, para 1
close(mat[1]); // fecha extremidade de escrita, porque foi duplicada
close(mat[0)); // fecha extremidade de leitura do pipe que nao vai ser usada
execlp("ls","ls",NULL);

//no filho que lê:
close(0); //liberta o stdin
dup(mat[0]); // duplica a extremidade do pipe para leitura, para 0
close(mat[0]); // fecha extremidade de leitura, porque foi duplicada
close(mat[1)); // fecha extremidade de escrita do pipe que nao vai ser usada
execlp("sort","sort",NULL);

questões:
o que acontece ao atendimento de sinais quando é feito fork()?
o que acontece ao atendimento de sinais quando é feito exec()?


Capítulo: 6 (comunicação entre processos) – Fundamentos de Sistemas Operativos, José Alves Marques, Paulo Guedes – 3ª edição – Editorial Presença

os mecanismos de comunicação permitem generalizar as interacções entre processo associando a sincronização e a transferência de informação.
o modelo de comunicação dos processos, produtor e consumidor, implica que ambos devem conhecer a estrutura de mensagens e o protocolo que utilizam na comunicação. a transferência de informação é suportada por um canal.
um processo pode possuir vários canais atribuindo-lhes significados específicos de forma a construírem canais de comunicação especializado, e um canal pode ser partilhado por diversos processos
quando um canal é criado fica a pertencer a um determinado processo com poderes para determinar quais os processo que se lhe poderão associar e para eliminar o canal
a primitiva de envio de menagem pode ter diversas semânticas:
assíncrona – o cliente envia o pedido e continua em execução
síncrona – o cliente fica bloqueado até o processo servidor leia a mensagem
cliente/servidor – o cliente fica bloqueado até que o servidor envie uma mensagem de resposta

Relações entre processo produtor e processo consumidor
comunicação mestre/escravo
a comunicação mestre/escravo tem a seguinte estrutura:
a actividade do escravo é totalmente controlada pelo mestre
o mestre não tem de pedir permissão para utilizar o escravo mas não lhe pode atribuir nova tarefa antes que este tenha terminado a anterior. a sincronização é portanto estrita entre as operações de envio e recepção de informação
o canal que define a associação entre o mestre e o escravo é fixo

Relações entre processo produtor e processo consumidor – mecanismo de correio
trata-se da transferência assíncrona de informação sob a forma de mensagens em que:
o emissor não tem de pedir permissão para enviar mensagens. pode contudo ficar bloqueado se a capacidade de memorização do canal tiver sido excedida
o emissor não tem qualquer controlo sobre o ou os receptores
o canal deve memorizar as mensagens durante o intervalo de tempo que decorre entre a sua produção e o seu consumo

Relações entre processo produtor e processo consumidor – mecanismo de diálogo
é estabelecido um canal de comunicação permanente entre dois processos, mas este é criado de forma dinâmica a associação durará o tempo da interacção, e ao terminar a ligação entre os dois processos é eliminada.
o diálogo é então:
existe um canal de comunicação fixo entre os processos, mas a sua criação é dinâmica
um dos processo deve requisitar o estabelecimento da ligação
a associação entre cliente e servidor é temporária
dialogo

Relações entre processo produtor e processo consumidor – mecanismo de difusão
na difusão pretende-se enviar a mesma informação a um conjunto de processos.

Comunicação do modelo computacional
Comunicação do modelo computacional – memória partilhada
a zona de memória faz parte simultaneamente do espaço de endereçamento de dois ou mais processos que lhe acedem como às suas variáveis internas. a memória partilhada permite a difusão de informação e quando complementada com os mecanismos de sincronização adequados a implementação de um modelo mestre/escravo.

Comunicação do modelo computacional – caixas de correio

Comunicação do modelo computacional – ligações virtuais

Capítulo: 11 (modelo computacional do unix) – Fundamentos de Sistemas Operativos, José Alves Marques, Paulo Guedes – 3ª edição – Editorial Presença

Processos
os processos são os elementos activos, existindo mecanismos para sincronização e comunicação. um processo executa uma imagem, em que imagem designa o ambiente de execução do processo. este ambiente é constituído por um conjunto de três unidades:
texto (código do programa)
dados (do utilizador)
pilha do utilizador (user stack)

um processo é identificado no sistema por um valor inteiro atribuído na criação do processo designado por process identifier – pid
a protecção no acesso a recurso é feito de acordo com as categorias:
dono (utilizador normalmente criou o recurso)
grupo (conjunto de utilizadores com afinidades de trabalho que justificam direitos semelhantes
restantes utilizadores

um processo tem associados dois identificadores que definem o seu numero de utilizador user identification (UID) e o seu número de grupo group identificanon (GID)

os processos são criados explicitamente através da primitiva fork. fork cria um novo processo absolutamente idêntico ao processo pai. o processo filho herda todo o contexto (nucleo e utilizador) do processo pai e continua a executar o mesmo código na instrução seguinte ao fork

IDProcesso = fork()
para o processo pai a primitiva fork retorna o identificador do novo processo enquanto o valor retornado para o processo filho é sempre zero. a primitiva retorna -1 se o fork não for possivel/erro

#include <stdio.h>
#include <unistd.h>
int main()
{
  int pid;
  pid = fork();
  if(pid == 0){
      printf("\nprocesso filho %d", getpid());
  }else{
    printf("\nprocesso pai %d", getpid());
  }
      printf("\no resto do ... %d", getpid());
  return 0;
}
saida:
processo filho 123
no resto do ... 123
processo pai 321
no resto do ... 123

a primitiva exec permite substituir o programa por outro, lido a partir de um ficheiro especificado como argumento da chamada.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
  int pid;
  pid = fork();
  if(pid == 0){
      printf("\nprocesso filho %d", getpid());
      execl("./testeE2","./testeE2", NULL);
      //execl, primeiro é o executavel e a seguir são os argumentos
      exit(1);
  }else{
    printf("\nprocesso pai %d", getpid());
  }
      printf("\no resto do ... %d", getpid());
  return 0;
}

 

#include <stdio.h>
#include <unistd.h>
void main()
{
  printf("\nsou o testeE2 ... %d", getpid());
}

 

saida:
processo pai 321
no resto do ... 321
sou o testeE2 ... 111

a primitiva exec modifica o segmento de dados e de texto do processo, contudo o contexto núcleo mantém-se inalterado. o novo processo tem o mesmo código de protecção (UID, GID) e ficheiros abertos que o processo possuía antes da execução de exec

Processos – terminar com processo
a função de sistema exit permite a um processo terminar normalmente a sua execução
o processo pai pode bloquear-se à espera da terminação do processo filho, através da primitiva wait

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
  int pid, estado, numero;
  pid = fork();
  if(pid == 0){
      printf("\nprocesso filho %d", getpid());
      exit(0);
  }else{
    printf("\nprocesso pai %d", getpid());
    pid = wait (&estado);
    //wait, fica bloqueado o pai à espera do filho
  }
    printf("\no resto do ... %d", getpid());
  return 0;
}
saida:
processo filho 123
processo pai 321
no resto do ... 321

Processos – acontecimentos assíncronos – signals
acontecimentos assíncronos e excepções podem ser assinalados a um processo em execução através da activação de um signal

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
  int pid, pid_term, estado;
  pid = fork();
  if(pid == 0){
      printf("\nprocesso filho %d", getpid());
      execl("./testeE2","./testeE2", NULL);
      printf("\n filho: erro no exec");
      exit(-1);
  }else{
    printf("\n processo pai %d", getpid());
    pid_term = wait (&estado);
    printf("\n pai terminou o processo %d com o estado %i", pid_term, estado);
  }
    printf("\no resto do ... %d", getpid());
  return 0;
}
sou o testeE2 ... 1111
processo pai 123
pai terminou o processo 1111 com o estado 5888
o resto do ... 123

alguns signals estão relacionada com interrupções provocadas pelo hardware.
existem também as excepções, como a divisão por zero ou número errado de argumentos numa chamada ao sistema
existem signals que estão relacionados com a interacção com os terminais tecla del, break ou detecção de linha não operacional.

Processos – acontecimentos assíncronos – tratamento de signals
para associar uma rotina ao signal enviado pela interrupção do terminal

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>

void ApanhaCTRLC(){
    char ch;
    printf("\n quer termina a execucao? s/n");
    ch = getchar();
    if(ch == 's'){
        exit(1);
    }else{
        printf("\nvamos continuar");
        signal(SIGINT,ApanhaCTRLC);
    }
}

int main()
{
  signal(SIGINT, ApanhaCTRLC);
  printf("associou-se um tratamento de SIGINT");
  for(;;){
      sleep(10);
  }
  return 0;
}
saida:
d 
d
d
CTRL+C
s
saiu

Um sinal pode também ser ignorado usando o SIG_IGN
Quando se executa um fork, o tratamento dos signals é herdado pelo processo filho. este tem a liberdade de modificar o seu tratamento. a primitiva exec mantém o estado dos signals ignorados mas obviamente perde as rotinas de tratamento

Processos – acontecimentos assíncronos – envio explicito de signals
os signals podem ser enviados a outros processos através da primitiva kill, e que termina com o processo destinatário se o signal estiver a ser tratado
kill(pid, sig)
quando um processo bloqueado num semáforo, num mecanismo de comunicação ou leitura de um ficheiro recebe um signal, é automaticamente desbloqueado
o signal SIGKILL não pode ser ignorado ou tratado pelo processo destinatário e conduz sempre à respectiva terminação.
se o PID especificado na rotina kill for 0, o signal é enviado a todos os processos que pertencem ao mesmo grupo
um processo pode bloquear-se à espera de um sginal através da função pause
e pode requerer que lhe seja enviado um sinal do tipo SIGALARM
alarm(segundos)
para adormecer um processo durante um determinado intervalo de tempo que utiliza as primitivas alarm e pause
sleep(segundos)

Sistema de ficheiros – ficheiros especiais
é possivel fazer a redirecção das operações sobre ficheiros para as E/S. apesar de nem todas as operações sobre ficheiros fazerem sentido, as mais importantes (opne, read, write, close) funcionam transparentemente sobre os gestores de periféricos

Comunicação entre processos – pipes
os pipes foram o mecanismo inicial para intercomunicação entre processos. um pipe pode ser visualizado como um canal ligando dois processos e permitindo um fluxo de informação unidireccional. funcionamento é idêntico à de uma caixa de correio em que as mensagens são sequenciais de qualquer dimensão
um processo é bloqueado quando escreve um pipe já cheio a primitiva de leitura, lê sempre o que já existe no pipe mesmo que o seu número seja inferior aos especificado na chamada a read. no caso em que o pipe está vazio o processo leitor é bloqueado.

um pipe pode ser utilizado como um ficheiro ou em substituição do periférico de entrada ou saída do programa.
(ver código abaixo)
é feita a redirecção da entrada para um processo filho. o processo cria um pipe antes de criar o processo filho, para o qual é passado todos os descritores. o processo filho fecha o seu descritor de entrada (deixando a entrada 0 na tabela livre) e duplica o descritor de leitura do pipe que irá ocupar a primeira posição na tabela de descritores (a posição 0 deixada livre pelo close executado anteriormente) o programa a executar-se no processo filho efectuara leituras sobre stdin, estas serão efectuadas transparentemente sobre o pipe que ocupou o seu lugar

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#define TAMMSG 1000

char mens[] = "mensagem de teste";
char buf[TAMMSG];

int main()
{
    int fd[2];

    if (pipe(fd) < 0)
    {
        printf("erro pipe\n");
    };
    if (fork() == 0)
    {
        //processo do filho
        //fecha stdin libertanto a posição zero da tabela
        close(0);
        //redireciona o stdin para o pipe de leitura
        dup(fd[0]);
        //fecha os descritores nao utilizados pelo filho
        close(fd[0]);
        close(fd[1]);

        read(0, buf, sizeof(mens));
        printf("filho %s\n", mens);
        exit(1);
    }
    else
    {
        write(fd[1], mens, sizeof("mens"));
        wait(0);
    }

    return 0;
}

saida:
filho: mensagem de teste

Comunicação entre processos - pipes com nome
utiliza-se o serviço de nomes do sistema de ficheiros para manter o identificador do pipe
o pipe com nome (named pipes) comporta-se como um ficheiro, existindo para o referenciar uma entrada na directoria do sistema de ficheiros
a maior limitação do uso de pipes advém da limitação baseada no sistema de ficheiros que não só restringe a funcionalidade (unidirecionalidade, interface byte stream) como limita o desempenho.

Comunicação entre processos - espera alternativa
a função selecte indica ao sistema que o processo pretende ficar bloqueado até que uma operação ou várias operações se tenham realizado.
este mecanismo é extremamente poderoso, pois possibilita tratar as operações de entrada/saída de forma assíncrona evitando manipulações complexas dos signals

Tags : , ,

0 thoughts on “SO1 – modelo de programação unix, processos, sinais, redireccionamento”

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.