System V IPC : 基于内核持续性
System V 消息队列:在程序之间传递数据的一种方法
System V 共享内存:用于在程序之间高效的共享数据
System V 信号量:用于管理对资源的访问
内核会为每个IPC对象维护一个数据结构:
struct ipc_perm
{
key_t key; /* 调用shmget()时给出的关键字*/
uid_t uid; /*共享内存所有者的有效用户ID */
gid_t gid; /* 共享内存所有者所属组的有效组ID*/
uid_t cuid; /* 共享内存创建 者的有效用户ID*/
gid_t cgid; /* 共享内存创建者所属组的有效组ID*/
unsigned short mode; /* Permissions + SHM_DEST和SHM_LOCKED标志*/
unsignedshort seq; /* 序列号*/
};
系统创建和打开IPC通道
1.从IPC键生成IPC标识符:
2.创建或打开一个IPC对象的逻辑:
ipcs 查看对应的IPC结构,
ipcrm 删除对应的IPC结构。
生成键值的函数:
key_t ftok(const char *path, int id); //返回值:成功返回键,出错返回(key_t)-1
简单用法: key_t ftok(".", 'a');
消息队列:消息队列是消息的链接表,存放在内核中并由消息队列标识符标识。
消息队列的数据结构:
struct msqid_ds {
struct ipc_perm msg_perm;
struct msg *msg_first; /* first message on queue,unused */
struct msg *msg_last; /* last message in queue,unused */
__kernel_time_t msg_stime; /* last msgsnd time */
__kernel_time_t msg_rtime; /* last msgrcv time */
__kernel_time_t msg_ctime; /* last change time */
unsigned long msg_lcbytes; /* Reuse junk fields for 32 bit */
unsigned long msg_lqbytes; /* ditto */
unsigned short msg_cbytes; /* current number of bytes on queue */
unsigned short msg_qnum; /* number of messages in queue */
unsigned short msg_qbytes; /* max number of bytes on queue */
__kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */
__kernel_ipc_pid_t msg_lrpid; /* last receive pid */
};
内核中的消息队列状态:
消息队列的几个基本参数:
1>一条消息最大是多大? cat /proc/sys/kernel/msgmax
2>消息队列中所有消息的总和最大是多大 ? cat /proc/sys/kernel/msgmnb
3>系统能够创建多少个消息队列? cat /proc/sys/kernel/msgmni
4>消息通道即是消息类型
消息队列的创建:
int msgget(key_t key, int msgflg); //msgflg 当消息队列已经存在,打开时置0,不存在创建时IPC_CREAT|0644
返回值:成功返回消息队列额id,出错返回-1
消息队列操作:(这里讨论做删除用时的用法)
int msgctl(int msqid, int cmd, struct msqid_ds *buf); //msqid msgget返回的id, cmd 一般置为IPC_RMID, 最后一个参数一般不关注,置0即可
返回值:成功返回0,出错返回-1
发送消息:
消息格式:
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[100]; /* message data,oflength nbytes */
};
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); //msgp--数据的起始地址 msgsz--有效数据的大小(不包括通道号)
//msgflg--一般为0 | IPC_NOWAIT
返回值:成功返回0, 出错返回-1
从消息队列中取出数据:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
//msgp--存放收到数据的缓冲区, msgsz--缓冲区大小(不包括通道大小) msgtyp(即是通道号)--从哪个通道接收数据
//int msgflg //0 | IPC_NOWAIT
msgtyp == 0 :返回队列中的第一个消息
返回值:成功返回0, 出错返回-1
实例应用:发送程序往指定的消息队列通道中写,接受程序从指定的消息队列通道中读
创建消息队列:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <sys/ipc.h>
int main()
{
int id =msgget(123, IPC_CREAT| 0644);
if(-1 == id) perror("msgget"),exit(1);
printf("msg create ok\n");
}
发送消息:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <unistd.h>
struct msgbuf{
long channel; // >0 表示通道号
char msgtext[100]; //信息缓存
};
int main()
{
struct msgbuf buf;
int id =msgget(123, 0);
if(-1 == id) perror("msgget"),exit(1);
while(1)
{
printf("please enter channel num:\n");
scanf("%d", &buf.channel);
printf("please enter message:\n");
scanf("%s", buf.msgtext);
if(-1 == msgsnd(id, &buf, strlen(buf.msgtext), 0))
perror("msgsnd"), exit(1);
printf("msg write ok!\n");
}
}
接受消息:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <unistd.h>
struct msgbuf{
long channel; // >0 表示通道号
char msgtext[100];
};
int main()
{
struct msgbuf buf;
int id =msgget(123, 0);
if(-1 == id) perror("msgget"),exit(1);
while(1)
{
int channel;
printf("please enter channel num you want to read:\n");
scanf("%d", &channel);
memset(&buf, 0x00, sizeof(buf));
msgrcv(id, &buf, 100, channel, 0);
printf("msgtext = %s\n", buf.msgtext);
}
}
删除消息队列:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int main()
{
int id = msgget(123, 0);
if(-1 == id) perror("msgget"), exit(1);
if (-1 == msgctl(id, IPC_RMID, 0)) perror("msgctl"), exit(1);
}
实例2:多个客户端进程分别向服务器程序发送消息,服务器程序接收到消息后转发回该程序。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <fcntl.h>
struct msgbuf{
long channel; //通道号
char msgtext[100]; //消息
};
int main()
{
int id = msgget(123, 0);
if(-1 == id) perror("msgget"), exit(1);
struct msgbuf buf;
while(1)
{
memset(&buf, 0x00, sizeof(buf));
*(long*)(buf.msgtext) = (long)getpid();
buf.channel = 1;
printf("please enter msg you want send:\n ");
scanf("%s", buf.msgtext + sizeof(long));
if(strncmp(buf.msgtext + sizeof(long), "quit", 4) == 0)
{
printf("exit");
break;
}
if( -1 == msgsnd(id, &buf , strlen(buf.msgtext + sizeof(long))+ sizeof(long), 0))
{
perror("msgsnd");
exit(1);
}
//这里的缓冲区大小的计算要考虑到前面加的pid的值在存储时会存在小端存储的问题
memset(&buf, 0x00, sizeof(buf));
if(-1 == msgrcv(id, &buf, 100, getpid(), 0))
{
perror("msgrcv");
exit(1);
}
printf("rcv = %s\n", buf.msgtext + sizeof (long));
}
}
服务器程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <fcntl.h>
struct msgbuf{
long channel; //通道号
char msgtext[100]; //消息
};
int main()
{
int id = msgget(123, IPC_CREAT|0644);
if(-1 == id) perror("msgget"), exit(1);
struct msgbuf buf;
while(1)
{
//清空缓存区
memset(&buf, 0x00, sizeof(buf));
//从1号通道接收,存放在buf
if (-1 == msgrcv(id, &buf, 100, 1, 0))
{
perror("msgrcv");
exit(1);
}
buf.channel = *(long*)(buf.msgtext);
printf("rcv %d msg %s\n", buf.channel, buf.msgtext + sizeof(long));
//将消息发送到返回通道,getpid得到的通号,这里存放在msgtext前sizeof(long)个字节中
if(-1 == msgsnd(id, &buf, strlen(buf.msgtext+sizeof(long)) + sizeof(long), 0))
{
perror("msgsnd");
exit(1);
}
printf("send ok\n");
}
}
清除程序:
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <fcntl.h>
int main()
{
int id = msgget(123, 0);
if(-1 == id) perror("msgget"), exit(1);
msgctl(id, IPC_RMID, 0);
}