由于内存管理的一些机制,进程的用户空间是相互独立的,一般是不能相互访问的,唯一例外的是共享内存。因此我们需要利用一些介质来完成进程间的通信。
进程间通信方式:
(1)管道
linux中的管道分为无名管道和有名管道,前者用于父进程和子进程间的通信,后者用于任意两个进程间的通信。无名管道由pipe(int filedis[2])函数创建。参数filedis返回两个文件描述符。filedes[0]为读而打开,filedes[1]为写而打开。filedes[1]的输出是filedes[0]的输入。
在linux中,有名管道可由两种方式创建:命令行方式mknod系统调用和函数mkfifo,生成一个myfifo的有名管道。 生成了有名管道后,就可以使用一般的文件I/O函数如open、close、read、write等来对它进行操作。
(2)消息队列(使用很少)
消息队列用于运行于同一台机器上的进程间通信,它和管道很相似,是一个在系统内核中用来保存消息的队列,它在系统内核中是以消息链表的形式出现。消息链表中节点的结构用msg声明。
事实上,它是一种正逐渐被淘汰的通信方式,我们可以用流管道或者套接口的方式来取代它,所以,我们对此方式也不再解释,也建议读者忽略这种方式。
(3)共享内存(使用最多)
共享内存时同一主机上两个进程间通信的最快方式,因为数据不需要在不同进程间复制,通常一个进程创建一块共享内存区,其余进程对这块内存区进行读写。得到共享内存区有两种方式:映射/dev/mem设备和内存映像文件。前一种方式不给系统带来额外的开销,但在现实中并不常用,因为它控制存取的将是实际的物理内存。后一种方式是常用的:通过shmXXX函数族来实现利用共享内存进行存储的。
首先要用的函数是shmget(),它获得一个共享存储标识符 --> 当共享内存创建后,其余进程可以调用shmat()将其连接到自身的地址空间中。
使用共享存储来实现进程间通信的注意点是对数据存取的同步,必须确保当一个进程去读取数据时,它所想要的数据已经写好了。可以通过使用shmctl函数设置共享存储内存的某些标志位如SHM_LOCK、SHM_UNLOCK等来实现。但更多时候我们利用下面要讲的信号量来实现对共享存储数据存取的同步。
(4)信号
信号量是一个计数器,它用来记录对某个资源(如共享内存)的存取状况。一般说来,为了获得共享资源,进程需要执行下列操作:
(a)测试控制该资源的信号量
(b)当信号量为正,则允许进行使用该资源,进程将信号量减1
(c)当信号量为0,则该资源目前不可用,进程进入睡眠状态,直至信号量大于0,进程被唤醒,转为步骤(a)
(d)当进程不再使用一个信号量控制的资源时,信号量值加1。如果此时有进程正在睡眠等待此信号量,则唤醒此进程。
(3)套接字
套接口(socket)编程是实现Linux系统和其他大多数操作系统中进程间通信的主要方式之一。我们熟知的WWW服务、FTP服务、TELNET服务 等都是基于套接口编程来实现的。除了在异地的计算机进程间以外,套接口同样适用于本地同一台计算机内部的进程间通信。