进程间通讯(五).message queue(2)

2021-09-15 20:20:53 浏览数 (1)

msgqueB.c

代码语言:javascript复制
#include <stdio.h>
#include <sys/msg.h> //key_t,ftok,msgget,msgrcv,msgctl,IPC_RMID 相关声明在这个头文件中有所包含
#include <string.h> //memset,strcmp 相关函数声明在这个头文件中有所包含
#define BUFSZ 1024


typedef struct message  //此结构体用于存放消息,从中可以看到消息的两个字段
{
  long msg_type; //消息类型,以整型值进行标示
  char msg_text[BUFSZ]; //消息内容体
}MSG; //取了一个别名

int main()
{
  int res=-1,qid=0;
  key_t key=IPC_PRIVATE;  //IPC_PRIVATE 就是0
  MSG msg;
  
  if(-1 == (key=ftok("/",18))) //调用ftok使用相同的参数生成key,用于获取一样的队列ID
  {
    perror("ftok");
    return res;
  }
  if(-1==(qid=msgget(key,IPC_CREAT|0600))) //创建一个消息队列,将id存到qid中(如果已经存在,则获取它的ID)
  {
    perror("msgget");
    return res;
  }
  printf("open queue %dn",qid);
  
  do
  {
    memset(msg.msg_text,0,BUFSZ); //将msg.msg_text的内容清零
    if(0 > msgrcv(qid,&msg,BUFSZ,0,0)) //从消息队列中获取信息并且存到msg中
    {
      perror("msgrcv");
      return res;
    } 
    printf("the message from process %ld is %s",msg.msg_type,msg.msg_text); //将信息内容在终端进行打印
  }while(strcmp(msg.msg_text,"quitn")); //如果内容为quit就进行跳出
  

   if( 0 > msgctl(qid,IPC_RMID,NULL) ) //将队列删除,如果不删除,在进程退出后,消息将依旧保留在内核中,直到重启系统,消息的持久性,界于进程与磁盘之间
   {
     perror("msgctl");
     return res;
   }
  res=0;
  return res;
}

编译执行

代码语言:javascript复制
emacs@ubuntu:~/c$ alias gtc
alias gtc='gcc -Wall -g -o'
emacs@ubuntu:~/c$ gtc  msgqueA.x msgqueA.c ; gtc msgqueB.x msgqueB.c
emacs@ubuntu:~/c$ 

执行 msgqueB.x 会等待输入

代码语言:javascript复制
emacs@ubuntu:~/c$ ./msgqueB.x 
open queue 98304

执行 msgqueA.x 会等待输入

代码语言:javascript复制
emacs@ubuntu:~/c$ ./msgqueA.x 
open queue 98304
please enter the message to queue:
(message start with 'START' will be valid,'quit' to exit)

msgqueA.x 端输入一些内容

代码语言:javascript复制
emacs@ubuntu:~/c$ ./msgqueA.x 
open queue 98304
please enter the message to queue:
(message start with 'START' will be valid,'quit' to exit)
STARTtest
please enter the message to queue:
(message start with 'START' will be valid,'quit' to exit)
123
please enter the message to queue:
(message start with 'START' will be valid,'quit' to exit)
START123
please enter the message to queue:
(message start with 'START' will be valid,'quit' to exit)
quit
emacs@ubuntu:~/c$

msgqueB.x 端会进行显示

代码语言:javascript复制
emacs@ubuntu:~/c$ ./msgqueB.x 
open queue 98304
the message from process 23670 is STARTtest
the message from process 23670 is START123
the message from process 23670 is quit
emacs@ubuntu:~/c$ 

编译执行过程中没有报错,从结果来看,符合预期


ftok

此函数的原型在 sys/ipc.h

代码语言:javascript复制
/* Generates key for System V style IPC.  */
extern key_t ftok (__const char *__pathname, int __proj_id) __THROW;

The ftok function uses the identity of the file named by the given pathname (which must refer to an existing, accessible file) and the least significant 8 bits of proj_id (which must be nonzero) to generate a key_t type System V IPC key

此函数把从pathname导出的信息与id的低序8位组合成一个整数IPC键


key_t

代码语言:javascript复制
emacs@ubuntu:/usr/include$ grep key_t sys/ipc.h
#ifndef __key_t_defined
typedef __key_t key_t;
# define __key_t_defined
extern key_t ftok (__const char *__pathname, int __proj_id) __THROW;
emacs@ubuntu:/usr/include$ grep key_t bits/types.h
__STD_TYPE __KEY_T_TYPE __key_t;	/* Type of an IPC key.  */
emacs@ubuntu:/usr/include$ grep __KEY_T_TYPE  bits/typesizes.h
#define __KEY_T_TYPE		__S32_TYPE
emacs@ubuntu:/usr/include$ grep __S32_TYPE bits/types.h
#define	__S32_TYPE		int
emacs@ubuntu:/usr/include$

可知 key_t 其实就是 int

key_t <= __KEY_T_TYPE <= __S32_TYPE <= int


IPC_PRIVATE

bits/ipc.h 中有关于 IPC_X 的宏定义

代码语言:javascript复制
/* Mode bits for `msgget', `semget', and `shmget'.  */
#define IPC_CREAT       01000           /* Create key if key does not exist. */
#define IPC_EXCL        02000           /* Fail if key exists.  */
#define IPC_NOWAIT      04000           /* Return error on wait.  */

/* Control commands for `msgctl', `semctl', and `shmctl'.  */
#define IPC_RMID        0               /* Remove identifier.  */
#define IPC_SET         1               /* Set `ipc_perm' options.  */
#define IPC_STAT        2               /* Get `ipc_perm' options.  */
#ifdef __USE_GNU
# define IPC_INFO       3               /* See ipcs.  */
#endif

/* Special key values.  */
#define IPC_PRIVATE     ((__key_t) 0)   /* Private key.  */

所以 IPC_PRIVATE 其实代表的是 0


msg.h 所包含的头文件

代码语言:javascript复制
emacs@ubuntu:/usr/include$ grep include sys/msg.h 
#include <features.h>
#include <stddef.h>
#include <sys/ipc.h>
#include <bits/msq.h>
#include <time.h>
emacs@ubuntu:/usr/include$ grep include sys/ipc.h 
#include <features.h>
#include <bits/ipctypes.h>
#include <bits/ipc.h>
emacs@ubuntu:/usr/include$

msgget

msgget 的原型定义在 sys/msg.h

代码语言:javascript复制
/* Get messages queue.  */
extern int msgget (key_t __key, int __msgflg) __THROW;

__msgflg 是读写权限的组合,相关的宏在 bits/ipc.h 中有所定义

返回值为一个整型,即消息队列的ID


msgsnd

msgsnd 的原型定义在 sys/msg.h

代码语言:javascript复制
/* Send message to message queue.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern int msgsnd (int __msqid, __const void *__msgp, size_t __msgsz,
                   int __msgflg);

__msqid 消息队列的ID

__msgp 消息结构体的指针

__msgsz 消息内容的长度

__msgflg 控制函数行为的标志 , 一般取0 , 表示忽略


msgrcv

msgrcv 的原型定义在 sys/msg.h

代码语言:javascript复制
/* Receive message from message queue.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern ssize_t msgrcv (int __msqid, void *__msgp, size_t __msgsz,
                       long int __msgtyp, int __msgflg);

__msqid 消息队列的ID

__msgp 消息结构体的指针

__msgsz 消息内容的长度

__msgtyp 消息类型,可以实现一种简单的接收优先级

如果 msgtype == 0,就获取队列中的第一个消息 如果 msgtype > 0,将获取具有相同消息类型的第一个信息 如果 msgtype < 0,就获取类型等于或小于msgtype的绝对值的第一个消息

__msgflg

这个参数依然是是控制函数行为的标志,取值可以是:0,表示忽略;IPC_NOWAIT,如果消息队列为空,则返回一个ENOMSG,并将控制权交回调用函数的进程。如果不指定这个参数,那么进程将被阻塞直到函数可以从队列中得到符合条件的消息为止。如果一个client 正在等待消息的时候队列被删除,EIDRM 就会被返回。如果进程在阻塞等待过程中收到了系统的中断信号,EINTR 就会被返回。MSG_NOERROR,如果函数取得的消息长度大于msgsz,将只返回msgsz 长度的信息,剩下的部分被丢弃了。如果不指定这个参数,E2BIG 将被返回,而消息则留在队列中不被取出。当消息从队列内取出后,相应的消息就从队列中删除了。

函数调用成功时,该函数返回放到接收缓存区中的字节数,消息被复制到由msgp指向的用户分配的缓存区中,然后删除消息队列中的对应消息; 失败时返回-1


msgctl

msgctl 的原型定义在 sys/msg.h

代码语言:javascript复制
/* Message queue control operation.  */
extern int msgctl (int __msqid, int __cmd, struct msqid_ds *__buf) __THROW;

__msqid 消息队列的ID

__cmd 将要采取的动作

bits/ipc.h 中有关于 IPC_X 的宏定义

代码语言:javascript复制
#define IPC_RMID        0               /* Remove identifier.  */ //删除消息队列
#define IPC_SET         1               /* Set `ipc_perm' options.  */ // 如果进程有足够的权限,就把消息列队的当前关联值设置为msgid_ds结构中给出的值 
#define IPC_STAT        2               /* Get `ipc_perm' options.  */ //把msgid_ds结构中的数据设置为消息队列的当前关联值,即用消息队列的当前关联值覆盖msgid_ds的值

__buf msqid_ds 结构体指针

对删除消息队列的处理不是很完善,因为每个消息队列没有维护引用计数(打开文件有这种计数器),所以在队列被删除以后,仍在使用这一队列的进程在下次对队列进行操作时会出错返回

函数成功时返回0,失败时返回-1


msqid_ds

bits/msq.h 中有关于 msqid_ds 的定义

代码语言:javascript复制
/* Structure of record for one message inside the kernel.
   The type `struct msg' is opaque.  */
struct msqid_ds
{
  struct ipc_perm msg_perm;	/* structure describing operation permission */
  __time_t msg_stime;		/* time of last msgsnd command */
#if __WORDSIZE == 32
  unsigned long int __unused1;
#endif
  __time_t msg_rtime;		/* time of last msgrcv command */
#if __WORDSIZE == 32
  unsigned long int __unused2;
#endif
  __time_t msg_ctime;		/* time of last change */
#if __WORDSIZE == 32
  unsigned long int __unused3;
#endif
  unsigned long int __msg_cbytes; /* current number of bytes on queue */
  msgqnum_t msg_qnum;		/* number of messages currently on queue */
  msglen_t msg_qbytes;		/* max number of bytes allowed on queue */
  __pid_t msg_lspid;		/* pid of last msgsnd() */
  __pid_t msg_lrpid;		/* pid of last msgrcv() */
  unsigned long int __unused4;
  unsigned long int __unused5;
};

ipc_perm

bits/ipc.h 中有关于 ipc_perm 的定义

代码语言:javascript复制
/* Data structure used to pass permission information to IPC operations.  */
struct ipc_perm
  {
    __key_t __key;			/* Key.  */
    __uid_t uid;			/* Owner's user ID.  */
    __gid_t gid;			/* Owner's group ID.  */
    __uid_t cuid;			/* Creator's user ID.  */
    __gid_t cgid;			/* Creator's group ID.  */
    unsigned short int mode;		/* Read/write permission.  */
    unsigned short int __pad1;
    unsigned short int __seq;		/* Sequence number.  */
    unsigned short int __pad2;
    unsigned long int __unused1;
    unsigned long int __unused2;
  };

Tip: 消息队列原来的实施目的是提供高于一般速度的IPC,但现在与其它形式的IPC相比,在速度方面已经没有什么差别了,考虑到使用消息队列可能带来的问题,在新的应用程序中不应当再使用它们


fgets

stdio.h 中有关于 fgets 的原型声明

代码语言:javascript复制
/* Get a newline-terminated string of finite length from STREAM.

   This function is a possible cancellation point and therefore not
   marked with __THROW.  */
extern char *fgets (char *__restrict __s, int __n, FILE *__restrict __stream)
     __wur;

总结

以下函数可以进行有名管道的创建和信号的控制

  • ftok
  • msgget
  • msgsnd
  • msgrcv
  • msgctl

通过各方面资料弄懂其参数的意义和返回值的类型,是熟练掌握的基础

原文地址http://soft.dog/2017/01/23/c-ipc-messagequeue/

0 人点赞