Linux-进程间通信

进程间通信

为什么要有进程间通信?

早期计算机任务简单一个进程就可以搞定,但是随着计算机的飞速发展,为了完成一个任务需要许多进程来共同协作,这就需要不同进程间来进行数据交互,这就是进程间通信。

进程间通信的目的:

数据传输:一个进程把数据发给另一个进程
资源共享:不同进程间需要用到同一种资源
事件通知:一个进程需要向另一个进程通知了某些事件的发生(子进程退出发给父进程信号)
进程控制:一个进程希望控制另一个进程

进程间通信的本质就是内核维护了一块公共缓冲区

管道

管道是最古老的进程间通信方式,一个进程连接到另一个进程的数据流称为一个管道。

匿名管道

管道创建

#include <unistd.h>
int pipe(int fd[2]); //fd是文件描述符组,fd[0]代表读端,fd[1]代表写端
//创建成功返回0, 失败返回错误代码

我们可以看一个例子,父进程创建一个管道,父进程向管道写数据,子进程从管道读数据:

int main()
{
	int pipefd[2] = {-1};
	
	if(pipe(pipefd) < 0){
		perror("pipe");
		return -1;
	}
	
	if(fork() == 0){ //child read data
		close(pipefd[1]);
		while(1){
			char buf[1024] ={0};
			read(pipefd[0], buf, sizeof(buf)-1);
			printf("buf:[%s]\n", buf);
		}
	}else{ // father write data
		close(pipefd[0]);
		while(1){
			char *str = "i am father!";
			write(pipefd[1], str, strlen(str));
			printf("---------------------");
		}
		wait(NULL);
	}
	return 0;
}

从结果可以看出来,管道中没有数据,read阻塞,进程暂时停止,一直等到数据来为止
当管道满了,write调用阻塞,直到有进程读走数据在这里插入图片描述
匿名管道的特点:

只能作用于有亲缘关系的进程之间的通信,通常一个匿名管道由一个进程创建,然后该进程调用fork,此后父子进程可以通过管道通信
进程退出,匿名管道释放,所以匿名管道的声明周期随进程
内核会对管道操作进行同步与互斥
管道是半双工的,即数据只能向一个方向流动,双方都需要通信时,需要建立两个管道

命名管道

匿名管道只能在亲缘进程间通信,我们想要让不想关的进程来进行通信,我们可以使用命名管道。

//使用命令创建命名管道
mkfifo filename
//使用函数
int mkfifo(const char *filename, mode_t mode);

命名管道个匿名管道使用方法类似,这里不在多说

消息队列

消息队列是一个进程向另一个进程发送一块数据
每个数据块被认为是有一个类型的,接收进程根据接收的数据块可以有不同的类型值
消息队列每个消息的最大长度是有上限的,可以查看/proc/sys/kernel/msgmax
每个消息队列的总字节数也是有上限的,在/proc/sys/kernel/msgmnb中
系统上消息队列的总是也有一个上限,在/proc/sys/kernel/msgmni中

创建一个消息队列

int msgget(key_t key, int msgflg); //key是消息队列的名字,msgflg是权限
//成功返回消息队列的标识码,失败返回-1

把一条消息添加到消息队列中

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
//msqid:由msgget函数返回的消息队列标识码
//msgp:指向准备发送的消息
//msgsz:消息的长度
//msgflg:当消息队列满或者到系统上限时要发生的事

从一个消息队列中接收消息

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
//msqid:由msgget函数返回的消息队列标识码
//msgp:指向准备接收的消息
//msgsz:消息的长度
//msgtype:实现接收优先级的简单形式
//msgflg:如果消息队列中没有相应类型的时候将要干的事

使用命令ipcs可以显示IPC资源,ipcrm可以手动删除IPC资源

共享内存

共享内存是最快的IPC方式,操作系统会在内核维护一段内存,将这块内存映射到进程的虚拟地址空间,此后进程间的数据传递不在涉及到内核,进程不再通过执行进入内核的系统调用来传递彼此数据

创建共享内存:

int shmget(key_t key, size_t size, int shmflg);

将共享内存段连接到进程地址空间:

void *shmat(int shmid, const void *shmaddr, int shmflg);
//shmaddr是指定连接的地址

将共享内存段与当前进程脱离:

int shmdt(const void *shmaddr);
//shmaddr是由shmat返回的指针

控制共享内存

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

一个进程建立共享内存向其中写数据

int main()
{
	//创建共享内存
	int shmid = shmget(0x66666, 32, IPC_CREATE|0664);
	if(shmid < 0){
		perror("shmget");
		return -1;
	}
	//建立映射
	void *mat = shmat(shmid, NULL, 0);
	if(mat == (void *)-1){
		perror("shmat");
		return -1;
	}
	int i = 0;
	while(1){
		sprintf(mat, "AAAA:%d\n", i++);
		sleep(1);
	}
	
	shmdt(mat);
	
	//删除共享内存
	shmctl(shmid, IPC_RMID, NULL);
	
	return;
}

我们可以看到进程像操纵自己的虚拟地址一样来操作共享内存,所以就决定了共享内存是最快的进程间通信方式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值