redis源码解析- sds实现

redis在底层使用sds来实现字符数组,我们看下其实怎么实现的。
在redis中,使用sds结构体来表示代替字符数组:

/* Note: sdshdr5 is never used, we just access the flags byte directly.
 * However is here to document the layout of type 5 SDS strings. */
struct __attribute__ ((__packed__)) sdshdr5 {
    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; /* used */
    uint8_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
    uint16_t len; /* used */
    uint16_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
    uint32_t len; /* used */
    uint32_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
    uint64_t len; /* used */
    uint64_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};

这其中,目前sdshdr5已经不在使用,而是用sdshdr8代替sdshdr5 struct __attribute__ 主要是告诉编译器不用内存对齐,而是按照实际占用内存字节数,从这里也看出,redis在底层很多地方进行了资源的节约。

sds sdsnew(const char *init) {
    size_t initlen = (init == NULL) ? 0 : strlen(init);
    return sdsnewlen(init, initlen);
}

通过sdsnew方法来申请一个sds,而实际上sds的定义是:

typedef char *sds;

最终是通过sds _sdsnewlen(const void *init, size_t initlen, int trymalloc) {进行实际的分配。
redis在底层对分配做了额外处理,分配通过je_malloc分配一段内存,然后通过结构体的偏移,来表示结构体中不同字段。比如对于sdshdr8,前24位,分别为

 uint8_t len; /* used */
    uint8_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /*
    ```
    然后sds是直接指向了结构体中的` char buf[];`数组。sds内存地址往前一位,就是`flags`字段,这个字段记录了当前sds是哪一种sds,是`sdshdr8`还是`sdshdr16 `。。。
    另外在分配的时候,redis封装了一层,每次分配会多用几个字节来记录这次分配了多少内存,在这里用一个全局字段记录了已经分配的内存大小,当释放内存的时候会对该字段进行运算,
   
   redis在对sds进行内存分配的时候,并不是按照请求多少分配多少,而是按照不同的内存规格来申请:
   32、256、65536 、4294967296
   结构体中的`len`来记录当前已经分配内存已经使用的长度,`alloc`记录分配的整个长度(这里不包含结构体开始的是哪个字段和\0)

   `其实,redis中单就sds来说,就是一个char指针,但是redis额外使用了sdshdr这个结构体用来额外进行一些描述,每个sds都是指向了一个sdshdrj结构体中的char buf[] 数组`
比如我们获取当前sds已经使用了多少空间:
```c
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
static inline size_t sdslen(const sds s) {
    unsigned char flags = s[-1];
    switch(flags&SDS_TYPE_MASK) {
        case SDS_TYPE_5:
            return SDS_TYPE_5_LEN(flags);
        case SDS_TYPE_8:
            return SDS_HDR(8,s)->len;
        case SDS_TYPE_16:
            return SDS_HDR(16,s)->len;
        case SDS_TYPE_32:
            return SDS_HDR(32,s)->len;
        case SDS_TYPE_64:
            return SDS_HDR(64,s)->len;
    }
    return 0;
}

可以看到,这里就是通过当前sds往前漂移lenallocflags三个字段长度,从而将指针指向了结构体首地址的。

我们对sds进行清空的时候,并没有立马释放,而是重置了len和结束字符位置:

void sdsclear(sds s) {
  sdssetlen(s, 0);
  s[0] = '\0';
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值