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); }