linux内存管理学习笔记



1 linux内存管理
 地址类型
   物理地址
       出现在cpu地址总线上的寻址物理内存的地址信号,是地址变换的最终结果
   线性地址(虚拟地址) 
在32位cpu架构下,可以表示4g的地址空间,用16进制表示就是        


0x00000000到0xffffffff
   逻辑地址
程序代码编译后,出现在汇编程序中的地址


 地址转换
   cpu将一个逻辑地址转换为物理地址:
       利用段式内存管理单元,将逻辑地址转换成线性地址
       再利用页式内存管理单元,将线性地址转换成物理地址
 段式管理


(16位cpu)
有20位的地址线,1m的内存空间,由于寄存器只有16位,只能访问65536
个存储单元,64k
         因此cpu采用了内存分段的管理模式,cpu内部加入了段寄存器,把1M的
空间分为若干个逻辑段,要求如下:
逻辑段的起始地址必须是16的倍数(最后四个二进制位必须为0)
逻辑段的最大容量最大为64k

      物理地址的形成方式:
段地址是16的倍数,形式为xxxx0h,即前16位是变化的,可以只保存前
        16位二进制位来保存整个段基地址,所以使用时需要用段寄存器左移补
4个0来得到实际的段地址


      确定内存单元在存储器中的具体位置: 段地址+偏移量
      逻辑地址=段基地址+段内偏移量
      物理地址=段寄存器值*16+逻辑地址
      
      16位cpu有四个段寄存器,程序可同时访问四个不同含义的段
       cs+ip 用于代码段的访问
cs  存放程序的段基址 用这两个寄存器就可以得到一个内存物理地址
ip  指向下条要执行的指令在cs段的偏移量
        
       ss+sp 用于堆栈段的访问 直接访问栈顶单元的内存地址
        ss 指向堆栈段的基地址
        sp 指向栈顶
       ds+bx 用于数据段的访问
ds 值左移四位得到数据段的起始地址
        bx 偏移量
       es+bx 用于附加段的访问
es 值左移四位得到附加段起始地址
        bx 偏移量


     (32位cpu)
有两种不同的工作方式:

实模式 与16位cpu一致


保护模式 段基地址长达32位 段寄存器存放的是一个地址


(段选择器), 选择器从内存中得到一个32位的段地址
 
 页式管理
线性地址分为固定长度的组,称为页


 linux中所有段的基地址均为0,所以线性地址=逻辑地址
 linux页式管理
   linux2.3.29内核采用了四级页管理架构
   页全局目录
   页上级目录
   页中间目录
   页表


2 linux进程地址空间
   linux采用虚拟内存管理技术,每个进程都有独立的进程地址空间,约3G


  linux将4G的虚拟地址空间划分为
     用户空间  从0到0xbfffffff  随进程切换发生变化
     内核空间  从3G到4G
  用户进程只能访问用户空间,可以通过系统调用访问内核空间
   


   查看线性地址
   px aux
   cat /proc/<pid>/maps


   内核内存分配
   应用程序中 malloc
   linux内核中 kmalloc
  
   函数原型: 
   #include<linux/slab.h>
   void *kmalloc(size_t size,int flags)
   size 要分配的内存大小
   flags 分配标志,控制kmalloc的行为
     GFP_AUTOMIC 在进程上下文之外的代码(包括中断处理)中分配内存,不睡眠
     GFP_KERNEL  进程上下文中的分配,可能睡眠(16M-896M)
     __GFP_DMA 分配能够DMA的内存区(物理地址在16m以下的页帧)   
     __GFP_HIGHMEM 分配的内存位于高端内存(896以上)


  
   按页分配
   get_zeroed_page(unsigned int flags)//返回指向新页面的指针,页面清零
   __get_free_page(unsigned int flags)//同上,但不清零
   __get_free_pages(unsigned int flags,unsigned int order)
   //分配若干个连续的页面,返回指向该内存区域的指什,不清零




   释放内存
   void free_page(unsigned long addr)
   void free_pages(unsigned long addr,unsigned long order)














3 linux内核地址空间
  内核空间是由内核负责映射,不会随进程改变发生变化
  物理内存为896M以上的为高端内 存
  
  内核空间分布:
    (3G开始)
   直接映射区(direct memory region) 896M
      8M
   动态映射区(vmalloc region)
      8k
   KMAP区4M
   固定映射区4M
      4k
    (4G结束)
  -----------------------------------------
   直接映射区 从3G开始,最大896M的线性地址区间
              线性地址=3G+物理地址
   动态映射区 地址由内核函数vmalloc来进行划分,线线空间连续,物理空间不 


              一定
   永久内存映射区(PKMap region)对于896m以上的高端内存
                 访问方法:
                    1 alloc_page(__GFP_HIGHMEM)分配高端内存页
                    2 kmap函数将分配到的高端内存映射到该区域
   固定映射区(fixing mapping region) 


4 linux内核链表
   链表的数据结构: 数据域和指针域
   定义如下:
    sturct list_head{
struct list_head *next,*prev;
    };
    
    内核链表为双向循环链表


   链表的操作主要有:
    /*初始化链表头*/
    static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
     
    /*插入节点*/
/**
 * 节点前插入
 * list_add - add a new entry
 * @new: new entry to be added
 * @head: list head to add it after
 *
 * Insert a new entry after the specified head.
 * This is good for implementing stacks.
 */
static inline void list_add(struct list_head *new, struct list_head 


*head)
{
__list_add(new, head, head->next);
}




/**
 *节点尾插入
 * list_add_tail - add a new entry
 * @new: new entry to be added
 * @head: list head to add it before
 *
 * Insert a new entry before the specified head.
 * This is useful for implementing queues.
 */
static inline void list_add_tail(struct list_head *new, struct 


list_head *head)
{
__list_add(new, head->prev, head);
}


  /*删除节点*/
/**
 * list_del - deletes entry from list.
 * @entry: the element to delete from the list.
 * Note: list_empty() on entry does not return true after this, the 


entry is
 * in an undefined state.
 */
  static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}


 /*提取数据结构*/
/**
 * list_entry - get the struct for this entry
 * @ptr:the &struct list_head pointer.
 * @type:the type of the struct this is embedded in.
 * @member:the name of the list_struct within the struct.
 */
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)


/* 遍历 */


/**
 * list_for_each-iterate over a list
 * @pos:the &struct list_head to use as a loop cursor.
 * @head:the head for your list.
 */
#define list_for_each(pos, head) \
for (pos = (head)->next; prefetch(pos->next), pos != (head); \
        pos = pos->next)






5 linux内核定时器


度量时间差
时钟中断 由系统的定时硬件以周期性的时间间隔产生,这个间隔由内核跟据hz来 


        确定,hz与体系结构无关,可配置(50-1200),x86平台默认为1000
        时钟中断发生后,全局变量jiffies(unsigned long)会加1,驱动程序常 


        利用其来计算不同事件的时间间隔
内核定时器 (双向链表)
   用于控制某个函数(定时器处理函数)在未来的某个特定时间执行(只执行一次)


struct timer_list{
struct list_head entry //内核使用
unsigned long expires  //超时的jiffies的值
void (*function)(unsigned long) 超时处理函数
unsigned long data   //超时处理函数参数
struct tvec_base *base //内核使用
}; 






/*初始化定时器队列*/
void init_timer(struct timer_list *timer);
/*启动定时器*/
void add_timer(struct timer_list *timer);
/*定时器超时前将其删除*/
void del_timer(struct timer_list *timer);


示例代码如下:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <asm/uaccass.h>


MODULE_LICENSE("GPL");
MODULE_AUTHOR("Retacn Yue");
MODULE_DESCRIPTION("timer module");
MODULE_ALIAS("timer module");


sturct timer_list timer;


void timer_function(int para){
prink("timer expired and para is %d\n",para);
}


int timer_init(){
/*初始化定时器*/
init_timer(&timer);


timer.data=5;  /*超时处理函数参数*/
timer.expires=jiffies+(20*HZ); /*超时jiffies的值*/
timer.function=timer_function; /*超时处理函数*/
/*启动定时器*/
add_timer(&timer);

return 0;
}


void timer_exit(){
del_timer(&timer);
}


module_init(timer_init);
module_exit(timer_exit);

posted @ 2013-03-12 20:50  retacn_yue  阅读(173)  评论(0编辑  收藏  举报