Linux进程地址空间及内存管理方式

进程地址空间

我们先看两段代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    int g_val = 0;
    pid_t id = fork();   //创建子进程
    if(id < 0)
    {
        perror("fork error");
        return -1;
    }
    
    else if(id == 0)
    {    
        printf("child_pid:[%d], g_val:%d, %p\n", getpid(), g_val, &g_val);
    }
    
    else
    { 
        printf("parent_pid:[%d], g_val:%d, %p\n", getpid(), g_val, &g_val);
    }
    sleep(1);
    return 0;
}

运行结果截图:
在这里插入图片描述
我们发现, 父子进程输出的 g_val 变量值和地址是一模一样的
接着将代码稍作改动

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    int g_val = 0;
    pid_t id = fork();
    if(id < 0)
    {
        perror("fork error");
        return -1;
    }
    
    else if(id == 0)
    { 
        //在子进程中修改变量值
        g_val=100;
        printf("child_pid:[%d], g_val:%d, %p\n", getpid(), g_val, &g_val);
    }

    else
    {
        //让父进程休眠,保证子进程先运行完毕
        sleep(3);
        printf("parent_pid:[%d], g_val:%d, %p\n", getpid(), g_val, &g_val);
    }
    sleep(1);
    return 0;
}

运行结果截图:
在这里插入图片描述
对比两次运行结果, 小问号, 你是否有很多朋友????
父子进程, 输出地址是一致的, 但是变量内容却不一样, 数据内容不同, 表示肯定没有使用同一块内存空间(一块内存空间不可能存储两个数据), 但地址相同又怎么解释

实际上进程中访问的地址都是虚拟地址, 而我们所说的程序地址空间实际上是一个进程的虚拟地址空间;
虚拟地址空间其实就是一个 mm_struct 结构体, 是一个对内存空间的描述, 通过这个描述向进程虚拟出一个完整的,连续的内存空间
同一个变量, 地址相同, 其实是虚拟地址相同, 内容不同其实是被映射到了不同的物理地址!

如何通过虚拟地址访问物理内存

操作系统为进程创建mm_struct虚拟地址空间的同时, 也创建了一个页表用于映射虚拟地址与物理地址的关系, 进程使用虚拟地址空间, 通过页表映射物理内存, 可以实现进程中数据在物理内存上的离散式存储, 通过这种方式提高了内存利用率
每个进程都有一个虚拟地址空间, 有一个页表, 并且通过页表可以实现内存访问控制, 进一步提高了进程的独立性
进程的独立性: 一个进程运行出问题, 其他的进程并不受影响

页表如何实现通过虚拟地址访问物理地址? — MMU

内存管理方式: 分页式内存管理/分段式内存管理/段页式内存管理
分页式内存管理的虚拟地址组成:

页号 + 页内偏移
页号: 页表中页表项的编号
页内偏移: 具体一个变量首地址相较于内存页起始位置的偏移量
物理内存块号 * 物理内存块大小 + 虚拟地址中的页内偏移 = 物理地址

假设内存大小4G, 页大小4096字节, 意味着页号所占的位是2^20
内存大小 / 页大小 = 页号所占位
虚拟地址的高20位都是页号, 低12位就是页内偏移(32位操作系统, 寻址空间大小总共就是32位)
分页式内存管理的优点: 将物理内存进行分块管理, 通过页表映射实现数据在物理内存上的离散式存储, 提高内存利用率

分段式内存管理:

地址组成:段号 + 段内偏移 / 段表 <段号 物理段起始地址>
优点: 使程序员对内存的管理更加方便, 将内存空间分为了代码段, 初始化全局段…; 不同变量在各自的段申请地址

段页式内存管理:

地址组成: 段号 + 段内页号 + 页内偏移 段表 + 段内页表(在每一个分段内, 又采用分页式管理)
每一个分段都有一个页表, 通过地址中的段号, 找到段表项, 通过段表项中段内页表起始地址找到自己的页表, 再通过地址中的段内页号
在这个页表中找到页表项, 通过页表项中的物理块号 + 页内偏移得到最终的物理地址

缺页中断: 每个进程的页表中记录了每一个虚拟地址对应的物理地址, 如果某个虚拟页面的物理内存中的数据被交换出去了, 保存到交换分区, 则将这个页表项置为缺页中断, 等待下次这个进程要访问这个被交换出去的数据的时候, 触发缺页中断, 重新从交换分区将数据交换回来

什么样的数据在内存不够用时会被交换出去??
LRU算法: 一种内存置换算法, 最久未使用 — 内存不够用时会将内存中最久未使用的数据拷贝到交换分区上, 腾出内存空间

编译器在编译程序的时候就会为每一个指令以及数据进行地址的分配, 一个程序使用哪些地址在编译链接完成后就已经定下了, 如果直接使用物理内存, 有可能造成冲突

进程直接访问物理内存:
1.进程中代码数据的使用都是连续的地址, 若直接使用连续的物理内存会造成内存的浪费
2.直接访问物理内存会因为缺乏内存访问控制导致进程的不安全

进程优先权

进程的优先权就是指cpu资源分配的先后顺序,优先权高的进程有优先执行权利
查看系统进程:

ps -l
UID: 代表执行者的身份
PID: 代表当前进程的代号
PPID:代表当前进程的父进程代号
PRI: 代表当前进程可被执行的优先级,PRI值越小优先级越高

在这里插入图片描述

进程性质

竞争性: 系统进程数目众多, 而CPU资源有限, 所以进程之间是具有竞争性的, 为了高效完成任务, 更合理使用资源, 便具有了优先级
独立性: 多进程运行, 需要独享各种资源, 进程间互不干扰影响
并行: 多个进程在CPU下同时运行
并发: 多个进程在CPU下采用进程切换的方式, 在一段时间之内, 让多个进程都得以推进

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值