vcpkg

使用模式

项目使用 vcpkg 解决依赖问题

参考这里

项目作为 port 供其他项目使用

创建内部 registry 供组织内使用

添加 port 到内部 registry

安装

参考

安装依赖

1
2
# arch
$ sudo pacman -S curl zip unzip tar cmake ninja

安装 vcpkg

1
2
$ git clone https://github.com/Microsoft/vcpkg.git
$ ./vcpkg/bootstrap-vcpkg.sh

概念

使用

引入 CMAKE_TOOLCHAINE_FILE

1
2
3
4
5
6
# 命令行
cmake ... -DCMAKE_TOOLCHAIN_FILE=/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake

# CMakeLists.txt 内
## NOTE: 要在 project 之前
set(CMAKE_TOOLCHAIN_FILE /path/to/vcpkg/scripts/buildsystems/vcpkg.cmake)

vcpkg-configuration.json

example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg-configuration.schema.json",
"default-registry": {
"kind": "git",
"repository": "https://internal/mirror/of/github.com/Microsoft/vcpkg",
"baseline": "eefee7408133f3a0fef711ef9c6a3677b7e06fd7"
},
"registries": [
{
"kind": "git",
"repository": "https://github.com/northwindtraders/vcpkg-registry",
"baseline": "dacf4de488094a384ca2c202b923ccc097956e0c",
"packages": [ "beicode", "beison" ]
}
],
"overlay-ports": [
"./team-ports",
"./custom-ports"
],
"overlay-triplets": [ "./my-triplets" ]
}

软件包搜索

Manifest 模式

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"name": "cook-cpp",
"version": "0.0.1",
"dependencies": [
"boost",
"fmt",
"spdlog"
],
"overrides": [
{
"name": "boost",
"version": "1.82.0"
},
{
"name": "fmt",
"version": "9.1.0"
},
{
"name": "spdlog",
"version": "1.11.0"
}
],
"builtin-baseline": "21bbb14c4113b89cd06402e852e075341722304f"
}
  • buildin-baseline
    • vcpkg 的 commit id
    • 如果要制定版本(使用 overrides),则必须设置该项

配置文件

  • vcpkg.json
  • vcpkg-configuration.json

添加内部库

项目使用 vcpkg

官方文档

Registries

获取 SHA512

1
vcpkg hash /path/to/source.tar.gz

获取 versions 的 git-tree

  • 添加 ports/<library> 目录及配置(profile.cmake、vcpkg.json) 等
  • 提交 commit
  • 使用如下命令获取 git-tree
1
git rev-parse HEAD:ports/<library>

Patch

1
2
3
4
# 生成
git diff > patch
# 应用
git apply patch

注意

  • 不要直接复制 patch 内容,容易出现 error: corrupt patch at line * 的问题

引入包

cmake targets

通过 CMake Targets 引入目标,适用于基于 CMake 编译的库,且导出目标配置文件({target}Config.cmake)。

1
2
find_package(target CONFIG REQUIRED)
target_link_libraries(main PRIVATE target)

find_library

1
2
find_package(TargetVar NAMES target REQUIRED)
target_link_libraries(main PRIVATE ${TargetVar})

pkgconfig

1
2
3
include(FindPkgConfig)
pkg_check_modules(target REQUIRED IMPORTED_TARGET target)
target_link_libraries(main PRIVATE PkgConfig::brpc)

Trouble Shooting

OUT_SOURCE_PATH

压缩包解压路径在 {VCPKG_HOME}/buildtrees/{library}/src/ar-{version}-{whatever}.clean

清除安装的包

1
2
rm -r {VCPKG_HOME}/buildtrees/{library}
rm -r {VCPKG_HOME}/packages/{library}

patch failed: error: corrupt patch at line

  • patch 文件过期,内容不对,需要重新生成 patch 文件
  • 不可见字符问题
    • 不要直接拷贝 patch 内容,要复制 patch 文件
      • 复制粘贴有可能导致不可见字符变更
      • 部分 IDE 会自动优化不可见字符,导致 patch 文件失效

pkg_check_modules 无法找到包

错误信息

1
2
3
4
-- Checking for module 'cassandra_static'
-- No package 'cassandra_static' found
CMake Error at /snap/cmake/1328/share/cmake-3.27/Modules/FindPkgConfig.cmake:607 (message):
A required package was not found

解决

正常来说,vcpkg 安装的包,如果有 pkgconfig 是可以通过 pkg_check_modules 找到的。出现问题的原因是 find_package 引入包时意外导致 pkg_check_modules 不可用,解决方案有两个。

  • 找到出现问题的 find_package 语句,并移到后面
  • 遍历 CMAKE_PREFIX_PATH 并追加 lib/pkgconfig 后加入到 ENV{PKG_CONFIG_PATH}

或者设置如下内容。

1
set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE)

以 tcmalloc 为例。

1
2
3
set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE)
include(FindPkgConfig)
pkg_check_modules(gperftools REQUIRED IMPORTED_TARGET GLOBAL libtcmalloc)

Boost

head only library

vcpkg.json

1
2
3
4
5
6
7
8
9
10
{
"name": "feature-generator-server",
"version": "0.0.1",
"dependencies": [
"boost-core",
"boost-stacktrace",
"boost-pfr",
"boost-..."
]
}

CMakeLists.txt

1
2
find_package(Boost REQUIRED)
include_directories(${Boost_INCLUDE_DIRS})

patch failed

在更新版本之后出现 patch failed 异常,尝试删除对应的缓存解决。

1
$ rm -r {VCPKG_HOME}/buildtrees/protobuf

引用问题

case 1 通过查看 share/{lib}/{lib}Config.cmake 查看

1
2
include_directories(${JSON11_INCLUDE_DIRS})
target_link_libraries({target} ${JSON11_LIBRARIES})

unable to find “Ninja”

full message

1
CMake Error: CMake was unable to find a build program corresponding to "Ninja".  CMAKE_MAKE_PROGRAM is not set.  You probably need to select a different build tool.

可能的原因

  • 没有或不是最新的 ninja,从 这里 下载最新 ninja 程序
  • 使用的是 ninja-build 程序,需要把 ninja 复制或软链称 ninja-build
  • 其他原因,排查错误信息的 vcpkg-manifest-install.log 文件
    • git 权限问题
    • 磁盘内存不足

submodule

在使用 vcpkg_from_git 时,不会初始化 submodule,需要自己处理。

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
set(GIT_URL {git-or-http-url})
set(GIT_REF {commit-id-or-tag})

set(SOURCE_PATH ${CURRENT_BUILDTREES_DIR}/${GIT_REF})
file(MAKE_DIRECTORY ${SOURCE_PATH})
message(STATUS "build {lib} [CURRENT_BUILDTREES_DIR=${CURRENT_BUILDTREES_DIR}, SOURCE_PATH=${SOURCE_PATH}]")
if (NOT EXISTS "${SOURCE_PATH}/.git")
message(STATUS "Cloning")
vcpkg_execute_required_process(
COMMAND ${GIT} clone ${GIT_URL} ${SOURCE_PATH}
WORKING_DIRECTORY ${SOURCE_PATH}
LOGNAME clone
)
endif ()

message(STATUS "Fetching submodules")
vcpkg_execute_required_process(
COMMAND ${GIT} submodule update --init
WORKING_DIRECTORY ${SOURCE_PATH}
LOGNAME submodule
)

message(STATUS "Checkout revision ${GIT_REF}")
vcpkg_execute_required_process(
COMMAND ${GIT} checkout ${GIT_REF}
WORKING_DIRECTORY ${SOURCE_PATH}
LOGNAME checkout
)

Checkout Git Tree

检出并保存在其他路径

1
2
mkdir /tmp/<tree-ish>
git archive <tree-ish> | tar -x -C /tmp/<tree-ish>

从 remote 检出

1
git archive --remote=https://github.com/user/repo.git <tree-ish> | tar -x -C /tmp/<tree-ish>

示例

从 vcpkg 检出 gRPC 1.22.0 的 port 文件到当前目录。

1
2
3
4
5
# 从本地检出
git -C ~/.local/vcpkg archive f9ee8bb31f04f4e6a8c0d3e96fbb98deeb448d45 | tar -x -C .

# 从 remote 检出 (不一定能行)
git archive --remote=git@github.com:grpc/grpc.git f9ee8bb31f04f4e6a8c0d3e96fbb98deeb448d45 | tar -x -C .

minikube usage

文档

安装

  • minikube install

  • docker install

    • 需要配置当前用户操作权限
  • kubectl 安装

    • ubuntu sudo snap install kubectl --classic
    • arch sudo pamac install kubectl

archilinux

driver=virtualbox

1
2
3
4
5
6
7
$ sudo pamac install virtualbox
$ sudo modprobe vboxdrv
$ sudo modprobe vboxnetadp
$ sudo modprobe vboxnetflt

# 启动 minikube
$ minikube start --driver=virtualbox

配置

启用私有仓库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ minikube addons configure registry-creds

Do you want to enable AWS Elastic Container Registry? [y/n]: n

Do you want to enable Google Container Registry? [y/n]: n

Do you want to enable Docker Registry? [y/n]: y
-- Enter docker registry server url: registry.cn-hangzhou.aliyuncs.com
-- Enter docker registry username: <username>
-- Enter docker registry password:

Do you want to enable Azure Container Registry? [y/n]: n
✅ registry-creds was successfully configured
$ minikube addons enable registry-creds

注册

1
2
3
4
5
6
7
8
9
10
11
$ kubectl create secret generic regcred \
--from-file=.dockerconfigjson=$HOME/.docker/config.json \
--type=kubernetes.io/dockerconfigjson \
--namespace=default

# 在 deployment.yml 中添加如下内容
spec:
...
spec:
imagePullSecrets:
- name: regcred

问题

driver=docker 时无法通过宿主机 ip 访问 service

  • service type 设置为 NodePort
  • 无法通过宿主机的 ip 或者 127.0.0.1 + service node port 访问服务

cuda

概念

核函数

核函数是运行在 GPU 设备上的函数,使用 __global__ 定义,且返回类型必须是 void

硬件相关

Streaming Multiprocessor(SM)

  • 多个 SP 加上其他资源组成一个 SM
  • 其他资源包括
    • warp scheduler
    • register
    • shared memery

Streaming Processor(SP)

  • 也称为 CUDA core
  • 最基本的处理单元

img

GPU 中的内存

  • 全局内存(Global Memory)
    • GPU 上所有线程共享访问
  • 共享内存(Shared Memory)
    • 位于 SM 中的一块高速缓存内存
    • 由线程块中的线程共享
  • 寄存器(Register)
    • 每个线程都有对应的本地寄存器缓存

通信

  • block 内的线程可以同步也可以通过 shared memory 通信
  • grid 内的线程共享 global memory

线程组织

  • thread
    • 最小任务单元
  • block
    • 分配给 SM 执行
    • 每个线程块最多有 1024 个线程
    • 可以是一维、二维、三维
  • warp
    • block 中的线程以 warp 为单位进行分组
    • warp 大小为 32(当前)
    • 同一个 warp 中的 thread 以不同数据资源执行相同指令(SIMT,单指令多线程)
    • GPU 执行程序时的调度、执行的基本单元
    • 由 SM 的硬件 warp scheduler 负责调度
  • grid
    • 单独的 kernel 启动的所有线程组成一个 grid
    • grid 中所有线程共享 global memory
    • 多个 block 组成一个线程网格 grid
    • 可以是一维、二维、三维

其他

  • 核函数中 <grid, block>
    • grid
      • block 的组织方式
      • 可以是一至三维
    • block
      • thread 的组织方式
      • 可以是一至三维
  • 指定网格结构
    • dim3 grid(8, 4, 2);

CHATGPT

如何提高 gpu 的吞吐能力

要提高 GPU 的吞吐能力,可以考虑以下几个方面的优化:

  1. 并行化和向量化:利用 GPU 的并行处理能力和 SIMD(Single Instruction, Multiple Data)特性,尽可能将任务划分为独立的子任务,并对数据进行向量化操作。这样可以最大程度地利用 GPU 的计算资源。
  2. 内存访问优化:合理管理数据的访问模式,尽量减少内存访问的延迟和带宽消耗。可以使用共享内存来提高访问速度,通过数据重用和局部性原则减少全局内存访问次数。
  3. 内核函数设计:设计高效的内核函数,在保持功能完整性的同时,最大限度地减少同步和分支等开销。避免线程间冲突和竞争条件,提高执行效率。
  4. 内存传输优化:尽量减少 CPU 和 GPU 之间的数据传输,可以通过使用异步内存传输、数据压缩、数据预取等技术来减少传输时间和带宽占用。
  5. 算法选择和优化:针对具体的问题和数据特点,选择适合 GPU 并行计算的算法和数据结构。根据硬件架构的特点,对算法进行调整和优化,例如减少不必要的计算步骤、减少数据依赖等。
  6. 合理利用异构计算:除了 GPU,可以结合 CPU 和其他加速器(如FPGA)进行协同计算,充分利用不同硬件之间的优势来提高整体系统的吞吐能力。
  7. 使用专门的优化工具和库:针对特定的 GPU 架构和编程模型,使用专门的优化工具和库,例如 NVIDIA 的 CUDA Toolkit 提供的性能分析工具和优化指南。

以上是一些常见的方法,但具体的优化策略会取决于具体的应用场景和问题需求。在实际优化过程中,通过性能分析和测试来识别瓶颈,并基于具体情况采取相应的优化手段是非常重要的。

manjaro usage

包管理工具

  • pacman / pamac (GUI)
  • AUR
  • snap
  • flatpak

搜索包

初始化

1
2
3
4
# 安装并启用 sshd.service
$ sudo pacman install openssl openssh
$ sudo systemctl enable sshd.service
$ sudo systemctl start sshd.service

pacman

查看帮助

1
2
$ pacman -h
$ pacman -S -h

切换源

1
2
3
4
5
6
# manjaro
# 1. 刷新
$ sudo pacman-mirrors -i -c China -m rank
# 选择合适的源
# 2. 更新
$ sudo pacman -Syy

或选择最近源。

1
$ sudo pacman-mirrors --geoip && sudo pacman -Syyu

参数

1
2
3
4
5
6
7
# -S
-S 安装
-S --needed --noconfirm
--needed: 跳过已经安装到最新版本的包
--noconfirm: 跳过确认

-R 卸载

查看安装的包

1
2
3
4
# 所有安装的包
$ pacman -Q
# 查询指定包
$ pacman -Q vim

搜索包

1
$ pacman -Ss vim

AUR

AUR(Arch User Repository)。

AUR Helpers

AUR helpers 可以帮助我们搜索、下载、编译 AUR 包,常见的 AUR helpers 有 yay、paru等。

Paru

安装

1
2
3
4
$ sudo pacman -S --needed base-devel
$ git clone https://aur.archlinux.org/paru.git
$ cd paru
$ makepkg -si

使用示例

1
2
# 搜索
$ paru -Ss gcc7
1
2
3
4
5
6
7
8
9
10
paru <target> -- Interactively search and install <target>.
paru -- Alias for paru -Syu.
paru -S <target> -- Install a specific package.
paru -Sua -- Upgrade AUR packages.
paru -Qua -- Print available AUR updates.
paru -G <target> -- Download the PKGBUILD and related files of <target>.
paru -Gp <target> -- Print the PKGBUILD of <target>.
paru -Gc <target> -- Print the AUR comments of <target>.
paru --gendb -- Generate the devel database for tracking *-git packages. This is only needed when you initially start using paru.
paru -Bi . -- Build and install a PKGBUILD in the current directory.

其他

可以手动搜索包,然后使用 git clone,并使用命令 makepkg -s 编译,使用命令 makepkg -i 安装,或直接使用 makepkg -is 命令编译安装。也可以通过 pamac 命令使用 aur。

1
2
3
4
5
6
# 安装 pamac
$ pacman -S pamac-cli
# 搜索 aur 包
$ pamac search ttf-ms-fonts --aur
# 安装
$ pamac build ttf-ms-fonts

安装 AUR 包

pamac build pkgp

安装官方包

pamac install pkg

大语言模型小试

Vicuna

VicunaGithub - FastChat

运行

安装依赖

1
2
3
4
5
6
$ pip3 install torch transformers fschat
# 安装 lfs
## ubuntu
sudo apt install git-lfs
# 验证
$ git lfs install

下载

1
2
3
4
# llama 原始模型
$ git clone https://huggingface.co/decapoda-research/llama-13b-hf
# vicuna 模型
$ git clone https://huggingface.co/lmsys/vicuna-13b-delta-v1.1

更新参数

1
2
3
4
$ python3 -m fastchat.model.apply_delta \
--base-model-path /data/llama-13b-hf \
--target-model-path /data/vicuna-13b \
--delta-path /data/vicuna-13b-delta-v1.1

Serving

1
2
3
nohup python3 -m fastchat.serve.controller &
nohup python3 -m fastchat.serve.model_worker --model-path /data/vicuna-13b --device cpu &
nohup python3 -m fastchat.serve.gradio_web_server &

LLM - startup

问题排查

nvidia-container-cli: initialization error: load library failed: libnvidia-ml.so.1 #154

详见这里

1
2
3
4
5
6
sudo apt-get purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras
sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl restart docker

no compatible GPUs were discovered / Failed to initialize NVML: Unknown Error

Ollama docker 容器找不到 GPU。

如果使用 docker compose,配置如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
services:
ollama:
image: ollama/ollama:latest
restart: always
hostname: ollama
runtime: nvidia
user: root
ports:
- '11434:11434'
volumes:
- /data/docker/llm/ollama:/root/.ollama
networks:
- default
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]

解决。宿主机编辑 /etc/nvidia-container-runtime/config.toml

1
no-cgroups = false # 修改, true -> false

重启 docker。

1
sudo systemctl restart docker

vector

配置

source

file

1
2
3
4
5
6
7
[sources.test]
type = "file"
include = [ "/tmp/test.log" ]
line_delimiter = "$$\n"

# unicode
line_delimiter = "\u0003\u0004\n"

line_delimiter

1
2
3
4
5
6
7
8
9
\b         - backspace       (U+0008)
\t - tab (U+0009)
\n - linefeed (U+000A)
\f - form feed (U+000C)
\r - carriage return (U+000D)
\" - quote (U+0022)
\\ - backslash (U+005C)
\uXXXX - unicode (U+XXXX)
\UXXXXXXXX - unicode (U+XXXXXXXX)

example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[api]
enabled = true
address = "0.0.0.0:8686"

[sources.test]
type = "file"
include = [ "/tmp/test.log" ]
line_delimiter = "$$\n"

[sinks.console]
inputs = ["test"]
target = "stdout"
type = "console"
encoding.codec = "text"

安装

docker

文档

1
2
3
4
5
6
7
8
9
10
# pull image
$ docker pull timberio/vector:0.30.0-debian
# create container
$ docker run \
-d \
-v $PWD/vector.toml:/etc/vector/vector.toml:ro \
-v /tmp/test.log:/tmp/test.log \
--name vector \
-p 8686:8686 \
timberio/vector:0.30.0-debian

trouble shooting

日志滚动后无法更新

可能原因

vector 在日志文件轮转之后,默认会计算日志文件的前n个字节的 check sum, 如果值一直,则不会监听新的文件。如果日志文件有固定的前缀,那么每次日志轮转,check sum 会保持一致,那么会无法切换至新的文件。
比如,vector 一开始监听 debug.log 文件,文件到达一定条件后做轮转操作,移动 debug.logdebug.log.1,创建新的 debug.log 文件。如果 debug.logdebug.log.1 有相同的 check sum 值,那么 vector 不会监听新的文件。

解决方案

修改 fingerprinter 的计算策略为 device_and_inode

1
2
3
4
5
6
7
8
[sources.debug-log]
type = "file"
include = ["/app/name/debug.log"]
max_line_bytes = 409600 # optional, default, bytes
ignore_older = 10 #10s
line_delimiter = "\u001e\u001f\n"
[sources.debug-log.fingerprinting]
strategy = "device_and_inode"

相关 issues

rpc 基础

服务端

请求处理模型

  • 单线程阻塞模型
  • 多线程/进程模型
  • 线程池模型
  • 异步非阻塞模型
  • 协程模型

异步非阻塞模型

epoll

epoll(event poll) 是 Linux 内核提供的一种高级 IO 多路复用机制,可以监听多个文件描述符上的事件,并将这些事件通知给应用程序,从而实现异步的 IO 操作。

epoll 监听的 fd(file descriptor)集合是常驻内核的,它有 3 个系统调用 (epoll_create, epoll_wait, epoll_ctl),通过 epoll_wait 可以多次监听同一个 fd 集合,只返回可读写那部分。

来自 ChatGPT

epoll 是 Linux 内核提供的一种高级 IO 多路复用机制,它可以同时监听多个文件描述符上的事件,并将这些事件通知给应用程序。与传统的 Select 和 Poll 机制相比,epoll 具有更高的性能和更好的可扩展性。

具体来说,epoll 通过以下几个关键部分来实现 IO 多路复用:

  1. epoll_create:创建一个 epoll 对象,并返回其对应的文件描述符。
  2. epoll_ctl:向 epoll 对象中添加或删除指定的文件描述符,以及设置相应的事件类型等参数。
  3. epoll_wait:等待指定文件描述符上的事件发生,并将这些事件收集起来返回给应用程序。

在使用 epoll 进行 IO 多路复用时,通常采取如下的处理流程:

  1. 创建 epoll 对象,并将需要监听的文件描述符添加到 epoll 的事件列表中。
  2. 使用 epoll_wait 等待事件的发生,当某个文件描述符上有事件发生时,就会触发 epoll_wait 返回。
  3. 根据事件类型进行相应的处理。如果是可读事件,则读取数据并进行处理;如果是可写事件,则进行写操作。
  4. 处理完毕后,将文件描述符从 epoll 的事件列表中删除。

需要注意的是,epoll 机制支持三种事件触发模式,包括边缘触发(Edge Triggered)、水平触发(Level Triggered)和一次性触发(One-Shot)。其中,边缘触发模式是最高效的一种模式,它只在文件描述符状态发生变化时才会通知应用程序;而水平触发和一次性触发模式需要更多的系统调用,因此相对较慢。

总的来说,epoll 机制具有高效、可扩展、灵活等优点,在高并发场景下被广泛使用。

epoll 如何通知应用程序

在 epoll 机制中,当 I/O 事件发生时,应用程序通过调用 epoll_wait 函数等待事件的发生。该函数会阻塞当前线程,直到有事件发生或者超时,然后将所有已经就绪的文件描述符加入到一个由 epoll_event 结构体组成的数组中,最后返回数组中的事件数量。

每个 epoll_event 结构体包含了一个就绪的文件描述符和该文件描述符上发生的事件类型。应用程序随后可以遍历这个数组,并根据文件描述符和其对应的事件类型进行相应的处理(如读取数据、写入数据等)。

需要注意的是,在边缘触发模式下,如果有多个事件同时发生,epoll_wait 只会返回一次通知。因此,应用程序在处理完某个文件描述符上的事件后,需要重新调用 epoll_wait 函数获取下一轮事件通知。

总之,epoll 机制的通知方式是通过将就绪的文件描述符和相应的事件类型打包成一个结构体,并返回给应用程序,从而实现了高效、可扩展、高并发的 IO 处理。

select

select 只有一个系统调用,每次要监听都要将其从用户态传到内核,有事件时返回整个集合。

来自 ChatGPT

select 是一种基于 IO 多路复用的系统调用,它可以同时监听多个文件描述符上的事件,并在其中有事件发生时通知应用程序进行处理。select 机制主要由以下三个部分组成:

  1. fd_set 数据结构:用于存储需要监听的文件描述符集合。
  2. select 函数:用于将需要监听的文件描述符集合传递给内核,以及等待事件的发生并返回事件列表。
  3. 相应的文件操作函数:例如 read、write 等,用于对文件描述符执行相应的 IO 操作。

在使用 select 进行 IO 多路复用时,通常采取如下的处理流程:

  1. 创建 fd_set 数据结构,并将需要监听的文件描述符添加到该数据结构中。
  2. 调用 select 函数等待事件的发生。当某个文件描述符上有事件发生时,就会触发 select 返回。
  3. 使用相应的文件操作函数对事件进行处理。如果是可读事件,则读取数据并进行相应的处理;如果是可写事件,则进行写操作。
  4. 循环执行上述过程,直到关闭连接或出现异常。

需要注意的是,在 select 模式下,文件描述符数量受到限制,通常不能超过系统默认值(例如 1024)。此外,在高并发情况下,select 的效率较低,因为每次都需要对所有监控的文件描述符进行扫描,无法有效利用 CPU 和内存等资源。因此,在实现高并发服务器时,通常采用更为高效的机制,如 epoll 和 kqueue 等。