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_COMPRESS和CONFIG_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




