LInux进程间通讯IPC

浪迹一生 2019-04-26

一、linux下进程间通讯主要有以下几种手段:

1、管道(pipe)和命名管道(named pipe)

    管道可用于具有亲缘关系进程间的通讯,命名管道克服了管道没有名称的限制,从而允许无亲缘关系进程间的通信。管道的特点:

    1)只支持单向数据流,管道两端,一端只能读,一端只能写。这意味要双向通讯,需要建立两个管道。读取和写入是阻塞的,当管道中没有信息的时候,读取进程会等待,直到写入进程放入信息;当管道被放满信息的时候,写入进程会等待,直到读取进程取出信息。当两个进程都终结时,管道也自动消失。

    2)匿名管道(pipe)只能用于具有亲缘关系的进程间通讯,有名管道(named pipe)不受限制;

    3)管道的缓冲区是有限的(管道是由内核管理的一个缓冲区,创建时为缓冲区分配一个页面大小)。Posix.1要求写入的PIPE_BUF不大于512字节,linux将保证写入的原子性,否则不再保证原子性。

    4)管道传送的是无格式字节流。读写的封装/解析协议需自行约定;

    5)命名管道遵循FIFO规则;所以一般用PIPE代指匿名管道,FIFO代指命名管道。

    6)单独构成一种独立的文件系统,对于管道两端的进程而言,就是一个文件,但它不是普通的文件,不属于某种文件系统,而自立门户,且只存在于内存中。匿名管道PIPE当两端进程都终止时,该文件删除;命名管道FIPO当两端进程终止时,该文件还在,只是内容被清空。所以FIFO可以先创建好,再在两个进程中使用。而PIPE由于只能用于亲缘进程,所以需要创建后再讲文件描述符给两个进程使用。

2、信号(signal)

    信号比较复杂,用于通知接收进程有某种事件发生。是unix系统中最古老的进程间通讯机制,传递异步信号。除了用于进程间通讯以外,也可以发送给进程本身。linux除了支持Unix早期信号语义函数signal以外,还支持语义符合Posix.1标准的信号函数sigaction。除了基本通知功能以外,还可以传递附加信息。

3、消息(message)

    消息(报文)队列,是内核地址空间中的内部链表(queue),通过linux内核在各个进程直接传递内容,包括Posix消息队列和System V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程可以读走队列中的消息。消息队列克服了:信号承载信息量少,管道只能承载无格式字节及缓存区大小受限等缺点。其特点:

    1)消息队列提供有格式字节流传输,相比于管道有利于降低开发人员的工作量;

    2)消息队列可作为优先级使用;

    3)每个消息队列的容量都有限制,因系统不同而不同。

          定义在linux/msg.h中:#define MSGMAX 8192  //消息总容量不可超过8192个字节。

    4)每个消息队列可以用IPC标识符进行唯一识别,不同消息队列相互独立,构成各自独立的链表。

    5)接收消息,可以按类型接收。

4、共享内存

    使多个进程可以访问同一块内存空间,以最快的形式实现IPC通信。是针对其它IPC通信机制运行效率较低而设计的。共享内存本身没有同步机制,需要程序员自己控制,所以往往与其它通信机制,如信号量、记录锁、互斥量等结合使用,来达到进程间的同步与互斥。

    两个不同的进程A/B共享内存的意思是,同一个块物理内存被映射到进程A和进程B各自的进程地址空间。进程A可以即时看到进程B对内存中数据的更新,反之亦然。

    像管道和消息队列等通信方式,则需要在用户空间和内核进行四次数据拷贝,而共享内存只拷贝两次数据。所以共享内存效率最高。

5、信号量(semaphone)

    主要作为进程间,以及同一个进程下不同线程之间,同步的手段。信号量是一种计数器,用于控制对多个进程共享资源的访问,所以常常被用作一个锁机制。在某个进程正在对特定资源进行操作时,信号量可以防止另外一个进程去访问它。信号量是特殊的变量,它只取正整数值并且只允许对这个值进行两种操作:等待(wait)和信号(signal)。(P/V操作,P等待,V信号)

    P(sv):如果sv的值大于0,就给它减1;如果它的值等于0,就挂起该进程的执行,相当于申请资源;

    V(sv):如果有其它进程因等待sv而挂起,就让它恢复执行;如果没有其它进程因等待sv而挂起,则给它加1。

6、套接字(socket)

    相比一般的进程间通讯机制,还可用于不同机器之间的进程间通讯。它的优势在于:

    1)进程不是阻塞在真正的I/O调用上;

    2)可以实现多个客户端连接同一个服务端。

7、内存映射(memory map)

    内存映射文件,是由一个文件到一个内存的映射。内存映射文件与虚拟内存有些相似,通过内存映射文件可以保留一个地址区域,同时将物理存储器提交给此区域,内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,并且对该文件进行操作前必须先对文件进行映射。

    使用内存映射文件处理存储在磁盘上的文件时,不必再对文件执行I/O操作,每一个使用该机制的进程通过把同一个共享的文件映射到自己的进程地址空间来实现多个进程间的通信。只要有一个进程对这个映射文件的内存进行操作,其它进程也能够马上看到。

    内存映射文件,还可以用于处理大文件提高效率。比如文件读写操作,普通做法是,先把磁盘上的文件拷贝到内核空间的一个缓冲区,再拷贝到用户空间(内存),用户修改以后,再讲这些数据拷贝到内核空间缓存区,最后再写入到磁盘文件。一共四次拷贝。如果文件数据量很大时,拷贝的开销是非常大的。内存映射文件的nmap()确实没有进行数据拷贝,真正的拷贝是在缺页中断处理的,由于nmap()将文件直接映射到了用户空间(内存),所以中断处理函数根据这个映射关系,直接将文件从硬盘拷贝到用户空间。读写只进行了两次拷贝。效率高于read/write。

    (“/dev/zero存储映射”和nmap)

 二、消息队列、信号量和共享内存的相似:

    消息队列、信号量和共享内存,被统称为XSI IPC,它们在内核中有相似的IPC结构(消息队列msgid_ds,信号量semid_ds,共享内存shmid_ds),且都有一个非负整数的标识符加以引用(消息队列msg_di,信号量sem_id,共享内存shm_id),标志符是IPC对象的内部名,每个IPC对象都有一个键(key_t key)相关联,将这个键作为该对象的外部名。

三、消息队列、信号量、共享内存与PIPE、FIFO的区别:

    1、XSI IPC的IPC结构是在系统范围内作用,没有使用引用计数。如果一个进程创建一个消息队列,并在消息队列中放入几个消息,进程终止后,即便现在已没有程序使用该消息队列,消息队列及其内容依然保留。而PIPE在最后一个引用管道的进程终止时,管道就被完全删除了。对于FIFO,最后一个引用进程终止时,虽然FIFO还在系统,但其中的内容会被删除。

    2、和PIPE/FIFO不一样,XSI IPC不使用文件描述符,不能用ls查看IPC对象,不能用rm命令删除,不能用chmod命令删除它们的访问权限。只能用ips和ipcrm来查看并删除它们。

四、共享文件和内存映射文件的区别:

    内存映射文件是利用虚拟内存把文件映射到进程的地址空间中去,在此后进程操作文件,就像进程操作空间里的地址一样,比如使用memcpy等内存操作函数。这种方法能够很好地应用在需要频繁处理的一个文件或一个大文件的场合,效率比普通I/O要高。

    共享内存是内存映射文件的一种特殊情况,共享内存映射的是一块内存,而非磁盘上的文件。操作系统默认会给每一个进程分配一个内存空间,每一个进程只允许访问操作系统分配给它的那段内存,而不能访问其它进程的。而操作系统提供给共享内存一套API,可以通过这一组定义好的API来访问多个进程之间共有的内存,各个进程访问这段内存就像访问一个磁盘文件一样。

五、内存映射文件与虚拟内存的区别和联系:

    联系:虚拟内存和内存映射都将是一部分内存加载到内存,另一部分放在磁盘的一种机制,对用户而言都是透明的。

    区别:1)虚拟内存是硬盘的一部分,是内存和硬盘的数据交换区,许多程序运行过程中把暂时不用的程序数据放入这块虚拟内存,节约内存资源。而内存映射文件是一个文件到一个内存的映射,通过内存指针直接访问文件。2)虚拟内存的硬件基础是分页机制和局部性原理(时间局部性和空间局部性),这样就可以将程序的一部分装入内存,其余部分留在磁盘,当访问的信息不存在内存时,再将所需数据调入内存。而内存映射文件并不是局部性的,它使虚拟地址空间的某个区域映射到磁盘的全部或部分,通过该区域对被映射的磁盘文件进行访问,不必进行文件I/O操作,也不需要对文件内容进行缓冲处理。

六、一般来说,linux下的进程包含以下几个关键要素:

1)一段可执行的程序;

2)有专用的系统堆栈空间;

3)内核中有它的控制块(进程控制块),描述进程所占用的资源,从而进程才能接受内核的调度;

4)独立的存储空间;

进程和线程有时候并不完全区分,而往往根据上下文理解其含义。

   

相关推荐