[Linux]进程间通信
Posted 一个正直的男孩
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Linux]进程间通信相关的知识,希望对你有一定的参考价值。
文章目录
1 进程间通信
进程间通信的本质:让进程看到同一块空间
,俩进程对这一块内存进行读写。其通信的作用就是传输数据,资源共享
1.1 通信方式
- 管道(系统自带)
- System V (主机内)
- POSIX 进程间通信(常用于网络传输)
1.1.1 管道
管道是如何传输的呢?其实很好理解,在现实生活中管一般都是**单向传输
**(一头传输,一头接收),其操作系统中管道也是如此。
在操作系统中有俩种管道
- 匿名管道
- 命名管道
1.1.1.1 匿名管道
常用于有亲属关系的进程(也就是进程自己fork出来的),也就是”父子“进程间通信
,利用的其实就是子进程所有的数据其实都是从父进程那拷贝的。那么父进程可以提前开打开一个文件
,在创建子进程,那么父子进程就可以依靠这个文件进行通信。(涉及到了文件的知识)
如图所示: >
问
那向文件写入不会写实拷贝吗?
答
其实我们并不是写入到文件中,而是写入到改文件的缓冲区。我们也没有修改数据和代码呀
实现父子进程间通信
- 父进程读写方式分别打开(毕竟如果子进程要读或者写总不可能自己打开吧)
- 各自关闭一个接口(管道是单向传输的,其实也可以不关)
- 传输数据与接收数据
C语言有个接口可以直接以读写方法打开,pipe很好就是管道
代码
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include<string.h>
int main()
int pipefd[2];
pipe(pipefd);//读写方式打开了同文件
pid_t pid= fork();
if(pid<0)//创建失败
perror("fork");
return 1;
else if(pid>0)//子进程
close(pipefd[0]);
const char *str="Hello i am child course";
int count =3;
while(count>0)
count--;
write(pipefd[1],str,strlen(str));//写入
sleep(1);
//写完后子进程退出
printf("child course just writing done ,now eixt\\n");
exit(1);//
else //父进程
close(pipefd[1]);
char buffer[1024];
while(1)
buffer[0]=0;//初始化
//从缓冲区中读取
ssize_t ret=read(pipefd[0],buffer,sizeof(buffer)-1);
buffer[ret]=0;
if(ret==0)
break;
else
printf("the massage form child course $$%s\\n",buffer);
//回收资源
int status;
wait(&status);
return 0;
运行结果:
底层运行原理
匿名管道的特性
- 只读不写 (读阻塞等待)
- 只写不读 (写阻塞等待,前提是IO流没有满)
- 读关闭,一直写入 (os会发信号杀死进程提高效率与节省资源)
- 一直读,写关闭 (read函数读取数为0,退出)
1.1.1.2 命名管道
匿名管道这种方式显然只适用于有血缘关系的进程,那么俩个毫无关系的进程如何进行通信呢?依旧坚持一个理念让不同的进程看到同一块空间
,而命名管道使用的是管道文件
(普通文件但或许会出错)
不同进程间通信
- 创建一块空间
- 一个写入一个接收
如何创建空间
C语言提供了一个接口
mkfifo创建一个管道文件
管道文件
代码:
service
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include <fcntl.h>
#include<string.h>
#include<stdlib.h>
int main()
if(mkfifo("fpp",0666)==-1)//创建管道文件
perror("mkfifo");
return 1;
int fd=open("fpp",O_RDONLY);//打开文件
//读取另一个进程的写入的数据
char *buffer[1024];
while(1)
buffer[0]=0;
ssize_t ret=read(fd,buffer,sizeof(buffer)-1);
buffer[ret]=0;
printf("massage## %s",buffer);
close(fd);
return 0;
client
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include <fcntl.h>
int main()
int fd=open("fpp",O_O_WRONLY);
while(1)
char str[64];
str[0]=0;
printf("请输入##### ");
fflush(stdout);
ssize_t ret=read(0,str,sizeof(str)-1);//从标准输入中获取
str[ret]=0;
write(fd,str,strlen(str));//将数据写入文件中
运行效果
管道的特性
1.
结论
从上述俩个管道其实可以得出一个结论,管道这种通行方式所
看到的同一块空间其实就是文件
1.1.2 System V
相比上面管道这种方式通信显然无法满足,假如我要多个进程读写呢?管道只适用于俩个进程间通信(毕竟只有俩个端口),那么就有一个新的通信方式System V
System V通信方式
在内存中开辟一块空间
,再把这块空间通过页表 映射到共享区中
,因为内存是 在内存中
的。那么别的进程也可以映射到自己的共享区中
,这就是所谓的System V的通信方式,前提是可以找到这块内存。
如何寻找
如果可以申请一块空间,那我如果没有限制的话我就可以一直申请,那么假如没有内存的限制,空间一多是否需要管理,且OS的工作是啥,进程、内存、驱动、文件管理。
先描述在组织
,在用一个数据结构维护起来。
如何创建共享内存呢?
C语言中有个接口shmge可以获取一块内存
问
如何获取唯一的key值呢???
答
C语言也提供了一个接口ftok
问
那那问题又来了,pathname和proj_id如何别的进程如何知道呢?不是需要唯一的key值吗
答
这个解决方法有很多,可以包同一个头文件,或者把他们写在同一个文件,然后读文件获取也可以
shared.h 头文件让不同进进程可以生成同一个pathname和proj_id
#pragma once
#include<stdio.h>
#define PNAME "/home/wxb/linux/day3"
#define PID 0x6666
#define SIZE 4096 //内存块大小
service.c 服务端接收数据
- 开辟共享内存与释放共享内存
如何查看,释放共享内存呢?
查看共享内存
有俩种方法1: shmctrl()C语言接口 2:ipcrm -shmid(标识符)指令
代码
#include "shared.h"
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<unistd.h>
int main()
key_t key=ftok(PNAME,PID);//创建唯一的key值
int shmid=shmget(key,SIZE,IPC_CREAT|IPC_EXCL|0666);
if(shmid < 0)//生气共享内存
perror("shmget");//申请失败
return 1;
sleep(5);
shmctl(shmid,0,NULL);
return 0;
client 写入数据
#include "shared.h"
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<unistd.h>
int main()
key_t key=ftok(PNAME,PID);//创建唯一的key值
int shmid=shmget(key,SIZE,IPC_CREAT);//存在获取之
if(shmid < 0)//生气共享内存
perror("shmget");//申请失败
return 1;
return 0;
运行结果
使用共享内存
既然共享内存已经开辟那么,如何使用呢?shmat() 将进程挂接到开辟的共享内存
中,既然可以挂接那么自然也要去挂接,shmdt()
那么shmat操作怎么和语言层面上的new malloc 一样 ,在堆上申请空间不也是如此吗?在内存申请空间在进行映射
service.c
char *str=(char*)shmat(shmid,NULL,0);
//to do
shmdt(str);
client.c
char *str=(char*)shmat(shmid,NULL,0);
//to do
shmdt(str);
问
如何查看一个共享内存的挂接数呢?
答
ipcs -m 面板中有一个 nattch这一列代表该共享内存的挂接数
共享内存双进程通信完整代码
service.c
#include"shared.h"
#include<sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include<unistd.h>
int main()
//创建
key_t key=ftok(PNAME,PID);
int shmid=shmget(key,SIZE,IPC_CREAT|IPC_EXCL|0666);//不存在创建,存在报错
if(shmid<0)
perror("shmget\\n");
return 1;
//挂接共享内存
char * sh_mem= (char*)shmat(shmid,NULL,0);
//从共享内存中读取
while(1)
printf("%s\\n",sh_mem);
sleep(1);
//使用完毕取消(服务端可取可不取)
shmdt(sh_mem);
//释放共享内存
shmctl(shmid,0,NULL);
client.h
#include"shared.h"
#include<sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include<unistd.h>
int main()
//创建
key_t key=ftok(PNAME,PID);
int shmid=shmget(key,SIZE,IPC_CREAT);
if(shmid<0)
perror("shmget\\n");
return 1;
//挂接共享内存
char *sh_mem=(char*)shmat(shmid,NULL,0);
//传输数据到共享内存
char ch='A';
while(1)
sh_mem[ch-'A']=ch;
if(sh_mem[ch-'A'] > 'Z')
break;
ch++;
sleep(2);
//取消挂接
shmdt(sh_mem);
运行结果:
从上述的运行结果可以看出service端是不会只阻塞式等待client端发送的数据,而是一直在获取共享内存中的数据,那么就说明共享内存不提供同步与互斥
的操作,双方独立
浅谈同步
多进程间互相通信,互相等待,内容一致性,的一种约束关系
浅谈互斥
当一个进程访问临界资源的时候,别的进程就要等待(阻塞式等待)之前进程访问完毕才可重新访问(解除阻塞)
浅谈临界资源
某种资源在同一时间,只可以被一个人访问,这种资源就是临界资源,
后面进程在访问资源中的代码叫做临界区
问
那么如何才可以让共享内存具备同步与互斥
答
只需要访问共享资源前加个锁、二元信号,下文介绍
问
是管道效率高还是system V的方式效率高?
答
system V 的效率的高,管道这个操作需要进行多次拷贝(io流),而system V(类似堆) 只需直接读取
1.2 信号量
信号量到底是啥????其实信号量是一个计数器,对临界资源描述个数的计数器
,但是他其实也是临界资源
原子性
当一个进程向共享内存写入数据的时候无非俩中情况
1. 写了 2. 没写
(这就是原子性)。而在多进程下无法保证。
问
如何让共享内存写入数据具有原子性呢?保证数据的不会被破坏
答
加锁也就是加限制,在访问这个共享内存期间只允许x个,其余的等待.
伪代码:
lock()
…………写或者读数据
unlock();
当一个进程访问共享内存的时。他是否会使用全部的共享内存资源?? 当然不会,所以就可以把共享资源分成多份,用一个计数器(信号量)来维护进程使用资源
伪代码:
int count =3;
begin:
lock()
if(count==0)
go to beign;
else
count—-;
unlock();
执行流程
上述的操作时申请信号量,访问共享内存,那进程退出也要让count(信号量)++,当信号量其实也是临界资源所也要要加锁,原子性
伪代码:
lock()
count++;
unlock()
上述只能干的申请信号量与归还信号量,在操作系统学科中叫做pv操作
问
如果把信号量设置为1呢?
答
那其实即使所谓的互斥,只允许一个进程访问或者写入,这不就是管道吗
结尾
上述其实就是进程间通信的全部内容了,希望对你会有所帮助
以上是关于[Linux]进程间通信的主要内容,如果未能解决你的问题,请参考以下文章