进程间通信(上)

   进程间通信的方式:命名管道和匿名管道、消息队列、信号量、共享内存等这几种方式,下面我们对其进行一一解读。

    linux下进程间通信的几种主要手段简介: 

1、管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;

2、信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接,用 sigaction函数重新实现了signal函数); 

3、报文(Message)队列(消息队列):消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。 
4、共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。 

5、信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。

6、套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix 系统上:Linux和System V的变种都支持套接字

一、什么是管道

    管道是由内核管理的一个缓冲区(buffer),相当于我们放入内存中的一个纸条。管道的一端连接一个进程的输出。这个进程会向管道中放入信息。管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息。一个缓冲区不需要很大,它被设计成为环形的数据结构,以便管道可以被循环利用。当管道中没有信息的话,从管道中读取的进程会等待,直到另一端的进程放入信息。当管道被放满信息的时候,尝试放入信息的进程会等待,直到另一端的进程取出信息。当两个进程都终结的时候,管道也自动消失。

   进程间通信的本质:让不同的管道看到同一块公共的资源(就如同不同的进程打开了同一个文件)。这个公共的资源为什么不同进程都能看到?因为文件的路径相同。

   管道是⼀一种最基本的 IPC机制,由pipe函数创建:  #include <unistd.h>
 int pipe(int filedes[2]); 调⽤用pipe函数时在内核中开辟⼀一块缓冲区(称为管道)⽤用于通信,它有⼀一个读端⼀一个写端,然后通过filedes参数传出给⽤用户程序两个⽂文件描述符,filedes[0]指向管道的读端,filedes[1]指向管道的 写端(很好记,就像0是标准输⼊入1是标准输出⼀一样)。所以管道在调⽤用户程序看起来就像⼀一个打开的⽂read(filedes[0]);或者write(filedes[1]);向这个⽂文件读写数据其实是在读写内核缓冲 区。pipe函数调⽤用成功返回0,调⽤用失败返回-1。

   通信即是进行数据交换,进程体现的是资源独占。

管道的一个重要特性是:并不支持双向通信,只支持单向通信。一端输入,另一端输出,先进先出FIFO。管道也是文件。管道大小4096字节。

特点:管道满时,阻塞;空时,阻塞。


二、对管道而言,进程间通信特点:
(1)单向通信(一个管道)
(2)目前我们所学的管道只能用于有血缘关系的进程,如父子进程。
(3)流式服务(可以随意多个字节发送,也可以随意任意字节个个数目接受,不受限制)
(4)(进程退出时,那块内存会被操作系统回收)管道依赖文件系统,所以管道的生命周期随进程。
(5)管道给我们的进程提供同步与互斥的机制。保证我们读写事件的正确性。
三、管道的各种情况:
   (*)管道的写端写一段时间后,不再进行写了(即关闭写端描述符),而读端不关闭,它在一直读,最后管道读完时会发生什么事?会发生阻塞。
   (*)写端一直写(子进程),(父进程)读端一直读,后面读端关闭,会发生什么事情??写端写满就不再进行写了,一直在等待,直到有新的空间出现。
   (*)写端一直写,读端一直读,过了一段时间,读文件描述符关闭(close(fd_pipe[0])------>关闭读端),不再读,会出现什么现象??父进程会给子进程写入的一方会发一个信号让子进程退出,且它是被异常退出的,可以得到异常码,但是得到他无意义,因为他是异常退出的。(写端无意义了,所以他会被异常退出)
   (*)写端写一段时间后,关闭写端,读端一直读,会出现什么情况??
四、管道的分类

     分类:匿名管道(仅父子进程间通信)位于内存;命名管道位于文件系统,没有亲缘关系管道只要知道管道名也可以通讯。
  匿名管道的最大缺陷:只能用于有血缘关系的管道间通信(即父子间进程)。
  命名管道(FIFO)相对于匿名管道的优点:可以用于不同进程(有血缘关系和无血缘关系的管道都可以利用)。它是面向字节流的。
五、为什么进程间需要通信?  

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

六、消息队列

  1、什么是消息队列?

       消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。  每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值。我们可以通过发送消息来避免命名管道的同步和阻塞问题。消息队列与管道不同的是,消息队列是基于消息的, 而管道是基于字节流的,且消息队列的读取不一定是先入先出。消息队列与命名管道有一 样的不足,就是每个消息的最大长度是有上限的(MSGMAX),每个消息队列的总的字节 数是有上限的(MSGMNB),系统上消息队列的总数也有一个上限(MSGMNI)。 

2、消息队列特点:
(1)函数接口较复杂
(2)生命周期随系统
(3)提供的是数据块服务
(4)消息队列的条数是受限制的

3、IPC对象数据结构 

      内核为每个IPC对象维护⼀一个数据结构(/usr/include/linux/ipc.h) 

struct ipc_perm

 { 

   key_t          __key;       /* Key supplied to xxxget(2)  */ 

   uid_t          uid;         /* Effective UID of owner */ 

   gid_t          gid;         /* Effective GID of owner */ 

   uid_t          cuid;        /* Effective UID of creator */ 

   gid_t          cgid;        /* Effective GID of creator */ 

   unsigned short mode;        /* Permissions */ 

   unsigned short __seq;       /* Sequence number */ }; 

消息队列,共享内存和信号量都有这样一个共同的数据结构。 

七、信号量

       信号量的本质是一种数据操作锁,它本身不具有数据交换的功能,而是通过控制其他的通信资源(文件,外部设备)来实现进程间通信,它本身只是一种外部资源的标识。信号量在此过程中负责数据操作的互斥、同步等功能。            当请求一个使用信号量来表示的资源时,进程需要先读取信号量的值来判断资源是否可用。大于0,资源可以请求,等于0,无资源可用,进程会进入睡眠状态直至资源可用。当进程不再使用一个信号量控制的共享资源时,信号量的值+1,对信号量的值进行的增减操作均为原子操作,这是由于信号量主要的作用是维护资源的互斥或多进程的同步访问。而在信号量的创建及初始化上,不能保证操作均为原子性。 

 1、为什么要使用信号量?

       为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域。 临界区域是指执行数据更新的代码需要独占式地执行。而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它,也就是说信号量是用来调协进程对共享资源的访问的。其中共享内存的使用就要用到信号量。 

2、信号量的工作原理 

      由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv)操作,他们的行为是这样的: P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行 V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运⾏行,如果没有进程因等待sv而挂 起,就给它加1.   举个例子,就是两个进程共享信号量sv,一旦其中一个进程执行了P(sv)操作,它将得到信号量,并可以进入临界区,使sv减1。而第二个进程将被阻止进入临界区,因为当它试图执行 P(sv)时,sv为0,它会被挂起以等待第一个进程离开临界区域并执行V(sv)释放信号量,这时第二个进程就可以恢复执行。 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章