Linux 性能实战 | 第 17 篇:strace 系统调用分析与性能调优 [特殊字符]

📋 本章摘要

在第十六章中,我们深入探讨了文件系统层面的性能优化,发现文件系统会引入 12% 的性能开销,元数据操作比数据写入慢 500-1000 倍,fsync 每次耗时约 5ms。通过优化自动驾驶日志系统(目录分片、批量 fsync、切换到 XFS),我们将写入次数降低 99%,延迟降低 95%。

但在应用程序和文件系统之间,还有一个关键的性能边界------系统调用。每次程序需要访问硬件资源(文件、网络、内存)时,都必须通过系统调用从用户态切换到内核态,这个过程本身就有 100-300 纳秒的开销。更糟糕的是,如果程序频繁进行系统调用(如每次读取 1 字节、每次获取时间戳),开销会累积到整体性能的 50% 以上。本章将揭示:系统调用的真实成本、如何使用 strace 诊断性能问题、高频系统调用的优化技巧、vDSO 如何将特定系统调用优化到 < 50ns,以及 strace 在生产环境的使用陷阱。


🔗 从文件系统到系统调用:用户态与内核态的边界

在上一章的日志系统优化中,我们发现频繁的 open()write()fsync()close() 调用导致了性能瓶颈。这些都是系统调用------应用程序请求内核服务的唯一途径。

让我们通过一个简单的例子来理解系统调用的开销:

c 复制代码
// 测试 1:频繁系统调用(低效)
for (int i = 0; i < 1000000; i++) {
    write(fd, "x", 1);  // 每次写入 1 字节
}
// 结果:2.5 秒(100 万次系统调用)

// 测试 2:批量操作(高效)
char buf[1000000];
memset(buf, 'x', sizeof(buf));
write(fd, buf, sizeof(buf));  // 一次写入 100 万字节
// 结果:0.003 秒(1 次系统调用,快 833 倍!)

为什么差异这么大?
系统调用(有开销)
切换
切换
用户态

应用程序
内核态

系统调用处理
用户态

返回结果
开销:~100-300ns

(上下文切换、

参数校验、

权限检查)
用户态调用(无开销)
函数 A
函数 B
函数 C
开销:~5ns

(直接跳转)

系统调用的开销来源

  1. 上下文切换:保存用户态寄存器,切换到内核栈(~50ns)
  2. 参数校验:验证用户传入的指针、文件描述符等(~20ns)
  3. 权限检查:验证进程是否有权限执行操作(~30ns)
  4. 实际工作:执行请求的操作(变化很大)
  5. 返回切换:恢复用户态寄存器,返回结果(~50ns)

在自动驾驶系统中,系统调用开销的影响尤为显著:

  • 传感器数据读取 :每帧需要数百次 read() 调用
  • 日志记录 :每条日志触发 open() + write() + close()
  • 时间戳获取 :感知算法频繁调用 gettimeofday()
  • 文件状态查询 :数据回放系统反复 stat() 检查文件

🔬 系统调用的成本:用户态 ↔ 内核态切换

1. 测量单次系统调用开销

基准测试

c 复制代码
#include <stdio.h>
#include <unistd.h>
#include <time.h>

int main() {
    struct timespec start, end;
    long iterations = 10000000;
    
    // 测试 getpid()(最简单的系统调用)
    clock_gettime(CLOCK_MONOTONIC, &start);
    for (long i = 0; i < iterations; i++) {
        getpid();
    }
    clock_gettime(CLOCK_MONOTONIC, &end);
    
    long ns = (end.tv_sec - start.tv_sec) * 1000000000L + 
              (end.tv_nsec - start.tv_nsec);
    printf("Average syscall cost: %ld ns\n", ns / iterations);
    
    return 0;
}

结果(不同硬件):

CPU 单次系统调用耗时
Intel i9-12900K (2022) ~85 ns
AMD Ryzen 9 5950X ~95 ns
ARM Cortex-A78 (嵌入式) ~180 ns

2. 系统调用类型的性能差异

并非所有系统调用都一样慢:

bash 复制代码
# 使用 strace 测量不同系统调用的耗时
strace -c ./test_program

常见系统调用耗时

系统调用 平均耗时 主要开销
getpid() ~85 ns 上下文切换
gettimeofday() ~100 ns 上下文切换 + 读时钟
read(1 byte) ~500 ns 上下文切换 + 缓冲区操作
write(1 byte) ~800 ns 上下文切换 + 缓冲区 + 可能的磁盘 I/O
open() ~5,000 ns 路径解析 + inode 查找 + 权限检查
stat() ~3,000 ns 路径解析 + inode 读取
mmap() ~8,000 ns 页表操作 + VMA 分配
fork() ~200,000 ns 进程复制(COW)

3. 自动驾驶场景中的系统调用开销

案例:感知算法的时间戳获取

cpp 复制代码
// ❌ 低效:每个点云点都获取时间戳
for (const auto& point : point_cloud) {
    struct timeval tv;
    gettimeofday(&tv, nullptr);  // 每次 ~100ns
    
    ProcessPoint(point, tv);
}
// 300,000 点 × 100ns = 30ms(纯系统调用开销)
cpp 复制代码
// ✅ 高效:批量处理前获取一次时间戳
struct timeval tv;
gettimeofday(&tv, nullptr);  // 只调用一次

for (const auto& point : point_cloud) {
    ProcessPoint(point, tv);
}
// 开销:100ns(忽略不计)

🛠️ strace:系统调用的"X光机"

1. strace 基础用法

启动新进程并追踪

bash 复制代码
strace ./my_program

追踪运行中的进程

bash 复制代码
strace -p <PID>

关键参数

参数 作用 示例
-c 统计模式(汇总) strace -c ./program
-T 显示每次调用耗时 strace -T ./program
-tt 显示微秒级时间戳 strace -tt ./program
-e 过滤特定系统调用 strace -e open,read ./program
-p 追踪运行中进程 strace -p 12345
-f 追踪子进程 strace -f ./program
-o 输出到文件 strace -o trace.log ./program

2. 统计模式(-c):快速定位热点

bash 复制代码
strace -c ./perception_node

输出示例

复制代码
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 45.23    2.345678          23    100000           read
 32.14    1.667890          16    100000           write
 12.45    0.645678        6456       100           open
  5.67    0.294567          29     10000           gettimeofday
  2.34    0.121456        1214       100           stat
  1.23    0.063890          63      1000           mmap
  0.94    0.048765          48      1000           close
------ ----------- ----------- --------- --------- ----------------
100.00    5.187924                311200           total

分析

  • read 占用 45.23% 时间:100,000 次调用,平均 23μs
  • write 占用 32.14%:100,000 次调用,平均 16μs
  • 问题定位:read/write 调用次数过多,应该批量处理

3. 详细模式(-T -tt):分析单次调用

bash 复制代码
strace -T -tt -e open,read,close ./data_loader

输出示例

复制代码
14:23:45.123456 open("/mnt/data/sensor.bag", O_RDONLY) = 3 <0.000089>
14:23:45.123567 read(3, "\x00\x01\x02...", 4096) = 4096 <0.000012>
14:23:45.123589 read(3, "\x03\x04\x05...", 4096) = 4096 <0.000011>
...
14:23:45.125678 read(3, "\xfe\xff\x00...", 4096) = 2048 <0.000010>
14:23:45.125698 close(3) = 0 <0.000008>

分析

  • <0.000089> 表示 open 耗时 89μs
  • <0.000012> 表示 read 耗时 12μs
  • 时间戳精确到微秒,可分析调用间隔

4. 过滤特定系统调用(-e)

bash 复制代码
# 只追踪文件操作
strace -e trace=file ./program

# 只追踪网络操作
strace -e trace=network ./program

# 只追踪内存操作
strace -e trace=memory ./program

# 自定义过滤
strace -e open,read,write,close ./program

🔍 实战案例:用户态程序 load 高但 CPU 空闲

1. 问题现象

一个自动驾驶数据处理程序,top 显示 CPU 使用率只有 15%,但 load average 却高达 8.5,程序运行极慢。

top 输出

复制代码
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
12345 user      20   0  2.5g    1.2g   800m R  15.3  3.8   5:23.45 data_processor

vmstat 1

复制代码
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 8  0      0 5234M  234M  12.3G    0    0   456   123 12k 45k  5 10 80  5  0

观察

  • r = 8:8 个进程处于 runnable 状态
  • us = 5%, sy = 10%:CPU 大部分时间空闲
  • cs = 45k:每秒 45,000 次上下文切换(异常高!)

2. 使用 strace 诊断

步骤 1:统计系统调用

bash 复制代码
strace -c -p 12345
^C  # 运行 10 秒后停止

输出

复制代码
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 78.45    7.845678           1   8456789           gettimeofday
 12.34    1.234567          12    100000           read
  5.67    0.567890          56     10000           stat
  2.34    0.234567         234      1000           open
  1.20    0.120456         120      1000           close
------ ----------- ----------- --------- --------- ----------------
100.00   10.002158               8567789           total

惊人发现

  • gettimeofday 调用了 845 万次(10 秒内)
  • 平均每秒 845,000 次!
  • 占用总时间的 78.45%

步骤 2:查看调用位置

bash 复制代码
strace -T -tt -e gettimeofday -p 12345 | head -50

输出

复制代码
14:23:45.123456 gettimeofday({tv_sec=1704441825, tv_usec=123456}, NULL) = 0 <0.000001>
14:23:45.123468 gettimeofday({tv_sec=1704441825, tv_usec=123468}, NULL) = 0 <0.000001>
14:23:45.123479 gettimeofday({tv_sec=1704441825, tv_usec=123479}, NULL) = 0 <0.000001>
...(连续调用)

时间间隔分析

复制代码
第 1 次调用:14:23:45.123456
第 2 次调用:14:23:45.123468(间隔 12μs)
第 3 次调用:14:23:45.123479(间隔 11μs)

每隔 10-20μs 就调用一次 gettimeofday(),几乎没有做任何实际工作!

3. 根因定位:代码分析

查看源码(使用 gdb + backtrace):

bash 复制代码
gdb -p 12345
(gdb) break gettimeofday
(gdb) continue
(gdb) backtrace

调用栈

cpp 复制代码
#0  gettimeofday() at /lib/libc.so.6
#1  GetCurrentTimestamp() at utils.cpp:45
#2  LogPerformance() at logger.cpp:123
#3  ProcessLidarPoint() at perception.cpp:567  // ⬅️ 问题代码
#4  ProcessPointCloud() at perception.cpp:234

问题代码

cpp 复制代码
// perception.cpp:567
void ProcessLidarPoint(const Point& p) {
    auto start = GetCurrentTimestamp();  // ⬅️ 每个点都调用
    
    // 实际处理(非常简单)
    double distance = sqrt(p.x * p.x + p.y * p.y + p.z * p.z);
    
    auto end = GetCurrentTimestamp();    // ⬅️ 每个点都调用
    LogPerformance("ProcessPoint", end - start);
}

// 300,000 点云点 × 2 次调用 = 600,000 次 gettimeofday
// 每次 100ns × 600,000 = 60ms(纯系统调用开销)

4. 优化方案

方案 1:减少时间戳获取频率

cpp 复制代码
// ✅ 优化:每 1000 个点记录一次性能
void ProcessPointCloud(const std::vector<Point>& cloud) {
    auto batch_start = GetCurrentTimestamp();
    
    for (size_t i = 0; i < cloud.size(); i++) {
        ProcessLidarPoint(cloud[i]);
        
        // 每 1000 个点记录一次
        if ((i + 1) % 1000 == 0) {
            auto batch_end = GetCurrentTimestamp();
            LogPerformance("Process1000Points", batch_end - batch_start);
            batch_start = batch_end;
        }
    }
}
// 系统调用次数:600,000 → 600(减少 1000 倍)

方案 2:使用 CLOCK_MONOTONIC_COARSE(低精度但快速)

cpp 复制代码
// clock_gettime(CLOCK_MONOTONIC_COARSE) 不是系统调用(vDSO)
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
// 精度降低(~1ms),但开销从 100ns 降至 ~20ns

方案 3:使用 RDTSC(CPU 周期计数器)

cpp 复制代码
static inline uint64_t rdtsc() {
    uint32_t lo, hi;
    __asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi));
    return ((uint64_t)hi << 32) | lo;
}

// 完全无系统调用,只有 ~10ns 开销
uint64_t start = rdtsc();
ProcessLidarPoint(p);
uint64_t end = rdtsc();
uint64_t cycles = end - start;

5. 优化效果

再次运行 strace -c

优化后

复制代码
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 52.34    0.523456          52     10000           read
 28.45    0.284567          28     10000           write
 10.23    0.102345         102      1000           stat
  5.67    0.056789          56      1000           open
  3.31    0.033123          33      1000           close
------ ----------- ----------- --------- --------- ----------------
100.00    1.000280                 23000           total

改善对比

指标 优化前 优化后 改善
gettimeofday 调用次数 845万/10秒 0 ↓ 100%
总系统调用次数 857万 2.3万 ↓ 99.7%
CPU 使用率 (us) 5% 65% ↑ 13倍(实际工作)
Load Average 8.5 1.2 ↓ 85%
处理延迟 450ms 35ms ↓ 92%

⚡ 高频系统调用的性能陷阱

1. stat() - 文件状态查询

低效代码

cpp 复制代码
// ❌ 数据回放系统:每帧都检查文件是否存在
for (int frame = 0; frame < 10000; frame++) {
    std::string filename = "/mnt/data/frame_" + std::to_string(frame) + ".bin";
    
    struct stat st;
    if (stat(filename.c_str(), &st) == 0) {  // 每次 ~3μs
        LoadFrame(filename);
    }
}
// 10,000 次 × 3μs = 30ms(纯系统调用开销)

优化方案

cpp 复制代码
// ✅ 一次性列出所有文件,缓存结果
std::set<int> available_frames;
DIR* dir = opendir("/mnt/data");
struct dirent* entry;
while ((entry = readdir(dir)) != nullptr) {
    int frame_num;
    if (sscanf(entry->d_name, "frame_%d.bin", &frame_num) == 1) {
        available_frames.insert(frame_num);
    }
}
closedir(dir);

// 后续查询无系统调用
for (int frame = 0; frame < 10000; frame++) {
    if (available_frames.count(frame)) {
        LoadFrame("/mnt/data/frame_" + std::to_string(frame) + ".bin");
    }
}
// 系统调用次数:10,000 → 1(减少 10000 倍)

2. open() + close() - 频繁打开/关闭文件

低效代码

cpp 复制代码
// ❌ 日志系统:每条日志都打开/关闭文件
void WriteLog(const std::string& message) {
    int fd = open("/var/log/app.log", O_WRONLY | O_APPEND);  // ~5μs
    write(fd, message.c_str(), message.size());
    close(fd);  // ~1μs
}
// 每条日志 ~6μs 系统调用开销
// 1000 条/秒 × 6μs = 6ms

优化方案

cpp 复制代码
// ✅ 保持文件描述符打开
class Logger {
private:
    int fd;
    
public:
    Logger() {
        fd = open("/var/log/app.log", O_WRONLY | O_APPEND);
    }
    
    ~Logger() {
        if (fd >= 0) close(fd);
    }
    
    void WriteLog(const std::string& message) {
        write(fd, message.c_str(), message.size());  // 只需 1 次系统调用
    }
};
// 系统调用次数:3000/秒 → 1000/秒(减少 67%)

3. read(1 byte) - 小块读取

低效代码

cpp 复制代码
// ❌ 逐字节读取文件
char c;
while (read(fd, &c, 1) == 1) {  // 每次 ~500ns
    process(c);
}
// 1MB 文件 = 1,048,576 次系统调用 × 500ns = 524ms

优化方案

cpp 复制代码
// ✅ 批量读取到缓冲区
char buffer[4096];
ssize_t n;
while ((n = read(fd, buffer, sizeof(buffer))) > 0) {
    for (ssize_t i = 0; i < n; i++) {
        process(buffer[i]);
    }
}
// 1MB 文件 = 256 次系统调用 × 500ns = 0.128ms(快 4000 倍)

🚀 vDSO:将系统调用优化到 < 50ns

1. vDSO 的工作原理

传统系统调用

复制代码
用户态 → 陷入内核(syscall 指令)→ 内核态 → 返回用户态
开销:~100-300ns

vDSO (Virtual Dynamic Shared Object)

复制代码
用户态 → 直接调用内核映射的共享内存 → 返回用户态
开销:~10-50ns(无上下文切换)

vDSO 优化(快)
直接读取
用户态

gettimeofday()
共享内存

时钟数据

(内核定期更新)
用户态

结果
耗时:~20ns
传统系统调用(慢)
syscall 指令

陷入内核
sysret 指令

返回用户态
用户态

gettimeofday()
内核态

读取时钟
用户态

结果
耗时:~100ns

vDSO 支持的系统调用(Linux x86_64):

  • gettimeofday()
  • clock_gettime()
  • time()
  • getcpu()

2. 验证 vDSO 是否生效

检查进程内存映射

bash 复制代码
cat /proc/self/maps | grep vdso

输出

复制代码
7ffff7ffa000-7ffff7ffc000 r-xp 00000000 00:00 0  [vdso]

性能对比测试

c 复制代码
#include <time.h>
#include <stdio.h>

int main() {
    struct timespec start, end, ts;
    long iterations = 10000000;
    
    // 测试 clock_gettime(vDSO 优化)
    clock_gettime(CLOCK_MONOTONIC, &start);
    for (long i = 0; i < iterations; i++) {
        clock_gettime(CLOCK_MONOTONIC, &ts);
    }
    clock_gettime(CLOCK_MONOTONIC, &end);
    
    long ns = (end.tv_sec - start.tv_sec) * 1000000000L + 
              (end.tv_nsec - start.tv_nsec);
    printf("clock_gettime (vDSO): %ld ns/call\n", ns / iterations);
    
    return 0;
}

结果

复制代码
clock_gettime (vDSO): 23 ns/call  ⬅️ 比传统系统调用快 4 倍

3. 自动驾驶场景优化

使用 vDSO 优化时间戳获取

cpp 复制代码
// ✅ 使用 clock_gettime(vDSO 优化)代替 gettimeofday
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
uint64_t timestamp_ns = ts.tv_sec * 1000000000ULL + ts.tv_nsec;

// 对于低精度场景,使用 CLOCK_MONOTONIC_COARSE
clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
// 精度 ~1ms,但开销更低(~15ns)

⚠️ strace 的性能开销与生产环境注意事项

1. strace 的性能影响

基准测试

bash 复制代码
# 无 strace
time ./test_program
# 结果:0.123 秒

# 使用 strace
time strace -o /dev/null ./test_program
# 结果:12.345 秒(慢 100 倍!)

原因

  • strace 使用 ptrace() 机制
  • 每次系统调用都会停止进程两次(进入内核前、返回用户态后)
  • 导致 50-100 倍的性能下降

2. 生产环境安全使用 strace

✅ 推荐做法

1. 使用统计模式(-c)减少开销

bash 复制代码
# 只统计,不记录详细调用
strace -c -p <PID>
# 开销降至 2-5 倍

2. 短时间采样

bash 复制代码
# 只追踪 5 秒
timeout 5 strace -c -p <PID>

3. 过滤特定系统调用

bash 复制代码
# 只追踪文件操作,减少拦截次数
strace -e trace=file -p <PID>

4. 使用 eBPF 替代(无性能影响)

bash 复制代码
# bpftrace 追踪系统调用(开销 < 1%)
bpftrace -e 'tracepoint:syscalls:sys_enter_* { @calls[probe] = count(); }'

❌ 避免的做法

  • 在高负载生产环境直接 strace -p <PID>
  • 追踪关键路径进程(如数据库、实时控制)
  • 长时间运行 strace(> 1 分钟)

3. strace 替代工具

工具 优势 开销 适用场景
strace 功能完整、详细 50-100x 开发调试
ltrace 追踪库函数调用 20-50x 库函数分析
perf trace 内核集成、低开销 2-5x 生产环境
bpftrace 动态追踪、几乎无开销 <1% 生产环境首选
systemtap 功能强大、可编程 1-5% 复杂分析

推荐用法

bash 复制代码
# 开发/测试环境:strace
strace -c ./my_program

# 生产环境:perf trace
perf trace -p <PID> -s

# 生产环境高级分析:bpftrace
bpftrace -e 'tracepoint:syscalls:sys_enter_read /pid == 12345/ { 
    @bytes = hist(args->count); 
}'

📝 总结与最佳实践

核心要点

  • 系统调用开销:单次 ~100-300ns,频繁调用会严重拖累性能
  • 上下文切换成本:用户态 ↔ 内核态切换是主要开销来源
  • 批量操作原则:将多次小系统调用合并为一次大调用(性能提升可达 1000 倍)
  • vDSO 优化:特定系统调用(时间、CPU 信息)可优化到 < 50ns
  • strace 开销巨大:生产环境慎用,优先使用 perf trace 或 bpftrace

系统调用优化清单

✅ 识别高频系统调用

bash 复制代码
# 统计系统调用
strace -c ./program

# 查看调用位置
strace -T -tt -e <syscall> -p <PID>

✅ 减少系统调用次数

cpp 复制代码
// 批量操作
- read(1 byte) × 1000  → read(1000 bytes) × 1
- open() + close() × 1000 → open() once, reuse fd

// 使用缓存
- stat() × 1000 → readdir() once, cache results

// 使用 vDSO
- gettimeofday() → clock_gettime(CLOCK_MONOTONIC)

✅ 生产环境诊断

bash 复制代码
# 低开销追踪
perf trace -s -p <PID>

# eBPF 动态追踪
bpftrace -e 'tracepoint:syscalls:sys_enter_* { @[comm] = count(); }'

自动驾驶系统调用优化建议

场景 常见问题 优化方案
时间戳获取 频繁 gettimeofday 使用 clock_gettime + 批量采样
传感器数据读取 小块 read() 增大缓冲区(4KB → 1MB)
日志记录 频繁 open/close 保持 fd 打开 + 批量 fsync
文件状态查询 重复 stat() 缓存 readdir() 结果
配置文件读取 频繁 open/read mmap + 共享内存

关键代码模式

cpp 复制代码
// ✅ 系统调用优化模式
1. 批量操作(减少调用次数)
2. 保持资源打开(避免重复 open/close)
3. 使用缓存(避免重复查询)
4. 使用 vDSO(时间戳获取)
5. 使用 mmap(大文件读取)

// ❌ 性能反模式
1. 循环内系统调用
2. 逐字节 read/write
3. 频繁 open/close
4. 重复 stat/access 检查
5. 每次操作都 fsync

🎯 下一章预告

在本章中,我们深入探讨了系统调用的真实成本------单次 ~100-300ns,但频繁调用会累积到整体性能的 50% 以上。我们学会了使用 strace 诊断性能问题(统计模式、详细模式、过滤模式),发现了一个感知算法每秒调用 845,000 次 gettimeofday 导致的性能崩溃,并通过批量优化将系统调用次数降低 99.7%,处理延迟从 450ms 降至 35ms。我们还探讨了 vDSO 如何将特定系统调用优化到 < 50ns,以及 strace 在生产环境的使用陷阱(50-100 倍开销)。

在下一章《ltrace 与库函数性能分析》中,我们将深入用户态库函数的性能分析:

  • 动态链接的性能开销:PLT/GOT 机制与延迟绑定
  • ltrace 的使用技巧:追踪 C 库、OpenCV、TensorFlow 等库函数
  • 慢函数定位:正则表达式、加密函数、JSON 解析的性能陷阱
  • LD_PRELOAD 技巧:运行时替换库函数进行性能监控
  • 静态链接 vs 动态链接:性能与灵活性的权衡

通过真实的自动驾驶目标检测算法案例,我们将揭示库函数调用对系统性能的影响,以及如何优化第三方库的使用。敬请期待!🚀


📖 系列封面

Dive Deep into System Optimization - 从基础观测到高级优化,从 CPU 到存储,从内核到用户态,深入 Linux 性能分析的每一个角落。

相关推荐
hweiyu001 小时前
Linux 命令:setfacl
linux·运维·服务器
wdfk_prog1 小时前
[Linux]学习笔记系列 -- [drivers]char
linux·笔记·学习
bst@微胖子1 小时前
PyTorch深度学习框架项目合集一
人工智能·pytorch·python
社会零时工2 小时前
Ubuntu安装的OpenCV如何更换版本
linux·opencv·ubuntu
Boxsc_midnight2 小时前
【vLLM服务器并发能力测试程序】写一个python小程序来进行并发测试
服务器·python·vllm
深蓝电商API2 小时前
爬虫日志分析:快速定位被封原因
爬虫·python
m0_528749002 小时前
C语言错误处理宏两个比较重要的
java·linux·算法
工业HMI实战笔记2 小时前
包装机械HMI:快速换型与配方管理的界面解决方案
ui·性能优化·自动化·汽车·交互