《Linux 编程 c》学习笔记--IPC
IPC包括:消息队列、信号量、共享内存
IPC特点:
1.IPC由内核维护,存放在内核中
1.随内核持续,IPC不会自我删除,停止使用的IPC结构会一直保存在内核中,直到内核重启或者显示删除该对象。
关键字 key_t:唯一标识一个IPC,可以由ftok()函数生成
消息队列:
创建消息队列
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
int qid;
key_t key;
if(argc<2){
printf("wrong parameters\n");
exit(0);
}
key = ftok(*(argv+1),'a');
if(key<0){
printf("fail to get key!\n");
exit(0);
}
qid = msgget(key,IPC_CREAT|0666);
if(qid<0){
printf("fail to create queue\n");
exit(0);
}
else{
printf("create msg queue!\n");
}
return 0;
}
key_t ftok(const char *pathname,int proj_id);
通过ftok来创建关键字,不同的路径会生成不同的消息队列的key_t值,ftok返回的key值是根据文件的物理索引创建的。
在两个路径下创建了两个消息队列的key值,注意消息队列位于内核中,只不过其key值由文件路径绑定!
ipcs -q
--------- 消息队列 -----------
键 msqid 拥有者 权限 已用字节数 消息
0x610718eb 65536 dxt 666 0 0
0x610718e8 98305 dxt 666 0 0
消息队列的控制
int msgctl(int maqid,int cmd,struct msqid_ds *buf);
cmd:
IPC_STAT: 在内核取此队列的msqid_ds结构, 并将它存放在buf指向的结构中.
IPC_SET: 按由buf指向结构中的值, 设置与此队列相关结构中的msg_perm.uid, msg_perm.gid, msg_perm.mode和msg_qbytes. (该命令只有下列两种进程可以执行: (1)有效用户ID等于msg_perm.cuid或msg_per.uid. (2)具有超级用户特权的进程. )
IPC_RMID: 从系统中删除该消息队列以及仍在该队列中的所有数据. 执行权限同上.
发送消息:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>
struct my_msg
{
long int msg_type;
char text[BUFSIZ];
}msgbuf;
int main(int argc, char const *argv[])
{
int msgid;
int isrunnig=1;
msgid = msgget((key_t)1234,IPC_CREAT|0666);
if(msgid<0){
printf("fail to create queue\n");
exit(0);
}
else{
printf("create msg queue!\n");
}
while(isrunnig){
printf("input msg you want to send:\n");
fgets(msgbuf.text,BUFSIZ,stdin);
msgbuf.msg_type =1;
if(msgsnd(msgid,(void*)&msgbuf,BUFSIZ,0)==-1){
perror("sending failed....");
exit(0);
}
else{
printf("already send\n");
}
if(strncmp(msgbuf.text,"end",3)==0){
printf("rec end\n");
isrunnig=0;
}
}
return 0;
}
接收消息:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>
struct my_msg
{
long int msg_type;
char text[BUFSIZ];
}msgbuf;
int main(int argc, char const *argv[])
{
int msgid;
long int msg_to_recv=0;
msgid = msgget((key_t)1234,IPC_CREAT|0666);
if(msgid<0){
printf("fail to create queue\n");
exit(0);
}
else{
printf("create msg queue!\n");
}
while(1){
if(msgrcv(msgid,(void*)&msgbuf,BUFSIZ,msg_to_recv,0)==-1){
perror("recv failed....");
exit(0);
}
else{
printf("the recv msg = %s\n",msgbuf.text);
}
if(strncmp(msgbuf.text,"end",3)==0){
printf("rec end\n");
break;
}
}
if(msgctl(msgid,IPC_RMID,0)==1){
perror("msgctl(IPC_RMID) fail...");
exit(1);
}
return 0;
}
BUFSIZ默认大小为8192B
只开发送进程,可以看到,发送出去的数据已经在消息队列里了:
打开接收进程:
接收进程接收到了‘ddd',并取出了消息队列中的信息。
注意! 在接收端一定要调用删除消息队列函数:
if(msgctl(msgid,IPC_RMID,0)==1){
perror("msgctl(IPC_RMID) fail...");
exit(1);
}
否自消息队列是无法自己删除的:(不调用msgctl(msgid,IPC_RMID,0)的情况)
共享内存
注意:共享内存需要信号量来同步
共享内存在解除映射时才写回文件
转自:https://blog.csdn.net/ypt523/article/details/79958188
例子:父子进程通过共享内存通信,以及用信号量同步
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define SHM_SIZE 1024
int main(int argc, char const *argv[])
{
int ret, //临时变量
pid, //进程id
sme_id, //信号量描述符
shm_id; //共享内存描述符
key_t sme_key; //信号量键值
key_t shm_key; //共享内存键值
char *shmp; //指向共享内存首地址
struct shmid_ds dsbuf; //共享内存信息结构变量
struct sembuf lock={0,-1,SEM_UNDO}; //信号量上锁数组指针
struct sembuf unlock = {0,1,SEM_UNDO|IPC_NOWAIT};//信号量解锁数组指针
sme_key = ftok(*(argv+1),2); //
if(sme_key<0){
perror("ftok");
exit(0);
}
sme_id = semget(sme_key,1,IPC_CREAT|0666);
if(sme_id<0){
perror("semget");
exit(0);
}
shm_id = shmget(shm_key,SHM_SIZE,IPC_CREAT|0666); //获取共享内存id
if(shm_id<0){
perror("shmget");
exit(0);
}
shmp = shmat(shm_id,NULL,0); //映射共享内存
if((int)shmp==-1){
perror("shmat");
exit(0);
}
pid=fork();
if(pid<0){
perror("fork");
exit(0);
}
else if(pid==0){
ret = semctl(sme_id,0,SETVAL,1); //初始化信号量初值=1
if(ret==-1){
perror("ret");
exit(0);
}
ret =semop(sme_id,&lock,1); //p操作
if(ret==-1){
perror("semop lock");
exit(0);
}
sleep(4);
strcpy(shmp,"hello\n"); //往共享内存写数据
if(shmdt((void*)shmp)<0){ //使共享内存脱离进程地址空间
perror("shmdt");
exit(0);
}
ret =semop(sme_id,&unlock,1); //v操作
if(ret==-1){
perror("semop unlock");
exit(0);
}
}
else{
sleep(1);
ret =semop(sme_id,&lock,1);
if(ret==-1){
perror("semop lock");
exit(0);
}
if(shmctl(shm_id,IPC_STAT,&dsbuf)<0){
perror("shmctl");
exit(0);
}
else{
printf("get share mem info\n");
printf("creater pid =%d\n",dsbuf.shm_cpid);
printf("recv msg=%s\n",(char*)shmp);
}
if(shmdt((void*)shmp)<0){
perror("shmdt");
exit(0);
}
ret =semop(sme_id,&unlock,1);
if(ret==-1){
perror("semop unlock");
exit(0);
}
//记得删除信号量和共享内存
if(shmctl(shm_id,IPC_RMID,NULL)<0){
perror("shmctl");
exit(0);
}
ret =semctl(sme_id,0,IPC_RMID,NULL);
if(ret==-1){
perror("semctl");
exit(0);
}
}
return 0;
}
输出:
get share mem info
creater pid =17494
recv msg=hello
注意:与消息队列一样,父进程退出时需要删除共享内存和信号量!
否则,会看到进程退出时,共享内存和信号量仍未释放
[email protected]:~/Desktop/SHI_XI/msgque$ ipcs -m
------------ 共享内存段 --------------
键 shmid 拥有者 权限 字节 连接数 状态0x2f2f2f2f 29425693 dxt 666 1024 0
[email protected]:~/Desktop/SHI_XI/msgque$ ipcs -s
--------- 信号量数组 -----------
键 semid 拥有者 权限 nsems
0x020718f1 0 dxt 666 1