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