linux-内核-内存知识储备/链表操作

大纲:
1.linux内存管理
2.linux进程地址空间
3.linux内核地址空间
一.linux内存管理
内存管理子系统是操作系统之一
1.地址类型—–
物理地址:出现在cpu地址总线上的寻址物理内存的地址信号,是地址变换的最终结果
线性地址(虚拟地址):在32位cpu架构下,可以表示4G的地址空间,用16进制表示就是0x00000000-oxffffffff
逻辑地址:程序代码经过编译后,出现在汇编程序中的地址
2.地址转换
cpu要将一个逻辑地址转化为物理地址,需要两步:首先cpu利用段式内存管理单元将逻辑地址转换成线程地址,再利用页式内存管理单元,把线性地址
最终转换为物理地址
3.什么是段式管理内存
为了能够访问1m的内存空间,cpu就采用了内存分段的管理模式,并在cpu内部加入段寄存器,16位cpu把1m内存空间分为若干个逻辑段,每个逻辑段要求如下
1)逻辑段的起始地址(段地址)必须是16的倍数,即最后4个二进制位必须全为0
2)逻辑段的最大容量为64K
物理地址形式:段地址基本是16的倍数,所以值的一般形式是xxxxOH,即前16位二进制是变化的,后4位是固定的0,鉴于段地址的这种特性,可以只保存前16位
二进制来保存整个段基地址,所以段寄存器左移动补个0(乘以16)来得到实际段地址
逻辑地址=段基地址+段内偏移
4.段寄存器为了对内存进行分段管理而增加的16位cpu有4段寄存器
1)cs+ip用于代码段的访问,cs指存放程序的段基址,IP指向下条要执行的指令在cs段的偏移量,用这两个寄存器就可以得到一个内存物理地址。
2)ss+sp用于堆栈段的访问,ss指向堆栈段的基地址,sp指向栈顶,可以通过ss和sp两个寄存器直接访问栈顶单元的内存物理地址。
3)ds+bx用于数据段的访问,ds指出当前程序使用的数据所存放段的最低地址,即存放数据段的段基值。
4)es+bx用于附加段的访问,es指出当前程序使用附加数据段的段基址,该段是串操作指令中目的串所在的段。
5.32位cpu段式管理
逻辑地址=段基地址+段内偏移量
32位pc采用了两种不同的工作方式:实模式和保护模式
实模式–32位与16位CPU一致
保护模式–段基地址长达32位,每个段容量达4G,段寄存器的值是段地址的选择器,用该选择器从内存中得到一个32位的段地址,存储单元的地址就是该
段地址加上该段偏移量
6.分页管理–针对线性地址
页称为物理页,分页单元把所有物理内存也划分位固定长度的管理单位,它的长度一般与线性地址页式相同的
这样的二级模式是否能够覆盖4g的物理地址空间?为什么?
(2**10)(2**10)(2**12)=2**32=4G
7.linux内存管理
linux仅仅有限度的使用了分段的机制,简化了linux内核设计
所有段的基地址均为0,由此可以看出每个段逻辑地址空间范围为0-4GB,所以每个段基地址为0,逻辑地址与线性地址保持一致,linux巧妙的绕过了段机制而完全利用了分页机制。
二.linux进程地址空间
1.linux操作系统采用虚拟内存管理技术,使得每个进程都有独立的进程地址空间,利用虚拟地址可以保护操作系统,而且用户程序可使用比实际物理内存更大
的地址空间
2.linux将4G虚拟地址空间划分为2个部分–用户空间和内核空间。用户进程只能访问用户的虚拟地址,不能访问内核空间的。例外:用户进程通过系统调用
访问内核空间
3.进程空间:用户空间对应进程,每当进程切换,用户空间就会跟着变化
4.内核内存分配
在应用程序中,使用malloc函数动态分配内存,而linux内核中,通常用kmalloc来分配动态内存

#include <Linux/slab.h> 
void *kmalloc(size_t size, int flags);
参数
size:要分配的内存大小
flags:分配标志,它控制kmalloc的行为
     GFP_ATOMIC 用来从中断处理和进程上下文之外的其他代码中分配内存. 从不睡眠.
     GFP_KERNEL 进程上下文中的分配,可能睡眠
     GFP_USER 用来为用户空间页来分配内存; 它可能睡眠.
     __GFP_DMA 要求分配在能够 DMA 的内存区
     __GFP_HIGHMEM 指示分配的内存可以位于高端内存(896M以上)
按页分配
get_zeroed_page(unsigned int flags)
_get_free_page(unsigned int flag)
_get_free_pages(unsigned int flag,unsigned int order)
释放
void free_page(unsigned long addr)
void free_page(unsigned long addr,unsigned long order)
如果释放的和先前分配数目不等的页面,会导致系统错误

三.linux内核地址空间
1.内核空间是由内核负责映射,它并不会跟随进程进行改变,是固定的
2.高端内存:物理内存896MB以上的部分
1)使用alloc_page(_GFP_HIGHMEM)分配高端内存页
2)使用kmap函数将分配到的高端内存映射到该区域
3.直接映射区:从3G开始,最大896M的线性地址区间(线性地址=3G+物理地址)
物理地址区间是0x100000-0x200000映射到线性空间是3G+0x100000-3G+0x200000
4.动态映射区:120M—该区域的地址由内核函数vmalloc来进行分配,特点是线性空间连续,但对应的物理空间不一定连续
5.永久映射区:4M
6.固定映射区:pkmap区上,有4M的线性空间,它和4G顶端只有4K的隔离带
四.linux内核链表
1.链表:是一种常用数据结构,它通过指针将一系列数据节点连接成一条数据链,相对于数组,链表具有更好的动态性,建立
链表的时候预先无需知道数据的总量,可以随机分配空间,可以高效的在链表中的任意位置实时的插入或者删除数据
2.链表的数据结构至少包括两个域,数据域(用于存储数据)和指针域(用于建立和下一个节点的联系)
3.在linux内核中使用了大量的链表结构来组织数据,大多采用[include/linux/list.h]链表结构的定义

struct list_head{
    struct list_head *next,*prev
    };
list_head结构包括2个指向list_head结构的指针prev和next,所以内核的链表具有双链表功能,实际双向循环链表
4.链表的操作
1)初始化链表头
INIT_LIST_HEAD(list_head *head)
2)插入节点
list_add(struct list_head *new, struct list_head *head)
list_add_tail(struct list_head *new, struct list_head *head)
3)删除节点
list_del(struct list_head *entry)
4)提取数据
list_entry(ptr, type, member)
一直数据结构的节点指针ptr,找出数据结构
5)遍历
list_for_each(struct list_head *pos,struct list_head *head)

实例:student.c

#include<linux/list.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>  //kmalloc和kfree的头文件
MODULE_LICENSE("GPL");
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(void){//传入参数为空,一定要写成(void)
  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);//插入的节点pstudent[i].list
    }
  list_for_each(pos,&student_list){//遍历
    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(void){
  int i;
  for(i=0;i<5;i++){
  list_del(&pstudent[i].list);
  }
  kfree(pstudent);
}
module_init(mylist_init);
module_exit(mylist_exit);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值