Message Queuing
In the SYSTEMV version of UNIX, At&t introduced three new forms of IPC functionality (Message Queuing, semaphores, and shared memory). But the BSD version of UNIX uses a nested interface as the main form of IPC. The Linux system supports these two versions at the same time.
System call Msgget ()
If you want to create a new message queue, or if you want to access an existing message queue, you can use the system call Msgget ().
System call: Msgget ();
Prototype: int msgget (key_t key, int msgflg);
Return value: If successful, returns the message queue identifier
If it fails, return -1:errno=eaccess (permission not allowed)
Eexist (queue already exists, cannot be created)
EIDRM (queue flag for deletion)
Enoent (queue does not exist)
ENOMEM (Insufficient memory when creating queues)
ENOSPC (exceeding maximum queue limit)
The first parameter in the system call Msgget () is a key value (usually returned by Ftok () ). This key value will then be compared with other keyword values that already exist in the system kernel. At this point, the open and access operation is related to the content in the parameter msgflg.
Ipc_creat if the queue is not in the kernel, create it.
IPC_EXCL when used with ipc_creat, it fails if the queue already exists.
If you use Ipc_creat alone, Msgget () either returns an identifier for a newly created message queue or a queue with the same key value. If Ipc_excl and ipc_creat are used together, msgget () either creates a new message queue or returns a failure value of 1 if the queue already exists. IPC_EXCL alone is no use.
Let's look at an example of opening and creating a message queue:
int Open_queue (key_t keyval)
{
Intqid;
if ((qid=msgget (Keyval, ipc_creat|0660)) ==-1)
{
Return (-1);
}
return (QID);
}
System call msgsnd ()
Once we get the queue identifier, we can perform the desired operation on the queue. If you want to send a message to the queue, you can use the system call MSGSND ():
System call: Msgsnd ();
Prototype: int msgsnd (int msqid,struct msgbuf*msgp,int msgsz,int msgflg);
Return value: If successful, 0.
If it fails, -1:errno=eagain (the queue is full and the ipc_nowait is used)
Eacces (no write permission)
Efault (Invalid MSGP address)
EIDRM (Message Queuing has been deleted)
EINTR (receive a signal when waiting for write operation)
EINVAL (Invalid message queue identifier, non-positive message type, or
Invalid message length for person)
Enomem (not enough memory to copy message buffers)
The first parameter of the system call MSGSND () is the message queue identifier, which is returned by the system call Msgget. The second parameter, MSGP, is a pointer to the message buffer. The parameter msgsz contains the byte size of the message, but does not include the length of the message type (4 bytes).
Parameter MSGFLG can be set to 0 (this parameter is ignored at this point), or use ipc_nowait.
If the message queue is full, the message is not written to the message queue, and control is returned to the calling process. If not indicated, the calling process is suspended until the message can be written to the queue.
The following is a program that sends messages:
int send_message (int qid, struct mymsgbuf *qbuf)
{
Intresult,length;
/*the length is essentially the size of the structure minus sizeof (mtype) * *
Length=sizeof (STRUCTMYMSGBUF)-sizeof (long);
if (result = msgsnd (qid, qbuf, length, 0)) ==-1)
{
Return (-1);
}
return (result);
}
System call:MSGRCV ();
Prototype: int MSGRCV (INTMSQID,STRUCTMSGBUF*MSGP,INTMSGSZ,LONGMTYPE,INTMSGFLG);
Return value: If successful, returns the number of bytes copied to the message buffer.
If it fails, return -1:errno=e2big (message length greater than MSGSZ, no msg_noerror)
Eacces (no Read permission)
Efault (MSGP point to address is invalid)
EIDRM (queue has been deleted)
Eintr (interrupted by signal)
Einval (msgqid invalid, or msgsz less than 0)
Enomsg (using ipc_nowait, while messages in the queue cannot meet the requirements)
The first parameter is used to specify the queue to read the message . The second parameter represents the address of the message buffer where the message is to be stored. The third parameter is the length of the message buffer, excluding the length of the mtype, which can be computed in the following way:
msgsz=sizeof (struct mymsgbuf)-sizeof (long);
The fourth parameter is the type of message to read from the message queue. If the value of this parameter is 0, then a message of the longest time in the queue is returned, regardless of its type.
If ipc_nowait is used as a flag in the call, the call returns ENOMSG to the calling process when no data is available. Otherwise, the calling process will suspend until a message in the queue satisfies the MSGRCV () parameter requirements. If the queue is empty when the client waits for a message, it returns EIDRM. Returns EINTR if the process catches a signal while waiting for a message.
The following is a program that reads messages from a queue:
int read_message (int qid,long type,struct mymsgbuf*qbuf)
{
Intresult,length;
/*the length is essentially the size of the structure minus sizeof (mtype) * *
length=sizeof (struct mymsgbuf)-sizeof (long);
if ((RESULT=MSGRCV (qid,qbuf,length,type,0)) ==-1)
{
Return (-1);
}
return (result);
}
after a message has been successfully read, the entry for this message in the queue is deleted.
The Msg_noerror bit in the parameter MSGFLG provides an additional purpose. If the actual length of the message is greater than MSGSZ and the msg_noerror is used, the message is truncated, and only messages that are equal to the MSGSZ length are returned. In general, the system call MSGRCV () returns 1, and the message will continue to be saved in the queue. We can use this feature to develop a program that allows you to view message queues and see if messages that meet our criteria have arrived:
int peek_message (int qid,long type)
{
int result,length;
if ((RESULT=MSGRCV (qid,null,0,type,ipc_nowait)) ==-1)
{
if (Errno==e2big)
return (TRUE);
}
return (FALSE);
}
In the above program, we ignored the address and length of the buffer. In this way, the system call will fail. Nevertheless, we can check the returned E2big value, which indicates that the eligible message does exist.
System call Msgctl ()
Here we go on to discuss how to use the internal data structure of a given message queue. We can use the system call MSGCTL () to control the operation of the message queue.
System call: Msgctl ();
Call prototype: int msgctl (int msgqid, int cmd, struct msqid_ds *buf);
Return value: 0, if successful.
-1 if failed: errno = eacces (no Read permission at the same time CMD is Ipc_stat)
Efault (buf point to address is not valid)
EIDRM (queue is deleted in read)
Einval (msgqid invalid, or msgsz less than 0)
Eperm (ipc_set or Ipc_rmid command is used, but the caller does not have write permission)
Let's take a look at a few commands you can use:
Ipc_stat
Reads the data structure MSQID_DS of Message Queuing and stores it in the address specified by B u f.
Ipc_set
Sets the value of the ipc_perm element in the data structure Msqid_ds of the message queue. This value is taken from the BUF parameter.
Ipc_rmid
Removes Message Queuing from the system kernel.
We discussed the data structure of Message Queuing (Msqid_ds) earlier. In the system kernel, an instance of this data structure is saved for each message queue in the system. By using the Ipc_stat command, we can get a copy of this data structure. The following procedure is the procedure for implementing this function:
int get_queue_ds (int qid, struct msgqid_ds *qbuf)
{
if (Msgctl (qid, ipc_stat, qbuf) = = 1)
{
Return (-1);
}
return (0);
}
If the internal buffer cannot be replicated, the calling process returns-1. Returns 0 if the call succeeds. The data structure in the message queue should be included in the buffer.
The only element that can be changed in the data structure in Message Queuing is ipc_perm. It includes the access rights of the queue and information about the queue creator and owner. You can change the user ID, the user's group ID, and the access rights of the message queue.
The following is a program that modifies the queue access mode:
int change_queue_mode (int qid, char *mode)
{
struct Msqid_ds tmpbuf;
/* Retrieve A current copy of the internal data structure * *
Get_queue_ds (qid, &tmpbuf);
/* Change the permissions using a old trick * *
SSCANF (Mode, "%ho", &tmpbuf.msg_perm.mode);
/* Update The internal data structure * *
if (Msgctl (qid, ipc_set, &tmpbuf) = = 1)
{
Return (-1);
}
Return
}
We read the queue's internal data structure by calling Get_queue_ds. We then call SSCANF () to modify the value of the mode member in the data structure msg_perm. But until Msgctl () is invoked, the change in permissions is not really complete. Here Msgctl () is using the Ipc_set command.
Finally, we use the Ipc_rmid command in the system call MSGCTL () to remove Message Queuing:
int remove_queue (int qid)
{
if (Msgctl (qid, ipc_rmid, 0) = = 1)
{
Return (-1);
}
return (0);
}
};