0. Objeto IPC
Além dos métodos mais primitivos de comunicação entre processos, sinais, pipes não nomeados e pipes nomeados, existem três métodos de comunicação entre processos, chamados de objetos IPC.
Classificação de objetos IPC: fila de mensagens, memória compartilhada, conjunto de semáforos
O objeto IPC também abre uma área no espaço do kernel. Após a criação de cada objeto IPC, ele será definido como global e receberá um número. Contanto que o número exclusivo seja encontrado, a comunicação poderá ser realizada, para que processos não relacionados possam passar comunicação de objeto IPC.
Após a criação do objeto IPC, ele ficará visível no sistema atual e sempre existirá, desde que não seja excluído ou o sistema seja desligado.
Visualize o objeto IPC criado:
ipcs Visualize todos os objetos IPC criados no sistema atual
ipcs -q Visualiza a fila de mensagens criada
ipcs -m Veja a memória compartilhada criada
ipcs -s Veja o semáforo criado
ipcrm exclui objetos IPC
Por exemplo: ipcs -q msqid exclui a fila de mensagens denominada msqid
1. Visão geral da fila de mensagens
A fila de mensagens é uma lista vinculada de mensagens, armazenada na memória e mantida pelo kernel.
Características da fila de mensagens:
- As mensagens na fila de mensagens são digitadas.
- As mensagens na fila de mensagens são formatadas.
- A fila de mensagens pode implementar consultas aleatórias de mensagens . As mensagens não precisam ser lidas na ordem de primeiro a entrar, primeiro a sair, você pode lê-las de acordo com o tipo de mensagem durante a programação.
- Uma fila de mensagens permite que um ou mais processos escrevam ou leiam mensagens nela.
- Assim como pipes não nomeados e pipes nomeados, quando as mensagens são lidas na fila de mensagens, os dados correspondentes na fila de mensagens serão excluídos.
- Cada fila de mensagens possui um identificador de fila de mensagens, e o identificador da fila de mensagens é exclusivo em todo o sistema.
- A fila de mensagens só será excluída quando o kernel for reiniciado ou a fila de mensagens for excluída manualmente. Se a fila de mensagens não for excluída manualmente, a fila de mensagens sempre existirá no sistema.
As restrições da fila de mensagens no Ubuntu 12.04 são as seguintes: (Basta entender)
- O conteúdo de cada mensagem pode ter até 8K bytes
- Cada fila de mensagens tem capacidade máxima de 16K bytes
- O número máximo de filas de mensagens no sistema é 1.609
- O número máximo de mensagens no sistema é 16384
2. Funções relacionadas à fila de mensagens
2.1 Gerar valor chave: função ftok
O mecanismo de comunicação IPC fornecido pelo System V requer um valor chave, através do qual um identificador exclusivo de fila de mensagens pode ser obtido dentro do sistema.
O valor da chave pode ser especificado manualmente ou obtido por meio da função ftok.
Se vários processos se comunicarem através de objetos IPC, eles deverão encontrar um identificador exclusivo, e o identificador exclusivo será determinado pela chave, portanto, desde que a chave seja conhecida, vários processos poderão se comunicar.
Função ftok:
#include<sys/types.h>
#include<sys/ipc.h>
key_t ftok(const char *nome do caminho, int proj_id);
Função: Crie um valor-chave por meio do nome do arquivo e do valor alvo e retorne o valor
parâmetro:
nome do caminho: qualquer nome de arquivo (nome do arquivo ou nome do diretório)
proj_id: valor alvo, o intervalo geralmente é 0 ~ 127 (oito bits inferiores)
valor de retorno:
Sucesso: valor chave
Falha: -1
Se você usar a função ftok para obter o valor da chave, o valor da chave obtido será o arquivo correspondente ao primeiro parâmetro do ftok.
A informação é determinada em conjunto com o segundo parâmetro.
Exemplo de código:
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
int main()
{
//使用ftok函数获取键值
//只要保证ftok的第一个参数对应的文件和第二个参数值相同,则不管
//程序运行多少遍或者多少个进程
//键值一定是唯一的
key_t mykey;
if ((mykey = ftok(".", 100)) == -1)
{
perror("fail to ftok");
exit(1);
}
printf("key = %#x\n", mykey);
return 0;
}
Captura de tela de execução:
2.2 Criar fila de mensagens: função msgget
função msgget:
#include<sys/msg.h>
int msgget(key_t chave, int msgflg);
Função:
Crie uma fila de mensagens e obtenha o ID da fila de mensagens
parâmetro:
chave: valor da chave, o valor da chave exclusiva determina a fila de mensagens exclusiva
Método 1: especifique um número arbitrário
Método 2: Use a função ftok para obter o valor da chave (recomendado)
msgflg: A permissão de acesso da fila de mensagens, geralmente definida como
IPC_CREAT | IPC_EXCL |0777
IPC_CREAT | 0777
valor de retorno:
Sucesso: ID da fila de mensagens
Falha: -1
Exemplo de código:
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<unistd.h>
int main()
{
key_t mykey;
if ((mykey = ftok(".", 100)) == -1)
{
perror("fail to ftok");
exit(1);
}
printf("mykey = %#x\n", mykey);
int msqid;
if ((msqid = msgget(mykey, IPC_CREAT | 0666)) == -1)
{
perror("fail to msgget");
exit(1);
}
printf("msqid = %d\n", msqid);
return 0;
}
Captura de tela de execução:
2.3 Enviar mensagem: função msgsnd
função msgsnd:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
Função: Enviar dados para a fila de mensagens especificada (operação de gravação)
parâmetro:
msqid: ID da fila de mensagens
msgp: Os dados a serem gravados precisam ser definidos por você.
estrutura estrutura mensagem{
long mtype; //Número da mensagem, deve ser maior que 0
char mtext[128]; //Texto da mensagem, vários membros podem ser definidos
} MSG;
msgsz: o tamanho do corpo da mensagem, excluindo o comprimento numérico da mensagem
msgflg: sinalizador bit
0 bloqueio
IPC_NOWAIT sem bloqueio
valor de retorno:
Sucesso: 0
Falha: -1
Exemplo de código:
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<unistd.h>
#define N 128
typedef struct {
long msg_type;//必须要有,且必须是long类型
char msg_text[N];//可以有多个,自己定义
}MSG;
#define MSGTEXT_SIZE (sizeof(MSG) - sizeof(long))
int main()
{
key_t key;
if ((key = ftok(".", 100)) == -1)
{
perror("fail to ftok");
exit(1);
}
int msgid;
if ((msgid = msgget(key, IPC_CREAT | 0777)) == -1)
{
perror("fail to msgget");
exit(1);
}
MSG msg1 = { 1, "hello world" };
MSG msg2 = { 2, "hello beijing" };
MSG msg3 = { 3, "nihao zhangsan" };
MSG msg4 = { 4, "lisi hello" };
if (msgsnd(msgid, &msg1, MSGTEXT_SIZE, 0) == -1)
{
perror("fail to msgsnd");
exit(1);
}
if (msgsnd(msgid, &msg2, MSGTEXT_SIZE, 0) == -1)
{
perror("fail to msgsnd");
exit(1);
}
if (msgsnd(msgid, &msg3, MSGTEXT_SIZE, 0) == -1)
{
perror("fail to msgsnd");
exit(1);
}
if (msgsnd(msgid, &msg4, MSGTEXT_SIZE, 0) == -1)
{
perror("fail to msgsnd");
exit(1);
}
return 0;
}
Captura de tela de execução:
2.4 Receber mensagens: função msgrcv
Função msgrcv:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, tipo de mensagem longo, int msgflg);
Função: Receber dados da fila de mensagens (operação de leitura), os dados recebidos serão excluídos da fila de mensagens
parâmetro:
msqid: ID da fila de mensagens
msgp: estrutura para salvar os dados recebidos
estrutura struct_name{
long mytpe; //Número da mensagem, deve ser maior que 0
char metxt[128]; //Texto da mensagem, vários membros podem ser definidos
}
msgsz: tamanho do corpo da mensagem
msgtype: Defina qual mensagem receber
0 Leia uma vez na ordem escrita na fila de mensagens
>0 Ler apenas a primeira mensagem na fila de mensagens com o número da mensagem do parâmetro atual
<0 Leia apenas a menor primeira mensagem na fila de mensagens que seja menor ou igual ao valor absoluto do parâmetro atual
msgflg: sinalizador bit
0 bloqueio
IPC_NOWAIT sem bloqueio
valor de retorno:
Sucesso: Comprimento do corpo da mensagem recebida
Falha: -1
Exemplo de código:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#define N 128
typedef struct {
long msg_type;
char msg_text[N];
}MSG;
#define MSGTEXT_SIZE (sizeof(MSG) - sizeof(long))
int main()
{
key_t key;
if ((key = ftok(".", 100)) == -1)
{
perror("fail to ftok");
exit(1);
}
int msgid;
if ((msgid = msgget(key, IPC_CREAT | 0777)) == -1)
{
perror("fail to msgget");
exit(1);
}
//通过msgrcv函数接收消息队列中的信息(读操作)
//注意:如果没有第四个参数指定的消息时,,msgrcv函数会阻塞等待
MSG msg;
//如果第四个参数为>0,则获取当前值的消息类型的数据
//if (msgrcv(msgid, &msg, MSGTEXT_SIZE, 2, 0) == -1)
// 如果第四个参数<0,则获取当前值的绝对值内消息类型最小的数据
//if (msgrcv(msgid, &msg, MSGTEXT_SIZE, -3, 0) == -1)
//如果第四个参数为0,则按照先进先出的方式读取数据
if (msgrcv(msgid, &msg, MSGTEXT_SIZE, 0, 0) == -1)
{
perror("fail to msgrcv");
exit(1);
}
printf("recv_msg = %s\n", msg.msg_text);
return 0;
}
Captura de tela de execução:
2.5 Controle da fila de mensagens: função msgctl
Função msgctl:
#include<sys/mg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
Função:
Execute vários controles na fila de mensagens, como modificar as propriedades da fila de mensagens ou excluir a fila de mensagens
parâmetro:
msqid: identificador da fila de mensagens
cmd: Controle de funções de função
IPC_RMID: Exclui a fila de mensagens indicada pelo msqid, removendo-a do sistema e destruindo estruturas de dados relacionadas.
IPC_STAT: Armazena o valor atual de cada elemento na estrutura de dados relacionada ao msqid na estrutura apontada por buf.
IPC_SET: Defina os elementos na estrutura de dados relacionada ao msqid para os valores correspondentes na estrutura apontada por buf.
buf: O endereço do tipo de dados msqid_ds, usado para armazenar ou modificar os atributos da fila de mensagens.
valor de retorno:
Sucesso: 0
Falha: -1
Exemplo de código:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/msg.h>
int main()
{
key_t key;
if ((key = ftok(".", 100)) == -1)
{
perror("fail to ftok");
exit(1);
}
int msqid;
if ((msqid = msgget(key, IPC_CREAT | 0777)) == -1)
{
perror("fail to msgget");
exit(1);
}
printf("msqid = %d\n", msqid);
system("ipcs -q");
if ((msgctl(msqid, IPC_RMID, NULL)) == -1)
{
perror("fail to msgctl");
exit(1);
}
system("ipcs -q");
return 0;
}
Captura de tela de execução:
Resumir:
Como mecanismo IPC, a fila de mensagens fornece funções poderosas para enviar e receber mensagens entre processos. Ao usar filas de mensagens, aplicativos complexos de múltiplos processos podem ser construídos para obter transmissão segura e comunicação ordenada de dados, melhorando significativamente as capacidades de processamento simultâneo do sistema. Porém, você também deve estar ciente das limitações da fila de mensagens, como o tamanho da fila, o número total de mensagens armazenadas, etc., que podem impactar no desempenho da aplicação.
Portanto, ao usar filas de mensagens, precisamos pesar suas vantagens e possíveis desafios para garantir que possamos manter um desempenho eficiente e uma operação estável, ao mesmo tempo em que atendemos às necessidades de negócios.