进程间通讯方式

一、进程间通讯:每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程A把数据从用户空间拷到内核缓冲区,进程B再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信。

二、进程间通讯方式:信号,管道,信号量,共享内存,消息队列,socket

0、信号是系统提前规定好的一些特定的事件,信号可以被产生,也可以被接收,产生和接收信号的主体都是进程。

接收信号的三种响应方式:忽略(SIG_IGN),默认(SIG_DFL),自定义(自定义的函数)

进程修改信号响应方式的函数:

typedef  void  (*Fun_Handle_t )(int);

Fun_Handle_t(int sig,Fun_Handle_t   fun);//将信号和自定义的处理函数绑定

int   kill(pid_t  pid,int  sig);//向指定的进程发送指定的信号,进程接收到信号,触发相应的响应函数

1、管道(管道容量:64k):半双工通讯

 管道随进程,进程在管道在,进程消失管道对应的端口也关闭,两个进程都消失管道也消失。

有名无名区别:

 (1)有名管道:在文件目录树中有文件标识,无名管道没有

 (2)使用限制:

有名管道:任意一个有权限访问管道文件的进程,可以让同一台计算机上的任何两个没有关系的进程通信;

无名管道:无名管道要想完成进程通讯,必须借助于父子进程共享fork之前打开的文件描述符;即需要通信的进程之间必须存在关系。

操作过程

(1)父进程创建管道,得到两个⽂件描述符指向管道的两端

(2)父进程fork出子进程,⼦进程也有两个⽂件描述符指向同⼀管道。

(3)父进程关闭fd[0],子进程关闭fd[1],即⽗进程关闭管道读端,⼦进程关闭管道写端(因为管道只支持单向通信)。⽗进程可以往管道⾥写,⼦进程可以从管道⾥读,管道是⽤环形队列实现的,数据从写端流⼊从读端流出,这样就实现了进程间通信。 

注意

如果一个进程以只写的方式打开一个管道文件,open函数不会立即返回,会阻塞运行,知道另一个进程以只读的方式打开文件。

2、信号量

  1) 信号量本质上是一个计数器,用于多进程对共享数据对象的读取,它和管道有所不同,它不以传送数据为主要目的,它主要是用来保护共享资源(信号量也属于临界资源),使得资源在一个时刻只有一个进程独享。

  2)只允许对它进行两个操作:

信号量S>=0时,S表示可用资源的数量。执行一次P操作意味着请求分配一个资源,因此S的值减1;

当S<0时,表示已经没有可用资源,S的绝对值表示当前等待该资源的进程数。请求者必须等待其他进程释放该类资源,才能继续运行。而执行一个V操作意味着释放一个资源,因此S的值加1;若S<0,表示有某些进程正在等待该资源,因此要唤醒一个等待状态的进程,使之运行下去。

  3)在信号量进行PV操作时都为原子操作(因为它需要保护临界资源)

原子操作:不会被线程调度机制打断的操作

  4)进程如何获得共享资源

(1)测试控制该资源的信号量

(2)信号量的值为正,进程获得该资源的使用权,进程将信号量减1,表示它使用了一个资源单位

(3)若此时信号量的值为0,则进程进入挂起状态(进程状态改变),直到信号量的值大于0,若进程被唤醒则返回至第一步。

注:信号量通过同步与互斥保证访问资源的一致性。

关于PV操作容易产生的一些疑问:

1)S大于0那就表示有临界资源可供使用,为什么不唤醒进程?

S大于0的确表示有临界资源可供使用,也就是说这个时候没有进程被阻塞在这个资源上,所以不需要唤醒。

2)S小于0应该是说没有临界资源可供使用,为什么还要唤醒进程?

V原语操作的本质在于:一个进程使用完临界资源后,释放临界资源,使S加1,以通知其它的进程,这个时候如果S<0,表明有进程阻塞在该类资源上,因此要从阻塞队列里唤醒一个进程来“转手”该类资源。比如,有两个某类资源,四个进程A、B、C、D要用该类资源,最开始S=2,当A进入,S=1,当B进入S=0,表明该类资源刚好用完, 当C进入时S=-1,表明有一个进程被阻塞了,D进入,S=-2。当A用完该类资源时,进行V操作,S=-1,释放该类资源,因为S<0,表明有进程阻塞在该类资源上,于是唤醒一个。

 

3)如果是互斥信号量的话,应该设置信号量S=1,但是当有5个进程都访问的话,最后在该信号量的链表里会有4个在等待,也是说S=-4,那么第一个进程执行了V操作使S加1,释放了资源,下一个应该能够执行,但唤醒的这个进程在执行P操作时因S<0,也还是执行不了,这是怎么回事呢?

当一个进程阻塞了的时候,它已经执行过了P操作,并卡在临界区那个地方。当唤醒它时就立即进入它自己的临界区,并不需要执行P操作了,当执行完了临界区的程序后,就执行V操作。

4)S的绝对值表示等待的进程数,同时又表示临界资源,这到底是怎么回事?

当信号量S小于0时,其绝对值表示系统中因请求该类资源而被阻塞的进程数目.S大于0时表示可用的临界资源数。注意在不同情况下所表达的含义不一样。当等于0时,表示刚好用完。

3、共享内存:

这是最快的一种IPC(IPC通讯:信号量,共享内存,消息队列)

    (1) 允许两个或多个进程共享一定的存储区。就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。因为数据不需要在客户机和服务器端之间复制,数据直接写到内存,不用若干次数据拷贝,所以这是最快的一种IPC。

   (2)  共享内存中的数据并不会像管道或者信号量等被一端读取之后就不存在。

   (3)优点:使用共享内存进行进程间的通信真的是非常方便,而且函数的接口也简单,数据的共享还使进程间的数据不用传送,而是直接访问内存,也加快了程序的效率。同时,它也不像无名管道那样要求通信的进程有一定的父子关系。
  缺点:共享内存没有提供互斥同步的机制,这使得我们在使用共享内存进行进程间通信时,往往要借助其他的手段比如信号量等来进行进程间的同步工作。 

4、消息队列

消息:数据+类型         队列:先进先出的一种数据结构

传递的数据是带有type的,不同的进程可以根据不同的需要获取不同类型的数据;如果队列中同一类型的数据有很多条,则采用队列先进先出的原则获取数据。

  (1) 消息队列是消息的链接表,存放在内核中并由消息队列标识符标识(key)。 

标识符是IPC对象的内部名, 而它的外部名则是key(键), 它的基本类型是key_t, 在头文件<sys/types.h>中定义为长整型.。键由内核变换成标识符。

  (2)用户可以从消息队列中读取数据和添加消息,其中发送进程添加消息到队列的末尾,接收进程在队列的头部接收消息;消息一旦被接收,就会从队列中删除。和FIFO有点类似,但是它可以实现消息的随机查询,比FIFO具有更大的优势(比如按消息的类型字段取消息)。

  (3)与FIFO不同的是:消息队列可以制定从消息队列中取走某一条消息

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值