普通视图

发现新文章,点击刷新页面。
昨天以前UoCat

异星工厂 - 无符号位移

作者 tqcq
2023年9月4日 23:50

1.1 无符号位移原理

1.1.1 背景

异星工厂中的算数运算器只提供了有符号位移 (sar),但是想要造一个高性能计算机需要实现无符号位移 (shr)。

1.1.2 sar 实现 shr 的原理

实现无符号右移有几个目标:

  • 高效:运算在 1 tick 以内(也就是说,和有符号位移速度一样快)
  • 简单:不需要太多的电路,这样才能保证计算机小巧。
  • 支持 32bit: 这样才能实现 32bit 计算机

首先,我们来看看有符号右移的原理:

有符号右移说明
x >= 0 此时,sarshr 的结果一致
x < 0 此时,sarshr 的结果不一致,可以对比下面两张图

1.1.3 实现负数的 无符号右移 (shr)

异星工厂中的算数运算器是 32-bit 有符号整数,我们利用加法溢出可以移除负数的最高位。

其中算数运算器的处理过程如下:

注意输出侧会有一次求和,可以利用这次求和让符号位溢出

​ 现在我们通过设置 红色信号 A 为 0x8000000 = -2147483648,就可以实现将绿色信号中 A 的符号为移除的效果。

1.1.3.1 移位修正

例子

我们用 16bit 有符号距离感,假设有一个 16-bit 数字右移 8 位分为有符号和无符号两种讨论 A>>>8 的计算方法

将 A 设置为 0x8000000 之后,实现了下面的变换:

  • 1xxx xxxx xxxx xxxx -> 0xxx xxxx xxxx xxxx
  • 0xxx xxxx xxxx xxxx -> 1xxx xxxx xxxx xxxx

移位之后有:

  • 1xxx xxxx xxxx xxxx -> 0xxx xxxx xxxx xxxx -> 0000 0000 0xxx xxxx
  • 0xxx xxxx xxxx xxxx -> 1xxx xxxx xxxx xxxx -> 1111 1111 1xxx xxxx

这里非常接近我们需要的结果,因为我们只需要在最后两个数字的第 8 位加一个 1,就可以得到需要的结果:

  • 0000 0000 0xxx xxxx + 0000 0000 1000 0000 -> 0000 0000 1xxx xxxx
  • 1111 1111 1xxx xxxx + 0000 0000 1000 0000 -> 0000 0000 0xxx xxxx

这里我们可以设置另外两个变量 X, Y
其中 X = Y = 0100 0000 0000 0000

当 A 为负数时,X,Y 会进位,替代被溢出丢失的符号位
当 A 位正数时,X,Y 会进位,但是这时是和我们设置的常量 0x80000000 求和溢出,X + Y + 0x8000000 = 0

1.1.4 当移位 31 位时

移位 31 位时,我们发现 X 和 Y 在求和之前都已经成为 0,无法进位抵消 A 的符号位,因此需要特判 B == 31 时,输出一个 A = 1 进行修正。

至此我们已经实现了一个 1 tick 的 shr 运算单元。

1.2 无符号右移 shr 蓝图

1
0eNq1ld2OmzAQhd9lrmEV/kqCtCt1n6JVVSECk81IYCNjokYR796xaSjdDbAk6k0kE8/xmflm7AvsyxZrRUJDcgHKpWgg+XGBht5EVppv+lwjJEAaK3BAZJVZZYr0sUJNuZvLak8i01JB5wCJAn9B4nXOokaBORWobgv43U8HUGjShL0juzinoq32qPiEQafCgtrKxRJzrdhQLUvkU2rZcLAU5nwjGHlPkQNnSNzwKeKDOFXBEWQzvoBnft4UohifRgUkAe8llbek7ZKddZ1J750hfy6xG3Y2Vzt+b0YrWaZ7PGYn4gDe9Ucm5f8KGnweSDU6/VDaEynd8pfBRL/DfYVevdGZQRx4ZlXVmbK2EnjmCNnqul2h+bXXrM9srRU6PShZpSRYA5JDVjbYTZRXYbFQXG9UWLMOudgO+J8Ltj3zCMJgob/nmmqK4l+lx0Bilh9N3Rs0MisbQNbIwK1veHm5D/kKqP57qFNcolm8/mfxftj4bxN5E7jDIcvrgMzDDndX2J69QQpSfTGs1C34Byo1qokbdWG8WjOvru+FcbgNvoTbhbt1Su3bSG0Qi0f37Bqx7/NigcV5d5PchBSthDRM5OZ6y/8HKt6dMF5HIsFm/Ng98CD1lePZsM9rMnrRHThxnrY4/pZx7fw4DuJdtA277je2Ga8W

熊猫拿铁配置无线网卡

作者 tqcq
2022年7月25日 05:40

1.1 熊猫拿铁配置无线网卡

1.1.1 背景

入手了一个熊猫拿铁板子,无奈自带的 Wi-Fi 传输速率感人,遂决定增加一块无线网卡水星UD6S

1.1.2 安装驱动

首先安装 DKSMDKSMDynamic Kernel Module Support)是用来生成内核模块的框架,每次更新内核时,都会自动编译相关模块,保证更新后驱动的可用性。

1
sudo apt-get install -y dkms

1.1.3 编译驱动

驱动使用了开源项目 rtl8821CU, 执行下面的命令,等待安装完成即可

1
2
3
4
5
6
7
8
9
## 安装当前版本内核头文件
sudo apt-get install -y linux-headers-`uname -r`

## 安装相关工具
sudo apt-get install -y make gcc bc git

## 克隆代码,然后编译安装
git clone https://github.com/brektrou/rtl8821CU.git
cd rtl8821CU && sudo bash dkms-install.sh

1.1.4 配置 wifi

这里使用 NetworkManager 管理 Wi-Fi

  • 首先安装 NetworkManger

    1
    2
    3
    sudo apt-get install -y network-manager
    ## 设置开机自启,并且立即启动 NetworkManager
    sudo systemctl enable --now NetworkManager
  • 查看可用设备

    1
    2
    3
    4
    $ nmcli device
    DEVICE TYPE STATE CONNECTION
    wlx23438 wifi disconnected --
    wlan0 wifi disconnected --

    这里有两个 wifi 设备,名字分别为 wlx23438wlan0

  • 查看可用 wifi

    1
    2
    3
    4
    5
    6
    7
    8
    $ nmcli device wifi
    IN-USE SSID MODE CHAN RATE SIGNAL BARS SECURITY
    angell Infra 11 130 Mbit/s 49 ▂▄__ WPA2
    1104 Infra 1 270 Mbit/s 45 ▂▄__ WPA1 WPA2
    -- Infra 1 270 Mbit/s 45 ▂▄__ WPA1 WPA2
    HUAWEI-0H1G91 Infra 1 130 Mbit/s 45 ▂▄__ WPA2
    Xiaomi_4892 Infra 11 270 Mbit/s 45 ▂▄__ WPA1 WPA2
    CMCC-xjcf_5G Infra 1 130 Mbit/s 44 ▂▄__ WPA1 WPA2
  • 连接 wifi

    1
    sudo nmcli device wifi connect "your-ssid" password "your-password" ifname wlx23438

    这里我们指定要链接的 wifi密码,并要求使用设备 wlx23438

git commit 签名

作者 tqcq
2023年8月7日 18:12

1.1 创建自己的 GPG 密钥

执行一下命令选择加密算法,并完善相关信息。

步骤简介

  1. 选择加密算法 (RSA 签名)
  2. 设置密钥长度 (4096)
  3. 设置密钥有效期 (1y),这是确保安全的最后防线
  4. 设置用户姓名、邮箱、注释(最好不要用常用邮箱,避免垃圾邮件骚扰)

1.1.1 运行 gpg 开始生成密钥

1
gpg --full-generate-key

输出如下,这里选择 4

1
2
3
4
5
6
7
8
9
10
gpg (GnuPG) 2.2.27; Copyright (C) 2021 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
(1) RSA and RSA (default)
(2) DSA and Elgamal
(3) DSA (sign only)
(4) RSA (sign only)
(14) Existing key from card

这里我们生成的是主密钥,只负责分发子密钥,因此只需要具有签名功能即可
这里我选择 4. RSA,有如下几个原因

  • RSA 久经验证,是行业标准。
  • 相关研究认为更长的 RSA,参考此论文: A RIDDLE WRAPPED IN AN ENIGMA
  • RSA 在各个平台上都有很好的支持。

1.1.2 设置密钥长度

现在计算机性能都不差,可以选择 4096 位的密钥长度,这样可以更好的保护你的密钥。

1
2
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072)

1.1.3 设置过期时常

这里非常推荐设置一个过期时长,避免密钥被盗用,一般设置为 1 年。

这里我们输入 1y,表示 1 年后过期。
如果有更高的安全需求,可以设置更短的过期时间。
即将过期的时候,可以通过 gpg --edit-key <key-id> 命令来延长密钥的有效期。

1
2
3
4
5
6
7
8
Requested keysize is 4096 bits
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
Key is valid for? (0)

1.1.4 接下来输入身份信息

从略

1.1.5 为主密钥设定密码

这一步应该设定一个强度高的密码。

1
2
3
4
5
6
7
8
lqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqk
x Please enter the passphrase to x
x protect your new key x
x x
x Passphrase: ________________________________________ x
x x
x <OK> <Cancel> x
mqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqj

1.1.6 完成创建

主密钥成就解锁

恭喜你,走到这一步已经获得了一个主密钥。

1
2
3
4
生成一个用于此用途的子密钥。
pub rsa4096 2023-08-07 [SC] [expires:2024-08-06]
AC27EFC2205EE9272D6B87F8DD6E204C56810DCB
uid tqcq &lt;[email protected]>

1.2 签发子密钥

1.2.1 首先进入 gpg 的交互模式

1
gpg --edit-key tqcq

1.2.2 添加子密钥

1
2
# 这里的 gpg> 是 gpg 的交互模式提示符,不需要输入
gpg> addkey

1.2.3 选择子密钥类型

这里选择 10 即可,ECC 兼顾速度和安全性

1
2
3
4
5
6
7
8
9
10
11
12
13
Please select what kind of key you want:
(3) DSA (sign only)
(4) RSA (sign only)
(5) Elgamal (encrypt only)
(6) RSA (encrypt only)
(7) DSA (set your own capabilities)
(8) RSA (set your own capabilities)
(10) ECC (sign only)
(11) ECC (set your own capabilities)
(12) ECC (encrypt only)
(13) Existing key
Your selection? 10

1.2.4 选择子密钥算法

注意

NIST、Brainpool、secp256k1 具有一些安全隐患,不推荐使用。

1
2
3
4
5
6
7
8
9
10
11
Please select which elliptic curve you want:
(1) Curve 25519 *default*
(2) Curve 448
(3) NIST P-256
(4) NIST P-384
(5) NIST P-521
(6) Brainpool P-256
(7) Brainpool P-384
(8) Brainpool P-512
(9) secp256k1
Your selection? 1

设定过期时间即可生成子密钥

1.3 设置 git 签名

1.3.1 本地 git 签名设置

这里我们用子密钥来签名

1
2
3
4
5
6
7
## 查看子密钥的 keyid
gpg --list-keys --keyid-format=long

## git 设置签名密钥
git config --global user.signingkey <sub-key-id>
## 设置每次commit自动签名
git config --global commit.gpgsign true

1.3.2 Github 公钥设置

首先导出你的公钥,用于验证签名

这里 --armor 导出公钥为 ASCII 格式

1
gpg --armor --export <sub-key-id>

添加完成后,你的提交就会被标记为 Verified

注意

如果签名失败,请在环境变量中指定 GPG_TTY, 具体原因参考 [2]

export GPG_TTY=$(tty)

1.4 常用 GPG 命令

列出所有密钥: gpg --list-keys

1.5 参考资料

chrome-allow-devonthink

作者 tqcq
2023年8月7日 09:56

1.1 取消 Chrome 中打开 Devonthink 的弹窗

我在使用 Devonthink 的时候,经常会遇到一个问题,就是在 Chrome 中点击 Devonthink 的链接,会弹出一个提示框,让我选择是否允许打开 Devonthink。

1.1.1 原因

出现这个问题的原因参见是Chrome商业版策略设置,对不信任的协议进行弹窗提示。

参见 URLAllowlist

1.1.2 解决方案

在 Mac 的 URLAllowlist 中添加要允许的协议即可,对DEVONThink其协议为 `x-devonthink://*`,所以在Mac中执行以下命令即可解决问题。
1
2
## Mac
defaults write com.google.Chrome URLAllowlist -array-add 'x-devonthink://*'

Linux 的配置方案参考 StackOverflow 问答:
how-to-configure-policies-preferences-for-chromium

top-password-2023

作者 tqcq
2023年8月6日 10:44

1.1 TopPassword 2023

收集了我的服务器在线期间被扫描的 Top10 密码,仅供参考。

1
2
3
4
5
6
7
8
9
10
11
12
13
              Mon Nov 13 01:13:50 PM UTC 2023
┌ ┐
123456 ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 2022.0
1234 ┤■■■■■■■■■■■■■■■■■■ 1118.0
admin ┤■■■■■■■■■■■■■■■ 954.0
root ┤■■■■■■■■■■■■■■■ 918.0
123 ┤■■■■■■■■■■■■■ 818.0
test ┤■■■■■■■■■■■■ 745.0
12345 ┤■■■■■■■■■■■■ 743.0
password ┤■■■■■■■■■■■ 701.0
toor ┤■■■■■■■■■■ 602.0
qwerty ┤■■■■■■■■■ 567.0
└ ┘

1.2 Generate command

执行如下命令即可生成上述图表。

1
2
3
4
5
6
7
8
grep 'authentication for user.*with password' log.txt  | \
awk '{print $10}' | \
sort | \
uniq -c | \
sort -nrk1 | \
head -n10 | \
awk '{print $2, $1}' | \
uplot bar -d ' ' -t "`date`"

uplot 是一个开源命令行绘图工具,参见此链接 YouPlot

Docker 常见问题

作者 tqcq
2023年7月25日 05:40

1.1 >> Docer 常见问题

1.1.1 CI/CD 如何利用已有 Image 缓存

背景: 在流水线构建中,每次都可能是一个新的环境,想要利用已有镜像的缓存

解决方案

通过每次拉取相关镜像,并指定将其作为缓存即可,同时减小 push 的大小。

1
2
docker pull $BASE_IMAGE` || echo "no image"
docker build --cache-from $BASE_IMAGE -t mytag

ResilioSync Pro Key

作者 tqcq
2023年7月23日 13:58

1.1 这是一个 ResilioSync 的 Pro 许可证

提供文件的 MD5 如下

faf812f3cc2de8988ccb310bad30a145

1
2
3
# 拷贝执行下面的代码即可在当前目录生成许可证
ResilioSyncProKey="YnRvczFfZXlKeklqb2dJbTV4TmpSbU5HZ3JXRGh4V0hGaWFXUm9kRXR5VFdkTmNFVmFNSGRZYVVKTGJFeHdTV0Z3U0RVeE16a3lhSGRxTUZGcFFXSk9UVEZHZDJKR1dHcEpRMEZoVjJ3NFYzWmlkVnBQZW5wSVRYWm1OVFJMV1UxWFZHVnNWRlpqUzNKR1FVcFlNbVZVTUd0dFQybENNbTh6UTFCalZGZDJWV2xDTHpSc1RUbFFOMUZzYWxWU1Z6UkVNVkZOVWt0V2NFaHdhRzVJVnpJdlptZHFOMDVNYVRobU1uaE5hMU5ETkVSd1JWVnBRMHBYWkhwUFdYRkliVzFPTUZkT1VFdDZRV2c0UmtKb2FuTnZVVk5CYTFoS04wOVdTMGM1V0Vsa1ZUY3pXbk5sVldOeFdXMVdWVW8wV0RjMFMwNXdZbWxZWmswdmVtbHNVMnBJSzBVNVkzWk1aMDkyWWxwbFZqbENaWGx2ZUROd1QzcFJZVUpPZUZOUVQzRllXRXBUUkhFMWJWUmhSWE12V0dwcFZHdEpWMjF1VkhKdWRWSm5iMDFEWjBSQk9IZEJTM1JJWWpSdlZrZHVXbTFqZVU4eE5saG9RM2R2TDJWa2VIRnhaejA5SWl3Z0ltUWlPaUFpWlhsS2FtRjVTVFpKUTBwcFpFYzVlbGw2Um1aYVdHeExZVzFPTlZOVVdrcFJNSEJKVmxab1lWWXhSbGRVYWtac1ZsZG9XVlpyV2xkTk1VcFhVMnRXVlZKVVJsWlpWRVpYWkZaYWNWVnNVbXBOTVVwNVdrY3hkMUZzVWtWUlZGWldVWHBzTVZacmFHdFVWazV3VDBoc1VGRXpVak5VVjNoclZqSk9jazFJYUZWaGVsSTFWWHBPUTFReVVsaGlSM1JUWWxWd2IxWnROVXRVUmxKWlZWUldWRko2UlhwV1ZWVTFUVlp3ZFZkdFJsVldiVkoxVlZkNFNrMXNXWGxYYmtac1ltMTRiMVpXYUhkbGJFVjNUVlZLYW1KWGFFNVhWbFl3WkZad1ZWSllaRlJTYkVwT1ZXNXdVMkpHYkhCa1JYQlZWbFJHU2xZd1VuTldNa1Y1WkVaU2FsWkZSWGRVYlRWdVRVZEtjazFXWkU1U1IyTjNWbXhXWVZSc1NuSk5WemxxVWxWd01GbFZVbE5sUlRGSllrWmFXbUV4UlhoVmJURnpUVVV4U1ZwSVpFNWlSM2hWV1d4b2IxSXlUa1ZTYkU1c1ZsaGtlVlZzV205T1YwWkdVbFJPVW1KV1NuVldWRVpEWVVkS1NWWnJXa3hOVjNoWlZtcEdiMkZ0U2pWUFZXeFNWbFJzVDFsdE1UTk5iRTV6VWxSQ1lXRnNTbEZXYlhCQ1pVWlJkMVZyU2xwaGExWjVWMjB3TVdSVk5YVlhibFpTVFRKbmVsbDZSbGRUYXpsR1RWVTFhRk5IZUZGVVJFRTFUVVpPYzJKRldsZFRSa3BYVmxWYVNrMXNVa1JrUmtwWFVqSm9VbFl3VWxOamJHeHlUbGhHVGxKSGVGcFhibXd3WWpKR1ZrMVZlRlpXTVZwVlUzcENkMlJGTlRaVmJscFVZVzE0U1ZwRVFuSmxWa1owVFZWc2FsSkdXWGRXUmxaVFpFWnJlV1ZJY0U5V2JXaExWMVprYjFNeFpFZFNhMXBxVFVoU01WVnNhRmRsYkUxM1ZHcFNhVkpzU2pWVVZtUjNaRmRPU0dGRk5XRmxiRlYzV1cweE5HSkZPVlZoUnpsVVUwVldlVlZXYUU1TlYxWlpWV3hTVGxJeVRUVlZSazVLWXpCc1JGTnRjR0ZSTUdzeVUxVk9TMkpIVmxaalNHUmFZbFJHYUZwSGRITmpWMGw1V2tkNGJGWllRVEpYYTJONFZERktTRk5ZYkZaaVdHaExXVmN3TldKc1RsaE9WVGxQVmpCd01GWkhlRWRoYlVwVllVaENWVkpWTlVOWlZscHlaVmRTU1ZGdFJsSk5SM041VlRGV1QxTXlWa2RXYmxKWFZrVktWRmx0Y3pGamJHdDNWMnMxVG1KVlZqTmFWVll3WVcxV2MxTnNhRnBsYTBweVZGVmFSbVZHUm5SbFJtUlVVbFJXTlZVeFpITk5NVzkzWWtoU1YyRnNTbkZWVkVKeVRXeE9WbFZyY0d4U1ZGWkdWbXhTVjFSc1drZGhla0pWVmxaS1ExbDZRbk5TUms1MFkwZHdUazFWV25kV1JFcHpVVzFHV0ZOcVZrNVdlbFpRVm10YVlWVkdiRFpSYlRsb1lrWndlbFpzWkhOVVZrVjNVbTVDWVZKWGFIcGFSRVozVmtaT1ZWZHJjRkpOU0VGNlZqSjRiMU15Vm5SVFdHeFBWakpvY0ZWVVFuTmxiRTVXVkd0MGEySlZOVXBXVnpWM1UyMUdkRTlYTldGWFIzaE1XV3hrU21WWFZraGtSMFpZVWxoQ1ZsWXllR3RVTWxaWFkwWnNWbFl5ZUZGWlZsWkhZMFpTZEdNemFGVk5WbHBJVmpKNFlWWkdTa2hWYTA1VlYwaENWRll5ZUVkV2JGWlZVbXhvVTFaR1draFdWM1JYVlRGV1YxVnJXazlXVm5CWVZGWldkMVV4VWxoalJrNVZWbXhzTkZkcVNuTlVWa1YzVW01Q1dtVnJjRmhaVldSVFUxWlNXR0pHUW1oV1ZWa3dWMjE0Ym1ReVRYZGlSVkpVWWxob2MxVXdWa2RqUmxGNVlrVktiRlpVUmxaV1ZsSkhWVVphUmxaWWJGVk5Sa3BEV2tSS1lWVnNRbFZOUjJ4dFZWUXdPVWxwZDJkSmJXeDFXbTA0YVU5cFFqZEpiVGw1V2tkV2VWTlhVV2xQYVVGcFdWYzFhR1JYZUcxWlZ6VXhZVEphZFdJeWRIVmlWekY2VFVSWk5XTlhiek5PUTBselNVTkthR05wU1RaSlJFRnpTVU5LYkdWSVFXbFBhVUY1VFZSUk1VOVVSVEpQUkVGM1RFTkJhVmt6U214SmFtOW5UVlJSTkUxVVVYbE9lazAwVG5sM1owbHVUakpaTUd4clNXcHZaMGx1VGpWaWJVNVJZMjA0ZUVscGQyZEpiVTU2WkVOSk5rbERTblpNVjJNeFRsVTFlbE5IVGxkV1UwbHpTVU5LZW1SdFRrUmlNbEpzU1dwdlowbHVUalZpYlU1UlkyMDRhVXhEUVdsWk1teHJTV3B2WjBsdFpIRmFWMjk1WW1sSmMwbERTakJsV0VKc1NXcHZaMGx1UW14amJrNTJZbTFHYzBscGQyZEpiVGwzWkVoTmFVOXBRamRKYlZwMllrZFNiR05zVG14Wk0wcHNaRU5KTmtsRFNrSldNRkpPVkhwU1JsVXhUa0pVYkdoQ1YyeEtWRTFyTUhsT1JscENUbXRrUWxJeFNsaFdWRkpSVGxaWmFVeERRV2xqTWxab1pFaE5hVTlwUVhobVdEQnpTVU5LYkdWSVFXbFBhVUY1VFZSUk1VOVVSVEpQUkVGM1psRTlQU0o5"
echo $ResilioSyncProKey | base64 -d > ResilioSyncPro.btskey

epoll - 基本使用和细节

作者 tqcq
2023年7月22日 09:48

1.1 epoll 默认设置

  • epoll 默认处于水平触发

1.2 epoll Events

参考 epoll.h [1]

事件简介补充
EPOLLIN 文件描述符可读。当有数据可读时触发该事件。
EPOLLPRI 有紧急数据可读。用于处理带外数据(out-of-band data)或优先级数据。
EPOLLOUT 文件描述符可写。当可以写入数据时触发该事件。
EPOLLRDNORM 有普通数据可读。类似于 EPOLLIN,用于读取操作。
EPOLLRDBAND 有带外数据可读。类似于 EPOLLPRI,用于处理带外数据。
EPOLLWRNORM 可写入普通数据。类似于 EPOLLOUT,用于写入操作。
EPOLLWRBAND 可写入带外数据。通常情况下不使用此事件。
EPOLLMSG 有消息数据可读。通常情况下不使用此事件。
EPOLLERR 文件描述符发生错误。当文件描述符发生错误时触发该事件。
EPOLLHUP 文件描述符挂起。当文件描述符的连接关闭时触发该事件。
EPOLLRDHUP 文件描述符被远程关闭连接或半关闭连接。在较新的内核版本中使用,旧版本使用 EPOLLHUP 来表示。
EPOLLEXCLUSIVE 独占模式。用于实现边缘触发(Edge Triggered)。
EPOLLWAKEUP 唤醒等待的线程。用于唤醒被 epoll_wait 阻塞的线程。
EPOLLONESHOT 单次事件监听。事件触发后需要重新添加到 epoll 集合中。
EPOLLET 边缘触发模式。边缘触发模式仅在文件描述符状态变化时通知一次,与水平触发模式不同。

warning

EPOLLEXCLUSIVE: 可以用于缓解多进程共享的 socket 惊群问题。但是同一进程两次 epoll_wait 之间,如果有新的连接到来并被其他进程处理,当前进程仍旧会被唤醒。[2] epoll_image_1

1.3 epoll 相关函数

epoll 在 kernel-6.4.1 中有 6 个函数,如下所示

1
2
3
4
5
6
7
8
extern int epoll_create (int __size) __THROW;
extern int epoll_create1 () // 相比 epoll_create,增加了flags参数
extern int epoll_ctl (int __epfd, int __op, int __fd,
struct epoll_event *__event) __THROW;
extern int epoll_wait (int __epfd, struct epoll_event *__events,
int __maxevents, int __timeout)
extern int epoll_pwait () // 可以设置阻塞过程中忽略的信号,防止被打断
extern int epoll_pwait2 () // 相比 epoll_wait 增加了超时控制

1.3.1 基本操作

1.3.1.1 创建一个 epoll 实例:

1
2
3
4
5
6
7
// #include <sys/epoll.h>

int epollfd = epoll_create1(0);
if (epollfd == -1) { // 返回 -1 代表失败, 这里必须检查
perror("epoll_create1");
return EXIT_FAILURE;
}

1.3.1.2 epoll_ctl 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// #include <sys/socket.h>
// #include <sys/types.h>

#define DEFAULT_PORT 8080
#define MAX_EVENTS 10

int ret = 0;
// ========= 创建 servfd =========
int servfd = socket(AF_INET /* IPv4 */, SOCK_STREAM, 0);
if (servfd == -1) {
// check error msg by errno
perror("socket error");
return -1
}

struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(DEFAULT_PORT);

ret = bind(servfd, (struct sockaddr*) &servaddr, sizeof(servaddr));
if (ret == -1) {
// check error msg by errno
perror("bind error");
return -1;
}

/**
* Linux2.2之前, 1024 是未完成链接队列最大长度,
* Linux2.2之后,表示完成连接等待应用处理的队列长度,如果需要制定等待连接的队列长度,请使用 tcp_max_syn_backlog (man 7 tcp 可以查看细节)
*/
ret = listen(servfd, 1024);
if (ret == -1) {
// check error msg by errno
perror("listen error");
return -1
}
// ========= 创建 servfd 结束 =========
/**
* struct epoll_event
* {
* uint32_t events; // Epoll events
* epoll_data_t data; // User data variable
* } __EPOLL_PACKED;
**/
struct epoll_event ev;
struct epoll_event events[MAX_EVENTS];

ev.event = EPOLLIN; // set read event
ev.data.fd = servfd;

for (;;) {
int nready = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if (nready == -1) {
perror("epoll_wait error");
return -1;
}

for (int i = 0; i < nready; i++) {
if (events[i].data.fd == servfd) {
int connfd = accept(servfd, (struct sockaddr*) &cliaddr, &addrlen);
if (connfd == -1) {
perror("accept error");
return -1;
}

setnonblocking(connfd);
// 设置 读事件监听、ET模式
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = connfd;
// 这里添加客户端的 fd 到epoll中
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ev) == -1) {
perror("epoll_ctl: EPOLL_CTL_ADD error");
return -1;
}
} else {
// pass
// 这里处理客户端sock的事件
}
}
}

1.4 常见问题

挖坑

惊群效应、C10K、连接丢失

1.5 引用

Study RxCpp(1)

作者 tqcq
2023年7月9日 16:22

1.1 仓库信息

以下是本笔记所使用的 RxCpp 仓库及其版本等信息。

AB
链接 https://github.com/ReactiveX/RxCpp/
commit761b932a80e2be6e2b62d232e754bd96fc448946

1.2 项目信息

为了学习 RxCpp,我们会实际使用 RxCpp,便于测试,加深理解。

1.2.1 构建简单的 CMake 项目

1
2
3
4
5
6
7
# mkdir rxcpp-study
# cd rxcpp-study
# touch CMakeList.txt main.cpp
# tree
.
├── CMakeLists.txt
└── main.cpp

这是一个简单的项目结构,我们将会一步一步完善这个项目。

1.2.1.1 首先是 CMakeList.txt

1
2
3
4
5
6
7
cmake_minimum_required(VERSION 3.25)
project(rxcpp_study)

set(CMAKE_CXX_STANDARD 17)

add_executable(rxcpp_study main.cpp)

1.2.1.2 接下来写一个简单的 main.cpp

1
2
3
4
5
6
#include <iostream>

int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}

1.2.1.3 运行这个项目

1
2
3
4
5
6
7
8
9
10
11
# 首先创建一个 build 目录,避免污染源代码目录
mkdir build && cd build
# 开始生成配置文件
cmake ..
# 用 make 构建项目
make
# 以上完成之后,可以得到 rxcpp_study
./rxcpp_study
# 执行完毕之后应该有以下输出:
# Hello, World!

1.2.2 导入 RxCpp 库

1.2.2.1 导入为第三方库

1
2
3
4
5
6
7
8
9
10
# 首先用git管理仓库
# 首先创建一个 third_party 目录来统一管理第三方库
mkdir third_party

# 接下来将 RxCpp 放到该目录
git submodule add https://github.com/ReactiveX/RxCpp/ third_party/RxCpp

# clone 的 RxCpp是默认分支,可能与本笔记版本不同,我们切换到统一版本
cd third_party/RxCpp
git checkout 761b932a80e2be6e2b62d232e754bd96fc448946

现在我们已经将 RxCpp 加入到我们的项目中来,并通过 git 管理该项目。

1.2.2.2 在 CMakeList.txt 中引用 RxCpp

只需在 CMakeList.txt 中增加两行即可使用 RxCpp

1
2
3
4
5
6
7
8
9
cmake_minimum_required(VERSION 3.25)
project(rxcpp_study)

set(CMAKE_CXX_STANDARD 17)

add_executable(rxcpp_study main.cpp)
## 以下是导入RxCpp 的头文件
target_include_directories(rxcpp_study
PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/third_party/RxCpp/Rx/v2/src")

1.2.2.3 使用 RxCpp

main.cpp 中导入 rxcpp/rx.hpp 即可

1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include <rxcpp/rx.hpp>

int main() {
rxcpp::observable<>::range(1, 5)
.as_blocking()
.subscribe([](int value){ std::cout << value << std::endl;},
[](){});
return 0;
}

运行结果如下:
1
2
3
4
5

❌
❌