Preface
Inter-process communication is needed for data transmission and resource sharing. In many cases, another process needs to notify another process of events. Sometimes there is a relationship between processes and another process needs to be controlled. So process communication is very necessary.
The first type of interprocess communication is message queue
What is a message queue
Message queues are linked lists of messages stored in the kernel and identified by message queue identifiers. Message queues provide a way to transmit data blocks between processes, each of which is considered a type. Each process has a message queue associated with it, which functions like a mailbox
Be careful
Because the pipeline follows the process, the process ends the pipeline life cycle, and for the message queue, it follows the operating system. Even if the process is prominent and the message queue is not released manually, the message queue still exists.
We can use ipcs-q to view the message queue of the operating system and ipcrm-q msgid to destroy the message queue.
Creation of message queues
Using the msgget function, we can create or access a message queue.
We still need to use ftok to get a key identifier before creating a message queue.
key_t ftok(const char *pathname, int proj_id);
Here the pathname is the path, and proj_id is used to specify the number to provide for the generation of a unique key. This function combines the information derived from pathname with the low order 8 bits of ID to form an integer IPC key.
Then create the queue.
int msgget(key_t key, int msgflg);
Msgflg uses IPC_CREATE and IPC_EXCL to create message queues. When msgflg uses IPC_CREATE and 0, it uses message queues.
The return value of the function returns an msgid and fails to return - 1.
Send and receive messages
Using message queues involves sending and receiving messages, of course. The msgsnd function and the msgrcv function provide these two functions.
Let's first look at the msgsnd function
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
Here we need to focus on the second parameter, which is a pointer to a structure called struct msgbuf.
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[User specified size]; /* message data */
};
The first variable of this structure is the type of message, and the second is the array of messages.
This structure is already defined in sys/msg.h, so we can use it directly.
Then look at the latter two parameters of msgsnd:
MSG SZ pays attention to the length of the message, not the length of the whole structure. That is to say, msg_sz does not include the length of the member variable of the long integer message type. It refers to the length of the message to which the second parameter is directed.
msgflg is used to specify the operations needed in the absence of data, which we often use IPC_NOWAIT, so that when the message queue is full, then send messages to the message queue without blocking.
When the call succeeds, the message data is copied into the message queue
Receive the message:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
The second parameter of the msgrcv function is also a pointer to the message queue structure. The third parameter and the fourth parameter are the same as above.
When the call is successful, the number of bytes in the cache is accepted by the return message queue, the message is copied to the user buffer, and the corresponding information in the message queue is deleted.
Control message queue
The function needed to control message queues is the msgctl function.
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
The cmd parameter in this function provides the required operations, such as deleting message queues using IPCRMID.
Example:
We use the server process to create a message queue, then use the client process to send data, and the service process receives data.
comm.h
#ifndef __COMM_H__
#define __COMM_H__
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/types.h>
#include<string.h>
#include<time.h>
#define _MSG_SIZE_ 1024
#define FILEPATH "."
#define PROJID 0
#define PERM 0666
#define SERVER_TYPE 1
#define CLIENT_TYPE 2
#define MYSIZE 128
struct msgbuf
{
long mtype;
char mtext[MYSIZE];
};
static int commmsg(int msgflg);
int createmsg();
int sendmsg(int msgid,long type,const char * msg);
int getmsg();
int recvmsg(int msgid,int type,char out[]);
int destorymsg(int msgid);
#endif //!__COMM_H__
comm.c
#include"comm.h"
static int commmsg(int msgflg)
{
key_t key=ftok(FILEPATH,PROJID);
if(key<0)
{
perror("ftok");
return -1;
}
int msqid=msgget(key,msgflg);
if(msqid<0)
{
perror("msgget");
return -2;
}
return msqid;
}
int createmsg()
{
return commmsg(IPC_CREAT|IPC_EXCL|PERM);
}
int getmsg()
{
return commmsg(0);
}
int sendmsg(int msgid,long type,const char *msg)
{
struct msgbuf buf;
buf.mtype=type;
strcpy(buf.mtext,msg);
int id=msgsnd(msgid,&buf,sizeof(buf.mtext),0);
if(id<0)
{
perror("msgsnd");
return -1;
}
return 0;
}
int recvmsg(int msgid,int type,char out[])
{
struct msgbuf buf;
int size=msgrcv(msgid,&buf,sizeof(buf.mtext),type,0);
if(size>0)
{
//buf.mtext[size]='\0';
strncpy(out,buf.mtext,size);
return 0;
}
perror("msgrcv");
return -1;
}
int destorymsg(int msgid)
{
if(msgctl(msgid,IPC_RMID,NULL)<0)
{
perror("msgctl");
return -1;
}
return 0;
}
client.c
#include"comm.h"
int main()
{
int msqid=getmsg();
char buf[MYSIZE];
char out[2*MYSIZE];
while(1){
printf("please input:");
fflush(stdout);
ssize_t _s=read(0,buf,sizeof(buf)-1);
if(_s>0)
{
buf[_s]='\0';
sendmsg(msqid,CLIENT_TYPE,buf);
}
if(recvmsg(msqid,SERVER_TYPE,out)<0)
{
break;
}
printf("server echo :%s\n",out);
}
return 0;
}
server.c
#include"comm.h"
int main()
{
int msqid=createmsg();
char buf[2*MYSIZE];
while(1){
if(recvmsg(msqid,CLIENT_TYPE,buf)<0)
{
break;
}
printf("client# %s\n",buf);
if(sendmsg(msqid,SERVER_TYPE,buf)<0)
{
break;
}
}
destorymsg(msqid);
return 0;
}