8.7 进程间的通讯:管道、消息队列、共享内存、信号量、信号、Socket
进程间的通讯
进程间为什么需要通讯?
共享数据、数据传输、消息通知、进程控制
进程间的通讯有哪些类型?
首先,联系前面讲过的知识,进程之间的用户地址空间是相互独立的,不能进行互相访问,但是,内核空间却是共享的,所以进程间的通信要通过内核。
这里以Linux为例,介绍几种常见的Linux进程间的通讯方式:共享内存、管道、消息队列、信号量、信号。
管道
如果你学习过linux命令肯定见过 | 这个竖线。
$ ps auxf | grep mysql
上面命令里的竖线 | 就是一个管道,它在这里的作用是:把前一个命令(ps auxf)的输出,作为后一个命令(grep mysql)的输入。
由此可见,管道传输数据是单向的。如果想互相通信,我们最少需要两个管道。
上面这例子的管道是没有名字的,所以我们称 | 这样的管道为匿名管道,用完即毁。
还有一种管道是命名管道,也叫做FIFO,先进先出的意思。
在使用命名管道前,需要通过mkfifo命令创建,还要加上管道的名字。
$ mkfifo myPipe
管道名字就是myPipe,在Linux中也是以文件的形式存在的:
w-rw-r--. 1 iron2222 iron2222 0 Dec 4 22:20 myPipe
我们尝试向这个管道里写入数据:
$ echo "hello" > myPipe //把数据写进去
//你会发现停住了,没反应
因为只有管道之中的数据被读完后,命令才可以正常退出:
$ cat < myPipe
hello
注意,一定要在另一个终端上,输入该命令,保持原终端界面保持不变,这样执行该命令后会很明显的看到,原终端命令可以正常退出了。
从这里可以看出来,管道这种方式:效率低,不适合频繁的交换数据。
消息队列
消息队列的通讯模式:A进程给B进程发消息,A进程把消息放到对应的消息队列之后就可以直接返回了,B进程需要的时候去读就可以了。
消息队列的本质是,保存在内核中的消息链表。发送方与接收方要约定好,消息体的数据类型与大小。读完之后,内核就会把这个消息体删除。
不好的地方:消息队列在通讯的过程中,存在用户态与内核态之间的数据拷贝开销。
共享内存
共享内存的机制,就是拿出来一块虚拟地址空间,映射到相同的物理内存中。读取效率很高。
缺点是,如果两个进程同时向一块物理地址中写数据时,会造成覆盖。
信号量
为了防止出现共享内存那样,出现多进程竞争共享资源,所以需要保护机制,让共享的资源,在任意时刻只能被一个进程访问。
信号量其实就是一个整型计数器,主要用于实现进咸亨间的互斥和同步,而不是用于缓存进程间通信的数据。
初始为1,有A进程去用就变成0,若此时有B进程想用,就变-1,使B进程阻塞。
就相当于一种标记,告诉进程这个共享资源有没有空。
信号
信号,一般用于那些处于异常情况下的进程间的通信,是一种异步信号,就是一个数字。
比如,在Linux中,为了相应不同的事件,提供了几十种信号,反别代表不同的意义:
$ 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
举一个大家常见的例子:
在shell终端里,我们可以使用某些快捷键,给进程发信号:
- Ctrl+C 会产生SIGINT信号,终止进程;
- Ctrl+Z 会产生SIGTSTP信号,表示停止进程,但进程未结束。
也可以通过kill命令,但前提要知道进程的PID号。
- kill -9 1050,表示给PID为1050的进程发送9) SIGKILL信号,立即结束该进程。
唯一的异步通信机制,进程需要为信号设置相应的监听处理,执行相关操作。
Socket
如果你想跨网络与不同主机上的进程进行通讯,就要用到Socket了。