msgqueB.c
#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
会等待输入
emacs@ubuntu:~/c$ ./msgqueB.x
open queue 98304
执行 msgqueA.x
会等待输入
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
端输入一些内容
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
端会进行显示
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
中
/* 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 的宏定义
/* 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
中
/* Get messages queue. */
extern int msgget (key_t __key, int __msgflg) __THROW;
__msgflg
是读写权限的组合,相关的宏在 bits/ipc.h
中有所定义
返回值为一个整型,即消息队列的ID
msgsnd
msgsnd
的原型定义在 sys/msg.h
中
/* 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
中
/* 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
中
/* Message queue control operation. */
extern int msgctl (int __msqid, int __cmd, struct msqid_ds *__buf) __THROW;
__msqid
消息队列的ID
__cmd
将要采取的动作
bits/ipc.h
中有关于 IPC_X 的宏定义
#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
的定义
/* 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
的定义
/* 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
的原型声明
/* 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/