Linux进程间通讯----管道,消息队列,信号量,共享内存

Linux进程间通讯手段主要有:管道,消息队列,信号量,共享内存,不同网络之间之间通讯会使用socket。
管道:
管道主要通过文件系统的辅助来实现通讯,有两种方式:有名管道与无名管道。
有名管道在磁盘上有一个管道文件标识,但是其进行数据交互时,并不占用磁盘空间,仅在磁盘上存在一个文件i-node节点信息。
有名管道的创建:
mafifo filename或
int mkfifo(char* filename ,int modes)
有名管道的通讯:与普通文件操作相同
int fd=open(char* file ,int flag)
close() read() write().
管道与普通文件的不同:
管道文件open()会阻塞,至少有一对读写操作同时打开才会结束阻塞,
write()时,没有正在读取数据的操作也会向管道文件写数据,直到管道满阻塞;
read()时,会发生阻塞,直到有数据可读或所有写端关闭。
无名管道同样借助文件系统,但是不需要管道文件,依赖父子进程共享fork()之间打开的文件描述符。
无名管道的使用:
int pipe(int fds[2]) fds[0] :读操作 fds[1]:写操作(fork()之前创建)
操作和有名管道一样。
消息队列,信号量,共享内存
消息队列,信号量,共享内存三者的实现原理相同,都是借助于4G虚拟地址空间上1G的内核空间上的内核对象。
内核对象:存在于1G内核空间中,可以看成一种实体 对象 结构体变量 ,纪录着内核对象的属性。
不同进程通过指定某一个相同的用户标识,来访问同一个内核对象,获取一个独一无二的内核对象标识,用于之后的进程通讯。
消息队列:
消息:类型+数据(定向(消息类型)发送数据)
队列:先进先出 优先级队列
消息被进程读取,则队列中便不再保存此条信息
消息队列的使用:
int msgget((key_t)key,int flag)//获取或创建内核对象 成功返回内核对象的id,失败返回-1
int msgsnd(int msgid ,void *ptr,size-t size,int flag)//发送消息
其中 ptr指向一个结构体变量
struct MsgData
{
long mType;//数据
char mText[];//类型
}
*int msgrcv(int msgid,void ptr,size-t size,long type ,int flag)//接收消息
释放内核对象
*int msgctl(int msgid,intcmd,struct msgid-ds buff)
cmd:IPC_RMID 从系统中删除消息队列的内核对象,并且删除还在队列中的数据 (立即删除)
消息队列 的内核对象管理命令:
ipcs -q 查看消息队列
ipcrm -q id 删除消息队列
消息队列的使用:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/msg.h>

typedef struct MsgData
{
	long mType;
	char mText[128];
}MsgData;
//消息队列的发送
int main()
{
	int msgid = msgget((key_t) 1234, 0664 | IPC_CREAT);
	assert(-1 != msgid);

	MsgData  data;
	data.mType = 100;
	strcpy(data.mText, "hello");

	msgsnd(msgid, &data, strlen(data.mText), 0);

	data.mType = 200;
	strcpy(data.mText, "world");

	msgsnd(msgid, &data, strlen(data.mText), 0);

	strcpy(data.mText, "hello");
	msgsnd(msgid, &data, strlen(data.mText), 0);

	//不能直接调用msgctl()删除消息队列    删除是立即删除
	
	exit(0);
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/msg.h>

typedef struct MsgData
{
	long mType;
	char mText[128];
}MsgData;

int main()
{
	int msgid = msgget((key_t) 1234, 0664 | IPC_CREAT);
	assert(-1 != msgid);

	MsgData  data;
	memset(&data, 0, sizeof(data));

	msgrcv(msgid, &data, 127, 100, 0);
	printf("data.type = %d\n", data.mType);
	printf("data.text = %s\n", data.mText);

	exit(0);
}

Linux进程间通讯----管道,消息队列,信号量,共享内存
信号量
信号量:类似于一个计数器,>0时,纪录临界资源的个数,=0时 临界资源用完,信号量实现进程间访问临界资源控制,来实现进程的同步(协同工作)控制。
一个内核对象维护的是一个信号量集
信号量的获取或创建:
int semget((key_t) key ,int nsems ,int flag)
nsms只有在创建时才有效,用来指定信号量集中包含信号量的个数
信号量必须在创建时完成初始化
int semctl(int semid,int semnum,int cmd ,…)
cmd:SETVAL
信号量的PV操作
int semop(int semid,struct sembuf *buf,int len)
buf :数组 len:数组长度
struct sembuf
{
int sem_num;
short sem_op;操作方式 P -1 V+1
short sem_flag;
};
信号量集的删除(立即删除)
int semctl(int semid ,int semnum,int cmd)
cmd:IPC_RMID
semnum无效,即可传任意值。
信号量集的内核对象管理命令:
ipcs -s 查看消息队列
ipcrm -s d 删除消息队列

#ifndef __SEM_H
#define __SEM_H

typedef union SemUn
{
	int  val;
}SemUn;

int  SemGet(int key,  int semVal[],  int nsems);

int  SemP(int  semid, int  index[],  int  sems);

int SemV(int semid,  int index[],  int  sems);

int  SemDel(int semid);


#endif
#include "sem.h"

#include <sys/sem.h>
#include <malloc.h>
#include <string.h>
#include <assert.h>


int SemGet(int key, int semVal[],  int nsems)
{
	int semid = semget((key_t)key,  nsems,  0664);
	if(semid == -1)
	{
		semid = semget((key_t)key,  nsems,  0664|IPC_CREAT);
		assert(semid != -1);

		int i = 0;
		for(; i < nsems; ++i)
		{
			SemUn un;
			un.val = semVal[i];

			semctl(semid, i, SETVAL, un);
		}
	}

	return semid;
}

int SemP(int semid, int index[], int sems)
{
	struct sembuf *buf = (struct sembuf*)malloc(sizeof(struct sembuf) * sems);
	assert(buf != NULL);

	int i = 0;
	for(; i < sems; ++i)
	{
		buf[i].sem_num = index[i];
		buf[i].sem_op = -1;   //   P
		buf[i].sem_flg = SEM_UNDO;
	}

	if(-1 == semop(semid,  buf,  sems))
	{
		free(buf);
		return -1;
	}

	free(buf);

	return 0;
}

int SemV(int semid, int index[], int sems)
{
	struct sembuf *buf = (struct sembuf*)malloc(sizeof(struct sembuf) * sems);
	assert(buf != NULL);

	int i = 0;
	for(; i < sems; ++i)
	{
		buf[i].sem_num = index[i];
		buf[i].sem_op = 1;   //   P
		buf[i].sem_flg = SEM_UNDO;
	}

	if(-1 == semop(semid,  buf,  sems))
	{
		free(buf);
		return -1;
	}

	free(buf);
	return 0;
}

int SemDel(int semid)
{
	return semctl(semid, 0, IPC_RMID);
}
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>

#include "sem.h"

int main()
{
	int semVal = 1;
	int semid = SemGet(1234, &semVal, 1);
	printf("semid = %d\n", semid);
	pid_t pid = fork();
	assert(pid != -1);

	if(pid == 0)
	{
		int i = 0;
		for(; i < 10; ++i)
		{
			int index = 0;
			SemP(semid, &index, 1);
			printf("A\n");
			sleep(1);
			SemV(semid, &index, 1);
		}
	}
	else
	{
		int i = 0;
		for(; i < 10; ++i)
		{
			int index = 0;
			SemP(semid, &index, 1);
			printf("B\n");
			sleep(1);
			SemV(semid, &index, 1);
		}
	}
}

Linux进程间通讯----管道,消息队列,信号量,共享内存
**共享内存:*最快的进程间通讯方式,共享内存是临界资源,所以必须使用信号量来控制进程同步
int shmget((key_t key)key,size_t size,int flag)size:共享内存的大小
使用shmget创建内核对象,内核对象创建完成后,内核对象在内存区域创建一块共享内存区域(物理内存);失败 返回-1 成功返回内核对象的id;
void
shmat(int shmid,voidaddr,int flag) addr常为null
int shmdt(void
shmptr)//断开链接,仅仅断开本进程指针与共享存储区域的连接
int shmctl(int shmid,int cmd ,struct shmid ds * buf)cmd:IPC_STAT IPC_SET IPC_RMID
对共享内存的内核对象的管理命令:
ipcs -m
ipcs -m id