Linux进程通信

目录

一、信号

 二、管道与FIFO文件

三、System V IPC

四、POSIX消息队列

五、Socket套接字


一、信号

     信号是Linux进程间异步通信的唯一机制,用于通知进程一个特定的事件并强迫进程执行对应的处理程序,如用户在键盘上按crtl+c,内核会给当前控制台上正在运行的进程发送一个SIGINT的信号,进程收到该信号执行默认处理程序,即终止进程。在代码中信号是一组预定义的无符号整数,用两个字节来保存,所以最多是64个,执行kill -l命令会列出Linux支持的所有的信号,如下图:

Linux进程通信_第1张图片

其中前31个是继承自Unix的标准信号,其余的都是POSIX标准引入的扩展信号,叫做实时信号,两者区别如下:

1、当一个标准信号被阻塞时,后续发送给同一进程的同一信号则都会被丢弃。实时信号不会丢弃,而会进入一个待处理队列排队,同一类型信号按照发送的顺序处理。对不同类型的信号按照下列顺序处理:SIGILL(4),SIGTRAP(5),SIGBUS(7),SIGFPE(8),SIGSEGV(11), SIGSYS(31),其他标准信号(优先处理取值小的),实时信号。

2、标准信号传递到目标进程时没有携带任何信息,实时信号可以携带额外的如发送方相关的信息。

3、实时信号处理的优先级高于标准信号

4、实时信号没有明确的含义,由使用者自己来决定如何使用,标准信号有明确的含义和用途。

5、实时信号的默认都是结束当前的进程,标准信号有多种默认操作。

      按照信号的用途可以分为以下几类:

1、进程控制,如SIGHUP挂机进程,SIGKILL强制终止进程,SIGTERM终止进程,SIGSTOP停止进程执行等

2、进程执行异常通知,如SIGILL非法指令,SIGABRT异常结束,SIGFPE浮点计算异常,SIGSEGV无效内存引用等

3、硬件错误通知,如SIGBUS总线错误,SIGSTKFLT协处理器栈错误,SIGPWR电源故障

4、跟踪进程执行,只有一个SIGTRAP

5、用户键盘操作通知,如SIGINT对应键盘的ctrl+c, SIGQUIT对应键盘的Ctrl+\,与SIGINT区别是会执行内存数据dump操作

6、定时通知,如SIGALRM,达到进程设置的时间后通知进程。

     进程接收信号的本质是内核将该信号写入进程描述符中信号相关的字段中,进程处理信号有三种方法:

1、显示的忽略信号;

2、执行与信号相关的默认操作,默认操作取决于信号的类型,包含以下几种:Treminate(终止进程),Dump(终止进程并尽可能做核心转储),Ignore(忽略信号),Stop(停止进程执行,即把进程置为TASK_STOPPED状态),Continue(让TASK_STOPPED状态的进程继续执行)

3、调用自定义的信号捕获程序

     信号如果不需要立即处理,可以阻塞信号,被阻塞的信号称为挂起信号(pending signal)。进程执行某个信号的处理程序时会自动阻塞该信号,即进程在处理完成前不会再接收同一信号,不会被同一信号中断,但是如果接收了其他信号则可能中断当前处理程序。注意SIGKILL和SIGSTOP信号不能被显示的忽略,捕获或者阻塞,必须执行默认的操作,从而保护系统安全。

     内核收到一个信号时,判断目标进程不是被跟踪,没有阻塞该信号,没有忽略该信号则将该信号加入到挂起信号队列上,然后通知进程有新的挂起信号,如果此时目标进程是TASK_INTERRUPTIBLE状态,则会唤醒信号将其状态改成TASK_RUNNING,注意如果进程是因为系统调用进入到TASK_INTERRUPTIBLE状态,此时唤醒可能系统调用尚未完成需要重新执行系统调用。当进程从内核态切换到用户态时,内核会检查是否存在待处理的信号,如果存在则调用对应的处理程序,如果是用户自定义的捕获函数,则切换到用户态后开始执行捕获函数,信号处理完成从挂起队列中删除该信号,然后恢复正常程序执行。

   参考:Linux signal 那些事儿(4)信号的deliver顺序

             Linux 的各种 signal

             Linux Signal

 二、管道与FIFO文件

        管道是进程之间通信使用的一个单向数据流,部分Unix系统实现了双向数据流即全双工的管道。管道被看做是一个打开的文件,但是文件系统中没有对应的映像,执行pipe()系统调用创建一个新的管道时会返回两个文件描述符,其中一个用于往管道写入数据,一个往管道读取数据,进程只能使用其中的一个文件描述符,注意Linux中不需要关闭另一个没有使用的文件描述符。因为管道的读写基于文件描述符而文件描述符可以在进程创建时传递给子进程,所以管道可以被同一个祖先的多个进程同时使用,使用时需要借助文件加锁机制进行同步。因为管道在文件系统中没有对应的映像,所以一个与创建管道的进程没有任何关系的进程无法获取该管道的文件描述符,即无法打开已经存在的管道。

        管道在内核中是一个由16个管道缓冲区组成的环形缓冲区,每个管道缓冲区是一个单独的页框,写进程不断的往缓冲区写入数据,如果当前所在的管道缓冲区剩余空间不足则利用写入下一个管道缓冲区,已经写入待读出的数据称为管道大小;读进程不断的从缓冲区中读取数据,由内核记录下一个待读取的字节所在的管道缓冲区和页框中的偏移量。从管道读取数据时,如果管道的缓冲区为空或者没有包含所请求的字节数时可能阻塞读进程,也可以不阻塞,有多少字节数读取多少,依据创建管道时的配置选项而定,读取过程中如果缓冲区数据已空则会释放对应页框。往管道写入数据时,如果写入字节数小于管道缓冲区大小时需要原子的执行,且不能与其他进程对同一缓冲区的写入操作交叉执行;如果管道没有对应的读进程,写入管道时内核会给该进程发送SIGPIPE信号并终止写入;写入时若管道缓冲区未分配页框会从伙伴系统申请页框。

        Linux中执行shell命令时,可以通过"|" ,">" 或者 "<" 利用管道,如执行ls | more命令,就是shell进程创建了一个管道和分别执行ls和more命令的两个子进程,并将返回的两个文件描述符传给子进程,执行ls命令的子进程往管道写入数据,执行more命令的子进程从管道读取数据,如下图所示

Linux进程通信_第2张图片

      为了解决管道复用的缺陷,Unix系统引入了命名管道,又称FIFO文件。FIFO文件同样通过内核缓冲区在进程间传递数据,同一个进程对同一个FIFO文件只能读或者写,与管道的区别是,FIFO文件有对应的磁盘索引节点,可以在系统的文件目录树中查看,因此命名管道可以被任何进程访问。以数据库服务器上的服务进程和客户端进程通信为例,数据库启动时创建一个FIFO文件,由客户端负责将请求写入该FIFO文件;客户端启动时创建一个专属的FIFO文件,客户端发出请求时告诉服务端该文件的名称,服务端从自己的FIFO文件读取请求后将处理结果写入到客户端对应的FIFO文件中,客户端读取自己的FIFO文件内容并把结果展示在控制台上,示例中服务端和客户端的FIFO文件都是可读可写的。

     参考:  《深入理解Linux内核》(第三版)

三、System V IPC

    IPC(Inter-Process Communication)是指多个进程之间相互通信,交换信息的方法,System V是Unix操作系统最早的商业发行版,由AT&T(American Telephone & Telegraph)开发。System V IPC是指Linux引入自System V的进程通信机制,一共有三种:(1)信号量,用来管理对共享资源的访问;(2)共享内存,用来高效地实现进程间的数据共享;(3)消息队列,用来实现进程间数据的传递。这三种统称IPC资源,每个IPC资源都是请求时动态创建的,都是永驻内存,除非被进程显示释放,都是可以被任一进程使用。每个IPC资源都使用一个32位的IPC关键字和32位的IPC标识符,前者类似文件系统中的路径名,由程序自由定制,后者类似打开文件的文件描述符,由内核统一分配,在系统内部是唯一的,当多个进程使用同一个IPC资源通信时需要该资源的IPC标识符。

     创建新的IPC资源时需要指定IPC关键字,如果没有与之关联的IPC资源,则创建一个新的IPC资源;如果已经存在,则判断当前进程是否具有访问权限,是否超过资源使用限制等,如果符合条件则返回该资源的IPC标识符。为了避免两个不同的IPC资源使用相同的IPC关键字,创建时可以指定IPC关键字为IPC_PRIVATE,由内核负责生成一个唯一的关键字。

    创建新的IPC资源时最后一个参数可以包括三个标志,PC_CREAT说明如果IPC资源不存在则必须创建它,IPC_EXCL说明如果资源已经存在且设置了PC_CREAT标志则创建失败,IPC_NOWAIT说明访问IPC资源时进程从不阻塞。

     IPC信号量支持一个信号量或者多个信号量组成的信号量集,使用单个信号量时同内核信号量一样,信号量大于0表示受保护的资源可用,等于0表示不可用;使用信号量集的时候,如果信号量集中每个信号量对应的资源对当前进程都可用时才能访问这些资源,如果只是部分资源可用,则进程必须释放已占用资源,对应信号量加1。 IPC信号量还提供了一种安全时效机制,如果进程成功获取信号量对应的资源后因为异常终止而没有释放对应的资源,IPC信号量能够恢复信号量,避免其他进程访问相同资源时因为信号量没有恢复导致无限阻塞。IPC信号量通过三个链表实现,每个进程维护一个undo_list链表,记录了该进程执行可取消操作的所有信号量,每个信号量维护一个undo链表和一个进程挂起队列,前者记录了对该信号量执行的所有可取消操作,后者记录了所有请求当前信号量而被挂起的进程,按照FIFO原则处理。当进程退出时,内核遍历undo_list链表找到执行了可取消操作的信号量,然后遍历信号量的undo链表,逐一回退该进程执行的可取消操作。

    使用IPC消息时,进程产生的每条IPC消息都会发送到一个消息队列中,消息由固定大小的首部和可变长度的正文组成的,通过一个整数值来表示消息类型,允许进程有选择的从消息队列中获取消息,当消息队列满或者达到了消息队列最大字节数则阻塞发送消息的进程,被阻塞的进程以链表形式保存。只要进程从IPC消息队列读出一条消息,内核就把这个消息删除,即每条消息只能有一个接受进程,如果队列为空或者指定的消息类型不存在时则会阻塞获取消息的进程,被阻塞的进程同样以链表形式保存。

    最有用的IPC机制是共享内存,允许多个进程通过把公共数据结构放入共享内存区来访问。访问共享内存区时,进程首先需要在自己的地址空间内划分一个线性区,但是此时页表并没有修改,即没有完成线性区到共享内存区的页框的映射。当进程开始访问共享内存区的某个数据时,会触发缺页异常,由缺页异常程序负责修改页表,完成共享内存区页框到进程线性区的映射。共享内存因为是进程直接读写内存,避免了数据在用户空间和内核空间之间的多次拷贝,所以效率是最高的。

       参考: 【Linux】Linux的信号量集

                      linux System V IPC总结

                    linux基础编程:进程通信之System V IPC:消息队列,信号量,共享内存

四、POSIX消息队列

    POSIX消息队列是POSIX标准在2001年定义的一种IPC机制,与System V中的消息队列相比有如下差异:

  • 更简单的基于文件的应用接口,Linux通过mqueue的特殊文件系统来实现消息队列,队列名跟文件名类似,必须以"/"开头,每个消息队列在文件系统内都有一个对应的索引节点,返回的队列描述符实际是一个文件描述符
  • 完全支持消息优先级,消息在队列中是按照优先级倒序排列的(即0表示优先级最低)。当一条消息被添加到队列中时,它会被放置在队列中具有相同优先级的所有消息之后。如果一个应用程序无需使用消息优先级,那么只需要将msg_prio指定为0即可。
  • 完全支持消息到达的异步通知,当新消息到达且当前队列为空时会通知之前注册过表示接受通知的进程。在任何一个时刻都只有一个进程能够向一个特定的消息队列注册接收通知。如果一个消息队列上已经存在注册进程了,那么后续在该队列上的注册请求将会失败。可以给进程发送信号或者另起一个线程调用通知函数完成通知。当通知完成时,注册即被撤销,进程需要继续接受通知则必须重新注册。
  • 用于阻塞发送与接收操作的超时机制,可以指定阻塞的最长时间,超时自动返回

    参考:Linux进程间通信之POSIX消息队列

五、Socket套接字

      套接字(Socket)是由Berkeley在BSD系统中引入的一种基于网络连接的IPC,是对网络接口(硬件)和网络协议(软件)的抽象。它既解决了无名管道只能在相关进程间单向通信的问题,又解决了网络上不同主机之间无法通信的问题。

  套接字有三个属性:域(domain)、类型(type)和协议(protocol),对应于不同的域,套接字还有一个地址(address)来作为它的名字。域(domain)指定了套接字通信所用到的协议族,最常用的域是AF_INET,代表网络套接字,底层协议是IP协议。对于网络套接字,由于服务器端有可能会提供多种服务,客户端需要使用IP端口号来指定特定的服务。AF_UNIX代表本地套接字,使用Unix/Linux文件系统实现。

  IP协议提供了两种通信手段:流(streams)和数据报(datagrams),对应的套接字类型(type)分别为流式套接字和数据报套接字。流式套接字(SOCK_STREAM)用于提供面向连接、可靠的数据传输服务。该服务保证数据能够实现无差错、无重复发送,并按顺序接收。流式套接字使用TCP协议。数据报套接字(SOCK_DGRAM)提供了一种无连接的服务。该服务并不能保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。数据报套接字使用UDP协议。

  参考:Linux编程---套接字

               linux OSI七层模型、TCP-IP协议栈及每层结构大揭秘

               TCP/IP协议集详解

      

你可能感兴趣的:(Hotspot和Linux内核)