【nginx流程分析之内存对齐和分配】

浅谈内存对齐

首先我们来简单说一下内存对齐,以及为什么要进行内存对齐。
首先现在我们的电脑是64位,也就是8个字节。对于我们cpu来说,并不是一个字节一个字节从内存中获取数据,因为这样效率太低,都是8个字节和8个字节去拿,所以分配的地址也尽量保证是8的整数倍,当然这个c语言底层已经帮助我们做好了,会有一个自然的对齐的策略,尤其在结构体中,当然存在内存的浪费,当然这个是在允许的范围内,如下:

struct align{
        char b;
        long long a;
        int c;
        char d;
};
 
int main()
{
        struct align align1;
        printf("&a = %p\n", &align1.a);
        printf("&b = %p\n", &align1.b);
        printf("&c = %p\n", &align1.c);
        printf("&d = %p\n", &align1.d);
        printf("sizeof(align) = %lu\n", sizeof(struct align));
        return;
}

/*
&a = 0x16f4275c0
&b = 0x16f4275b8
&c = 0x16f4275c8
&d = 0x16f4275cc
sizeof(align) = 24
*/

我们以这个为例子来简单分析一下,刚开始很多人会认为sizeof(struct align) 会是 1 + 8 +4 + 1 = 14。其实不然,我们从地址看应该是如下:

在这里插入图片描述
一共占用 3 * 8 =24 个字节。之所以char b 占用8个字节,是为了满足内存对齐的要求,而后面的char d 只占用4个字节,是因为可以和变量c int 一起占用8个字节,起到了节省内存的作用。

当然还有其他情况,这边我们就不细说,感兴趣的可以搜一下。

判断是否对齐

在上一篇中,我们可以看到,在ngx_crc32_table_init可以看到,如下代码

//ngx_crc32_table_short 是ngx_crc32_table16的首地址
uint32_t *ngx_crc32_table_short = ngx_crc32_table16;

 //判断 ngx_crc32_table_short的地址是否是 按照ngx_cacheline_size的对齐的 详见
    if (((uintptr_t) ngx_crc32_table_short & ~((uintptr_t) ngx_cacheline_size - 1))
        == (uintptr_t) ngx_crc32_table_short)
    {
        return NGX_OK;
    }

这里就是为了判断ngx_crc32_table_short的地址是否和ngx_cacheline_size对齐。

ngx_crc32_table_short可以看出来,取的是ngx_crc32_table16的首地址。
然后 & 和 ~ 都是c语言中的位操作。& 是 and的意思 ,~ 就是取反。
我们以二进制举例子。比如 01 & 11 = 01; ~01= 10;
所以集合起来, &~ 就是低位清空,

然后这边可以看到ngx_cacheline_size是32/64, 就以64为例子,那么ngx_cacheline_size-1=63;同时在前面加上了uintptr_t的类型转换为8字节。
64的二进制 10000000,64-1= 63 的二进制 00111111, 然后取反是 11000000,8字节前面还是都是一样的,就省略了。
所以如果是64的备注,其实就是 64 <<n, 那么和 11000000(63的取反) 取&的,一定是一样的,因为11000000 前面都是1,有点绕,多想想就好了。
然后就是判断是否是64的倍数,如果是64的倍数那么就返回,如果不是那么接下来就要继续走。就是重新分配ngx_crc32_table16的地址了

分配内存

我们先看下分配内存的代码

static uint32_t  ngx_crc32_table16[] = {
    0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
    0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
    0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
    0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
};
  p = ngx_alloc(16 * sizeof(uint32_t) + ngx_cacheline_size, ngx_cycle->log);
    if (p == NULL) {
        return NGX_ERROR;
    }

首先是16 * sizeof(uint32_t) 比较好理解,因为ngx_crc32_table16有16个类型为uint32_t的成员变量。然后ngx_cacheline_size就是要对齐的长度,之所以加这个长度,是因为malloc也不一定是ngx_cacheline_size的倍数,所以要加上这个长度。
然后就是调用ngx_alloc,然后这边是 16 * 4 + 64 = 128 字节。

获取符合ngx_cacheline_size的地址

上面我们可以看到我们已经申请了128字节的内存,然后就是获取开始为64倍数的地址了,然后我们看一下nginx是怎么做的:

    //地址按照ngx_cacheline_size 进行对齐
    p = ngx_align_ptr(p, ngx_cacheline_size);
    #define ngx_align_ptr(p, a)                                                   
    (u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))

这边p就是我们调用malloc返回的地址,然后a就是64,然后有了上面的说明就比较好理解了,其实((uintptr_t) § + ((uintptr_t) a - 1)),就是把地址 达到64位向左边移动了一个,然后配合& ~((uintptr_t) a - 1)移除地位达到目的。

赋值

先看一下代码,

 //把ngx_crc32_table16 赋值给p
    ngx_memcpy(p, ngx_crc32_table16, 16 * sizeof(uint32_t));

    //再把 p的地址赋值给ngx_crc32_table_short 
    ngx_crc32_table_short = p;

其实这边就是比较好理解了,就是把ngx_crc32_table16赋值给p变量,因为p此时已经是64位对齐的了,然后ngx_crc32_table_short的地址和p保持一致。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值