Linux进程通信之消息队列与共享内存

消息队列

消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识

首先需要包含头文件

#include <sys/msg.h>

先是创建或者打开消息队列,其中key_t key一般用ftok来生成,pathname传入一个路径,一般是当前路径". ",proj_id随便填写一个数,要做通信的话通信的另外一端要与这个数保持一致才能找到对应的icpID,返回值为生成一个独有的数,msgflg确定消息队列的访问权限,以下为相关的flag

  • IPC_CREAT 找到当前的key对应的IPC,如果没有IPC则进行创建
  • IPC_EXCL 如果存在则返回失败
  • IPC_NOWAIT 返回值为当前ipc对应的id值
int msgget(key_t key, int msgflg);// 创建或打开消息队列:成功返回队列ID,失败返回-1
key_t ftok(const char *pathname, int proj_id);

数据传输函数,ptr指定为结构体包含内容和类型,msgflg通常设定为0表示不阻塞

int msgsnd(int msqid, const void *ptr, size_t size, int flag);// 添加消息:成功返回0,失败返回-1

数据传输函数,ptr指定为结构体包含内容和类型,msgflg通常设定为0表示阻塞

int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);// 读取消息:成功返回消息数据的长度,失败返回-1

控制消息队列函数,可以用来删除队列,对于cmd参数如下
IPC_RMID:从系统中删除该消息队列以及仍在该队列中的所有数据
IPC_SET:按由buf指向结构中的值, 设置与此队列相关结构中的msg_perm.uid, msg_perm.gid,msg_perm.mode和msg_qbytes. 该命令只有下列两种进程可以执行:
有效用户ID等于msg_perm.cuid或msg_per.uid.
具有超级用户特权的进程.
IPC_STAT:取此队列的msqid_ds结构, 并将它存放在buf指向的结构中*
IPC_INFO:获取ipc信息 和ipcs
一般使用IPC_RMID 删除队列

int msgctl(int msqid, int cmd, struct msqid_ds *buf);// 控制消息队列:成功返回0,失败返回-1

DOME

struct msgbuf{
              long mtype;       /* message type, must be > 0 */
              char mtext[128];    /* message data */
        };

//第一个进程
int main()
{
        struct msgbuf readBuf;

        key_t key;
        key=ftok(".",'z');
        if(key==-1){
			perror("key");
			exit(-1);
		}
        int msgID = msgget(key,IPC_CREAT|0777);

        if(msgID==-1){
                perror("msgget");
                exit(-1);
        }
        struct msgbuf sendBuf;
        sendBuf.mtype=988;
        memset(&sendBuf.mtext,0,128);
        strcpy(sendBuf.mtext,"this is another");
        msgsnd(msgID,&sendBuf,strlen(sendBuf.mtext),0);

        msgrcv(msgID,&readBuf,sizeof(readBuf.mtext),888,0);
        printf("read from que:%s\n",readBuf.mtext);

        msgctl(msgID,IPC_RMID,NULL);

        return 0;
}
//第二个进程
struct msgbuf{
              long mtype;       /* message type, must be > 0 */
              char mtext[128];    /* message data */
        };


int main()
{
        struct msgbuf sendBuf={888,"this is message from quen"};
        struct msgbuf readBuf;

        key_t key;
        key=ftok(".",'z');
        if(key==-1){
			perror("key");
			exit(-1);
		}
        int msgID = msgget(key,IPC_CREAT|0777);
        if(msgID==-1){
                printf("get que failer\n");
                exit(-1);
        }

        msgrcv(msgID,&readBuf,sizeof(readBuf.mtext),988,0);
        printf("read from que:%s\n",readBuf.mtext);

        msgsnd(msgID,&sendBuf,strlen(sendBuf.mtext),0);


        msgctl(msgID,IPC_RMID,NULL);
        return 0;
}

要点:消息队列中的一个节点如果被读出了就不存在了

共享内存

共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区。

特点

  • 共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
  • 因为多个进程可以同时操作,所以需要进行同步。
  • 信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。

首先包含头文件

#include <sys/shm.h>

创建共享内存,size指定内存大小 ,这里的内存最小单位1M即1024byte,flag确定消息队列的访问权限,其中IPC_CREAT找到当前的key对应的IPC,如果没有IPC则进行创建

int shmget(key_t key, size_t size, int flag);// 创建或获取一个共享内存:成功返回共享内存ID,失败返回-1

映射共享内存,其中第二个参数是用来指定将共享内存放在虚拟地址空间的什么位置的,大部分人都会将第二个参数设置为NULL的时候,表示用户并不在意,一切交由内核做主
shmaddr的地址不是NULL的时候目标是进程希望将共享内存attach到该地址,但是该地址必须是系统分页的整数倍,否则会返回EINVAL错误,内核提供了一个shmflgSHM_RND,表示该地址不是系统分页的整数倍也没关系,系统会在用户给出的地址附近,就近找一个系统分页整数倍的地址。
如果指定的shmaddr落在已经在用的地址范围内,就会导致EINVAL错误,但是linux提供了一个非标准的扩展SHNM_REMAP,这个标志为表示替换位于shmaddr处且长度为共享内存段的长度的任何内存映射,很明显,设置了SHNM_REMAP标志位,shmaddr参数就不能再为NULL了。
如果进程仅仅是读取共享内存段的内容,并不修改,则可以指定SHM_RDONLY标志位

void *shmat(int shm_id, const void *addr, int flag);// 连接共享内存到当前进程的地址空间:成功返回指向共享内存的指针,失败返回-1

断开shmat建立的关系

int shmdt(void *addr); // 断开与共享内存的连接:成功返回0,失败返回-1

根据参数cmd执行相应的操作。常用的是IPC_RMID(从系统中删除该共享内存),当cmdIPC_STATIPC_SET时需要用到第三个参数,其中shmid_ds结构体的定义如下

int shmctl(int shm_id, int cmd, struct shmid_ds *buf);// 控制共享内存的相关信息:成功返回0,失败返回-1
struct shmid_ds {
	struct ipc_perm shm_perm;       
	int     shm_segsz;              
	time_t  shm_atime;              
	time_t  shm_dtime;              
	time_t  shm_ctime;              
	unsigned short  shm_cpid;       
	unsigned short  shm_lpid;       
	short   shm_nattch;   
 ...;

DEMO

//进程一
int main()
{
        char *shmaddr;
        int shmid;
        key_t key;

        key=ftok(".",1);
        shmid= shmget(key,1024*4,IPC_CREAT|0666);

        if(shmid==-1){
                printf("shmget noOK\n");
                exit(-1);
        }

        shmaddr=shmat(shmid,0,0);
        printf("shmat ok\n");
        strcpy(shmaddr,"chenlichen");

        sleep(5);   //没有阻塞防止退出

        shmdt(shmaddr);
        shmctl(shmid,IPC_RMID,0);

        printf("quit\n");
        return 0;
}
//进程二
int main()
{
        char *shmaddr;
        int shmid;
        key_t key;

        key=ftok(".",1);
        shmid= shmget(key,1024*4,0);

        if(shmid==-1){
                printf("shmget noOK\n");
                exit(-1);
        }

        shmaddr=shmat(shmid,0,0);
        printf("shmat ok\n");
        printf("data:%s\n",shmaddr);


        shmdt(shmaddr);

        printf("quit\n");
        return 0;
}

在linux中ipcs -m可以查看有哪些共享内存,删除共享内存ipcrm -m+内存id

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值