You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Linux 2.6内核模块开发:内存实时压缩解压内核API咨询

好问题!在Linux 2.6内核模块里做实时压缩解压,完全可以用内核内置的API搞定,根本不用引入和编译外部库。我给你梳理下最靠谱的几种方案,都是经过大量实践验证的:

内核空间可用的压缩/解压API(Linux 2.6)

1. Zlib:兼容性最好的选择

Linux 2.6内核已经内置了zlib的内核实现,这是最稳妥的选项——几乎所有2.6分支都支持,不用额外配置内核。

压缩操作

你需要包含头文件 <linux/zlib.h>,核心函数是 compress2

int compress2(unsigned char *dest, unsigned long *destLen, const unsigned char *source, unsigned long sourceLen, int level);
  • 参数解释:
    • dest:目标缓冲区,可以是内核内存,也可以是用户空间(但用户空间的话得用copy_to_user写数据)
    • destLen:输入时是目标缓冲区的总大小,输出时会被设置为实际压缩后的字节数
    • source:源缓冲区,同样支持内核/用户空间(用户空间要先用copy_from_user读进来)
    • level:压缩级别,1-9可选——1最快但压缩比最低,9最慢但压缩比最高,填0就是不压缩
  • 返回值:0表示成功,非0是错误码(比如Z_MEM_ERROR就是内存不够)

解压操作

对应的解压函数是 uncompress

int uncompress(unsigned char *dest, unsigned long *destLen, const unsigned char *source, unsigned long sourceLen);
  • 逻辑和压缩类似,destLen输入是目标缓冲区的最大容量,输出是实际解压后的长度
  • 返回值0成功,非0则是数据损坏(Z_DATA_ERROR)之类的错误

实用小技巧

  • 压缩前可以用 compressBound(sourceLen) 计算所需的最大目标缓冲区大小,避免缓冲区溢出
  • 内核里分配内存记得用 kmalloc/kzalloc,一定要检查分配是否成功,不然容易Oops

给你个简单的内核内存压缩示例:

#include <linux/zlib.h>
#include <linux/slab.h>

int kernel_compress(const char *src_data, size_t src_len) {
    unsigned long dest_size = compressBound(src_len);
    char *dest_buf = kmalloc(dest_size, GFP_KERNEL);
    if (!dest_buf) {
        return -ENOMEM;
    }

    int ret = compress2((unsigned char *)dest_buf, &dest_size,
                       (const unsigned char *)src_data, src_len, Z_BEST_SPEED);
    if (ret != Z_OK) {
        kfree(dest_buf);
        return -EINVAL;
    }

    // 这里处理压缩后的数据...
    kfree(dest_buf);
    return 0;
}

2. LZO:更快的轻量选择

如果你的2.6内核开启了CONFIG_LZO_COMPRESSCONFIG_LZO_DECOMPRESS选项,还可以用LZO算法——它比zlib快不少,压缩比略低,适合对速度要求高的场景。

需要包含头文件 <linux/lzo.h>,核心函数:

  • 压缩:int lzo1x_1_compress(const unsigned char *src, size_t src_len, unsigned char *dst, size_t *dst_len, void *wrkmem);
    • 注意要提前分配一块工作内存wrkmem,大小是LZO1X_1_MEM_COMPRESS,用kmalloc就行
  • 解压:int lzo1x_decompress_safe(const unsigned char *src, size_t src_len, unsigned char *dst, size_t *dst_len);

3. 用户空间缓冲区的正确处理方式

如果源或目标缓冲区在用户空间,一定要严格遵守内核的内存访问规则:

  • 读用户空间数据:先用copy_from_user把数据拷贝到内核缓冲区,再做压缩/解压
  • 写用户空间数据:先在内核里完成压缩/解压,再用copy_to_user把数据写回去
  • 操作前可以用access_ok检查用户指针的有效性,避免非法访问导致内核崩溃

比如从用户空间读数据压缩后写回去的示例:

int user_compress(char __user *user_src, size_t src_len, char __user *user_dest, size_t __user *user_dest_len) {
    unsigned long dest_size = compressBound(src_len);
    char *src_buf = kmalloc(src_len, GFP_KERNEL);
    char *dest_buf = kmalloc(dest_size, GFP_KERNEL);
    unsigned long actual_dest_len = dest_size;
    int ret = -ENOMEM;

    if (!src_buf || !dest_buf) {
        goto cleanup;
    }

    // 从用户空间读数据
    if (copy_from_user(src_buf, user_src, src_len)) {
        ret = -EFAULT;
        goto cleanup;
    }

    // 压缩
    ret = compress2((unsigned char *)dest_buf, &actual_dest_len,
                   (const unsigned char *)src_buf, src_len, Z_BEST_COMPRESSION);
    if (ret != Z_OK) {
        ret = -EINVAL;
        goto cleanup;
    }

    // 写回用户空间
    if (copy_to_user(user_dest, dest_buf, actual_dest_len) ||
        copy_to_user(user_dest_len, &actual_dest_len, sizeof(size_t))) {
        ret = -EFAULT;
        goto cleanup;
    }

    ret = 0;
cleanup:
    kfree(src_buf);
    kfree(dest_buf);
    return ret;
}

最后提一下版本兼容性

  • zlib的内核实现几乎覆盖所有2.6.x版本,不用操心配置问题
  • LZO需要内核开启对应的配置选项,早期2.6版本可能没有
  • 别用那些未公开的内核内部函数,就用上面这些标准接口,模块的可移植性才好

内容的提问来源于stack exchange,提问作者Massimo

火山引擎 最新活动