find function symbols from a corrupted stack
公司面试时,被问到gdb里bt显示全是?时怎么办? 这个情况遇到过就是栈写坏了,但我只能说我不知道。进公司后,私下跟面试我的同事请教到了,可以通过 x 命令从rsp寄存器的地址,向上打印,来找到更上层的调用函数,这样能帮助缩小定位范围。
公司面试时,被问到gdb里bt显示全是?时怎么办? 这个情况遇到过就是栈写坏了,但我只能说我不知道。进公司后,私下跟面试我的同事请教到了,可以通过 x 命令从rsp寄存器的地址,向上打印,来找到更上层的调用函数,这样能帮助缩小定位范围。
Recently, I discovered that my Lua script for Wireshark fails on PCAP files captured in unit tests—the RTP dissector stops without any error message. After spending considerable time debugging, I identified the issue: the RTP header version was incorrect.
记录一个epoll使用问题。在一个新项目写了一个协议转换的小功能。把收到的RTP包封装客户的头再转发出去,用的epoll来收RTP。在公司环境测试正常。可是到了客户现场出现奇怪的现象,每次RTP流只能转发前面几秒的流,后面就不转发了。关闭/打开监听,又是只转发前面几秒。
程序运行时编译和执行c代码。公司有个基础库可以修改和打印进程的状态,这个库是基于GNU的bfd的,代码比较复杂。在新项目中,发现dlsym(libdl)可以实现类似功能,并且代码非常简洁。
在HN上看到一个很强大的示例也是用的libdl https://nullprogram.com/blog/2014/12/23/, 在运行时改代码,实际也是修改了状态。
NCM 全称“Network Control Model”,是USB定义的用来交换以太网帧。也就是USB网卡,但是相比Windows的RNDS,速度快多了。实测在300MBps以上。而且听说是不安全导致Linux废弃了RNDS的支持。
在网上买了一个USB供电板,免焊接的。用大模型生成了USB网卡配置脚本
#!/bin/bash
set -ex
CONFIGFS=/sys/kernel/config
GADGET=usb_gadget/g1
GADGET_DIR=$CONFIGFS/$GADGET
UDC=$(ls /sys/class/udc | head -n1)
# 1. Load libcomposite and mount configfs
modprobe libcomposite
mount -t configfs none $CONFIGFS || true
# 2. Create gadget directory
mkdir -p "$GADGET_DIR"
cd "$GADGET_DIR"
# 3. Set vendor/product IDs (example values—you can adjust)
echo 0x1d6b > idVendor # Linux Foundation
echo 0x0104 > idProduct # Multifunction Composite Gadget
# 4. Create English strings
mkdir -p strings/0x409
echo "serial1234" > strings/0x409/serialnumber
echo "MyManuf" > strings/0x409/manufacturer
echo "My NCM Gadget" > strings/0x409/product
# 5. Add NCM function
mkdir -p functions/ncm.usb0
# You may optionally set MACs, interface name, qmult, etc.
# echo "12:34:56:78:9a:bc" > functions/ncm.usb0/host_addr
# echo "12:34:56:78:9a:bd" > functions/ncm.usb0/dev_addr
# echo "usb0" > functions/ncm.usb0/ifname
#!/bin/bash
set -ex
CONFIGFS=/sys/kernel/config
GADGET=usb_gadget/g1
GADGET_DIR=$CONFIGFS/$GADGET
UDC=$(ls /sys/class/udc | head -n1)
# 1. Load libcomposite and mount configfs
modprobe libcomposite
mount -t configfs none $CONFIGFS || true
# 2. Create gadget directory
mkdir -p "$GADGET_DIR"
cd "$GADGET_DIR"
# 3. Set vendor/product IDs (example values—you can adjust)
echo 0x1d6b > idVendor # Linux Foundation
echo 0x0104 > idProduct # Multifunction Composite Gadget
# 4. Create English strings
mkdir -p strings/0x409
echo "serial1234" > strings/0x409/serialnumber
echo "MyManuf" > strings/0x409/manufacturer
echo "My NCM Gadget" > strings/0x409/product
# 5. Add NCM function
mkdir -p functions/ncm.usb0
# You may optionally set MACs, interface name, qmult, etc.
# echo "12:34:56:78:9a:bc" > functions/ncm.usb0/host_addr
# echo "12:34:56:78:9a:bd" > functions/ncm.usb0/dev_addr
# echo "usb0" > functions/ncm.usb0/ifname
# 6. Create config and assign function
mkdir -p configs/c.1
mkdir -p configs/c.1/strings/0x409
echo "Config NCM" > configs/c.1/strings/0x409/configuration
ln -s functions/ncm.usb0 configs/c.1/
# 7. Bind to UDC (activate gadget)
echo "$UDC" > UDC
echo "NCM gadget activated"
# Wait for interface to appear
for i in {1..10}; do
if ip link show usb0 &>/dev/null; then
break
fi
sleep 0.5
done
# move to usb_ncm_up.service
#nmcli connection up usb0-shared || true
nmcli connection add type ethernet ifname usb0 con-name usb0-shared ipv4.method shared
nmcli connection up usb0-shared
设备上的USB网卡配置脚本要在NetworkManager启动前执行,而拉起设备要在启动NetworkManager后执行,所以分拆成两个服务。
usb_ncm_gadget.service
[Unit]
Description=Setup USB NCM Gadget
Before=NetworkManager.service
After=local-fs.target sysfs.mount
[Service]
Type=oneshot
ExecStart=/usr/local/bin/usb_ncm_gadget.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
等NetworkManager启动后,拉起网口
usb_ncm_gadget_up.service
[Unit]
Description=Bring Up NM connection for usb0
After=NetworkManager.service
Requires=NetworkManager.service
Requires=usb_ncm_gadget.service
Wants=sys-subsystem-net-devices-usb0.device
After=sys-subsystem-net-devices-usb0.device
[Service]
Type=oneshot
ExecStart=/usr/bin/nmcli connection up usb0-shared
[Install]
WantedBy=multi-user.target
系统的cuda开发编译环境(/usr/local/cuda)可以不安装,除非需要本地编译。为了避免编译,就下载whl包。 whl包是别人用nvcc编译的二进制包,所以固定了cuda版本。例如torch-2.5.1+cu121表示用了cuda 12.1的编译工具编译的。 而且为了vllm能调用pytorch, vllm就必须和pytorch一样的cu版本,所以我两个都下载了cu121。
npx 相当于npm -g, 来安装一个全局的包+cli。
npx install transient-package
transient-package create --source vllm --source-version 0.7.2 --target vllm-pascal --target-version 0.7.2 --output-directory .
uv pip install vllm-0.7.2-py3-none-any.whl
WARNING 03-14 23:02:38 init.py:26] The vLLM package was not found, so its version could not be inspected. This may cause platform detection to fail.
INFO 03-14 23:02:38 init.py:211] No platform detected, vLLM is running on UnspecifiedPlatform
0.7.2
uv pip install ./*.whl命令不会安装依赖,运行一个一个这样报缺包再安装包很烦,所以直接到vllm的代码里找requrement,一次安装。还有到pytorch官网看对应torch==2.5.1的torch-audio torch-vision包。
"torch_dtype": "bfloat16", 而显卡只能支持float16, 所以修改了模型配置。sudo apt install -y cuda-11-7
新项目应该用 uv add, 在用 uv add 之前,需要先执行 * uv init 初始化项目 * uv venv ./.venv 创造虚拟环境
| NVIDIA-SMI 580.95.05 Driver Version: 580.95.05 CUDA Version: 13.0 |
nvcc --version来确认nvcc 是/usr/local/cuda/bin/下的,也就是cuda toolkits,cuda目录通常是软链接到实际的cuda toolkits
lrwxrwxrwx 1 root root 22 Nov 3 18:23 cuda -> /etc/alternatives/cuda
lrwxrwxrwx 1 root root 25 Nov 3 18:23 cuda-11 -> /etc/alternatives/cuda-11
drwxr-xr-x 15 root root 4096 Nov 3 18:23 cuda-11.7
nvcc --version确认版本
nvcc --version
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2022 NVIDIA Corporation
Built on Wed_Jun__8_16:49:14_PDT_2022
Cuda compilation tools, release 11.7, V11.7.99
Build cuda_11.7.r11.7/compiler.31442593_0
11.7 小于 nvidia-smi打印的 13.0, 所以这个toolkit是支持的。
虽然GPU driver 可以向前兼容,所以 Driver 535可以替代Driver 515。但是硬件架构限定了cuda capabilities,无法向前兼容的。例如Tesla P40的硬件架构是Pascal sm_6.1,而pytorch 的2.7.0 版本不支持sm_6.1了。
因为 cuda capability限制,知道最高版本支持sm 6.1的pytorch版本是2.6.0, 而pytroch有专门对cuda特定版本的二进制包,在官网上能找到 https://pytorch.org/get-started/previous-versions/
下面可见,2.6.0版本有 cuda-11.8 cuda-12.4 cuda-12.6版本的二进制安装包。
# ROCM 6.1 (Linux only)
pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 --index-url https://download.pytorch.org/whl/rocm6.1
# ROCM 6.2.4 (Linux only)
pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 --index-url https://download.pytorch.org/whl/rocm6.2.4
# CUDA 11.8
pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 --index-url https://download.pytorch.org/whl/cu118
# CUDA 12.4
pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 --index-url https://download.pytorch.org/whl/cu124
# CUDA 12.6
pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 --index-url https://download.pytorch.org/whl/cu126
# CPU only
pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 --index-url https://download.pytorch.org/whl/cpu
uv pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 --index-url https://download.pytorch.org/whl/cu118
vllm 0.9 开始不支持pytorch 2.6了,所以只能用vllm 0.8.5。从githup release找到了二进制安装包 vllm-0.8.5.post1+cu118-cp38-abi3-manylinux1_x86_64.whl
下载到本地后运行 uv pip install ~/vllm-0.8.5.post1+cu118-cp38-abi3-manylinux1_x86_64.whl
可惜,vllm 0.8.5 不支持 pytorch-cuda-6.1, 不知道这跟pytroch 支持 sm-6.1有什么区别。好在可以手动编译vllm来支持
git clone https://github.com/vllm-project/vllm.git
cd vllm
git checkout v0.8.5
export TORCH_CUDA_ARCH_LIST="6.1"
pip install -e . --no-build-isolation
dtype 是参数类型,所需的大小分别是 * FP32 4B 单精度 * BP16 2B 双精度 * FP16 2B 单精度 * INT8 1B * INT4 0.5B 量化 vllm 可以运行时指定量化
而大模型的参数常见有32B,7B。7B的大模型,使用FP32时,需要VRAM=47G。 使用FP16时,只需要27G。
注意,模型的config.torch_dtype是可以改的,最大输出token也是可以改的。
https://github.com/vllm-project/vllm/pull/4290
pytroch 2.7 不支持 6.1: https://www.reddit.com/r/LocalLLaMA/comments/1lrerwe/pytorch_27x_no_longer_supports_pascal_architecture/ vllm 0.11.5 才能支持Qwen/Qwen3-VL-32B-Instruct,否则报错 "'Qwen3VLConfig' object has no attribute 'vocab_size'" https://huggingface.co/Qwen/Qwen3-VL-32B-Instruct/discussions/3
aria2c支持很多类型的协议下载,http, bt等,支持任务列表,支持前台运行和后台运行, 还支持并发下载(单连接限速)和断点续传(前提是服务器支持)。因为现代浏览器普遍也支持断点续传和并发下载,所以aria2更适合用来批量下载。
就类似wget fcurl * 单个文件下载 aria2c "下载链接" * 文件列表下载 aria2c -i list.txt
aria2c 本身不支持类似-d/--daemon参数,所以要么在终端执行下面的命令,要么用systed来运行。
* 后台运行 aria2c -c -i list.txt -j4 --load-cookies=rapidshare > /tmp/aria2c.log 2>&1 &
* 查看日志 tail -f /var/log/aria2c.log
创建文件~/.config/systemd/user/aria2cd.service,内容如下。这时只能通过rpc协议和aria2c交互,可以用curl和web来发送rpc。
[Unit]
Description=Aria2 Daemon
[Service]
Type=simple
ExecStart=/usr/bin/aria2c --conf-path=/etc/aria2/aria2.conf
[Install]
WantedBy=default.target
有时aria2c运行在没有网络的服务器上,需要代理运行,如下添加http代理
all-proxy=http://ip:port
http-proxy=http://ip:port
https-proxy=http://ip:port
https://www.linuxquestions.org/questions/linux-software-2/putting-aria2c-in-background-769997/
以前总不明白,创建虚拟机时填写的域名 example.com 的作用,直到自己用pve创建了多个虚拟机,
一开始我直接在系统里用hostnamectl来修改,然后发现执行pct命令时,报错没有虚拟机的配置文件,原来pct用hostname来隔离配置
$ pct set 110 --hostname docker.pve
Configuration file 'nodes/NEW_HOSTNAME/lxc/110.conf' does not exist
这个开始在虚拟机里改,后来知道正确方法是用pct工具
pct set ID --hostname NEW_HOSTNAME
https://virtualizeeverything.com/2021/11/12/how-to-change-the-hostname-of-a-lxc-using-command-line-proxmox-7/
我用闲暇台式机安装了pve, 创了一些虚拟机和lxc容器来跑本地部署的应用,例如代码管理的gitea, web应用,nginx反向代理等。 反向代理容器要访问其他容器,也要从外部访问容器,例如我的笔记本。 正好最近工作遇到些dns的问题,于是想自己搭建一个dns服务器。用来实现从外面,从容器都能通过域名来访问。就如同docker compose 里能通过容器名称来访问其他容器一样。反代nginx可以用frp等技术实现公网访问。
在笔记本上能访问 gitea.pve, 在nginx反代容器里也能访问 gitea.pve,但是前者得到10地址,后者得到192地址。
pve在安装了自动创建了网桥,桥接物理网卡,网络10.8.10.0/24,外面可以直接访问。后我自己创建了虚拟网络 192.168.10.1/24。于是每个容器和虚拟机都有两个地址,我称之外部地址和内部地址。 pve 创建网络位置在 Datacenter-> pve -> system -> network
在AI的帮助下,了解了bind9的view专门用来完成这样的事情。先修改文件 /etc/bind/named.conf.default-zones, 将原来的部分套上 'external view',然后加上自定义根域名'pve'
+view "external" {
// prime the server with knowledge of the root servers
zone "." {
type hint;
file "/usr/share/dns/root.hints";
};
...
+zone "pve" {
+ type master;
+ file "/etc/bind/db.pve.external";
+};
+};
修改文件 /etc/bind/named.conf.local,原本这文件是空的,现在添加 'internal view'
//
// Do any local configuration here
//
// Consider adding the 1918 zones here, if they are not used in your
// organization
//include "/etc/bind/zones.rfc1918";
+acl "internal-nets" {
+ 192.168.10.0/24; // VM subnet
+ 127.0.0.1; // localhost
+};
+
+view "internal" {
+ match-clients { internal-nets; };
+
+ zone "pve" {
+ type master;
+ file "/etc/bind/db.pve.internal";
+ };
};
然后在/etc/bind/db.pve.external 中添加域名记录,ip是外部地址, 在/etc/bind/db.pve.internal添加域名记录,ip是内部地址, 内容如下
$TTL 86400
@ IN SOA ns1.pve. admin.pve. (
2025082701 3600 1800 1209600 86400 )
IN NS ns1.pve.
ns1 IN A 192.168.10.1
pve IN A 192.168.10.1
docker IN A 192.168.10.101
frp IN A 192.168.10.102
gitea IN A 192.168.10.103
然后检查配置和更新配置
rndc reconfig pve
rndc reload
注意: AI说修改了域名记录,要增加SOA号,例如 2025082701 -> 2025082702
上面dig命令指定了dns服务器的ip, 为了让系统自动选择pve上的dns服务器来解析 *.pve 域名,要修改笔记本配置,使用的NetworkManager
nmcli connection modify WIFI_CONNECTION ipv4.dns-search "~pve"
nmcli connection modify MI-MINI_5G_D300 ipv4.dns 10.8.10.77
nmcli connection modify MI-MINI_5G_D300 ipv4.dns-priority -1
而我用了有以太网和wifi两个网络,我只修改了wlp5s0。通过前后 resolvectl status 对比,区别如下
Link 3 (wlp5s0)
- Current Scopes: LLMNR/IPv4 LLMNR/IPv6 mDNS/IPv4 mDNS/IPv6
+ Current Scopes: DNS LLMNR/IPv4 LLMNR/IPv6 mDNS/IPv4 mDNS/IPv6
Protocols: -DefaultRoute +LLMNR +mDNS -DNSOverTLS DNSSEC=no/unsupported
+ DNS Servers: 10.8.10.77
+ DNS Domain: ~pve
Default Route: no
这样是做了dns转发,把*.pve的域名转发给10.8.10.77来处理。 效果如下
# ping gitea.pve
PING gitea.pve (10.8.10.130) 56(84) bytes of data.
64 bytes from gitea.local (10.8.10.130): icmp_seq=1 ttl=64 time=0.588 ms
64 bytes from gitea.local (10.8.10.130): icmp_seq=2 ttl=64 time=0.767 ms
64 bytes from gitea.local (10.8.10.130): icmp_seq=3 ttl=64 time=2.95 ms
但是我配置 systemd-resolved 的 search 功能不生效,只能全写域名, 不能像在pve容器里那样省略'.pve'。
pve创建容器时,默认拷贝宿主机的dns配置即/etc/resolve.conf,先将其修改成如下,search 的作用是简化域名 gitea.pve 成 gitea
nameserver 192.168.10.1
nameserver 1.0.0.1
search pve local
这样新建的容器的dns自动用搭建好的。 在已创建的容器里找到dns选项,手动修改。效果
# ping gitea
PING gitea.pve (192.168.10.103) 56(84) bytes of data.
64 bytes from 192.168.10.103 (192.168.10.103): icmp_seq=1 ttl=64 time=0.022 ms
64 bytes from 192.168.10.103 (192.168.10.103): icmp_seq=2 ttl=64 time=0.046 ms