进程间通信

目录

进程间通信的介绍

匿名管道

命名管道

共享内存

信号量


进程间通信的介绍

进程之间可能会存在特定的协同工作的场景!如下图

概念:一个进程要把自己的数据交付给另一个进程,让其进行处理

因为进程具有独立性!所以如果要进行进程间通信,就得交互数据,成本一定很高,同时,操作

系统也要设计通信方式!

由于进程具有独立性,所以一个进程是看不到另一个进程的资源的,而两个进程要互相通信,那就

必须得先看到一份公共的资源!这个资源就是一段内存(属于操作系统)——可能以文件的方式提

供,也可能以队列的方式,也可能提供的就是原始的内存块,这也是通信方式有很多种的原因!

进程间通信的前提本质:有os参与,提供一份所有通信进程能看到的公共资源

匿名管道

如下图,父子进程能够看到一份公共的资源文件,父进程执行后,将数据写入缓冲区,但

是不刷新到磁盘,交给子进程去做,这种经由文件的方式,叫做管道

如下图,pipe是用来创建管道的系统调用接口,其中pipefd[2]是输出型参数,我们想通过这个参

数读取到两个打开的fd

如下图,让父子进程进行通信,子进程写,父进程读,那子进程就关闭0,父进程就关闭1

注意:子进程有sleep,父进程没有sleep

如果read的返回值为0,意味着子进程关闭文件描述符了!

如下图,子进程没有sleep,父进程有sleep,子进程不会去等父进程读了才去写,而是会以字节为

单位一个个地写

如下图,让子进程每一次写一个字节,父进程一直休眠,当读满64KB时,子进程也就不再写了,

因为管道有大小!至于为什么不写了,是因为要让父进程来读,否则继续写,就会把之前写的覆盖

掉,前面的工作就白做了!!!

如下图,让父进程每隔一秒读一次

如下图,让子进程每隔5秒写一次,父进程没有sleep,可得知此时以写为主

如下图,子进程5秒后写一次,然后将1关闭,然后退出,父进程也会随之退出

  

如下图,子进程一直写,而父进程等5秒后再读,然后break跳出来关掉0,父进程结束,而此时子

进程还在写入,站在操作系统层面,是严重不合理的,因为已经没有人读了,你还在写,本质上

就是在浪费os的资源,os会直接终止子进程!os给目标进程发送SIGPIPE信号!

  

如下图,获取子进程退出的情况

 

总结

pipe本质:是通过子进程继承父进程资源的特性,达到让不同的进程看到同一份资源!

4种情况

读端不读或者读得慢,写端要等读端

读端关闭,写端收到SIGPIPE信号,进程终止

写端不写或者写得慢,读端要等写端

写端关闭,读端读完pipe内部的数据然后再读,会读到0,表明读到文件结尾!

5个特点

管道是一个只能单向通信的通信信道

管道是面向字节流的!——tcp、FILE、fstream

仅限于父子通信——具有血缘关系的进程进行进程间通信

管道自带同步机制,原子性写入

管道的生命周期是随进程的!

注意:管道是文件,如果一个文件只被当前进程打开,相关进程退出了,被打开的文件,会被os

自动关闭!(引用计数ref为0)

命名管道

如下图,命名管道是用mkfifo指令创建的文件,以p开头,echo写入,cat来读取,即一个进程将

据交给另一个进程,这就是管道文件

我们通常标识一个磁盘文件,是用路径/文件名的方式,具有唯一性!!!

概念

如下图,A、B进程用路径/文件名的方式将同一份文件从磁盘中打开并加载到内存中,然后A进程

写入数据到文件,B进程从文件中读取数据,这就是命名管道!

具体实现

首先创建两个文件server.c和client.c,让其都成为可执行程序,然后成为两个进程,client.c用来

入,server.c用来读取,Makefile文件和需要用到的头文件,如下图

如下图,首先在server.c文件中用mkfifo创建一个管道文件,同时还需要设置一下掩码,否则我

们自己输入的参数066,与创建的管道文件的权限不是对应的

如下图,在client.c中,我们从键盘读取数据,然后再拿到数据进行写入,再在server.c中,我们

取数据!

注意:必须server先执行,client后执行,否则无法运行,因为管道文件是server中创建的!

因为命名管道也是基于字节流的,所以实际上,信息传递的时候,是需要通信双方定制协议的!

(这里不考虑)

如下图,在server.c中创建一个子进程,以达到一个进程控制另一个进程的目的!

如下图,让读取的进程每读一次就休息10秒钟,命名管道的数据,为了效率 ,不会刷新到磁盘!

我们之前的pipe叫作匿名管道,文件没有名字,是因为它是通过父子继承的方式,看到同一份资

源,不需要名字来标识同一个资源!

我们现在的pipe叫作命名管道,为了保证不同的进程看到同一份文件,是必须得字!

SystemV 

前面讲的管道都是基于文件的通信方式,而SystemV是在os层面专门为进程间通信设计的一个方

案,由计算机科学家和程序员设计,给用户使用,又因为os不信任任何用户,给用户提供功能的

时候,采用系统调用的方式

三种方案:共享内存;消息队列(有点落伍);信号量

共享内存

如下图,用两步操作,来让不同进程看到同一份资源!

通过某种调用,在内存中创建一份内存空间

通过某种调用,让进程(参与通信的多个进程)"挂接"到这份新开辟的内存空间上!

挂接:即在进程的地址空间中,开辟一块虚拟空间,让其起始地址通过页表可以映射到新开的物理

内存空间的起始地址 

共享内存不用了,也分为两步操作

去关联(去挂接)

释放共享内存

os内可能存在多个进程,同时使用不同的共享内存来进行进程间通信,即共享内存在系统中可能

在多份!那操作系统就必须得管理这些不同的共享内存,管理方式:先描述,再组织

共享内存一定要有一定的标识唯一性的ID,方便让不同的进程能识别同一个共享内存资源!这个ID

是存放在描述共享内存的结构体中的!

这个唯一的标识符,是用来进行进程间通信的,本质:让不同的进程能看到同一份资源——你得先

同的进程看到同一个ID(需要由用户自己设定)

认识接口

创建共享内存

参数key_t key 

需要通过接口ftok来获取key,这个接口有两个参数,一个是自定义路径名,另一个则是自定义项

目ID,这两个可以随便填,但是不一定成功,返回-1就失败了!

这个key也就是前面所提到的唯一性的ID!!!会设置进内核的关于shm在内核中的数据结构中!

参数size

即你想要创建的共享内存的大小,建议是4KB的整数倍!

参数shmflg

这个参数可能是单独的IPC_CREAT,也可能是IPC_CREAT | IPC_EXCL,而IPC_EXCL单独使用

有意义!

如果单独使用IPC_CREAT,或者flg为0:创建一个共享内存,如果创建的共享内存已经存在,则

直接返回当前已经存在的共享内存,不存在则创建(基本不会空手而归)

IPC_CREAT | IPC_EXCL:如果不存在共享内存,则创建,如果已经有了共享内存,则返回出

错!(意义:如果我调用成功,得到的一定是一个最新的,没有被别人使用过的共享内存)

只要我们形成key的算法+原始数据是有意义的,形成同一个ID,就能保证不同的进程看到的是同

个共享内存!

如下图,打印key值和shmid的返回值

如下图,./server执行完毕了,进程运行结束,但是该进程曾经创建的共享内存没有被释放!

如下图,用ipcs -m查看共享内存,ipcs不带选项会默认显示消息队列,共享内存,信号量

systemV的IPC资源,生命周期是随内核的!只能通过程序员显示的释放(命令或系统调用、或os

重启)

用命令释放IPC资源

如下图,用命令ipcrm -m来释放共享内存,同时,可得知,

key:只是用来在系统层面进行标识唯一性的,不能用来管理

shmid:是os给用户返回的id,用来在用户层进行shm管理

控制共享内存(只讲删除)

参数shmid是创建共享内存接口shmget的返回值,cmd由于只讲删除,所以传IPC_RMID,后面

结构体指针传NULL即可

如下图,用shmctl来释放共享内存

如下图,在创建共享内存的最后一个参数中再按位或上一个数字,比如0666,来为共享内存创建

权限!

如下图,shmat,让进程"挂接"到新开辟的内存空间上,参数shmid是shmget的返回值shmaddr

是确定挂接到地址空间的某个范围,我们无法确定,就由操作系统去做,所以shmaddr和shmflg

使用时就设为NULL和0即可!返回值是创建的共享内存的起始地址,不过这个地址和malloc的返

回值一样,都是虚拟地址!!!

shmdt:去关联,参数是shmat的返回值,注意,并不是释放共享内存,而是取消当前进程和共享

内存的关系!

如下图,是对"挂接"的一个实验验证,nattch是"挂接"共享内存的进程个数

如下图,当client没有写入,甚至没有启动的时候,server端会直接读取shm,但是没有数据,根

本不会等待client写入

共享内存不提供任何同步或互斥机制,需要程序员自行保证数据的安全!

共享内存一旦建立好并映射进自己进程的地址空间,该进程就可以直接看到该共享内存,就如同

malloc的空间一样,不需要任何系统调用接口(比如read或write)!所以共享内存是所有的进程间

中速度最快的!

read或write的本质,将数据从内核拷贝到用户,或者从用户拷贝到内核

SIZE

如下图,因为共享内存在内核中申请的基本单位是页,内存页(4KB),所以建议共享内存申请

4096的整数倍,而如果你要申请4097个字节,操作系统就给你4097个字节,但是实际上操作系统

在底层申请的时候,是申请的4096*2个字节

信号量

管道(匿名or命名),共享内存,消息队列,都是以传输数据为目的的!信号量不是以传输数据为

目的的!通过共享"资源"方式,来达到多个进程的同步和互斥的目的!

临界资源:凡是被多个执行流同时能够访问的资源就是临界资源!比如:同时向显示器打印!进程

间通信的时候,管道,共享内存,消息队列等,都是临界资源

凡是要进行进程间通信,必定要引入被多个进程看到的资源(通信需要),同时,也引入一个新的

问题,临界资源的问题!

信号量的本质,是一个计数器,类似int count,衡量临界资源中的资源数目

以电影院的某个放映厅为例,要看电影,你就得买票,而买票的本质就是对临界资源的预定机制,

而一个放映厅最怕的就是只有100个座位,却卖出了110张票(卖不出去的情况不考虑),所以就需要

信号量来控制,卖出一张票count--,访问电影院座位的人离开count++

临界区:进程的代码可是有很多的,其中,用来访问临界资源的代码,就叫做临界区

原子性:一件事情要么不做,要么就做完,没有中间态,就叫做原子性!比如父子进程,都能看到

一个变量count,值为100,父子进程对其进行--操作,父进程先开始--,值为99,再到子进程运行

完后,值为5,父进程再去操作时,又从99开始--,多进程对全局数据操作出现错乱问题,也证明

count--本身不是原子的

每个人想进入电影院,必须先对count--,前提时每个人都得先看到count!count本身也是临界资

源!!!信号量本身就是资源!就要求信号量的--和信号量的++操作是安全的,所以必须是原子

的,也被称为p操作和v操作

互斥:任意一个时刻,只能允许一个执行流访问临界资源,执行它自己的临界区,比如有一间VIP

自习室,只能一个人自习,即int sem = 1,当有人在的时候,其他人就无法进入,当他离开,

sem--,当没有人在,有人进入,sem++,sem只有0和1两种值,也被称为二元信号量!

注意:信号与信号量,根本毫无关系!!!

  • 13
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值