|
1 |
| -IPC是一种允许多进程间通信的一种机制。 |
2 |
| -有许多IPC的实现方式,他们依赖于不同架构的运行环境。运行在同一台机器上的进程可以由多种进程间通信方式,比如共享内存、消息队列和管道。如果多进程处于物理上分布式集群上上,我们可以使用RPC的方式。 |
3 |
| -第五章中我们介绍了Multiprocessing和ProcessPoolExecutor模块,学习了常规的管道的用法。我们学习了共有一个父进程的各个子进程间通信方式,但是有时候我们需要在不相关的进程间传递信息(子进程不共有同一个父进程),我们会想,通过不相关进程的地址空间,是否能够在它们之间建立通信。虽然一个进程不能获取另外一个进程的地址空间,但是我们需要使用一种新的机制——命名管道。 |
| 1 | +##理解进程间通讯 |
| 2 | +IPC是允许多进程间通信的一种机制。 |
| 3 | +有许多IPC的实现方式,他们依赖于不同架构的运行环境。运行在同一台机器上的进程可以由多种进程间通信方式,比如共享内存、消息队列和管道。如果多进程处于物理上分布式集群上上,我们可以使用RPC的方式。 |
| 4 | +第五章中我们介绍了Multiprocessing和ProcessPoolExecutor模块,学习了常规的管道的用法。我们学习了同一个父进程的各个子进程间通信方式,但是有时候我们需要在不相关的进程间传递信息(子进程不共有同一个父进程),我们会想,通过不相关进程的地址空间,是否能够在它们之间建立通信。然而一个进程不能获取另外一个进程的地址空间,因此我们需要使用一种新的机制——命名管道。 |
| 5 | + |
4 | 6 | ###探索命名管道
|
5 |
| -对于POSIX系统,例如linux,我们可以把所有归结为文件,我们每操作一个文件,我们可以找到一个文件与之对应,我们还可以找到另外一个文件用于描述这个任务,该文件使得我们可以操控任务对应的文件。 |
6 |
| -命名管道通过使用文件描述符(该文件描述符对应于要执行的任务,比如FIFO方式读写文件任务)的方式实现进程间IPC通信。在对信息的管理上命名管道不同于常规管道, |
| 7 | +对于POSIX系统,例如linux,我们可以把所有东西归结为文件,我们每操作一个任务,我们可以找到一个文件与之对应,同时还有一个文件描述符与该文件相联系,通过文件描述符就可以操作该文件. |
| 8 | + |
| 9 | +命名管道是一种允许通过某些特殊文件的文件描述符实现IPC通讯的机制.这些特殊文件使用特殊的模式(例如先入先出)来读写数据.在对信息的管理上命名管道不同于常规管道,命名管道通过文件系统中的特殊文件及其文件描述符来实现,而普通管道是在内存中创建的. |
| 10 | + |
| 11 | +###在python中使用命名管道 |
| 12 | +在python中使用命名管道很容易. 下面我们会通过两个非直接通讯的程序来展示如何使用命名管道. 第一个程序名为write\_to\_named_pip.py,它的作用是写一条22个字节的消息到管道中,该消息包括一个信息字符串及产生消息的PID. 第二个程序名为read\_from\_named\_pip.py, 它会从管道中读取消息并展示消息内容及其PID. |
| 13 | + |
| 14 | +在执行的最后,read\_from\_named\_pipe.py进程会显示一条形如"I pid [<The PID of reader process>] received a message => Hello from pid [the PID of writer process"的消息 |
| 15 | + |
| 16 | +为了展示读写进程的相互依赖关系,我们会在两个独立的控制台上运行这两个程序. 在展示结果前,让我们先分析一下两个程序的代码. |
| 17 | + |
| 18 | +####往命名管道写入数据 |
| 19 | +在python中,命名管道是通过系统调用来实现的. 下面我们会逐行解释write\_to\_named\_pipe.py中代码的功能. |
| 20 | + |
| 21 | +我们首先导入os模块,这样我们在后面的代码中才能调用系统调用. |
| 22 | +```python |
| 23 | +import os |
| 24 | +``` |
| 25 | + |
| 26 | +接下来我们会解释\_\_main\_\_代码块,在该代码块中创建了命名管道以及一个用于存储消息的FIFO的特殊文件. \_\_main\_\_代码块中的第一行代码定义了命名管道的标签. |
| 27 | +```python |
| 28 | +named_pipe = "my_pipe" |
| 29 | +``` |
| 30 | + |
| 31 | +接下来我们检查该命名管道是否已经存在,若不存在,则调用mkfifo系统调用来创建这个命名管道. |
| 32 | +```python |
| 33 | +if not os.path.exists(named_pipe): |
| 34 | + os.mkfifo(named_pipe) |
| 35 | +``` |
| 36 | + |
| 37 | +mkfifo调用会创建一个特殊的文件,该文件对通过命名管道读写的消息实现了FIFO机制. |
| 38 | + |
| 39 | +我们再以一个命名管道和一个行如"Hello from pid [%d]"的消息来作为参数调用函数write\_message. 该函数会将消息写入到(作为参数传递给它的)命名管道所代表的文件中. write\_message函数定义如下: |
| 40 | +```python |
| 41 | +def write_message(input_pipe, message): |
| 42 | + fd = os.open(input_pipe, os.O_WRONLY) |
| 43 | + os.write(fd, (message % str(os.getpid()))) |
| 44 | + os.close(fd) |
| 45 | +``` |
| 46 | + |
| 47 | +我们可以观察到,在函数的第一行,我们调用一个系统调用:open. 该系统调用若成功的话会返回一个文件描述符, 通过该文件描述符我们就能够读写那个FIFO文件中的数据. 请注意,我们可以通过flags参数控制打开FIFO文件的模式. 由于write\_message函数紧紧需要写数据,因此我们使用如下代码: |
| 48 | +```python |
| 49 | +fd = os.open(input_pipe, os.O_WRONLY) |
| 50 | +``` |
| 51 | + |
| 52 | +在成功打开命名管道后,我们使用下面代码写入消息: |
| 53 | +```python |
| 54 | +os.write(fd, (message % os.getpid())) |
| 55 | +``` |
| 56 | + |
| 57 | +最后,请一定记着使用close关闭通讯渠道,这样才能释放被占用的计算机资源. |
| 58 | +```python |
| 59 | +os.close(fd) |
| 60 | +``` |
| 61 | + |
| 62 | +####从命名管道读取数据 |
| 63 | +我们实现了read\_from\_pipe.py来读取命名管道. 当然,改程序也需要借组os模块才能操作命名模块. 改程序的主要代码很简单:首先,我们定义了所使用命名管道的标签,该标签需要与写进程所用的命名管道同名. |
| 64 | +```python |
| 65 | +named_pipe = "my_pipe" |
| 66 | +``` |
| 67 | + |
| 68 | +然后,我们调用read\_message函数,该函数会读取write\_to\_named\_pipe.py写入的内容. read\_message函数的定义如下: |
| 69 | +```python |
| 70 | +def read_message(input_type): |
| 71 | + fd = os.open(input_pipe, os_RONLY) |
| 72 | + message = ( |
| 73 | + "I pid [%d] received a message => %s" |
| 74 | + % (os.getpid(), os.read(fd, 22)) |
| 75 | + os.close(fd) |
| 76 | + return message |
| 77 | +``` |
| 78 | + |
| 79 | +上面代码中,open调用相比无需再介绍了,唯一没见过的只有read调用. 该调用从命名管道中读取指定字节的内容. 这里我们从文件描述符中读取22个字节的内容. 消息被读取出来之后,又被该函数作为返回值返回. 最后依然记得调用close调用来关闭通讯渠道. |
| 80 | + |
| 81 | +最终,下面的截屏显示了write\_to\_named\_pip和read\_from\_named\_pipe程序的执行结果. |
0 commit comments