原创作品,转载时请务必以超链接形式标明文章原始出处:http://blog.youkuaiyun.com/gqb666/article/details/8351080,作者:gqb666
最近在学习Linux虚拟总线platform驱动框架,对其中为一字符串申请内存使用strlen时未加1有点疑惑,于是通过写几个demo把研究发现的心得,记录了下来,分享给大家,有理解不够准确的地方还请大家多多拍砖。
注:本文代码实例在ubuntu10.04下使用gcc-4.4.3编译测试,使用其他编译器可能会造成对齐与本文结果不致的现象,特别说明一下。
Linux内核2.6.37源码目录driver/base/platform.c下分配platform_device内存空间,实现如下:
platform_device定义:
struct platform_device {
const char * name;
int id;
struct device dev;
u32 num_resources;
struct resource * resource;
const struct platform_device_id *id_entry;
/* arch specific additions */
struct pdev_archdata archdata;
};
定义一个platform_device的结构体变量omap3evm_snd_device:
static struct platform_device *omap3evm_snd_device;
为omap3evm_snd_device分配内存空间并命名为"soc-audio":
omap3evm_snd_device = platform_device_alloc("soc-audio", -1);
platform_device_alloc定义:
struct platform_device *platform_device_alloc(const char *name, int id)
{
struct platform_object *pa;
pa = kzalloc(sizeof(struct platform_object) + strlen(name), GFP_KERNEL);
if (pa) {
strcpy(pa->name, name);
pa->pdev.name = pa->name;
pa->pdev.id = id;
device_initialize(&pa->pdev.dev);
pa->pdev.dev.release = platform_device_release;
}
return pa ? &pa->pdev : NULL;
}
熟悉C语言的TX应该要问第5行在使用strlen时,后面为什么没有加1,也就是为什么没有用
pa = kzalloc(sizeof(struct platform_object) + strlen(name) + 1, GFP_KERNEL);
如果字符串“soc-audio”后面地址的内容不是'\0"时不会造成内存访问越界吗?
看一下结构体platform_object的定义:
struct platform_object {
struct platform_device pdev;
char name[1];
};
定义中的char name[1]有什么特别的呢?看下面几段代码:
仅有一个元素的char型数组情况:
#include <stdio.h>
struct test {
char name[1];
};
int main()
{
printf("sizeof struct test=%d\n",sizeof(struct test));
return 0;
}
打印出"sizeof struct test=1"
仅有一个元素的int型数组情况:
#include <stdio.h>
struct test {
int name[1];
};
int main()
{
printf("sizeof struct test=%d\n",sizeof(struct test));
return 0;
}
打印出"sizeof struct test=4"
两个元素的int型数组情况:
#include <stdio.h>
struct test {
int name[2];
};
int main()
{
printf("sizeof struct test=%d\n",sizeof(struct test));
return 0;
}
打印出"sizeof struct test=8"
上面3个例子很好理解:编译器再给数组分配空间时会以数组实际空间分配内存。但如果结构体包含数组和其他数据类型的情况呢?
#include <stdio.h>
struct test {
char a;
char name[1];
};
int main()
{
printf("sizeof struct test=%d\n", sizeof(struct test));
return 0;
}
打印出"sizeof struct test=2",可以看出编译器仅为char name[1]数组分配1个字节的内存。实际上,编译器在为数组分配内存的时候会以结构体对齐的方式增加一块内存(本例为platform_objec分配内存时不是1个字节,应该是4个字节),而数组名仅是数组起始地址的别名,并不占用堆内存空间,而多出来的4个字节就可以放置'\0'了,从而不需要strlen(name)+1。
举例来说明:
#include <stdio.h>
struct test {
int a;
char name[1];
};
int main()
{
printf("sizeof struct test=%d\n", sizeof(struct test));
return 0;
}
打印出"sizeof struct test=8",编译器会以int格式也就4字节对齐的方式为数组name分配了4个字节,也就是可以有4个字节可以放置“\0”。
而如果定义为指针的话:
#include <stdio.h>
struct test {
char a;
char *name;
};
int main()
{
printf("sizeof struct test=%d\n", sizeof(struct test));
return 0;
}
打印出"sizeof struct test=8",大家知道指针在32位系统是占4个字节的,如果定义为指针则必须使用pa = kzalloc(sizeof(struct platform_object) + strlen(name) + 1, GFP_KERNEL);
下面用内存分配图来形象说明使用数组与指针两种情况的内存分配图(为了作图方便将struct platform_device的空间当成4个字节,实际不然):
所以总体上内核实现方法很巧妙,不仅避免了分配置内存时使用strlen+1,而且还有3个字节的内存空间可以扩展使用,对linux内核高手来说这并不是什么难事,严格的来说内核方法巧妙地节省char *name指针所用的4字节内存空间。
Linux kernel作为一个强大成熟稳定的操作系统核心,为了做到节省系统资源,开发Linux内核的黑客大牛们将C语言的特性发挥到极致,死扣每一个字节。有太多值到我们学习的地方了!