[go: up one dir, main page]

Saltar para o conteúdo

Comunicação entre processos

Origem: Wikipédia, a enciclopédia livre.

A comunicação entre processos, em inglês Inter-Process Communication (IPC), é o grupo de mecanismos que permite aos processos transferirem informação entre si.

A execução de um processo pressupõe por parte do sistema operativo, entre outras coisas, a criação de um contexto de execução próprio que, de certa forma, abstrai o processo dos componentes reais do sistema. Devido a esta virtualização dos recursos, o processo não tem conhecimento acerca dos outros processos e, como tal, não consegue trocar informação.

Modelos de Comunicação entre Processos

[editar | editar código-fonte]

Memória Compartilhada: uma região de memória é compartilhada entre os processos.

Troca de Mensagem: Ocorre por meio de troca de mensagens entre os processos.

Ambiente de Cooperação entre Processos

[editar | editar código-fonte]

• Motivos:

– Compartilhamento de informações

– Agilidade na computação

– Modularidade

– Conveniência

• Necessidade de mecanismo para prover a comunicação entre processos

– (Interprocess Comunication – IPC)

• Dois modelos:

– Memória compartilhada

– Troca de mensagem

Método Sistemas Operativos
Sinais Todos
Pipes unidireccionais POSIX
Pipes nomeados POSIX
Memória partilhada POSIX
Filas de mensagens POSIX
Remote Procedure Calls Todos

Mecanismos locais

[editar | editar código-fonte]

Pipes nomeados (FIFO)

[editar | editar código-fonte]
Ver artigo principal: Pipe nomeado

Os pipes nomeados, ou FIFOs, são ficheiros especiais que servem de canal de comunicação entre processos. Um processo abre o FIFO para escrita, outro para leitura.

Pipes unidirecionais

[editar | editar código-fonte]

Esta é a forma mais divulgada de IPC. Um exemplo:

C:\> type text.txt | more

Este exemplo em DOS iria canalizar o output do comando TYPE como input para o programa MORE.

Filas de mensagens

[editar | editar código-fonte]
Ver artigo principal: Fila de mensagens

Uma fila de mensagens ou message queue permite criar uma zona de intercâmbio de mensagens de tamanho fixo ao nível do sistema operativo, oferecendo um maior nível de segurança sobre quais os processos que a ela podem aceder. Além do sistema de permissões, a fila de mensagens é criada com uma chave que deverá ser apenas do conhecimento dos utilizadores deste recurso.

Uma das característica deste mecanismo é que as mensagens podem ser retiradas selectivamente, já que cada uma é identificada por um tipo de mensagem. A extração pode ser por tipo ou do tipo FIFO.

Memória Compartilhada

[editar | editar código-fonte]
Ver artigo principal: Memória partilhada

Dois ou mais processos utilizam a região de memória compartilhada, conectando-a no seu espaço de endereçamento.

Deve se ter a garantia de que os dois processos não estejam gravando dados no mesmo local simultaneamente.

Exemplo: Problema Produtor-Consumidor

• Paradigma para processos em cooperação • Processo produtor produz informações que são consumidas por um processo consumidor – Buffer ilimitado não impõe limite prático sobre o tamanho do buffer – Buffer limitado assume que existe um tamanho de buffer fixo.

#ifndef BUFFER_H_
#define BUFFER_H_
class Buffer {
public:
    int BUFFER_SIZE = 5;
    int count; // Número de itens no buffer.
    int in; // Posição de inserção, próxima posição livre.
    int out; // Próxima posição cheia.
    int *buffer;
    Buffer();
    virtual ~Buffer();
    void insert(int item);
    int remove();
    void print();
};
#endif /* BUFFER_H_ */

Buffer vinculado – método insert()

void Buffer::insert(int item) {
    while(count == BUFFER_SIZE) // Cheio?
        ; // faça nada.
    // add um item no buffer.
    count++;
    buffer[in] = item;
    // Incremento Circular.
    in = (in + 1) % BUFFER_SIZE;
}

Buffer vinculado – método remove()

int Buffer::remove() {
    int item;
    while(count == 0) // Vazio?
        ; // faça nada.
    // remove o item do buffer.
    count--;
    item = buffer[out];
    out = (out + 1) % BUFFER_SIZE;
    return item;
}

Mecanismos cliente/servidor

[editar | editar código-fonte]
Ver artigo principal: Socket

Os sockets também são considerados IPC, embora mais orientados para uma arquitectura cliente-servidor.

Remote Procedure Calls

[editar | editar código-fonte]
Ver artigo principal: RPC

Os RPC são também considerados IPC.

Sincronização entre processos

[editar | editar código-fonte]

A sincronização entre processos permite gerir o acesso concorrente a recursos do sistema operativo de forma controlada por parte dos processos, de maneira que um recurso não seja modificado em simultâneo, ou que os processos não fiquem em espera que o recurso seja libertado.

Ver artigo principal: Sinal

A sinalização é um mecanismo largamente utilizado em UNIX e funciona analogamente a um trigger (disparo). Um processo receptor de um sinal irá parar a sua execução imediatamente, para passar a processar o sinal. Desta forma, o processo é assim "despertado" para um qualquer evento, consoante o sinal recebido. Um exemplo comum é o sinal KILL (matar) enviado a um processo bloqueado:

# kill -KILL 3516
Ver artigo principal: Semáforo

Os semáforos são o mecanismo de sincronização mais complexo, já que permitem, simultaneamente, gerir o acesso concorrente quer em modo de exclusividade (1 utilizador) quer em modo de cooperação (N utilizadores).

Em modo de exclusividade o semáforo apenas permite um utilizador do recurso. A parte do código do processo delimitada pela activação do semáforo e sua libertação denomina-se secção crítica. Esta deverá ser o mais pequena e rápida possível, a fim de minimizar o tempo de espera dos processos concorrentes.

Este mecanismo é bloqueante, em oposição aos sinais.

Um dos primeiros mecanismos de comunicação interprocessos disponíveis em sistemas UNIX. • São interrupções de software que notificam ao processo que um evento ocorreu, são utilizados pelo núcleo.

• Permitem somente o envio de uma palavra de dados (código do sinal (1 a 64)) para outros processos.

• Não permitem que processos especifiquem dados para trocar com outros processos.

• Dependem do SO e das interrupções geradas por software suportadas pelo processador do sistema.

• Quando ocorre um sinal, o SO determina qual processo deve receber o sinal e como esse processo responderá ao sinal.

Quando sinais são gerados

• Criados pelo núcleo em resposta a interrupções e exceções, os sinais são enviados a um processo ou thread.

• Em consequência da execução de uma instrução (como falha de segmentação)

• Em um outro processo (como quando um processo encerra outro) ou em um evento assíncrono.

Estão definidos 64 sinais.

1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX

• Um kill -9 pid tem o mesmo efeito de um kill -SIGKILL pid

Tratamento de Sinais

[editar | editar código-fonte]

• Processos podem:

– Capturar: especificando uma rotina que o SO chama quando entrega o sinal.

– Ignorar: Neste caso depende da ação padrão do SO para tratar o sinal.

– Mascarar um sinal: Quando um processo mascara um sinal de um tipo específico, o SO não transmite mais sinais daquele tipo para o processo até que ele desbloqueie a máscara do sinal.

• Um processo/thread pode tratar um sinal

1. Capturando o sinal

– quando um processo capta um sinal, chama o tratador para responder ao sinal.

2. Ignorando o sinal

– os processos podem ignorar todos, exceto os sinais SIGSTOP e SIGKILL.

3. Executando a ação default

– Ação definida pelo núcleo para esse sinal.

Ações default

[editar | editar código-fonte]

– Abortar: terminar imediatamente.

– Descarga de memória: copia o contexto de execução antes de sair para um arquivo do núcelo (memory dump).

– Ignorar.

– Parar (isto é, suspender).

– Continuar (isto é, retomar a execução).

Bloqueio de Sinais

[editar | editar código-fonte]

• Um processo ou thread pode bloquear um sinal.

• O sinal não é entregue até que o processo/thread pare de bloqueá-lo.

• Enquanto o tratador de sinal estiver em execução, os sinais desse tipo são bloqueados por default.

• Ainda é possível receber sinais de um tipo diferente (não bloqueados).

• Os sinais comuns não são enfileirados.

• Os sinais de tempo real podem ser enfileirados.

Mecanismos de passagem de mensagem

[editar | editar código-fonte]

• Pipe:

– Permite a criação de filas de processos;

– Saída de um processo é a entrada de outro;

– Existe enquanto o processo existir; • Fifo (Named pipe):

– Extensão de pipe;

– Continua existindo mesmo depois que o processo terminar;

– Criado com chamadas de sistemas;

• O processo produtor escreve dados para o pipe.

• Depois disso, o processo consumidor lê dados do pipe na ordem “primeiro a entrar, primeiro a sair”.

• Quando um pipe é criado, um inode que aponta para o buffer do pipe (página de dados) é criado.

• O acesso ao pipe é controlado por descritores de arquivo.

– Podem ser passados entre processos relacionados (por exemplo, pai e filho).

• Pipes nomeados (FIFOs).

– Podem ser acessados por meio da árvore de diretório.

• Limitação: buffer de tamanho fixo.

int pipe(int pipefd[2]);
int pipe2(int pipefd[2], int flags);
pipe() cria um pipe, um canal de dados unidirecional que pode
ser usado em IPC.
O array pipefd é usado para retornar dois descritores de
arquivo:
 pipefd[0] referencia o lado de leitura
 pipefd[1] o lado de escrita.
Os dados escritos são colocados em um buffer pelo kernel até
ser lido pelo lado de leitura.
Se flags é 0, então pipe2() é o mesmo que pipe().
Mais detalhes: man pipe

Exemplo de Pipe

int main(void) {
    pid_t pid;
    int mypipe[2];
    /* Criar o pipe. */
    if (pipe(mypipe)) {
        fprintf(stderr, "Falha ao criar o Pipe.\n");
        return EXIT_FAILURE;
    }
    /* Criar o processo filho. */
    pid = fork();
    if (pid == (pid_t) 0) {
        /* No processo filho. */
        close(mypipe[1]);
        read_from_pipe(mypipe[0]);
        return EXIT_SUCCESS;
    } else if (pid < (pid_t) 0) {
        /* pid negativo, falha no fork. */
        fprintf(stderr, "Falha ao executar o Fork.\n");
        return EXIT_FAILURE;
    } else {
        /* Processo pai. */
        close(mypipe[0]);
        write_to_pipe(mypipe[1]);
        return EXIT_SUCCESS;
    }
}

• Permitem que pares de processos troquem dados estabelecendo canais diretos de comunicação bidirecional.

• Usados principalmente para comunicação bidirecional entre vários processos em sistemas diferentes, mas podem ser usados para processos no mesmo sistema.

• Armazenados internamente como arquivos.

• O nome do arquivo é usado como endereço do socket, que é acessado por meio do VFS.

• Sockets de fluxo

– Implementam o tradicional modelo cliente/servidor.

– Os dados são transferidos como um fluxo de bytes.

– Usam TCP para comunicação, de modo que são mais apropriados quando a comunicação tem de ser confiável.

• Sockets de datagrama

– Comunicação mais rápida, mas menos confiável.

– Os dados são transferidos por meio de pacotes de datagramas.

• Par de Sockets

– Par de soquetes conectados e não denominados.

– Limitado para ser usado por processos que compartilham descritores de arquivo.

Passagem de mensagem

[editar | editar código-fonte]

• Provê troca de mensagens entre processos rodando em máquinas diferentes;

• Utiliza-se de duas primitivas de chamadas de sistema: send e receive;

• Podem ser implementadas como procedimentos: send (destination,&message); receive (source,&message);

• O procedimento send envia para um determinado destino uma mensagem, enquanto que o procedimento receive recebe essa mensagem em uma determinada fonte; Se nenhuma mensagem está disponível, o procedimento receive é bloqueado até que uma mensagem chegue.

Passagem de mensagem - Problemas

[editar | editar código-fonte]

• Mensagens podem ser perder na transmissão;

– Mensagem especial acknowledgement (ack)  o procedimento receive envia um ack para o procedimento send. Se esse ack não chega no procedimento send, esse procedimento retransmite a mensagem já enviada;

• A mensagem é recebida mas o ack se perde.

– receive checa se cada mensagem enviada pelo send satisfaz uma seqüência de números. Ao receber uma nova mensagem, receive verifica essa identificação, se ela for semelhante a de alguma mensagem já recebida, receive descarta a mensagem.

• Desempenho: copiar mensagens de um processo para o outro é mais lento do que operações com semáforos e monitores.

• Autenticação → Segurança;

• Invocação de método remoto (RMI) é um mecanismo da Java semelhante às RPCs.

• RMI permite que um programa Java em uma máquina chame um método em um objeto remoto..

Ligações externas

[editar | editar código-fonte]
Ícone de esboço Este artigo sobre informática é um esboço. Você pode ajudar a Wikipédia expandindo-o.