内容全部是自我理解梳理而成,可能存在错误内容。
涉及文件
- sds.h
- sds.c
具体了解sds的可以看 http://github.com/antirez/sds
结构体定义
struct test {int age;int len;unsigned char flags;char name[];} test;
内存分布

通过这个图,(如果不考虑结构体对齐)可知
dd->name 下移一位 就是flags的地址, unsigned char flags = dd->name[-1] printf(“输出输出flags:%c\n”, dd->name[-1]);===> output: 【输出flags:B】
获取结构体, 通过结构体获取其他属性 struct test sh = (void ) ((dd->name) - sizeof(struct test));
printf(“name:%s\n”, sh->name); ===> output: 【name址:chenshun001】
而实际上会出现内存对齐,导致flags也会占用4个字节,从而导致移动内存获取结构体指针失败。
概念
结构体内存对齐: 按照int 4个字节对齐,此时通过sizeof(struct test)进行计算,结构体test占用12个字节。
struct test {int age;int len;unsigned char flags;char name[];} test;
结构体属性压缩: 只占用对应的内存长度,此时通过sizeof(struct test)进行计算,结构体test占用9个字节。
struct __attribute__ ((__packed__)) test {int age;int len;unsigned char flags;char name[];} test;
实际案例
```c
include
include
include
include
struct attribute ((packed)) test { int age; int len; unsigned char flags; char name[]; } test;
int main() { struct test dd = malloc(sizeof(struct test)); if (dd == NULL) { return 1; } char f = “chenshun001”; dd->age = 456; dd->len = 456; dd->flags = 66;
printf("输出flags:%d\n", dd->name[-1]);printf("结构体地址:%p\n", &dd);printf("结构体指向的地址:%p\n", *&dd);printf("结构体age地址:%p\n", &dd->age);printf("结构体len地址:%p\n", &dd->len);printf("结构体flags地址:%p\n", &dd->flags);memcpy(dd->name, f, strlen(f));printf("结构体name地址:%p\n", &dd->name);//如果加上内存对齐,那么这里获取的内存地址是错误的struct test *sh = (void *) ((dd->name) - sizeof(struct test));printf("name址:%s\n", sh->name);free(dd);return 0;
} //output //输出flags:66 //结构体地址:0x7ffee97a48c0 //结构体指向的地址:0x7fe937c05a20 //结构体age地址:0x7fe937c05a20 //结构体len地址:0x7fe937c05a24 //结构体flags地址:0x7fe937c05a28 //结构体name地址:0x7fe937c05a29 //name址:chenshun001
<a name="cKGFu"></a>### Redis SDS结构体```c// sdshdr 结构 低版本struct sdshdr {int len;int free;char buf[];};//=============================================================================//高版本struct __attribute__ ((__packed__)) sdshdr5 {//低3个字节表示类型 高5个字节表示长度 2^5=32,必须小于32unsigned 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[];};
Redis 内部操作
创建一个SDS
/*sds本身就是一个字符指针,使用sds可以直接在上层使用c标准库的函数*/sds sdsnewlen(const void *init, size_t initlen) {void *sh;sds s;//确定sds结构体类型SDS_TYPE_5/SDS_TYPE_8/SDS_TYPE_16/SDS_TYPE_32/SDS_TYPE_64char type = sdsReqType(initlen);//...移除一点点代码//获取结构体需要的内存 sizeof(struct 对应的结构体)int hdrlen = sdsHdrSize(type);//char指针unsigned char *fp; /* flags pointer. *///分配内存 = 结构体需要的内存+初始化的长度内存+1('\0'需要的内存)sh = s_malloc(hdrlen + initlen + 1);//分配内存失败if (sh == NULL) return NULL;//结构体指针的起始地址+hdrlen的地址==sds char柔性数组的开始地址s = (char *) sh + hdrlen;//char指针赋值,这样下边的*fp就是指向sds结构体的flags字段了fp = ((unsigned char *) s) - 1;switch (type) {case SDS_TYPE_5: {//设置fp指针的数据高5位 就是initlen,字符长度*fp = type | (initlen << SDS_TYPE_BITS);break;}case SDS_TYPE_8: {//调用SDS_HDR_VAR获取到sh的结构体指针, 初始化结构体数据struct sdshdr8* sh = (void*)(s - sizeof(struct sdshdr8));sh->len = initlen;sh->alloc = initlen;*fp = type;break;}case SDS_TYPE_16: {//调用SDS_HDR_VAR获取到sh的结构体指针, 初始化结构体数据struct sdshdr16* sh = (void*)(s - sizeof(struct sdshdr16));sh->len = initlen;sh->alloc = initlen;*fp = type;break;}case SDS_TYPE_32: {//调用SDS_HDR_VAR获取到sh的结构体指针, 初始化结构体数据struct sdshdr32* sh = (void*)(s - sizeof(struct sdshdr32));sh->len = initlen;sh->alloc = initlen;*fp = type;break;}case SDS_TYPE_64: {//调用SDS_HDR_VAR获取到sh的结构体指针, 初始化结构体数据struct sdshdr64* sh = (void*)(s - sizeof(struct sdshdr64));sh->len = initlen;sh->alloc = initlen;*fp = type;break;}}//使用memcpy进行数据拷贝if (initlen && init)memcpy(s, init, initlen);//字符串结尾部分放置位'\0', 直接利用c标准库的函数s[initlen] = '\0';return s;}
获取SDS信息
//sds内存的起始地址#define SDS_HDR(T, s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))//返回sds剩余可用空间 = sds.alloc - sds.len;static inline size_t sdsavail(const sds s) {unsigned char flags = s[-1];switch (flags & SDS_TYPE_MASK) {case SDS_TYPE_5: {return 0;}case SDS_TYPE_8: {struct sdshdr8 *sh = SDS_HDR(8, s);return sh->alloc - sh->len;}case SDS_TYPE_16: {struct sdshdr16 *sh = SDS_HDR(16, s);return sh->alloc - sh->len;}case SDS_TYPE_32: {struct sdshdr32 *sh = SDS_HDR(32, s);return sh->alloc - sh->len;}case SDS_TYPE_64: {struct sdshdr64 *sh = SDS_HDR(64, s);return sh->alloc - sh->len;}}return 0;}
SDS扩容

sds sdsMakeRoomFor(sds s, size_t addlen) {//2个sds的结构体指针,这里的sds都特指为struct sds,而不是char* sds.void *sh, *newsh;//获取free大小free=avail (available)size_t avail = sdsavail(s);size_t len, newlen, reqlen;//获取sds的结构体类型char type, oldtype = s[-1] & SDS_TYPE_MASK;//结构体字节数大小int hdrlen;/* 如果可用字节数> 待分配字节数 Return ASAP if there is enough space left. */if (avail >= addlen) return s;//获取已使用字节数,如果是3.2版本的,直接可用通过 (struct sds* (void*)(s-sizeof(struct sds)))->len 获取//这里必须根据type 去判断一下headerlen = sdslen(s);//sds的开始内存地址向下移动 sdsHdrSize(oldtype) 位就是sds结构体的开始地址, 结构体指针*sh指向sds结构体内存地址,完美sh = (char *) s - sdsHdrSize(oldtype);//需要的内存数 = len(已使用) + addlen(待添加的字节数)reqlen = newlen = (len + addlen);assert(newlen > len); /* Catch size_t overflow *///如果小于1M,进行翻倍,后续拓展就不需要重新分配内存了if (newlen < SDS_MAX_PREALLOC)newlen *= 2;else//大于1M,每次只加一M,和Netty的内存分配策略有异曲同工之妙newlen += SDS_MAX_PREALLOC;//获取新的sds typetype = sdsReqType(newlen);/* Don't use type 5: the user is appending to the string and type 5 is* not able to remember empty space, so sdsMakeRoomFor() must be called* at every appending operation. */if (type == SDS_TYPE_5) type = SDS_TYPE_8;//type对应的结构体指针内存大小hdrlen = sdsHdrSize(type);assert(hdrlen + newlen + 1 > reqlen); /* Catch size_t overflow *///内存没有发生变化if (oldtype == type) {//分配内存newsh = s_realloc(sh, hdrlen + newlen + 1);if (newsh == NULL) return NULL;s = (char *) newsh + hdrlen;} else {/* Since the header size changes, need to move the string forward,* and can't use realloc *///新分配一块地址newsh = s_malloc(hdrlen + newlen + 1);//分配地址失败if (newsh == NULL) return NULL;//将sds的数据拷贝到新生成的sds,拷贝数据为真实数据+1('\0')memcpy((char *) newsh + hdrlen, s, len + 1);//释放内存s_free(sh);//指向新的地址s = (char *) newsh + hdrlen;//设置结构体sds的types[-1] = type;//设置新的长度sdssetlen(s, len);}//设置总的内存大小sdssetalloc(s, newlen);return s;}
