linux内核显存分配的方式用户变量:用户空间定义的变量,地址为前3G
内核变量:内核空间定义的变量,地址为后1G
1.1kmalloc/kfree
void*kmalloc(size_tsize,gfp_tflags)
函数功能:
1.内核空间分配显存;
2.从内核1G的直接显存映射区中分配显存
3.化学地址和虚拟地址都是连续的(一一映射)
参数:
size:要分配显存的大小
最小为32字节
最大4M(2.6.20之前内核最大128K)
flags:指定分配显存时的行为
GFP_KERNEL:告知内核,请努力把显存分配成功,假如显存不足,会进行休眠操作,
等待空闲显存出现,不能用于中断上下文。
GFP_ATOMIC:假如显存不足,不会进行休眠操作,可以用于中断上下文;
返回值:保存分配的虚拟显存的首地址
比如:
void*addr;
addr=kmalloc(0x100000,GFP_KERNEL);
voidkfree(void*addr);
功能:释放显存
addr:分配的显存的首地址
1.2__get_free_pages/free_pages
明晰:linux系统,一页为4K
unsignedlong__get_free_pages(gfp_tgfp_mask,unsignedintorder)
函数功能:
1.内核空间分配显存
2.从直接显存映射区分配显存
3.化学显存和虚拟显存都连续
4.大小范围:
最小为1页;
最大为4M;
参数:
gfp_mask:指定分配显存时的行为
GFP_KERNEL:
告知内核,请努力把显存分配成功,假如显存不足linux入门,会进行休眠操作,等待空闲显存出现,不能用于中断上下文
GFP_ATOMIC:
假如显存不足,不会进行休眠操作,可以用于中断上下文;
order:
order=0,分配1页
order=1,分配2页
order=2,分配4页
order=3,分配8页
......
函数返回值:保存分配的内核虚拟显存的首地址
voidfree_pages(unsignedlongaddr,intorder);
功能:释放显存
addr:首地址
order:大小
比如:
unsignedlongaddr;
addr=__get_free_pages(GFP_KERNEL,4);
//注意addr数据类型的转换
free_page(addr,4);
1.3.vmalloc/vfree
void*vmalloc(intsize)
功能:
1.从动态显存映射区分配显存
2.大小理论上默认120M
3.虚拟上连续linux操作系统好吗,化学上不一定连续
4.假如显存不足,会造成休眠,不能用于中断上下文
参数:
size:分配显存的大小
返回值:保存分配的内核虚拟显存的首地址
voidvfree(void*addr);
释放显存
比如:
void*addr;
addr=vmalloc(0x200000);
vfree(addr);
经验:通常内核分配显存都是在驱动的入口函数linux 内核 占用内存,先把显存分配到手!
1.4.定义全局链表
staticcharbuf[5*1024*1024];
说明:
在BSS段中linux 内核 占用内存,BSS段的内容不会影响到可执行文件的大小;
或则:
staticcharbuf[5*1024*1024]={0xaa};
//假如程序不访问使用
说明:
在数据段中,数据段的内容会影响到可执行文件的大小,并且因为程序没有访问这个字段,编译器进行优化,最终字段的大小不会影响到可执行文件的大小;
或则
staticcharbuf[5*1024*1024]={0xaa};
//假如程序访问使用
说明:
程序访问,但是在数据段中,最终可执行文件的大小超过5M,最终会影响到ko文件的加载速率;
课下:百度搜索:__attribute__usedunused
1.5.在内核启动参数中,添加vmalloc=250M,表明内核启动时,动态显存映射区的大小有默认的120M更改为250M(从直接显存映射区抢)
setenvbootargsroot=/dev/nfs...vmalloc=250M
save
重启
查看内核复印信息,看动态显存映射区的大小
1.6.在内核启动参数中,添加mem=8M(事例,5M10M都可以),表明告诉内核,化学显存的最后8M要预留,
内核无需进行管理,将来驱动可以单独使用最后的8M化学显存;
将来驱动借助大名鼎鼎的ioremap函数将前面8M化学显存进行映射即可访问!
setenvbootargroot=/dev/nfs...mem=8M
ioremap函数明晰:在内核空间不容许直接访问设备的化学地址,须要访问对应的虚拟地址;
void*ioremap(unsignedlongphys_addr,intsize);
函数功能:
1.将化学地址映射到内核空间的虚拟地址上
2.将化学地址映射到动态显存映射区的虚拟地址上;
将来访问映射的虚拟地址就是在访问对应的数学地址
参数:
phys_addr:设备的起始化学地址
size:设备的"数学显存"大小
"数学显存":不单单指显存,还指诸如GPIO对应的硬件寄存器对应的储存空间;
返回值:保存映射的虚拟显存的首地址
若果不再使用,记得要解除地址映射关系:
voidiounmap(void*addr)
功能:
解除化学地址和虚拟地址的映射关系
参数:
映射的虚拟显存的首地址
比如:
GPC0_3,GPC0_4两个硬件GPIO对应的寄存器分别为:
GPCCON:配置寄存器
GPCDATA:数据寄存器
明晰:寄存器对于CPU来说是一种外设,所以CPU要访问,必需要获取寄存器的地址,分别:
GPCCON:0xE0200060,储存空间为4字节
GPCDATA:0xExE0200064,储存空间为4字节
但是两个寄存器占用的地址空间是连续的;
明晰:内核不能直接访问以上化学地址,须要做映射:
unsignedlong*gpiocon,*gpiodata;
gpiocon保存配置寄存器的内核虚拟地址
gpiodata保存数据寄存器的内核虚拟地址
映射的方式1:
gpiocon=ioremap(0xE0200060,4);
gpiodata=ioremap(0xE0200064,4);
一旦有化学地址对应的内核虚拟地址,就可以访问外设
*gpiocon...
*gpiodata...
映射方式2:
gpiocon=ioremap(0xe0200060,8);
gpiodata=gpiocon+1;
*gpiocon...
*gpiodata...
解除映射:
iounmap(gpiocon);
#include
案例
借助ioremap实现LED开关操作,不容许使用GPIO操作库函数;
#include
#include
#include
#include
#include
#include
//定义LED开关命令
#define LED_ON 0x100001
#define LED_OFF 0x100002
//定义保存配置寄存器和数据寄存器的内核虚拟地址变量
static unsigned long *gpiocon, *gpiodata;
static long led_ioctl(struct file *file,
unsigned int cmd,
unsigned long arg)
{
//分配内核缓冲
int kindex;
//拷贝用户数据到内核
copy_from_user(&kindex, (int *)arg, sizeof(kindex));
//解析命令
switch(cmd) {
case LED_ON:
if (kindex == 1)
*gpiodata |= (1 << 3);
else if (kindex == 2)
*gpiodata |= (1 << 4);
break;
case LED_OFF:
if (kindex == 1)
*gpiodata &= ~(1 << 3);
else if (kindex == 2)
*gpiodata &= ~(1 << 4);
break;
}
printk("%s:配置寄存器=%#x, 数据寄存器=%#xn",
__func__, *gpiocon, *gpiodata);
return 0;
}
//定义初始化硬件操作方法.unlocked_ioctl
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = led_ioctl
};
//定义初始化混杂设备对象
static struct miscdevice led_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "myled",
.fops = &led_fops
};
static int led_init(void)
{
//注册混杂设备对象
misc_register(&led_misc);
//将配置寄存器和数据寄存器的物理地址映射到内核虚拟地址上
gpiocon = ioremap(0xE0200060, 8);
gpiodata = gpiocon + 1;
//配置GPIO为输出口,输出0
*gpiocon &= ~((0xf << 12) | (0xf << 16));
*gpiocon |= ((1 << 12) | (1 << 16));
*gpiodata &= ~((1 << 3) | (1 << 4));
return 0;
}
static void led_exit(void)
{
//输出0,解除地址映射
*gpiodata &= ~((1 << 3) | (1 << 4));
iounmap(gpiocon);
//卸载混杂设备
misc_deregister(&led_misc);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
#include
#include
#include
#include
//./led_test on 1
//./led_test off 1
//./led_test on 2
//./led_test off 2
//定义LED开关命令
#define LED_ON 0x100001
#define LED_OFF 0x100002
int main(int argc, char *argv[])
{
int fd;
int uindex; //用户缓冲区,保存灯的编号
if (argc != 3) {
printf("Usage:n %s n", argv[0]);
return -1;
}
fd = open("/dev/myled", O_RDWR);
if (fd 123
uindex = strtoul(argv[2], NULL, 0);
//向设备发送命令和灯的编号
if (!strcmp(argv[1], "on"))
ioctl(fd, LED_ON, &uindex);
else
ioctl(fd, LED_OFF, &uindex);
close(fd);
return 0;
}