ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

进程间通信(匿名管道 | 命名管道 | 共享内存)

2021-04-05 15:58:33  阅读:317  来源: 互联网

标签:共享内存 int 间通信 shmid 管道 进程 include


进程通信的目的

  • 数据传输:一个进程需要将它的数据发送给另一个进程
  • 资源共享:多个进程之间共享同样的资源。
  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止
    时要通知父进程)。
  • 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程所有的陷入和异常,并能够及时知道它状态的改变。

进程是具有独立性的(进程的代码和数据与另外一个进程的代码和数据是没有关系的)。可是,要通信,就意味着两个进程要进行数据交互,那么数据便有可能相关了。


要让两个进程实现通信,前提条件是得先让两个进程看到同一份资源
资源:内存空间,它由谁提供,以什么样的方式提供,这就表现了不同的通信方式。

一、管道

什么是管道?

  • 把从一个进程连接到另一个进程的一个数据流称为一个“管道”

匿名管道

#include <unistd.h>
	int pipe(int fd[2]);

功能:创建一个匿名管道。

fd:文件描述符数组,默认会以读写方式打开,把读写方式打开的文件描述符,分别保存到fd[0]和fd[1]中。

返回值:成功返回0,失败返回错误代码。

下面的代码演示了创建管道,同时创建子进程,并实现让子进程写,父进程读的过程。

#include<stdio.h>                                                                                                                                   
#include<unistd.h>
#include<string.h>
int main()
{
	int pipefd[2] = {0};
	pipe(pipefd);
	
	pid_t id = fork();
	if(id == 0)
	{
		// child, write
		close(pipefd[0]);// 关掉读
		const char* msg = "i am child......\n";
		while(1)
		{
			write(pipefd[1], msg, strlen(msg));
			sleep(1);
		}
	}
	else{
		// father, read
		close(pipefd[1]);// 关掉写
		char buff[64];
		ssize_t s = read(pipefd[0], buff, sizeof(buff)); 
		if(s > 0)
		{
			buff[s] = 0;
		}
		printf("father get message : %s\n", buff);
	}
	return 0;
}  

在这里插入图片描述
管道的四种情况(在代码层面体现的四个特征):

1、如果写端不关闭文件描述符,且不写入,此时读出条件不满足(管道为空),那么读端可能会进行长时间阻塞(状态由R变S,由运行队列放置等待队列)。
2、当我们实际在进行写入时,如果写入条件不满足(管道满了),写端会进行阻塞。
3、如果写端关闭文件描述符,读端在读取完数据后,会读取到文件结尾。
4、如果读端关闭,写端进程可能在后续被直接杀掉。因为操作系统不做任何浪费空间和低效的事情,只要发现,它就会进行修正。没有读端,写这个动作就浪费了系统资源和内存。会通过13号信号杀掉。

管道属性特征:
1、只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父子进程之间就可应用该管道。
2、管道提供流式服务
3、当进程退出时,管道释放,所以管道的生命周期随进程。
4、内核会对管道操作进行同步与互斥。
5、管道是半双工的,数据只能向一个方向流动;需要双向通信时,需要建立两个管道。

命名管道

作用:两个毫不相关的进程,实现进程通信。

使用FIFO文件通信,它经常被称为命名管道。

创建:
可以使用命令行mkfifo filename

也可以使用函数:

#include<sys/types.h>
#include<sys/stat.h>
	int mkfifo(const char *filename,mode_t mode);

用命令行创建举例:
在这里插入图片描述
可以发现,命名管道是一种特殊类型的文件,文件类型为p。
可以把fifo简单理为一种符号或标志,代表的是两个进程通过它通信。

操作系统在内存中创造管道文件,在内存通信,这样数据不会也不需要刷新到磁盘。如果真的是一个进程往文件里写,另一个进程从文件中读,那么就是IO,效率太低。

应用实例:客户端发送,服务端接收

  • server.c
#include<stdio.h>                                                          
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>

#define FIFO_FILE "./fifo"
int main()
{
	if(-1 == mkfifo("FIFO_FILE",0644))
	{
		perror("mkfifo");
		return 1;
	}
	
	//打开文件
	int fd = open(FIFO_FILE,O_RDONLY);
	if(fd >= 0)
	{
		char buff[64];
		while(1)
		{
			ssize_t s = read(fd, buff, sizeof(buff) - 1);
			if(s > 0)
			{
				buff[s] = 0;
				printf("client %s\n", buff);
			}
			else if(s == 0)
			{
				printf("client quit, my too.\n");
				break;
			}
			else{
				perror("read");
				break;
			}
		}
	}
	return 0;
}

  • client.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>

#define FIFO_FILE "./fifo"
int main()
{
	//打开文件
	int fd = open(FIFO_FILE,O_WRONLY);
	if(fd >= 0)
	{
		char buff[64];
		while(1)
		{
			printf("please enter message ");
			fflush(stdout);
		
			ssize_t s = read(0, buff, sizeof(buff) - 1);
			if(s > 0)
			{
				buff[s] = 0;
				write(fd, buff, s);
			}
		}
	}
	return 0;
}

在这里插入图片描述
匿名与命名的区别:

匿名管道由pipe函数创建并打开。
命名管道由mkfifo函数创建,打开用open。
匿名管道只能让具有亲缘关系的进程之间进行通信,常用于父子;命名管道可以两个毫不相关的进程之间进行通信。

二、共享内存

有没有绕过文件系统的通信方式?

共享内存几乎是进程通信中速度最快的方式,因为它拷贝的次数少,不需要用户到内核,内核到用户。

管道是基于文件实现通信,而共享内存是操作系统给我们提供的通信相关的数据结构,来实现通信。

管道一般是两个进程通信,而共享内存可以多个。

在这里插入图片描述
我们来认识一些使用共享内存的函数。

  • shmget函数

功能:用来创建共享内存

#include <sys/ipc.h>
#include <sys/shm.h>
	int shmget(key_t key, size_t size, int shmflg);

key:这个共享内存段名字
size:共享内存大小
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

  • shmat函数

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

#include <sys/ipc.h>
#include <sys/shm.h>
	void *shmat(int shmid, const void *shmaddr, int shmflg);

shmid:共享内存标识
shmaddr:指定连接的地址
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1

  • shmdt函数

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

#include <sys/ipc.h>
#include <sys/shm.h>
	int shmdt(const void *shmaddr);

shmaddr:由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段

  • shmctl

功能:删除共享内存

#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1

在使用这些函数之前,我们要先使用ftok函数来生成一个唯一值key。

#include <sys/types.h>
#include <sys/ipc.h>
	key_t ftok( const char * fname, int id )

ftok这两个参数可以随便填写,但要保证让在同一块共享内存通信的进程所填写的参数相同才行。也就是说,ftok的实际上是通过算法来生成一个唯一值。

举例实现共享内存的创建、关联、取消关联、删除

  • comm.h
#pragma once 
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/ipc.h>

#define PATHNAME "/tmp"
#define PROJ_ID 0x6688
#define SIZE 4096
  • server.c
#include"comm.h"

int main()
{
	//创建了key值,算法生成,保证key唯一性
	key_t k = ftok(PATHNAME, PROJ_ID);
	
	int shmid = shmget(k, SIZE, IPC_CREAT| IPC_EXCL| 0664);
	if(shmid < 0){
		perror("shmget");
		return 1;
	}
	
	printf("shmid:%d\n", shmid);
	
	//关联
	char* str = (char*)shmat(shmid, NULL, 0);
	
	while(1)
	{
		sleep(1);
		printf("%s\n",str);
	}
	
	//取消关联
	shmdt(str);
	
	//删除共享内存
	shmctl(shmid, IPC_RMID, NULL);
	return 0;
}

  • client.c
#include"comm.h"

int main()
{
	//也得调ftok,传同样的参数
	key_t k = ftok(PATHNAME, PROJ_ID);
	
	int shmid = shmget(k, SIZE, 0);
	if(shmid < 0){
		perror("shmget");
		return 1;
	}
	
	printf("shmid:%d\n", shmid);
	
	//关联
	char* str = (char*)shmat(shmid, NULL, 0);
	
	char c = 'a';
	for(; c<= 'z'; c++)
	{
		str[c - 'a'] = c;
	}
	
	//取消关联
	shmdt(str);
	
	//删除共享内存
	shmctl(shmid, IPC_RMID, NULL);
	return 0;
}

查看共享内存:ipcs -m
在这里插入图片描述
ipc资源的生命周期随内核。

共享内存底层不提供任何的同步与互斥机制,而管道提供。所以管道里为空或为满时,对应的读端或写端会被阻塞。


总结

进程间通信的本质就是看到同一份资源。
在共享内存中,这里的资源是由操作系统提供的。
而管道是由文件系统提供的,文件系统也属于操作系统。

所以进程间所有的共享资源都是由操作系统提供的,只不过是通过文件部分提供还是进程管理部分提供。

操作系统也要对所有通信的进程和资源进行管理。

标签:共享内存,int,间通信,shmid,管道,进程,include
来源: https://blog.csdn.net/qq_45872217/article/details/115435088

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有