Comunicação entre processos - Pipes nomeados
Pipes foram feitos para a comunicação entre processos relacionados. Podemos usar canais para comunicação de processos não relacionados, digamos, queremos executar o programa cliente de um terminal e o programa servidor de outro terminal? A resposta é Não. Então, como podemos alcançar a comunicação de processos não relacionados, a resposta simples é Pipes Nomeados. Embora isso funcione para processos relacionados, não dá sentido usar os canais nomeados para comunicação de processos relacionados.
Usamos um tubo para comunicação unilateral e dois tubos para comunicação bidirecional. A mesma condição se aplica a Pipes nomeados. A resposta é não, podemos usar um único pipe nomeado que pode ser usado para comunicação bidirecional (comunicação entre o servidor e o cliente, mais o cliente e o servidor ao mesmo tempo), pois o Named Pipe suporta a comunicação bidirecional.
Outro nome para o pipe nomeado é FIFO (First-In-First-Out). Vamos ver a chamada do sistema (mknod ()) para criar um pipe nomeado, que é uma espécie de arquivo especial.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int mknod(const char *pathname, mode_t mode, dev_t dev);
Essa chamada de sistema criaria um arquivo especial ou nó do sistema de arquivos, como arquivo comum, arquivo de dispositivo ou FIFO. Os argumentos para a chamada do sistema são pathname, mode e dev. O nome do caminho junto com os atributos de modo e informações do dispositivo. O nome do caminho é relativo, se o diretório não for especificado, ele será criado no diretório atual. O modo especificado é o modo de arquivo que especifica o tipo de arquivo, como o tipo de arquivo e o modo de arquivo conforme mencionado nas tabelas a seguir. O campo dev é para especificar as informações do dispositivo, como números de dispositivos principais e secundários.
Tipo de arquivo | Descrição | Tipo de arquivo | Descrição |
---|---|---|---|
S_IFBLK | bloco especial | S_IFREG | Arquivo normal |
S_IFCHR | personagem especial | S_IFDIR | Diretório |
S_IFIFO | FIFO especial | S_IFLNK | Link Simbólico |
Modo de Arquivo | Descrição | Modo de Arquivo | Descrição |
---|---|---|---|
S_IRWXU | Ler, escrever, executar / pesquisar pelo proprietário | S_IWGRP | Permissão de escrita, grupo |
S_IRUSR | Permissão de leitura, proprietário | S_IXGRP | Permissão de execução / pesquisa, grupo |
S_IWUSR | Permissão de gravação, proprietário | S_IRWXO | Ler, escrever, executar / pesquisar por outros |
S_IXUSR | Permissão de execução / pesquisa, proprietário | S_IROTH | Permissão de leitura, outros |
S_IRWXG | Ler, escrever, executar / pesquisar por grupo | S_IWOTH | Permissão de escrita, outros |
S_IRGRP | Permissão de leitura, grupo | S_IXOTH | Permissão para executar / pesquisar, outros |
O modo de arquivo também pode ser representado em notação octal, como 0XYZ, onde X representa o proprietário, Y representa o grupo e Z representa outros. O valor de X, Y ou Z pode variar de 0 a 7. Os valores para leitura, gravação e execução são 4, 2, 1, respectivamente. Se necessário, em combinação de leitura, gravação e execução, adicione os valores de acordo.
Digamos, se mencionarmos, 0640, isso significa ler e escrever (4 + 2 = 6) para o proprietário, ler (4) para o grupo e nenhuma permissão (0) para os outros.
Esta chamada retornaria zero em caso de sucesso e -1 em caso de falha. Para saber a causa da falha, verifique com a variável errno ou função perror ().
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode)
Esta função de biblioteca cria um arquivo especial FIFO, que é usado para o pipe nomeado. Os argumentos para esta função são o nome do arquivo e o modo. O nome do arquivo pode ser um caminho absoluto ou um caminho relativo. Se o nome do caminho completo (ou caminho absoluto) não for fornecido, o arquivo será criado na pasta atual do processo de execução. As informações do modo de arquivo são descritas na chamada do sistema mknod ().
Esta chamada retornaria zero em caso de sucesso e -1 em caso de falha. Para saber a causa da falha, verifique com a variável errno ou função perror ().
Vamos considerar um programa que executa o servidor em um terminal e o cliente em outro. O programa executaria apenas comunicação unilateral. O cliente aceita a entrada do usuário e envia a mensagem ao servidor, o servidor imprime a mensagem na saída. O processo continua até que o usuário insira a string “fim”.
Vamos entender isso com um exemplo -
Step 1 - Crie dois processos, um é fifoserver e outro é fifoclient.
Step 2 - O processo do servidor executa o seguinte -
Cria um pipe nomeado (usando a chamada do sistema mknod ()) com o nome “MYFIFO”, se não for criado.
Abre o canal nomeado para fins somente leitura.
Aqui, é criado o FIFO com permissões de leitura e escrita para o Proprietário. Leia para o grupo e nenhuma permissão para os outros.
Espera infinitamente por mensagem do Cliente.
Se a mensagem recebida do cliente não for “fim”, imprime a mensagem. Se a mensagem for “fim”, fecha o fifo e finaliza o processo.
Step 3 - O processo do cliente realiza o seguinte -
Abre o canal nomeado apenas para fins de gravação.
Aceita a string do usuário.
Verifica se o usuário insere “fim” ou algo diferente de “fim”. De qualquer forma, ele envia uma mensagem ao servidor. Porém, se a string for “final”, fecha o FIFO e também finaliza o processo.
Repete-se infinitamente até que o usuário insira a string “fim”.
Agora, vamos dar uma olhada no arquivo do servidor FIFO.
/* Filename: fifoserver.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "MYFIFO"
int main() {
int fd;
char readbuf[80];
char end[10];
int to_end;
int read_bytes;
/* Create the FIFO if it does not exist */
mknod(FIFO_FILE, S_IFIFO|0640, 0);
strcpy(end, "end");
while(1) {
fd = open(FIFO_FILE, O_RDONLY);
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
to_end = strcmp(readbuf, end);
if (to_end == 0) {
close(fd);
break;
}
}
return 0;
}
Etapas de compilação e execução
Received string: "this is string 1" and length is 16
Received string: "fifo test" and length is 9
Received string: "fifo client and server" and length is 22
Received string: "end" and length is 3
Agora, vamos dar uma olhada no código de amostra do cliente FIFO.
/* Filename: fifoclient.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "MYFIFO"
int main() {
int fd;
int end_process;
int stringlen;
char readbuf[80];
char end_str[5];
printf("FIFO_CLIENT: Send messages, infinitely, to end enter \"end\"\n");
fd = open(FIFO_FILE, O_CREAT|O_WRONLY);
strcpy(end_str, "end");
while (1) {
printf("Enter string: ");
fgets(readbuf, sizeof(readbuf), stdin);
stringlen = strlen(readbuf);
readbuf[stringlen - 1] = '\0';
end_process = strcmp(readbuf, end_str);
//printf("end_process is %d\n", end_process);
if (end_process != 0) {
write(fd, readbuf, strlen(readbuf));
printf("Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
} else {
write(fd, readbuf, strlen(readbuf));
printf("Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
close(fd);
break;
}
}
return 0;
}
Vamos dar uma olhada na saída de chegada.
Etapas de compilação e execução
FIFO_CLIENT: Send messages, infinitely, to end enter "end"
Enter string: this is string 1
Sent string: "this is string 1" and string length is 16
Enter string: fifo test
Sent string: "fifo test" and string length is 9
Enter string: fifo client and server
Sent string: "fifo client and server" and string length is 22
Enter string: end
Sent string: "end" and string length is 3
Comunicação bidirecional usando canais nomeados
A comunicação entre os tubos deve ser unidirecional. Os tubos eram restritos à comunicação unilateral em geral e precisam de pelo menos dois tubos para a comunicação bidirecional. Os tubos destinam-se apenas a processos inter-relacionados. Pipes não podem ser usados para comunicação de processos não relacionados, digamos, se quisermos executar um processo de um terminal e outro processo de outro terminal, não é possível com tubos. Temos alguma forma simples de comunicação entre dois processos, digamos, processos não relacionados de uma forma simples? A resposta é sim. O pipe nomeado é destinado à comunicação entre dois ou mais processos não relacionados e também pode ter comunicação bidirecional.
Já vimos a comunicação unidirecional entre pipes nomeados, ou seja, as mensagens do cliente para o servidor. Agora, vamos dar uma olhada na comunicação bidirecional, ou seja, o cliente enviando a mensagem ao servidor e o servidor recebendo a mensagem e enviando outra mensagem ao cliente usando o mesmo pipe nomeado.
A seguir está um exemplo -
Step 1 - Crie dois processos, um é fifoserver_twoway e outro é fifoclient_twoway.
Step 2 - O processo do servidor executa o seguinte -
Cria um pipe nomeado (usando a função de biblioteca mkfifo ()) com o nome “fifo_twoway” no diretório / tmp, se não for criado.
Abre o canal nomeado para fins de leitura e gravação.
Aqui, é criado o FIFO com permissões de leitura e escrita para o Proprietário. Leia para o grupo e nenhuma permissão para os outros.
Espera infinitamente por uma mensagem do cliente.
Se a mensagem recebida do cliente não for “fim”, imprime a mensagem e inverte o string. A string invertida é enviada de volta ao cliente. Se a mensagem for “fim”, fecha o fifo e finaliza o processo.
Step 3 - O processo do cliente realiza o seguinte -
Abre o canal nomeado para fins de leitura e gravação.
Aceita string do usuário.
Verifica se o usuário insere “fim” ou algo diferente de “fim”. De qualquer forma, ele envia uma mensagem ao servidor. Porém, se a string for “final”, fecha o FIFO e também finaliza o processo.
Se a mensagem for enviada como não “final”, ele espera pela mensagem (string invertida) do cliente e imprime a string invertida.
Repete-se infinitamente até que o usuário insira a string “fim”.
Agora, vamos dar uma olhada no código de amostra do servidor FIFO.
/* Filename: fifoserver_twoway.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "/tmp/fifo_twoway"
void reverse_string(char *);
int main() {
int fd;
char readbuf[80];
char end[10];
int to_end;
int read_bytes;
/* Create the FIFO if it does not exist */
mkfifo(FIFO_FILE, S_IFIFO|0640);
strcpy(end, "end");
fd = open(FIFO_FILE, O_RDWR);
while(1) {
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("FIFOSERVER: Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
to_end = strcmp(readbuf, end);
if (to_end == 0) {
close(fd);
break;
}
reverse_string(readbuf);
printf("FIFOSERVER: Sending Reversed String: \"%s\" and length is %d\n", readbuf, (int) strlen(readbuf));
write(fd, readbuf, strlen(readbuf));
/*
sleep - This is to make sure other process reads this, otherwise this
process would retrieve the message
*/
sleep(2);
}
return 0;
}
void reverse_string(char *str) {
int last, limit, first;
char temp;
last = strlen(str) - 1;
limit = last/2;
first = 0;
while (first < last) {
temp = str[first];
str[first] = str[last];
str[last] = temp;
first++;
last--;
}
return;
}
Etapas de compilação e execução
FIFOSERVER: Received string: "LINUX IPCs" and length is 10
FIFOSERVER: Sending Reversed String: "sCPI XUNIL" and length is 10
FIFOSERVER: Received string: "Inter Process Communication" and length is 27
FIFOSERVER: Sending Reversed String: "noitacinummoC ssecorP retnI" and length is 27
FIFOSERVER: Received string: "end" and length is 3
Agora, vamos dar uma olhada no código de amostra do cliente FIFO.
/* Filename: fifoclient_twoway.c */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "/tmp/fifo_twoway"
int main() {
int fd;
int end_process;
int stringlen;
int read_bytes;
char readbuf[80];
char end_str[5];
printf("FIFO_CLIENT: Send messages, infinitely, to end enter \"end\"\n");
fd = open(FIFO_FILE, O_CREAT|O_RDWR);
strcpy(end_str, "end");
while (1) {
printf("Enter string: ");
fgets(readbuf, sizeof(readbuf), stdin);
stringlen = strlen(readbuf);
readbuf[stringlen - 1] = '\0';
end_process = strcmp(readbuf, end_str);
//printf("end_process is %d\n", end_process);
if (end_process != 0) {
write(fd, readbuf, strlen(readbuf));
printf("FIFOCLIENT: Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("FIFOCLIENT: Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
} else {
write(fd, readbuf, strlen(readbuf));
printf("FIFOCLIENT: Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
close(fd);
break;
}
}
return 0;
}
Etapas de compilação e execução
FIFO_CLIENT: Send messages, infinitely, to end enter "end"
Enter string: LINUX IPCs
FIFOCLIENT: Sent string: "LINUX IPCs" and string length is 10
FIFOCLIENT: Received string: "sCPI XUNIL" and length is 10
Enter string: Inter Process Communication
FIFOCLIENT: Sent string: "Inter Process Communication" and string length is 27
FIFOCLIENT: Received string: "noitacinummoC ssecorP retnI" and length is 27
Enter string: end
FIFOCLIENT: Sent string: "end" and string length is 3