进程的地址空间

一、写个代码见一见地址空间

1、问题

在代码中我们在第五秒时会在子进程中改变全局变量 g_val 但是我们发现了一个奇怪的现象:在子进程中改变 g_val ,由于进程的独立性,所以子进程和父进程的值不一样是可以理解的,但是为什么变量的地址也是相同的?

这是因为程序中的各种数据其实是存放在虚拟地址中的,虚拟地址是一样的,但是实际上物理内存中的地址已经是不一样的。

所以地址空间也要被操作系统控制,也是内核数据结构,页表也是。

创建子进程时会把父进程的大部分内核数据都拷贝(浅拷贝)一份。

2、举例

那我们如果要改子进程中的数据怎么办呢?

为保证进程独立性,父进程数据不能修改,这就要发生写时拷贝,操作系统会另外开辟一段空间,把数据拷贝供子进程修改,这时候子进程的页表的实际地址就要发生改变,而虚拟地址不变,也就解释了开头的问题。

3、细节

(1)若父子进程不写入数据,未来的全局变量就默认被父子进程共享,代码共享。

(2)为什么不是深拷贝,而是浅拷贝,其实很多时候数据在父子进程中不会被修改,若都是深拷贝就会有很多空间的浪费。

二、理解地址空间

1、Linux中的地址空间

地址空间本质上就是struct结构体,里面定义很多属性表示边界的 start, end

看到上图的Linux源码中地址空间是 mm_struct

2、为什么要有虚拟地址空间

(1)连续地访问区域

将在物理内存中存放无序的数据变成在虚拟地址中存放有序的数据,让进程以统一的视角看待物理内存和自己运行的区域。以统一的,线性的方式连续访问各个区域。

(2)进程管理模块和内存管理模块的解耦

具体实现:你进程要空间?可以,你在你的页表填虚拟地址,但如果你不是马上要用空间,我就不先给你申请物理内存,等你要用了我再给你申请。

这样就可以提高运行效率。

(3)拦截非法请求

如果访问了非法地址,在操作系统查看页表时就可以拦截非法请求,保护了物理内存。

三、理解页表和写时拷贝

(1)我们提到过,当进程挂起时他的数据和代码会被唤出到磁盘中,对应的就是页表中某一个选项为0,表示物理内存中没有该数据了。

(2)页表中也存有地址空间的权限,例如常量区不可被写入,所以代码cahr* str = "hello"; *str = 'h';是不对的,就是在数据写入时操作系统发现了错误。

(3)操作系统发生错误的三种情况

a、是不是数据不在物理内存?这就是缺页中断。

b、是不是要写时拷贝?

c、都不是那就报错

四、理解虚拟地址

首先程序中就有地址。例如main函数中调用其他函数就需要函数地址,这就是虚拟地址,也叫逻辑地址。

所以地址空间和页表左侧的虚拟地址是直接从程序中读就可以,称为平坦模式。

所以为什么fork()函数返回两个id值,实质就是创建子进程时发生写时拷贝,对id进行写入,导致id值不同但是虚拟地址相同。

五、Linux2.6内核进程调度队列

在Linux中有nice值 [-20, 19] 的存在,优先级范围是 [60, 99] 共40个。

在操作系统的 runqueue 中存在一个queue[140] 存放 task_struct

而40个优先级对应的下标就是100~139

当有进程的优先级重复时就会被链入链表中。

runqueue中还有 bitmap[5] 的位图来表示[0, 139]中是否有进程,这样查找特定进程时间复杂度就是O(1)

*active指针指向的queue进程只出不进,*expired指针指向的queue进程只进不出,当操作系统遍历bitmap时发现活跃队列中的进程已经全部出队就会进行swap(active, expired)此时活跃队列中进程就可以再次只出不进,以此来完成进程的调度。这就是进程调度O(1)算法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值