进程之间的通信方式

进程之间的通信方式包括管道,消息队列,共享内存,信号,信号量,socket六种方式,下面来对这6种方式分别进行介绍。

一、管道

进程之间的通信方式_第1张图片

管道的结构示意图如上所示,管道包含一个输入端和一个输出端,输入端将数据写入到管道中,输出端可以从管道中将数据读出。 

管道存在于内存中,并不存在于文件系统中,也就是说,管道其实是一段缓存,而且,在数据写入管道后,进程会阻塞,直到管道中的数据被读出位置

可以在linux上面执行下面的指令进行测试

$ mkfifo myPipe //创建管道
$ echo "hello" > myPipe // 将数据写进管道
                        // 停住了 ...
$ cat < myPipe // 读取管道⾥的数据(需要另开一个终端)
hello

管道的缺点:

1.通信效率太低

2.只能单向进行通信

3.管道具有生命周期,会随着进程的销毁而销毁

为解决管道单向通信的问题,可以使用命名管道代替匿名管道进行通信。对于命名管道,它其实是创建了一个管道类型的设备文件,通过这个设备文件可以实现在不同进程之间的双向通信。

二、消息队列

操作系统维护一个队列结构,用于存放消息,在进程A向进程B发送消息时,先将消息放入消息队列中,这里面的消息是指预先定义好的数据结构,等到进程B从消息队列中取出该消息时,便可以对这条消息进行处理。在进程B接收了这条消息后,操作系统会把这条消息从消息队列中移除。

优点:

1.支持异步处理消息

2.消息队列并没有生命周期,只要操作系统没有关闭,或者没有主动清空消息队列,消息队列中的数据会一直存在。

缺点:

1.通信不及时

2.所存放的消息具有大小上的限制,不适合大数据之间的通信

3.消息队列维护在系统内核中,存在用户态与内核态之间切换的开销

三、共享内存

由于现代操作系统中使用的是虚拟内存,也就是说将为每个物理内存分配一块虚拟内存空间进行映射,操作系统的指令是在虚拟内存中执行,但在操作系统在增删改查数据时,通过虚拟内存找到实际的物理内存,进行数据的增删改查。进程A和进程B的都会分配到一块属于自己的虚拟内存,那么按照这个逻辑,我们可以让进程A和进程B的虚拟内存映射同一块物理内存空间,这样就可以实现两个进程之间的通信。

进程之间的通信方式_第2张图片

优点:

1.通信及时

2.通信效率较高

缺点:

在多个进程共享同一块物理内存时,可能会发生数据安全性的问题,即两个进程如果在同一时刻对共享内存的同一个变量进行写入操作时,后写入的结果会覆盖前写入的结果。

四、信号量

信号量是用于解决共享内存通信方式中的数据安全问题的。

信号量是一个整形计数器,信号量其实是⼀个整型的计数器,主要⽤于实现进程间的互斥与同步,⽽不是⽤于缓存进程间通信的数据。

控制信号量的方式有两种原子操作:

1)⼀个是 P 操作,这个操作会把信号量减去 1,相减后如果信号量 < 0,则表明资源已被占⽤,进程需阻塞等待;相减后如果信号量 >= 0,则表明还有资源可使⽤,进程可正常继续执⾏。
2)另⼀个是 V 操作,这个操作会把信号量加上 1,相加后如果信号量 <= 0,则表明当前有阻塞中的进程,于是会将该进程唤醒运⾏;相加后如果信号量 > 0,则表明当前没有阻塞中的进程。

按照上面的方式举一个信号量实现进程A和B互斥访问的例子:

1.进程A和B要对同一块共享内存进行操作,共享内存的信号量初始化为1.

2.假设进程A先进程操作,进程A需要在操作共享内存之间先执行P操作,设置信号量,此时信号量应该-1,变为0,根据定义进程A可以正常执行

3.线程B要对共享内存操作时,需要先设置信号量,此时信号量-1后为-1,根据定义,进程B需要阻塞等待

4.进程A在执行完毕后执行V操作,将信号量+1,此时信号量为0,根据定义,此时有进程被阻塞,进程A需要唤醒被阻塞的进程B

5.进程B被唤醒后,再次执行P操作,将信号量-1,此时信号量为0,进程B可以正常执行

进程之间的通信方式_第3张图片

五、信号

信号量主要是针对正常状态进程的,对于异常状态的进程,需要使用信号来见通信

可以通过kill -l查看所有的信号

$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX

其实我们平时按的快捷键或者指令就对应着信号

比如Ctrl + C 对应SIGINT信号,表示终止该进程

再比如 kill -9 pid 表示给对应pid的进程发信号,立即终止该进程

对于信号,用户进程有以下几种处理方式

1.执⾏默认操作。Linux 对每种信号都规定了默认操作,例如,上⾯列表中的 SIGTERM 信号,就是终⽌进程的意思。
2.捕捉信号。我们可以为信号定义⼀个信号处理函数。当信号发⽣时,我们就执⾏相应的信号处理函数。
3.忽略信号。当我们不希望处理某些信号的时候,就可以忽略该信号,不做任何处理。有两个信号是应⽤进程⽆法捕捉和忽略的,即 SIGKILL 和 SEGSTOP ,它们⽤于在任何时候中断或结束某⼀进程。

六、socket

socket用于实现跨网络不同主机之间的通信

socket是一个套接字,通过四元组(在tcp中为四元组,即源ip地址,目的ip地址,源端口号,目的端口号,在udp中只需要源ip地址和源端口号即可)来唯一标识接收和发送数据的两方,用于这两方之间的通信。

socket通信的过程如下:

进程之间的通信方式_第4张图片

1)服务端和客户端初始化 socket ,得到⽂件描述符;
2)服务端调⽤ bind ,将绑定在 IP 地址和端⼝;
3)服务端调⽤ listen ,进⾏监听;
4)服务端调⽤ accept ,等待客户端连接;
5)客户端调⽤ connect ,向服务器端的地址和端⼝发起连接请求;
6)服务端 accept 返回⽤于传输的 socket 的⽂件描述符;
7)客户端调⽤ write 写⼊数据;服务端调⽤ read 读取数据;
8)客户端断开连接时,会调⽤ close ,那么服务端 read 读取数据的时候,就会读取到了 EOF ,待处理完数据后,服务端调⽤ close ,表示连接关闭

以上是TCP传输协议下socket的工作过程,对于UDP,因UDP 是没有连接的,所以不需要三次握⼿,也就不需要像 TCP 调⽤ listen 和 connect,但是 UDP 的交互仍然需要 IP 地址和端⼝号,因此也需要 bind。

你可能感兴趣的:(计算机网络,java,网络,计算机网络)