一、Linux内存管理(子系统)
地址类型:
1)物理地址:物理地址是指出现在CPU外部地址总线上的寻址物理内存的地址信号,是地址变换的最终结果。
2)线性地址(虚拟地址)32位 ox00000000---0xffffffff
3)逻辑地址:程序代码经过编译后在汇编程序中使用的地址。
通过段式管理单元得到线性地址,再通过页式管理得到物理地址
什么是段式管理
逻辑地址=段内偏移量
1》对于16位CPU
PA = 段寄存器的值*16 + 逻辑地址
0=======2^16-1 (2^16*0)
0=======2^16-1 (2^16*1)
0=======2^16-1 (2^16X2)
0=======2^16-1 (2^16X4)
0=======2^16-1 (2^16X15)
2》对于32位的X86cpu,段基地址寄存器内存放的不再是基地址,而是内存区域的地址,通过该地址找到基地址。
3》实模式 保护模式
什么是页式管理
2^10*2^10*2^12共覆盖2^32 ---两页
Linux内存管理时,巧妙的绕过了段式管理。
段基地址都是为0
二、Linux进程地址空间
每个进程都有独立的进程地址空间3G
用户看到和接触到得都是虚拟地址
还有1G是内核空间
两空间互相访问:系统调用和中断
每当进程切换,用户空间跟着变化cr3寄存器的值变化
/cat/proc/<pid>/maps查看 线性地址相同 物理地址不同
fork() execve malloc只是分配到线性地址 也就是虚拟地址 用的时候才会分配物理地址
有4G的范围 可以交错使用 不能同时使用
比如:分配了100字节的内存 不写数据不分配物理内存
内核中分配内存:kmalloc,有两个参数:大小和标志
GFP_KERNEL:如果没有内存分配 就会导致睡眠 16-896M之间分配
GFP_ATOMIC:不会睡眠
__GFP_DMA:DMA传输的内存在16M以下的页帧
__GFP_HIGHMEM:896以上
按页分配(分配比较大时)
get_zeroed_page 清零的
__get_free_page
当程序使用完后 一定要free
free_page free_pages
内存使用:
用户空间:
malloc fork excute mmap:虚拟内存
内核:
kmalloc:物理内存
Vmalloc:虚拟内存
slab 内存管理机制?
三、Linux内核地址空间(1G)
内核地址空间不会随着进程改变
物理内存896M-1G是高端内存,
1》直接映射区:线性地址=3G+物理地址--kmalloc 最大896M
2》Vmalloc区 动态内存映射区 最小120M
3》永久内存映射区 高端内存 KMAP区 固定4M
4》固定映射区 如ACPI_BASE 地址映射寄存器 不要修改,固定4M
四、Linux内核链表
单链表 双链表 循环链表
include/linux/list.h中 一套精彩的链表实现
可以将其移植到应用程序中
内核中,更多的时候是双向链表
五、Linux内核定时器
Linux内核定时器 驱动时会用到
通过时钟中断计算时间间隔
HZ宏可以配置
例如 一秒钟1000个中断
每次中断(1/1000秒),全局变量jiffies加1
jiffies unsigned long
利用jiffies做一个延迟
内核定时器控制让某个函数在某时间执行
这个定时器不是循环的 只执行一次
删除定时器 超时之前取消 若已经超时 无需del
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/list.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Xie");
MODULE_DESCRIPTION("List Module");
MODULE_ALIAS("List module");
struct student
{
char name[100];
int num;
struct list_head list;
};
struct student *pstudent;
struct student *tmp_student;
struct list_head student_list;
struct list_head *pos;
int mylist_init()
{
int i = 0;
INIT_LIST_HEAD(&student_list);
pstudent = kmalloc(sizeof(struct student)*5,GFP_KERNEL);
memset(pstudent,0,sizeof(struct student)*5);
for(i=0;i<5;i++)
{
sprintf(pstudent[i].name,"Student%d",i+1);
pstudent[i].num = i+1;
list_add( &(pstudent[i].list), &student_list);
}
//#define list_for_each(pos, head) \
//for (pos = (head)->next; prefetch(pos->next), pos != (head); pos = pos->next)
list_for_each(pos,&student_list)
{
//#define container_of(ptr, type, member) ({const typeof( ((type *)0)->member ) *__mptr = (ptr); (type *)( (char *)__mptr - offsetof(type,member) );})
tmp_student = list_entry(pos,struct student,list);
printk("<0>student %d name: %s\n",tmp_student->num,tmp_student->name);
}
return 0;
}
void mylist_exit()
{
int i ;
/* 实验:将for换成list_for_each来遍历删除结点,观察要发生的现象,并考虑解决办法 */
for(i=0;i<5;i++)
{
list_del(&(pstudent[i].list));
}
kfree(pstudent);
}
module_init(mylist_init);
module_exit(mylist_exit);