https://www.cnblogs.com/biyeymyhjob/archive/2012/11/03/2751593.html
Unix IPC(进程间通信(IPC,Inter-Process Communication))包括:管道(pipe)、命名管道(FIFO)与信号(Signal)

管道(pipe)

管道可用于具有亲缘关系进程间的通信,命名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;

实现机制:

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

从原理上,管道利用fork机制建立,从而让两个进程可以连接到同一个PIPE上。最开始的时候,上面的两个箭头都连接在同一个进程上。当fork复制进程的时候,会将这两个连接也复制到新的进程。随后,每个进程关闭自己不需要的一个连接 ,这样,剩下的红色连接就构成了PIPE。

命名管道(named PIPE)

由于基于fork机制,所以管道只能用于父进程和子进程之间,或者拥有相同祖先的两个子进程之间 (有亲缘关系的进程之间)。为了解决这一问题,Linux提供了FIFO方式连接进程。FIFO又叫做命名管道(named PIPE)。

FIFO (First in, First out)为一种特殊的文件类型,它在文件系统中有对应的路径。当一个进程以读(r)的方式打开该文件,而另一个进程以写(w)的方式打开该文件,那么内核就会在这两个进程之间建立管道,所以FIFO实际上也由内核管理,不与硬盘打交道。之所以叫FIFO,是因为管道本质上是一个先进先出的队列数据结构,最早放入的数据被最先读出来,从而保证信息交流的顺序。FIFO只是借用了文件系统(file system,命名管道是一种特殊类型的文件,因为Linux中所有事物都是文件,它在文件系统中以文件名的形式存在。)来为管道命名。写模式的进程向FIFO文件中写入,而读模式的进程从FIFO文件中读出。当删除FIFO文件时,管道连接也随之消失。FIFO的好处在于我们可以通过文件的路径来识别管道,从而让没有亲缘关系的进程之间建立连接

注意:ls命令的输出结果中的第一个字符为p,表示这是一个管道。最后的|符号是由ls命令的-F选项添加的,它也表示是这是一个管道。

FIFO读写规则

1.从FIFO中读取数据: 约定:如果一个进程为了从FIFO中读取数据而阻塞打开了FIFO,那么称该进程内的读操作为设置了阻塞标志的读操作

2.从FIFO中写入数据: 约定:如果一个进程为了向FIFO中写入数据而阻塞打开FIFO,那么称该进程内的写操作为设置了阻塞标志的写操作。

详见:http://blog.csdn.net/MONKEY_D_MENG/article/details/5570468

信号(Signal)

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

信号种类

每种信号类型都有对应的信号处理程序(也叫信号的操作),就好像每个中断都有一个中断服务例程一样。大多数信号的默认操作是结束接收信号的进程;然而,一个进程通常可以请求系统采取某些代替的操作,各种代替操作是:

忽略信号。随着这一选项的设置,进程将忽略信号的出现。有两个信号 不可以被忽略:
SIGKILL,它将结束进程;
SIGSTOP,它是作业控制机制的一部分,将挂起作业的执行。

恢复信号的默认操作。

执行一个预先安排的信号处理函数。进程可以登记特殊的信号处理函数。当进程收到信号时,信号处理函数将像中断服务例程一样被调用,当从该信号处理函数返回时,控制被返回给主程序,并且继续正常执行。

但是,信号和中断有所不同。中断的响应和处理都发生在内核空间,而信号的响应发生在内核空间,信号处理程序的执行却发生在用户空间。

那么,什么时候检测和响应信号呢?通常发生在两种情况下:

当前进程由于系统调用、中断或异常而进入内核空间以后,从内核空间返回到用户空间前夕;

当前进程在内核中进入睡眠以后刚被唤醒的时候,由于检测到信号的存在而提前返回到用户空间。

函数原型等详见:http://www.cnblogs.com/biyeymyhjob/archive/2012/08/04/2622265.html

信号本质

信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。

信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有哪些事情发生了。信号机制经过POSIX实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息。

信号来源

信号事件的发生有两个来源:硬件来源(比如我们按下了键盘或者其它硬件故障);软件来源,最常用发送信号的系统函数是kill, raise, alarm和setitimer以及sigqueue函数,软件来源还包括一些非法运算等操作。

关于信号处理机制的原理(内核角度)

内核给一个进程发送软中断信号的方法,是在进程所在的进程表项的信号域设置对应于该信号的位。这里要补充的是,如果信号发送给一个正在睡眠的进程,那么要看该进程进入睡眠的优先级,如果进程睡眠在可被中断的优先级上,则唤醒进程;否则仅设置进程表中信号域相应的位,而不唤醒进程。这一点比较重要,因为进程检查是否收到信号的时机是:一个进程在即将从内核态返回到用户态时;或者,在一个进程要进入或离开一个适当的低调度优先级睡眠状态时。

内核处理一个进程收到的信号的时机是在一个进程从内核态返回用户态时。所以,当一个进程在内核态下运行时,软中断信号并不立即起作用,要等到将返回用户态时才处理。进程只有处理完信号才会返回用户态(上面的例子程序中,在步骤5中,解除阻塞后,先打印caught SIGQUIT,再打印SIGQUIT unblocked,即在sigprocmask返回前,信号处理程序先执行),进程在用户态下不会有未处理完的信号。

内核处理一个进程收到的软中断信号是在该进程的上下文中,因此,进程必须处于运行状态。如果进程收到一个要捕捉的信号,那么进程从内核态返回用户态时执行用户定义的函数。而且执行用户定义的函数的方法很巧妙,内核是在用户栈上创建一个新的层,该层中将返回地址的值设置成用户定义的处理函数的地址,这样进程从内核返回弹出栈顶时就返回到用户定义的函数处,从函数返回再弹出栈顶时,才返回原先进入内核的地方,接着原来的地方继续运行。这样做的原因是用户定义的处理函数不能且不允许在内核态下执行(如果用户定义的函数在内核态下运行的话,用户就可以获得任何权限)。

在信号的处理方法中有几点特别要引起注意。

第一,在一些系统中,当一个进程处理完中断信号返回用户态之前,内核清除用户区中设定的对该信号的处理例程的地址,即下一次进程对该信号的处理方法又改为默认值,除非在下一次信号到来之前再次使用signal系统调用。这可能会使得进程在调用signal之前又得 到该信号而导致退出。在BSD中,内核不再清除该地址。但不清除该地址可能使得进程因为过多过快的得到某个信号而导致堆栈溢出。为了避免出现上述情况。在 BSD系统中,内核模拟了对硬件中断的处理方法,即在处理某个中断时,阻止接收新的该类中断。

第二个要引起注意的是,如果要捕捉的信号发生于进程正在一个系统调用中时,并且该进程睡眠在可中断的优先级上(若系统调用未睡眠而是在运行,根据上面的分 析,等该系统调用运行完毕后再处理信号),这时该信号引起进程作一次longjmp,跳出睡眠状态,返回用户态并执行信号处理例程。当从信号处理例程返回 时,进程就象从系统调用返回一样,但返回了一个错误如-1,并将errno设置为EINTR,指出该次系统调用曾经被中断。这要注意的是,BSD系统中内 核可以自动地重新开始系统调用,或者手如上面所述手动设置重启。

第三个要注意的地方:若进程睡眠在可中断的优先级上,则当它收到一个要忽略的信号时,该进程被唤醒,但不做longjmp,一般是继续睡眠。但用户感觉不 到进程曾经被唤醒,而是象没有发生过该信号一样。所以能够使pause、sleep等函数从挂起态返回的信号必须要有信号处理函数,如果没有什么动作,可 以将处理函数设为空。

第四个要注意的地方:内核对子进程终止(SIGCLD)信号的处理方法与其他信号有所区别。当进程正常或异常终止时,内核都向其父进程发一个SIGCLD 信号,缺省情况下,父进程忽略该信号,就象没有收到该信号似的,如果父进程希望获得子进程终止的状态,则应该事先用signal函数为SIGCLD信号设 置信号处理程序,在信号处理程序中调用wait。

SIGCLD信号的作用是唤醒一个睡眠在可被中断优先级上的进程。如果该进程捕捉了这个信号,就象普通信号处理一样转到处理例程。如果进程忽略该信号,则 什么也不做。其实wait不一定放在信号处理函数中,但这样的话因为不知道子进程何时终止,在子进程终止前,wait将使父进程挂起休眠。

信号生命周期

参考资料:
http://www.cnblogs.com/vamei/archive/2012/10/10/2715398.html
http://bbs.chinaunix.net/thread-1947211-1-1.html