SYN Queue and Accept Queue

0x00 A Single Queue or Two Queues

TCP/IP stack has two options to implement the backlog queue for a socket in the LISTEN state.

Door #1: A Single Queue

A single queue can contain connections in two different states:

  • SYN RECEIVED
  • ESTABLISHED

And only connections in ESTABLISHED state can be returned to the application by calling the accept() syscall.

Therefore, the length of this queue is determined by the backlog argument of the listen() syscall.

Door #2: A SYN Queue & An Accept Queue

In this case, connections in state SYN RECEIVED are added to the SYN queue, and later moved to the accept queue when their state changes to ESTABLISHED.

Read More

Toml to Golang Struct

之前写某个东西的副产品,本质上和 https://xuri.me/toml-to-go/ 一样。

解析 Toml 仍然依赖 github.com/BurntSushi/toml 这个库。

Read More

auto-cfs-cores C++ 版 automaxprocs

之前写了一篇分析 uber 的 automaxprocs 的源码,后面抽个了时间写了一个 C++ 版本的。

其一是为了加深对原库的理解;其二是避免太长时间没写 C++ 手生,以及顺带体验一下 C++ 17 的感觉。

代码在:https://github.com/kingsamchen/Eureka/tree/master/auto-cfs-cores

Read More

uber automaxprocs 源码分析

最近直播内部的 golang 服务都使用了 uber 出品的 automaxprocs 这个库。

据伟大的 @ice 马总说,这个库解决了一个困扰B站整个golang(container)技术栈一年多的问题。

出于好奇这个库到底做了什么 magic,能够解决这个持续了一年多的 pain in the ass,抽了一点时间,稍微翻了一下库的源代码,记录如下。

automaxprocs 解决了什么问题

线上容器里的服务通常都对 CPU 资源做了限制,例如默认的 4C。

但是在容器里通过 lscpu 仍然能看到宿主机的所有 CPU 核心:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
rchitecture:          x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 48
On-line CPU(s) list: 0-47
Thread(s) per core: 2
Core(s) per socket: 12
Socket(s): 2
NUMA node(s): 2
Vendor ID: GenuineIntel
CPU family: 6
Model: 79
Model name: Intel(R) Xeon(R) CPU E5-2650 v4 @ 2.20GHz
Stepping: 1

这就导致 golang 服务默认会拿宿主机的 CPU 核心数来调用 runtime.GOMAXPROCS(),导致 P 数量远远大于可用的 CPU 核心,引起频繁上下文切换,影响高负载情况下的服务性能。

Read More

一个轮子:基于 token bucket 的 rate-limit

地址:https://github.com/kingsamchen/Eureka/tree/master/token-bucket-rate-limit

Read More

Windows 上使用 Git Tips 两则

Windows Terminal 中 git log 显示 UTF-8 编码的中文

这个准确的说其实是 powershell 自己的问题。

Read More

复习:DCLP 和 Memory Reordering

本文是对 Jeff Preshing 的 Double Checked Locking Is Fixed in C++ 11 笔记。

0x00 传统实现

因为 synchronization 只需要在实例第一次创建时保证;此后( instance != nullptr 时)都不需要锁来保证 synchronization。

在第一次判断实例为空和上锁之间存在一个 potential race,因此上锁后需要再一次判断实例是否为空。

这也是 double checked 的来由。

所以一个传统但并不100%正确的 DCLP 实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Singleton {
public:
static Singleton* GetInstance()
{
if (instance == nullptr) {
std::lock_guard<std::mutex> lock(mtx);
if (instance == nullptr) {
instance = new Singleton(); // <-- key point
}
}

return instance;
}

private:
// omit explicit static field initialization.
static std::mutex mtx;
static Singleton* instance;
};

这基本是早起 C++ DCLP 的实现架子。

0x01 问题:内存乱序以及为什么锁帮不了忙

语句

1
instance = new Singleton();	

在 C++ 中实际上等效于

1
2
3
tmp = operator new(sizeof(Singleton));  // step 1: allocate memory via operator new
new(tmp) Singleton; // step 2: placement-new for construction
instance = tmp; // step 3: assign addr to instance

注意:其中除了分配内存是固定第一步之外,构造对象和赋值内存地址的生成代码顺序是由编译器自己决定。这里的顺序只是一种可能性。

Read More