1.哪些是进程间通讯:
每位进程的用户地址空间都是独立的,通常而言不能相互访问,但内核空间是每位进程都共享的,所以进程之间要通讯必须通过内核。
在32位Linux系统下redhat linux 9.0,进程的虚拟地址空间大小为4G,其中高地址的1G是内核地址空间,其余3G是用户空地址空间。用户地址空间对于每位进程来说是独立的,但每位进程的内核地址空间关联的都是相同的化学显存。
进程在用户态时,只能访问用户地址空间的显存;只有切换到内核态后,才可以访问内核空间的显存。
2.进程间通讯的六种形式:2.1管线(pipe):
管线分为无名管线(pipe)和有名管线(fifo)两种,一般指的是无名管线。
管线是Unix系统最古老的进程间通讯方法。
无名管线(PIPE):
管线是一种半双工的通讯方法,数据只能双向传输linux内核通信方式,并且只能在有亲缘关系的进程间使用(兄妹进程间)。假如想要进行全双工的单向传输,则须要构建两个管路。
比如Linux命令中的这个竖线“|”就是一个管线,它的功能是前一个命令(psaux)的输出,作为后一个命令(grepmysql)的输入(由此也可将管线传输是双向的):
ps -aux | grep mysql
无名管线的创建,须要通过下边这个系统调用:
int pipe(int fd[2]);
这儿会创建两个文件描述符fd[0]、fd[1],此后,通过调用fork()创建子进程,创建的子进程会复制父进程的文件描述符,这样才能做到了两个进程各有两个文件描述符fd[0]和fd[1]。
为了防止母子进程同时写入和读出引起的消息混乱,通常的做法是父进程关掉读取的fd[0],只保留写入的fd[1],子进程关掉写入的fd[1],只保留读取的fd[0]。
所以假如须要单向通讯,则应当创建两个管线(在调用fork之前创建两个管线)。
命名管线(FIFO):
命名管线也是半双工的通讯方法,并且它容许无亲缘关系的进程间的通讯。
所谓的管线,就是内核里的一串缓存,从管线的一端写入的数据,实际上是缓存在内核中的,另一端读取,也是从内核中读取这段数据。因为管线的数据传递须要发送端从用户空间将消息拷贝到内核空间,再由接收方将消息从内核空间拷贝出到用户空间,所以通讯效率很低。
另外,管线传输的数据是无格式的流且大小受限。
2.2消息队列(messagequeue):
因为管线的通讯方法效率很低,不适宜进程间频繁的交换数据,因而引入了消息队列的进程间通讯方法。
消息队列是保存在内核中的消息数组,在发送数据时,会分成一个一个独立的数据单元,也就是消息体(数据块),消息体是用户自定义的数据类型,消息的发送方和接收方要约定好消息体的数据类型,所以每位消息都是固定大小的储存块,不像管线是无格式的字节流数据。
消息队列的缺点是:通讯不及时、附件有大小限制。
消息队列不适宜比较大的数据的传输,这是由于内核中每位消息体都有一个最大宽度的限制,且队列中所有消息的总宽度也有限制。
消息队列通讯过程同样存在用户态与内核态之间的数据拷贝开支。
2.3共享显存(sharedmemory):
共享显存是速率最快的进程间通讯方法。
后面提及的管线、消息队列的形式,因为在消息收发过程中存在用户态到内核态的数据拷贝开支,造成通讯效率低,共享显存解决了这个问题。
共享显存机制的实现原理是,由一个进程初始化一块共享显存地址,其他进程也都将虚拟地址空间中的一块映射到相同的化学显存地址起来,这样发消息时就不须要将数据从用户空间拷贝到内核空间,接收消息时只须要去对应的地址起来读消息即可。
共享显存存在一个问题linux内核通信方式,因为多个进程可以同时操作同一块化学显存地址空间,有可能会发生写冲突(写覆盖)。为解决多进程竞争共享资源,须要引入讯号量的机制。
共享显存API函数:
#include
#include
int shmget(key_t key, size_t size, int shmflg);
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
int shmclt(int shmid, int cmd, struct shmid_ds *buf);
2.4讯号量(semophore):
讯号量的主要作用在于解决使用共享显存时存在的多进程写冲突的问题。
讯号量本质上是一个整型计数器,主要拿来实现进程间的互斥与同步,而不是拿来缓存进程间通讯的数据。
讯号量表示资源的数目,控制讯号量有两种原子操作:
一个是P操作,将讯号量的个数-1;
一个是V操作,将讯号量的个数+1。
P操作用在步入共享资源之前,V操作用在离开共享资源以后,这两个操作必须是成对出现的
2.5讯号(signal):
讯号是惟一的异步进程间通讯机制。
Linux系统中为了响应各类各样的风波,提供了几十种讯号,使用kill-l命令可以进行查看:
比如,在shell终端进程中,通过Ctrl+C可以形成SIGINT讯号,表示中止该进程;通过Ctrl+Z可以形成SIGTSTP讯号,白哦是停止该进程,但还未结束,等。
使用kill命令发送讯号的格式是:
kill + signo + pid
例如:
kill -9 1050
一旦有讯号形成,用户进程对讯号的处理方法有三种:
执行默认操作:
Linux对每种讯号都规定了默认的操作,比如SIGTERM讯号表示中止进程等。捕捉讯号:
可以为讯号定义一个讯号处理函数,当讯号发生时,程序都会执行相应的讯号处理函数。忽视讯号:
倘若不希望处理个别讯号,就可以忽视该讯号,不做任何处理。
有两个讯号是应用进程难以捕捉和忽视的:
SIGKILL(9)和SIGSTOP(19)linux设置环境变量,它们用于在任何时侯中断或结束某一进程。2.6套接字(socket):
套接字也是一种进程间通讯机制,与其他通讯机制不同的是,它可以用于不同机器间的进程间通讯。