Linux内存空间知识复习

一、用户空间和内核空间

  1. linux操作系统采用虚拟地址空间,以32位系统为例,寻址空间是4G(2的32次方)。操作系统为每一个进程都分配了4G的内存空间,这个内存空间是虚拟的。
    操作系统将这4G内存空间分为内核空间和用户空间
    内核空间是操作系统内核访问的区域,是受保护的内存空间
    用户空间是普通用户程序可访问的内存区域
    空间分配如下:
    在这里插入图片描述
    每个进程可以通过系统调用进入内核,内核空间由所有进程共享

  2. 区分内核空间和用户空间的原因:应用程序随便访问内存太危险,比如把系统搞崩溃,清除操作系统的数据;用户态程序不能操作内核地址空间,对操作系统具有安全保护作用

  3. 内核态和用户态:当进程线程运行在内核空间时就处于内核态,而进程线程运行在用户空间时则处于用户态;在内核态下,CPU可以执行任何指令,可以自由访问任何有效地址;用户态下,进程只能访问其地址空间的页表项中的虚拟地址

  4. 用户态到内核态的切换:当一个进程执行系统调用而陷入内核代码中执行时,称进程处于内核运行态;还有软中断和硬中断可以实现用户态到内核态的切换

二、虚拟内存到真实内存的映射
用户空间的内存映射:段页式地址映射
逻辑地址、线性地址、物理地的概念
逻辑地址 ----(段表)—> 线性地址 — (页表)—> 物理地址
在这里插入图片描述

内核空间的映射:
高端内存
内核空间的内存地址,一对一映射到真实的物理内存地址,如逻辑地址0xc0000003对应的物理地址为0×3,逻辑地址与物理地址对应关系为:物理地址 = 逻辑地址 – 0xC0000000,内核虚拟地址在“高端”,映射的物理地址在低端

逻辑地址 物理内存地址
0xc0000000 0×0
0xc0000001 0×1
0xc0000002 0×2
0xc0000003 0×3
… …
0xe0000000 0×20000000
… …
0xffffffff 0×40000000 ??

如果按照上面的方法简单映射,那么内核只能访问1G的地址空间,如果机器安装了8G内存,那么只能访问前面1G,后面7G物理内存要怎么访问呢?
x86架构将内核地址空间划分为三部分:ZONE_DMA、ZONE_NORMAL、ZONE_HIGNMEM,ZONE_HIGNMEM就是高端内存

在x86结构中,三种类型的区域(从3G开始计算)如下:

ZONE_DMA 内存开始的16MB

ZONE_NORMAL 16MB~896MB

ZONE_HIGHMEM 896MB ~ 结束(1G)

在这里插入图片描述
当内核想要访问高于896M物理地址内存时,就从从0xF8000000 ~ 0xFFFFFFFF地址空间范围内找一段相应大小空闲的逻辑地址空间,建立临时映射,用完再还回去:
在这里插入图片描述

三、64位ARM的内存空间划分

在这里插入图片描述

四、Linux为什么一定要copy_from_user?
可以从两个层面思考:

  1. 第一个层面是为什么要拷贝,可不可以不拷贝?
    假设以下代码:
struct globalmem_dev {
        struct cdev cdev;
        unsigned char *mem;
        struct mutex mutex;
};
 
 
static ssize_t globalmem_write(struct file *filp, const char __user * buf,
                               size_t size, loff_t * ppos)
{
        struct globalmem_dev *dev = filp->private_data;
 
 
        dev->mem = buf; //假想的烂代码
 
 
        return ret;
}

在内核中直接使用const char __user * buf指针是灾难性的,因为buf虚拟地址,只在这个进程空间有效,跨进程是无效的。切换了进程后,页表都变了,原来的进程虚拟地址啥都不是!
因此,正确的做法是,把buf拷贝到一个跨中断、跨进程、跨workqueue、跨内核线程的长期有效的内存里面

  1. 第二个层面是为什么要用copy_from_user而不直接用memcpy?
    这个主要有两个方面的原因:一个是copy_from_user有自带的access_ok检查,如果用户传进来的buf不属于用户空间而是内核空间,根本不会拷贝;二是copy_from_user有自带的page_fault后exception修复机制。
    第一个access_ok检查,会判断用户传进来的buf是不是属于用户空间,因为如果用户传进来内核空间地址,对内核地址进行拷贝,会造成严重的安全漏洞。
    第二个page_fault后有exception修复机制,假设用户程序随便胡乱传个用户空间的地址给内核:

void main(void)
{
        int fd;
 
 
        fd = open("/dev/globalfifo", O_RDWR, S_IRUSR | S_IWUSR);
        if (fd != -1) {
                int ret = write(fd, 0x40000000, 10);//假想的代码
                if (ret < 0)
                        perror("write error\n");
        }
}

0x40000000可以通过access_ok检查,但是不是有效的地址,不是heap也不是stack;如果内核驱动用memcpy,我们会看到一段内核oops:
在这里插入图片描述
用户进程会被killed掉,如果设置了/proc/sys/kernel/panic_on_oops,就会直接panic
而如果内核使用的是copy_from_user,内核不会oops,用户态应用程序会收到bad address的错误;copy_from_user有exception fixup机制,而memcpy没有

五、copy_to_user与mmap的区别?
copy_to_user在每次拷贝时都需要检测指针的合法性,而且每次都需要进行数据的拷贝,频繁访问内存;mmap只在第一次使用时为进程建立页表,也就是将一段物理地址映射到一段虚拟地址上,以后操作不再检查其地址的合法性;另一方面内核可以直接操作mmap地址,不用进行拷贝

六、缺页异常机制(page fault)
CPU通过地址总线访问内存等设备,从CPU发出的是虚拟地址,而不是实际内存物理地址,虚拟地址需要经过MMU转化成物理地址,再从地址总线上发出;MMU里的虚拟地址到物理地址的转换关系,是需要提前创建的,如果没有这个转换关系,MMU就会通知CPU产生一个缺页异常(中断)
arm的缺页处理函数是do_page_fault函数,会根据异常类型,进行映射关系的建立

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值