Skip to content

Commit

Permalink
Merge pull request #243 from xinxinxiangying258/main
Browse files Browse the repository at this point in the history
format and add detail
  • Loading branch information
xiaolincoder authored May 23, 2024
2 parents 045c982 + 68314b1 commit b041fe9
Show file tree
Hide file tree
Showing 3 changed files with 6 additions and 6 deletions.
4 changes: 2 additions & 2 deletions network/3_tcp/challenge_ack.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ tcp_send_challenge_ack 函数里就会调用 tcp_send_ack 函数来回复一个
在 Linux 上有个叫 killcx 的工具,就是基于上面这样的方式实现的,它会主动发送 SYN 包获取 SEQ/ACK 号,然后利用 SEQ/ACK 号伪造两个 RST 报文分别发给客户端和服务端,这样双方的 TCP 连接都会被释放,这种方式活跃和非活跃的 TCP 连接都可以杀掉。


killcx 的工具使用方式也很简单,如果在服务端执行 killcx 工具,只需指明客户端的 IP 和端口号,如果在客户端执行 killcx 工具,则就指明服务端的 IP 和端口号。
killcx 的工具使用方式也很简单,如果在服务端执行 killcx 工具,只需指明客户端的 IP 和端口号,如果在客户端执行 killcx 工具,则就指明服务端的 IP 和端口号。

```csharp
./killcx <IP地址>:<端口号>
Expand Down Expand Up @@ -222,7 +222,7 @@ killcx 工具则是属于主动获取,它是主动发送一个 SYN 报文,
这两种工具都是通过伪造 RST 报文来关闭 TCP 连接的,但是它们获取「对方下一次期望收到的序列号的方式是不同的,也正因此,造就了这两个工具的应用场景有区别。

- tcpkill 工具只能用来关闭活跃的 TCP 连接,无法关闭非活跃的 TCP 连接,因为 tcpkill 工具是等双方进行 TCP 通信后,才去获取正确的序列号,如果这条 TCP 连接一直没有任何数据传输,则就永远获取不到正确的序列号。
- killcx 工具可以用来关闭活跃和非活跃的 TCP 连接,因为 killcx 工具是主动发送 SYN 报文,这时对方就会回复 Challenge ACK,然后 killcx 工具就能从这个 ACK 获取到正确的序列号。
- killcx 工具可以用来关闭活跃和非活跃的 TCP 连接,因为 killcx 工具是主动发送 SYN 报文,这时对方就会回复 Challenge ACK,然后 killcx 工具就能从这个 ACK 获取到正确的序列号。

完!

Expand Down
6 changes: 3 additions & 3 deletions os/8_network_system/reactor.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

但是引入了线程池,那么一个线程要处理多个连接的业务,线程在处理某个连接的 `read` 操作时,如果遇到没有数据可读,就会发生阻塞,那么线程就没办法继续处理其他连接的业务。

要解决这一个问题,最简单的方式就是将 socket 改成非阻塞,然后线程不断地轮询调用 `read` 操作来判断是否有数据,这种方式虽然该能够解决阻塞的问题,但是解决的方式比较粗暴,因为轮询是要消耗 CPU 的,而且随着一个 线程处理的连接越多,轮询的效率就会越低。
要解决这一个问题,最简单的方式就是将 socket 改成非阻塞,然后线程不断地轮询调用 `read` 操作来判断是否有数据,这种方式虽然该能够解决阻塞的问题,但是解决的方式比较粗暴,因为轮询是要消耗 CPU 的,而且随着一个线程处理的连接越多,轮询的效率就会越低。

上面的问题在于,线程并不知道当前连接是否有数据可读,从而需要每次通过 `read` 去试探。

Expand Down Expand Up @@ -163,7 +163,7 @@ Redis 是由 C 语言实现的,它采用的正是「单 Reactor 单进程」
- Handler 对象不再负责业务处理,只负责数据的接收和发送,Handler 对象通过 read 读取到数据后,会将数据发给子线程里的 Processor 对象进行业务处理;
- 子线程里的 Processor 对象就进行业务处理,处理完后,将结果发给主线程中的 Handler 对象,接着由 Handler 通过 send 方法将响应结果发送给 client;

单 Reator 多线程的方案优势在于**能够充分利用多核 CPU 的能**,那既然引入多线程,那么自然就带来了多线程竞争资源的问题。
单 Reator 多线程的方案优势在于**能够充分利用多核 CPU 的性能**,那既然引入多线程,那么自然就带来了多线程竞争资源的问题。

例如,子线程完成业务处理后,要把结果传递给主线程的 Reactor 进行发送,这里涉及共享数据的竞争。

Expand All @@ -187,7 +187,7 @@ Redis 是由 C 语言实现的,它采用的正是「单 Reactor 单进程」

方案详细说明如下:

- 主线程中的 MainReactor 对象通过 select 监控连接建立事件,收到事件后通过 Acceptor 对象中的 accept 获取连接,将新的连接分配给某个子线程;
- 主线程中的 MainReactor 对象通过 select 监控连接建立事件,收到事件后通过 Acceptor 对象中的 accept 获取连接,将新的连接分配给某个子线程;
- 子线程中的 SubReactor 对象将 MainReactor 对象分配的连接加入 select 继续进行监听,并创建一个 Handler 用于处理连接的响应事件。
- 如果有新的事件发生时,SubReactor 对象会调用当前连接对应的 Handler 对象来进行响应。
- Handler 对象通过 read -> 业务处理 -> send 的流程来完成完整的业务流程。
Expand Down
2 changes: 1 addition & 1 deletion os/8_network_system/zero_copy.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ write(socket, tmp_buf, len);
![](https://cdn.jsdelivr.net/gh/xiaolincoder/ImageHost2/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/%E9%9B%B6%E6%8B%B7%E8%B4%9D/%E4%BC%A0%E7%BB%9F%E6%96%87%E4%BB%B6%E4%BC%A0%E8%BE%93.png)
首先,期间共**发生了 4 次用户态与内核态的上下文切换**,因为发生了两次系统调用,一次是 `read()` ,一次是 `write()`,每次系统调用都得先从用户态切换到内核态,等内核完成任务后,再从内核态切换回用户态。
首先,期间共**发生了 4 次用户态与内核态的上下文切换**,因为发生了两次系统调用,一次是 `read()` ,一次是 `write()`,每次系统调用都得先从用户态切换到内核态,等内核完成任务后,再从内核态切换回用户态。
上下文切换的成本并不小,一次切换需要耗时几十纳秒到几微秒,虽然时间看上去很短,但是在高并发的场景下,这类时间容易被累积和放大,从而影响系统的性能。
Expand Down

0 comments on commit b041fe9

Please sign in to comment.