Linux 进程间通信(管道,消息队列,共享内存)

进程间通信分类

管道

  • 匿名管道pipe
  • 命名管道

System V IPC

  • System V 消息队列
  • System V 共享内存
  • System V 信号量

POSIX IPC

  • 消息队列
  • 共享内存
  • 信号量
  • 互斥量
  • 条件变量
  • 读写锁

管道

匿名管道

#include <unistd.h>
功能:创建匿名管道
原型
int pipe(int fd[2]);
参数
fd:文件描述符数组,其中fd[0]表⽰读端, fd[1]表示写端
返回值:成功返回0,失败返回错误代码。
例:从键盘读取数据,写入管道,读取管道,写到屏幕
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>

int main(void)
{
	int fds[2];
	char buf[100];
	int len;
	
	if(pipe(fds) == -1)
		perror("make pipe"),exit(1);

	//read from stdin
	while(fgets(buf,100,stdin))
	{
		len = strlen(buf);
		//write into pipe
		if(write(fds[1],buf,len) != len)
		{
			perror("write to pipe");
			break;
		}
		memset(buf,0x00,sizeof(buf));

		//read from pipe
		if((len=read(fds[0],buf,100))==-1)
		{
			perror("read from pipe");
			break;
		}

		//write to stdout
		if(write(1,buf,len) != len)
		{
			perror("write to stdout");
			break;
		}
	}
}

运行结果:


管道读写规则

当没有数据可读时
O_NONBLOCK disable:read调⽤阻塞,即进程暂停执⾏,⼀直等到有数据来到为⽌。

O_NONBLOCK enable:read调⽤返回-1,errno值为EAGAIN。

当管道满的时候
O_NONBLOCK disable: write调⽤阻塞,直到有进程读⾛数据
O_NONBLOCK enable:调⽤返回-1,errno值为EAGAIN
如果所有管道写端对应的⽂件描述符被关闭,则read返回0
如果所有管道读端对应的⽂件描述符被关闭,则write操作会产⽣信号SIGPIPE,进⽽可能导致write
进程退出
当要写⼊的数据量不⼤于PIPE_BUF时,linux将保证写⼊的原⼦性。

当要写⼊的数据量⼤于PIPE_BUF时,linux将不再保证写⼊的原⼦性。

管道特点

只能⽤于具有共同祖先的进程(具有亲缘关系的进程)之间进⾏通信;通常,⼀个管道由⼀个进程
创建,然后该进程调⽤fork,此后⽗、⼦进程之间就可应⽤该管道。
管道提供流式服务。
⼀般⽽⾔,进程退出,管道释放,所以管道的⽣命周期随进程
⼀般⽽⾔,内核会对管道操作进⾏同步与互斥

管道是半双⼯的,数据只能向⼀个⽅向流动;需要双⽅通信时,需要建⽴起两个管道。

命名管道

创建⼀个命名管道

命名管道可以从命令⾏上创建,命令⾏⽅法是使⽤下⾯这个命令:

$ mkfifo filename

命名管道也可以从程序里创建,相关函数有:

int mkfifo(const char *filename,mode_t mode);
int main(int argc, char *argv[])
{
mkfifo("p2", 0644);
return 0;
}

匿名管道与命名管道的区别

匿名管道由pipe函数创建并打开。
命名管道由mkfifo函数创建,打开⽤open
FIFO(命名管道)与pipe(匿名管道)之间唯⼀的区别在它们创建与打开的⽅式不同,⼀但这些

⼯作完成之后,它们具有相同的语义。


命名管道的打开规则

如果当前打开操作是为读⽽打开FIFO时
O_NONBLOCK disable:阻塞直到有相应进程为写⽽打开该FIFO
O_NONBLOCK enable:⽴刻返回成功
如果当前打开操作是为写⽽打开FIFO时
O_NONBLOCK disable:阻塞直到有相应进程为读⽽打开该FIFO

O_NONBLOCK enable:⽴刻返回失败,错误码为ENXIO、


例:⽤命名管道实现server&client通信

服务器

server.c

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>

#define ERR_EXIT(m)\
do{\
	perror(m);\
	exit(EXIT_FAILURE);\
}while(0);


int main()
{
	umask(0);
	if(mkfifo("mypipe",0644) < 0)
	{
		ERR_EXIT("mkfifo");
	}
	int rfd = open("mypipe",O_RDONLY);
	if(rfd < 0)
	{
		ERR_EXIT("open");
	}

	char buf[1024];
	while(1)
	{
		buf[0] = 0;
		printf("please wait...\n");
		ssize_t s = read(rfd,buf,sizeof(buf)-1);
		if(s > 0)
		{
			buf[s-1] = 0;
			printf("client say# %s\n",buf);
		}
		else if(s == 0)
		{
			printf("client quit,exit now!\n");
			exit(EXIT_SUCCESS);
		}
		else
		{
			ERR_EXIT("read");
		}
	}

	close(rfd);
	return 0;
}

客户端

client.c

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>

#define ERR_EXIT(m)\
do{\
    perror(m);\
	exit(EXIT_FAILURE);\
}while(0);


int main()
{
	int wfd = open("mypipe",O_WRONLY);
	if(wfd < 0)
	{
		ERR_EXIT("open");
	}

	char buf[1024];
	while(1)
	{
		buf[0] = 0;
		printf("please enter# ");
		fflush(stdout);
		ssize_t s = read(0,buf,sizeof(buf)-1);
		if(s > 0)
		{
			buf[s] = 0;
			write(wfd,buf,strlen(buf));
		}
		else if(s <= 0)
		{
			ERR_EXIT("read");
		}
	}

	close(wfd);
	return 0;
}

运行结果:



消息队列

消息队列提供了⼀个从⼀个进程向另外⼀个进程发送⼀块数据的⽅法
每个数据块都被认为是有⼀个类型,接收者进程接收的数据块可以有不同的类型值
消息队列也有管道⼀样的不⾜,就是每个消息的最⼤⻓度是有上限的(MSGMAX),每个消息队
列的总的字节数是有上限的(MSGMNB),系统上消息队列的总数也有⼀个上限(MSGMNI)

msgget函数
功能:用来创建和访问一个消息队列
原型
int msgget(key_t key, int msgflg);
参数
key: 某个消息队列的名字
msgflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该消息队列的标识码;失败返回-1
msgctl函数
功能:消息队列的控制函数
原型
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
参数
msqid: 由msgget函数返回的消息队列标识码
cmd:是将要采取的动作,(有三个可取值)
返回值:成功返回0,失败返回-1
cmd:将要采取的动作(有三个可取值),分别如下:


msgsnd函数
功能:把一条消息添加到消息队列中
原型
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数
msgid: 由msgget函数返回的消息队列标识码
msgp:是一个指针,指针指向准备发送的消息,
msgsz:是msgp指向的消息长度,这个不度不含保存消息类型的那个long int长整型
msgflg:控制着当前消息队列满或到达系统上限时将要发生的事情
msgflg=IPC_NOWAIT表示队列满不等待,返回EAGAIN错误。
返回值:成功返回0;失败返回-1
说明:
1.消息结构在两⽅⾯受到制约:
⾸先,它必须⼩于系统规定的上限值;
其次,它必须以⼀个long int⻓整数开始,接收者函数将利⽤这个⻓整数确定消息的类型
2.消息结构参考形式如下:
struct msgbuf {
long mtype;
char mtext[1];
}
msgrcv函数

功能:是从一个消息队列接收消息
原型
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
参数
msgid: 由msgget函数返回的消息队列标识码
msgp:是一个指针,指针指向准备接收的消息,
msgsz:是msgp指向的消息长度,这个长度不含保存消息类型的那个long int长整型
msgtype:它可以实现接收优先级的简单形式
msgflg:控制着队列中没有相应类型的消息可供接收时将要发生的事
返回值:成功返回实际放到接收缓冲区里去的字符个数,失败返回-1
代码如下:

#pragma once

#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<string.h>

#define PATHNAME "."
#define PROJ_ID 0x6666

#define SERVER_TYPE 1
#define CLIENT_TYPE 2

struct msgbuf
{
	long mtype;
	char mtext[1024];
};

int createMsgQueue();
int getMsgQueue();
int destroyMsgQueue(int msgid);
int sendMsg(int msgid,int who,char *msg);
int recMsg(int msgid,int recvType,char out[]);
#include"comm.h"

//success > 0 failed == -1

static int commMsgQueue(int flags)
{
	key_t _key = ftok(PATHNAME,PROJ_ID);
	if(_key < 0)
	{
		perror("ftok");
		return -1;
	}
	//int msgid = msgget(_key,IPC_CREAT|IPC_EXCL);
	int msgid = msgget(_key,flags);
	if(msgid < 0)
	{
		perror("megget");
	}

	return msgid;
}

int createMsgQueue()
{
	return commMsgQueue(IPC_CREAT|IPC_EXCL|0666);
}

int getMsgQueue() 
{
	return commMsgQueue(IPC_CREAT);
}

int destroyMsgQueue(int msgid)
{
	if(msgctl(msgid,IPC_RMID,NULL)<0)
	{
		perror("msgctl");
		return -1;
	}
	return 0;
}

int sendMsg(int msgid,int who,char *msg)
{
	struct msgbuf buf;
	buf.mtype = who;
	strcpy(buf.mtext,msg);

	if(msgsnd(msgid,(void*)&buf,sizeof(buf.mtext),0) < 0)
	{
		perror("msgsnd");
		return -1;
	}

	return 0;
}


int recMsg(int msgid,int recvType,char out[])
{
	struct msgbuf buf;
	if(msgrcv(msgid,(void*)&buf,sizeof(buf.mtext),recvType,0) < 0)
	{
		perror("msgrcv");
		return -1;
	}
	strcpy(out,buf.mtext);
	return 0;
}
#include"comm.h"

int main()
{
	int msgid = createMsgQueue();

	char buf[1024];

	while(1)
	{
		buf[0] = 0;
		recMsg(msgid,CLIENT_TYPE,buf);
		printf("[client#] %s\n",buf);

		printf("please enter#");
		fflush(stdout);
		ssize_t s = read(0,buf,sizeof(buf));
		if(s > 0)
		{
			buf[s-1] = 0;
			sendMsg(msgid,SERVER_TYPE,buf);
			printf("send done,wait recv...\n###################################\n");
		}
	}
	destroyMsgQueue(msgid);
	return 0;
}
#include "comm.h"

int main()
{
	int msgid = getMsgQueue();

	char buf[1024];

	while(1)
	{
		buf[0] = 0;
		printf("please enter#");
		fflush(stdout);
		ssize_t s = read(0,buf,sizeof(buf));
		if(s > 0)
		{
			buf[s-1] = 0;
			sendMsg(msgid,CLIENT_TYPE,buf);
			printf("send done,wait recv...\n================================\n");
		}

		recMsg(msgid,SERVER_TYPE,buf);
		printf("[server#] %s\n",buf);
	}
	return 0;
}

运行结果:

服务器:

客户端:

ipcs & ipcrm命令

  • ipcs: 显示IPC资源
  • ipcrm: 手动删除IPC资源
结果显示:


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值