22
33直接开讲!
44
5- ![ ] ( https://cdn.jsdelivr.net /gh/xiaolincoder/ImageHost/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/%E8%BF%9B%E7%A8%8B%E9%97%B4%E9%80%9A%E4%BF%A1/3-%E6%8F%90%E7%BA%B2.jpg )
5+ ![ ] ( https://cdn.xiaolincoding.com /gh/xiaolincoder/ImageHost/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/%E8%BF%9B%E7%A8%8B%E9%97%B4%E9%80%9A%E4%BF%A1/3-%E6%8F%90%E7%BA%B2.jpg )
66
77---
88
99每个进程的用户地址空间都是独立的,一般而言是不能互相访问的,但内核空间是每个进程都共享的,所以进程之间要通信必须通过内核。
1010
11- ![ ] ( https://cdn.jsdelivr.net /gh/xiaolincoder/ImageHost/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/%E8%BF%9B%E7%A8%8B%E9%97%B4%E9%80%9A%E4%BF%A1/4-%E8%BF%9B%E7%A8%8B%E7%A9%BA%E9%97%B4.jpg )
11+ ![ ] ( https://cdn.xiaolincoding.com /gh/xiaolincoder/ImageHost/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/%E8%BF%9B%E7%A8%8B%E9%97%B4%E9%80%9A%E4%BF%A1/4-%E8%BF%9B%E7%A8%8B%E7%A9%BA%E9%97%B4.jpg )
1212
1313Linux 内核提供了不少进程间通信的机制,我们来一起瞧瞧有哪些?
1414
@@ -69,7 +69,7 @@ int pipe(int fd[2])
6969
7070这里表示创建一个匿名管道,并返回了两个描述符,一个是管道的读取端描述符 `fd[0]`,另一个是管道的写入端描述符 `fd[1]`。注意,这个匿名管道是特殊的文件,只存在于内存,不存于文件系统中。
7171
72- 
72+ 
7373
7474
7575其实,**所谓的管道,就是内核里面的一串缓存**。从管道的一段写入的数据,实际上是缓存在内核中的,另一端读取,也就是从内核中读取这段数据。另外,管道传输的数据是无格式的流且大小受限。
@@ -78,23 +78,23 @@ int pipe(int fd[2])
7878
7979我们可以使用 `fork` 创建子进程,**创建的子进程会复制父进程的文件描述符**,这样就做到了两个进程各有两个「 `fd[0]` 与 `fd[1]`」,两个进程就可以通过各自的 fd 写入和读取同一个管道文件实现跨进程通信了。
8080
81- 
81+ 
8282
8383管道只能一端写入,另一端读出,所以上面这种模式容易造成混乱,因为父进程和子进程都可以同时写入,也都可以读出。那么,为了避免这种情况,通常的做法是:
8484
8585- 父进程关闭读取的 fd[0],只保留写入的 fd[1];
8686- 子进程关闭写入的 fd[1],只保留读取的 fd[0];
8787
8888
89- 
89+ 
9090
9191所以说如果需要双向通信,则应该创建两个管道。
9292
9393到这里,我们仅仅解析了使用管道进行父进程与子进程之间的通信,但是在我们 shell 里面并不是这样的。
9494
9595在 shell 里面执行 `A | B`命令的时候,A 进程和 B 进程都是 shell 创建出来的子进程,A 和 B 之间不存在父子关系,它俩的父进程都是 shell。
9696
97- 
97+ 
9898
9999所以说,在 shell 里通过「`|`」匿名管道将多个命令连接在一起,实际上也就是创建了多个子进程,那么在我们编写 shell 脚本时,能使用一个管道搞定的事情,就不要多用一个管道,这样可以减少创建子进程的系统开销。
100100
@@ -134,7 +134,7 @@ int pipe(int fd[2])
134134
135135**共享内存的机制,就是拿出一块虚拟地址空间来,映射到相同的物理内存中**。这样这个进程写入的东西,另外一个进程马上就能看到了,都不需要拷贝来拷贝去,传来传去,大大提高了进程间通信的速度。
136136
137- 
137+ 
138138
139139---
140140
@@ -155,7 +155,7 @@ P 操作是用在进入共享资源之前,V 操作是用在离开共享资源
155155
156156接下来,举个例子,如果要使得两个进程互斥访问共享内存,我们可以初始化信号量为 `1`。
157157
158- 
158+ 
159159
160160具体的过程如下:
161161
@@ -171,7 +171,7 @@ P 操作是用在进入共享资源之前,V 操作是用在离开共享资源
171171
172172那么这时候,就可以用信号量来实现多进程同步的方式,我们可以初始化信号量为 `0`。
173173
174- 
174+ 
175175
176176具体过程:
177177
@@ -257,7 +257,7 @@ int socket(int domain, int type, int protocal)
257257
258258> 针对 TCP 协议通信的 socket 编程模型
259259
260- 
260+ 
261261
262262- 服务端和客户端初始化 `socket`,得到文件描述符;
263263- 服务端调用 `bind`,将绑定在 IP 地址和端口;
@@ -276,7 +276,7 @@ int socket(int domain, int type, int protocal)
276276
277277> 针对 UDP 协议通信的 socket 编程模型
278278
279- 
279+ 
280280
281281UDP 是没有连接的,所以不需要三次握手,也就不需要像 TCP 调用 listen 和 connect,但是 UDP 的交互仍然需要 IP 地址和端口号,因此也需要 bind。
282282
@@ -315,7 +315,7 @@ Linux 内核提供了不少进程间通信的方式,其中最简单的方式
315315
316316那么,就需要**信号量**来保护共享资源,以确保任何时刻只能有一个进程访问共享资源,这种方式就是互斥访问。**信号量不仅可以实现访问的互斥性,还可以实现进程间的同步**,信号量其实是一个计数器,表示的是资源个数,其值可以通过两个原子操作来控制,分别是 **P 操作和 V 操作**。
317317
318- 与信号量名字很相似的叫**信号**,它俩名字虽然相似,但功能一点儿都不一样。信号是进程间通信机制中**唯一的异步通信机制 **,信号可以在应用进程和内核之间直接交互,内核也可以利用信号来通知用户空间的进程发生了哪些系统事件,信号事件的来源主要有硬件来源(如键盘 Cltr+C )和软件来源(如 kill 命令),一旦有信号发生,**进程有三种方式响应信号 1. 执行默认操作、2. 捕捉信号、3. 忽略信号**。有两个信号是应用进程无法捕捉和忽略的,即 `SIGKILL` 和 `SEGSTOP `,这是为了方便我们能在任何时候结束或停止某个进程。
318+ 与信号量名字很相似的叫**信号**,它俩名字虽然相似,但功能一点儿都不一样。信号是**异步通信机制 **,信号可以在应用进程和内核之间直接交互,内核也可以利用信号来通知用户空间的进程发生了哪些系统事件,信号事件的来源主要有硬件来源(如键盘 Cltr+C )和软件来源(如 kill 命令),一旦有信号发生,**进程有三种方式响应信号 1. 执行默认操作、2. 捕捉信号、3. 忽略信号**。有两个信号是应用进程无法捕捉和忽略的,即 `SIGKILL` 和 `SIGSTOP `,这是为了方便我们能在任何时候结束或停止某个进程。
319319
320320前面说到的通信机制,都是工作于同一台主机,如果**要与不同主机的进程间通信,那么就需要 Socket 通信了**。Socket 实际上不仅用于不同的主机进程间通信,还可以用于本地主机进程间通信,可根据创建 Socket 的类型不同,分为三种常见的通信方式,一个是基于 TCP 协议的通信方式,一个是基于 UDP 协议的通信方式,一个是本地进程间通信方式。
321321
@@ -330,6 +330,6 @@ Linux 内核提供了不少进程间通信的方式,其中最简单的方式
330330
331331***哈喽,我是小林,就爱图解计算机基础,如果觉得文章对你有帮助,欢迎微信搜索「小林coding」,关注后,回复「网络」再送你图解网络 PDF***
332332
333- 
333+ 
334334
335335----
0 commit comments