Linux之进程间通信(二):共享内存、信号灯集、消息队列

目录

共享内存

特点

步骤

函数接口

系统命令

代码演示

信号灯集

特点

步骤

命令

函数接口

代码演示

消息队列

特点

步骤

操作命令

函数接口

代码演示


提醒:Linux之进程间通信(一):无名管道、有名管道、信号量

共享内存

特点

        1共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝

        2为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间

        3进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高的效率。

        4由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等

步骤

        1.创建key值

        2.创建或打开共享内存

        3.映射共享内存到用户空间

        4.撤销映射

        5.删除共享内存

函数接口

key_t ftok(const char *pathname, int proj_id);
功能:产生一个独一无二的key值
参数:
	Pathname:已经存在的可访问文件的名字
	Proj_id:一个字符(因为只用低8位)
返回值:成功:key值
	      失败:-1
int shmget(key_t key, size_t size, int shmflg);
功能:创建或打开共享内存
参数:
	key  键值
        size   共享内存的大小
        shmflg   IPC_CREAT|IPC_EXCL|0777
返回值:成功   shmid
              出错    -1
void  *shmat(int  shmid,const  void  *shmaddr,int  shmflg);
功能:映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
参数:
	shmid   共享内存的id号
        shmaddr   一般为NULL,表示由系统自动完成映射
                      如果不为NULL,那么有用户指定
        shmflg:SHM_RDONLY就是对该共享内存只进行读操作
                            0     可读可写		
返回值:成功:完成映射后的地址,
	      出错:-1的地址
	用法:if((p = (char *)shmat(shmid,NULL,0)) == (char *)-1)
int shmdt(const void *shmaddr);
功能:取消映射
参数:要取消的地址
返回值:成功0  
	      失败的-1
int  shmctl(int  shmid,int  cmd,struct  shmid_ds   *buf);
功能:(删除共享内存),对共享内存进行各种操作
参数:
	shmid   共享内存的id号
      	cmd     IPC_STAT 获得shmid属性信息,存放在第三参数
             	    IPC_SET 设置shmid属性信息,要设置的属性放在第三参数
                    IPC_RMID:删除共享内存,此时第三个参数为NULL即可	
返回:  成功0 
	    失败-1
用法:shmctl(shmid,IPC_RMID,NULL);

系统命令

ipcs -m : 查看共享内存

ipcrm -m shmid: 删除共享内存

代码演示

#include 
#include 
#include 
#include 
#include 
#include 
int main(int argc, const char *argv[])
{
	key_t key;
	int shmid;
	char *p=NULL;
    key=ftok("./app",'b');//创建key值
	if(key < 0)//判断key值是否创建成功
	{
		perror("ftok err\n");
		return -1;
	}
	shmid = shmget(key,64,IPC_CREAT|IPC_EXCL|0666);//创建内存空间
	if(shmid <= 0)//判断
	{
		if(errno==EEXIST)//排除已存在的情况,让其继续执行
			shmid = shmget(key,64,0666);
		else
		{
			perror("shmget error\n");
			return -1;
		}
	}
	printf("shmid:%d  key:%#x\n",shmid,key);
	p =(char *)shmat(shmid ,NULL,0);//映射
	if(p ==(char*)-1)//判断映射是否成功
	{
		perror("shmat error\n");
		return -1;
	}
	strcpy(p,"hello");
	printf("%s\n",p);
    shmdt(p);//取消映射
    shmctl(shmid,IPC_RMID,NULL);//删除内存

	return 0;
}

信号灯集

特点

信号灯(semaphore),也叫信号量。它是不同进程间或一个给定进程内部不同线程间同步的机制;System V的信号灯是一个或者多个信号灯的一个集合。其中的每一个都是单独的计数信号灯。而Posix信号灯指的是单个计数信号灯。

通过信号灯集实现共享内存的同步操作

步骤

  1. 创建或打开信号灯集:semget

  2. 初始化信号灯:semctl

  3. PV操作:semop

  4. 删除信号灯集:semctl

命令

ipcs -s:查看信号灯集

ipcrm -s semid:删除信号灯集

函数接口

int semget(key_t key, int nsems, int semflg);
功能:创建/打开信号灯
参数:key:ftok产生的key值
	nsems:信号灯集中包含的信号灯数目
	semflg:信号灯集的访问权限,通常为IPC_CREAT |0666
返回值:成功:信号灯集ID
	      失败:-1
int semop ( int semid, struct sembuf  *opsptr,  size_t  nops);
功能:对信号灯集合中的信号量进行PV操作
参数:semid:信号灯集ID
          struct sembuf {
   		short  sem_num; // 要操作的信号灯的编号
   		short  sem_op;  
			 //    0 :  等待,直到信号灯的值变成0
                         //   1  :  释放资源,V操作
                        //   -1 :  分配资源,P操作                    
   	     	short  sem_flg; 
			 // 0(阻塞),IPC_NOWAIT, SEM_UNDO
	  };
        nops:  要操作的信号灯的个数 1
返回值:成功 :0
	      失败:-1
用法:申请资源 P操作:
		mysembuf.sem_num = 0;
		mysembuf.sem_op = -1;
		mysembuf.sem_flg = 0;
		semop(semid, &mysembuf, 1);
	   释放资源 V操作:
		mysembuf.sem_num = 0;
		mysembuf.sem_op = 1;
		mysembuf.sem_flg = 0;
		semop(semid, &mysembuf, 1);
int semctl ( int semid, int semnum,  int cmd…/*union semun arg*/);
功能:信号灯集合的控制(初始化/删除)
参数:semid:信号灯集ID
	   semnum: 要操作的集合中的信号灯编号
	   cmd:
			GETVAL:获取信号灯的值,返回值是获得值
			SETVAL:设置信号灯的值,需要用到第四个参数:共用体
			IPC_RMID:从系统中删除信号灯集合
返回值:成功 0
	      失败 -1
用法:初始化:
	union semun{
		int val;
	}mysemun;
	mysemun.val = 10;
	semctl(semid, 0, SETVAL, mysemun);	
	获取信号灯值:函数semctl(semid, 0, GETVAL)的返回值
	删除信号灯集:semctl(semid, 0, IPC_RMID);

代码演示

#include 
#include 
#include 
#include 
#include 
#include 
union semun
{
	int val;//信号灯的值
};

int main(int argc, const char *argv[])
{
	key_t key;
	int semid;
	union semun mysem;
	struct sembuf buf;
	if((key = ftok(".",'a'))<0)
	{
		perror("ftok err");
		return -1;
	}
	//创建或打开信号灯集
	semid = semget(key,2,IPC_CREAT|IPC_EXCL|0666);
	if(semid<=0)
	{
		if(errno == EEXIST)
			semid = semget(key,2,0666);
		else
		{
			perror("semget error\n");
			return -1;
		}
	}
	//信号灯集中灯的初始化
	mysem.val=10;
	semctl(semid,0,SETVAL,mysem);//编号为0的信号灯的值初始化为10;

	mysem.val=0;
	semctl(semid,1,SETVAL,mysem);//编号为1的初始化为0;
	printf("%d\n",semctl(semid,0,GETVAL));
	printf("%d\n",semctl(semid,1,GETVAL));
	
	buf.sem_num = 0;//信号灯的编码
	buf.sem_op = -1;//申请资源p操作
	buf.sem_flg = 0;//0表示阻塞
	semop(semid,&buf,1);//1:表示呀操作的信号灯个数

	buf.sem_num = 1;
	buf.sem_op = 1;//释放资源v操作
	buf.sem_flg = 0;
	semop(semid,&buf,1);
	printf("%d\n",semctl(semid,0,GETVAL));//获取信号灯的值
	printf("%d\n",semctl(semid,1,GETVAL));
	semctl(semid,0,IPC_RMID);//删除信号灯集
	return 0;
}

消息队列

特点

消息队列是IPC对象的一种

消息队列由消息队列ID来唯一标识

消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等。

消息队列可以按照类型来发送/接收消息

步骤

        1产生key值ftok

        2创建或打开消息队列

        3添加消息:按照类型把消息添加到已打开的消息队列末尾

        4读取消息:可以按照类型把消息从消息队列中取走

        5删除消息队列

操作命令

ipcs -q :查看消息队列

ipcrm -q msgid :删除消息队列

函数接口

int msgget(key_t key, int flag);
功能:创建或打开一个消息队列
参数:  key值
	     flag:创建消息队列的权限IPC_CREAT|IPC_EXCL|0666
返回值:成功:msgid
	      失败:-1
int msgsnd(int msqid, const void *msgp, size_t size, int flag); 
功能:添加消息
参数:msqid:消息队列的ID
	   msgp:指向消息的指针。常用消息结构msgbuf如下:
			struct msgbuf{
    			long mtype;          //消息类型
    			char mtext[N]}//消息正文
	   size:发送的消息正文的字节数
	   flag:IPC_NOWAIT消息没有发送完成函数也会立即返回			    
                        0:直到发送完成函数才返回
返回值:成功:0
	     失败:-1
使用:msgsnd(msgid, &msg,sizeof(msg)-sizeof(long), 0)
注意:消息结构除了第一个成员必须为long类型外,其他成员可以根据应用的需求自行定义。
int msgrcv(int msgid,  void* msgp,  size_t  size,  long msgtype,  int  flag);
功能:读取消息
参数:msgid:消息队列的ID
	  msgp:存放读取消息的空间
	  size:接受的消息正文的字节数
	  msgtype:
		0:接收消息队列中第一个消息。
		大于0:接收消息队列中第一个类型为msgtyp的消息.
		小于0:接收消息队列中类型值不小于msgtyp的绝对值且类型值又最小的消息。
	  flag:0:若无消息函数会一直阻塞
		IPC_NOWAIT:若没有消息,进程会立即返回ENOMSG
返回值:成功:接收到的消息的长度
	      失败:-1
int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
功能:对消息队列的操作,删除消息队列
参数:msqid:消息队列的队列ID
	  cmd:
		IPC_STAT:读取消息队列的属性,并将其保存在buf指向的缓冲区中。
		IPC_SET:设置消息队列的属性。这个值取自buf参数。
		IPC_RMID:从系统中删除消息队列。
	  buf:消息队列缓冲区
返回值:成功:0
	      失败:-1
用法:msgctl(msgid, IPC_RMID, NULL

        消息队列中没有消息时,msgrcv函数阻塞;消息队列类似管道数据读走后消息队列中就没有此数据

代码演示

#include 
#include 
#include 
#include 
#include 
struct msgbuf
{
	long mtype;
	char ch;
	int num;
};
int main(int argc, const char *argv[])
{
	key_t key;
	int msgid;
	struct msgbuf buf,data;
	if((key =ftok(".",'a'))<0)
	{
		perror("ftok err");
		return -1;
	}
	msgid = msgget(key,IPC_CREAT|IPC_EXCL|0666);
	if(msgid <= 0)
	{
	 	if(errno == EEXIST)
			msgid = msgget(kyy,0666);
		else
		{
			perror("msgget err");
			return -1;
		}
	}
	size_t s = sizeof(buf)-sizeof(long);
	buf.mtype = 100;
	buf.ch ='a';
	buf.num = 123;
	//增加消息
	msgsnd(msgid,&buf,s,0);


	msgrcv(msgid,&data,s,100,0);//读取数据类型为100的数据
	printf("%c %d\n",data.ch,data.num);

    //消息被读出,阻塞
	//msgrcv(msgid,&data,s,100,0);
	//printf("%c %d\n",data.ch,data.num);

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

你可能感兴趣的:(java,jvm,开发语言,linux)