之所以进程间需要通信,包括进行数据传输、资源共享、通知事件和进程控制等等。
现在Linux使用的进程间通信方式包括:
一、管道
1、这里主要指无名管道,具有如下特点:
#、只能用于具有亲缘关系的进程之间通信。
#、是一个半双工通信模式,具有固定的读写端,先进先出。
#、可以看成是一种特殊的文件,可以使用普通的read()等函数,但是它不是普通文件,不属于任何文件系统,并且只存在于内核的内存空间中。
#、数据被读出后,将从管道中删除,其他读进程将不能再读到这些数据。
2、管道是基于文件描述符的通信方式,管道建立时,创建两个文件描述符fd[0]、fd[1],fd[0]固定用于读管道,fd[1]固定用于写管道。关闭管道只需要用close将两个文件描述符逐个关闭即可。创建管道可以调用pipe()来实现,语法要点如下:
实际应用中,通常是先创建一个管道,然后通过fork()创建一个子进程,它会继承父进程所创建的管道,就可以实现父子进程之间的读写操作。由于父子进程运行的次序不能保证,所以可以调用sleep()函数。另外,应该调用waitpid()函数来保证父子进程的同步。
二、有名管道(FIFO)
有名管道突破了无名管道的限制,可以在互不相关的进程间进行通信。它在文件系统中是可见的,可当成普通文件进行读写操作,严格遵循先进先出原则,不支持如lseek()等文件定位操作。有名管道可以用mkfifo()函数创建,语法要点如下:
打开FIFO时,非阻塞标志(O_NONBLOCK)将对以后的读写产生影响:
#、没有使用O_NONBLOCK:访问要求无法满足时进程阻塞,如试图读取空的FIFO,将导致进程阻塞。
#、使用O_NONBLOCK:访问要求无法满足时不阻塞,立刻出错返回,errno是ENXIO。
三、信号
信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。用户进程对信号的相应有三种方式,即忽略信号(注:SIGKILL、SIGSTOP不能被忽略)、捕捉信号和执行缺省操作。
下面列举几种常见的信号:
@SIGHUP:从终端上发出的结束信号
@SIGINT:来自键盘的中断信号(ctrl+c)
@SIGKILL:该信号结束接受信号的进程
@SIGTERM:kill命令发出的信号
@SIGCHLD:标识子进程停止或结束的信号
@SIGSTOP:来自键盘(ctrl+Z)或调试程序的停止执行信号
@SIGALRM:当一个定时器到时的时候发出信号
1、信号的发送与捕捉
a、kill()和raise()
它们不仅可以发送终止信号,也可以向进程发送其他信号,raise()只允许进程向自身发送信号。
函数语法要点如下:
b、alarm()和pause()
alarm()可以在进程设置一个定时器,当指定时间到时,向进程发送SIGALARM信号,需要注意的是,一个进程只能有一个闹钟时间,如果调用alarm之前已为进程设置过闹钟时间,而且没有超时,则被新值所代换。
pasue()用于将调用进程挂起直到捕捉到信号为止,通常用于判断信号是否已到。
2、信号的处理
信号处理主要两种方式,一种使用简单的signal()函数,另一种是使用信号集函数组。主要介绍信号处理函数。
signal()只要指出处理的信号和处理函数即可,不支持信号传递信息,语法要点如下:
首先该函数原型指向一个无返回值并且带一个整形参数的函数指针,接着该原型带有两个参数,其中第二个参数可以是用户自定义的信号处理函数的函数指针。Linux还支持一个更新的信号处理函数sigaction()