渣渣兔 发表于 2025-4-3 08:20:26

操作系统 :进程概念

操作系统 :进程概念

弁言

理解进程的概念不但有助于我们掌握操作系统的工作原理,还能为后续学习多任务处置处罚、并发编程、内存管理等高级主题奠定结实的基础。本文将深入探讨进程的方方面面,包括其根本概念、状态管理、优先级调度、地址空间等焦点内容,并联合实际示例和代码分析,帮助读者构建完备的知识体系。


1. 冯诺依曼体系布局

https://i-blog.csdnimg.cn/direct/5c26f298135748bbb77d51ab3d6be419.png
关于冯诺依曼必须要强调的几点:


[*] 这里的存储器指的是内存。
[*] 不考虑缓存的环境,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备)。
[*] 外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存中读取。
[*] 总结:全部设备都只能直接和内存进行交互。
[*] 软件运行,必须先加载(CPU执行加载到的代码,访问加载到的数据),而数据是从一个设备“拷贝”到另一个设备的。因此体系布局的效率是由设备的“拷贝”效率决定的。
[*] 步伐运行之前,在磁盘中加载文件。
2. 操作系统概念及定位



[*]概念
操作系统是任何计算机系统都包含的根本步伐聚集,操作系统也是一款进行软硬件管理的软件。
https://i-blog.csdnimg.cn/direct/83b6d7bb3936464cbfdd659bde7a5ba1.png


[*]设计OS的目的
对上,为用户步伐提供一个良好的执行环境。
https://i-blog.csdnimg.cn/direct/df07f7b642de4dfd97b535fd335e31e0.png
对下,与硬件交互,管理全部的软硬件资源(手段)
小知识:
   

[*]访问操作系统必须利用系统调用,而系统调用其实就是系统提供的函数。
[*]只要步伐访问了硬件,那么它必须贯穿整个软硬件体系布局。
[*]我们所相识的库在底层封装了系统调用。
管理:
   管理者和被管理者不见面,管理者是根据有中心层获取的“数据”进行管理。
管理方式就是“先描述在组织”——描述被管理的对象,组织被管理对象。
举例:校长管理门生就可以转化为校长对于execl表格中数据的管理。


[*] 系统调用和库函数概念
       在开辟角度,操作系统对外会体现为一个团体,但是会袒露自己的部分接口,供上层开辟利用,这部由操作系统提供的接口,叫做系统调用。可以理解为操作系统要向上提供对应的服务,但是不相信任何用户。
    系统调用在利用上,功能比力基础,对用户的要求相对也比力高,以是,故意的开辟者可以对部分系统调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开辟者进行二次开辟。
3. 进程

3.1 进程的根本概念与操作

https://i-blog.csdnimg.cn/direct/576a5f25bb024a5c9e61272766d64335.png


[*] test_struct

[*] 标示符: 描述本进程的唯一标示符,用来区别其他进程。(pid)
[*] 状态: 任务状态,退出代码,退出信号等。
[*] 优先级: 相对于其他进程的优先级。
[*] 步伐计数器: 步伐中即将被执行的下一条指令的地址。
[*] 内存指针: 包括步伐代码和进程相干数据的指针,还有和其他进程共享的内存块的指针
[*] 上下文数据: 进程执行时处置处罚器的寄存器中的数据。
[*] I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程利用的文件列表。
[*] 记账信息: 可能包括处置处罚器时间总和,利用的时钟数总和,时间限制,记账号等。
[*] 其他信息
   
[*] 进程的组织情势
       在内核源代码中我们以可以看到,全部运行在系统里的进程都是以test_struct链表的情势存在内核里的。
    https://i-blog.csdnimg.cn/direct/8a1095622e5b42f1ad9825430277f867.png
[*] 查看进程

[*] 进程的信息可以通过/proc系统文件夹查看。如:要获取PID为21381的进程信息,你必要查看 /proc/21381 这个文件夹。https://i-blog.csdnimg.cn/direct/40a2b62d0d9b4915b6fafbf4614b0328.png
我们可以看到进程信息中有一个cwd(current work dir),其作用是进程会记载下来自己的当前路径,这也就印证了为什么我们在C语言中fopen时,既可以利用绝对路径,也可以直接利用文件名。
[*] 大多数进程信息同样也可以利用top,ps这些用户级工具来获取。
https://i-blog.csdnimg.cn/direct/7c6c95ccee0f4263b524f4710fdcbf1e.png
ps的参数选项:

[*]a:显⽰⼀个终端全部的进程,包括其他⽤⼾的进程。
[*]x:显⽰没有控制终端的进程,例如背景运行的守护进程。
[*]j:显⽰进程归属的进程组ID、会话ID、父进程ID,以及与作业控制相干的信息。
[*]u:以⽤⼾为中心的格式显示进程信息,提供进程的详细信息,如⽤⼾、CPU和内存使⽤环境等。
      
[*] 通过系统调用获取进程标识符
进程id:pid;父进程id:ppid
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
        printf("pid: %d\n", getpid());
        printf("ppid: %d\n", getppid());
        return 0;
}

   
3.2 进程的状态



[*] kernel源代码中的进程状态
       /*
*The task state array is a strange "bitmap" of
*reasons to sleep. Thus "running" is zero, and
*you can test for combinations of others with
*simple bit tests.
*/
static const char *const task_state_array[] = {
        "R (running)", /*0 */               
        "S (sleeping)", /*1 */                //可中断休眠,浅睡眠
        "D (disk sleep)", /*2 */        //不可中断睡眠,深度睡眠(磁盘)
        "T (stopped)", /*4 */                //进程出错,OS强制暂停
        "t (tracing stop)", /*8 */        //被debug断点,将进程暂停
        "X (dead)", /*16 */                        //释放PCB的进程
        "Z (zombie)", /*32 */                //为了获取退出信息,而未释放PCB的进程
};


[*]R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
[*]S睡眠状态(sleeping): 意味着进程在等候事件完成(这里的睡眠有时候也叫做可中断睡眠(interruptible sleep))。
[*]D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等候IO的竣事。
[*]T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
[*]X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
    https://i-blog.csdnimg.cn/direct/dd21556b26054db9bc151f3d6b517474.png
    运行:进程在调度队列中,进程的状态都是running。
    壅闭:等候某种设备或者资源就绪(键盘,显示器,网卡,磁盘,摄像头,话筒…)。
    进程状态的变化体现之一,就是要在差别的队列中进行活动,本质都是数据布局的增删查改。
   
[*] 僵尸进程
       僵死状态(Zombies)是一个比力特殊的状态。当进程退出而且父进程没有读取到子进程退出的返回代码时就会产生僵尸进程。
    僵死进程会以终止状态保持在进程表中,而且会一直在等候父进程读取退出状态代码。
    以是,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态。
[*] 僵尸进程的危害——内存泄露
       进程的退出状态必须被维持下去,因为他要告诉父进程,你交给我的任务,我办的怎么样了。可父进程如果一直不读取,子进程就会一直处于Z状态。
    维护退出状态本身就是要用数据维护,也属于进程根本信息,以是生存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB就一直都要维护。
    如果一个父进程创建了许多子进程,就是不接纳,就会造成内存资源的浪费。因为数据布局对象本身就要占用内存。
[*] 知识点
       进程退出了,内存泄漏问题就不存在了。
    常驻内存的进程所具有的内存泄漏问题,是比力麻烦的。
    内核布局申请中,存在数据布局对象的缓存,是指进程竣事后的PCB从调度队列转为垃圾队列中,等候下一次的废物利用。
[*] 孤儿进程
       在父子进程关系中,如果父进程先退出,此时子进程就被称为“孤儿进程”。
    孤儿进程会被1号进程(暂且理解为操作系统)领养,防止孤儿进程进一步酿成僵尸进程从而导致内存泄露。
    被领养的孤儿进程会酿成背景进程,因此利用Ctrl + c是无法竣事进程的,且背景进程可以向前台打印消息。
3.3 进程的优先级



[*] 概念
       cpu资源分配的先后序次,就是指进程的优先权(priority),优先权可以办理目的资源短缺必要合理分配的问题。
    优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。
    优先权也是一种值,值越低,优先级越高。
    还可以把进程运行到指定的CPU上,这样一来,把不紧张的进程安排到某个CPU,可以大大改善系统团体性能。
    Linux是一种基于时间片的分时操作系统,考虑公平性,优先级可能变化,但是变化的幅度不能太大。
[*] 查看系统进程
       在Linux系统中,用ps -l查看系统进程。
    https://i-blog.csdnimg.cn/direct/0673af5a7c0748c48ca8e9e061788d73.png

[*]UID : 代表执行者的身份user id,在Linux中访问任何资源都是进程访问,进程就代表用户。因此系统通过比力UID和文件所属id,就能知道用户是拥有者照旧所属组亦或是other。
[*]PID : 代表这个进程的代号。
[*]PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号。
[*]PRI :代表这个进程可被执行的优先级,其值越小越早被执行。(默认值为80)
[*]NI :代表这个进程的nice值,其是进程优先级的修正数值(用来更改PRI的)。
   
[*] PRI & NI
       进程的真实优先级 = PRI(默以为80)+ NI
    优先级的极值:如果优先级设置的不合理,会导致优先级低的进程,长时间得不到CPU资源,进而导致进程饥饿。因此nice值只能取到[-20 , 19],那么进程的优先级范围就是共40个品级。
    用top下令更改已经存在进程的nice值:输入top->按r->输入进程PID->输入要修改的nice值。
[*] 竞争、独立、并行、并发
       竞争性: 系统进程数量众多,而CPU资源只有少量,乃至1个,以是进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相干资源,便具有了优先级。
    独立性: 多进程运行,必要独享各种资源,多进程运行期间互不干扰。
    并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行。
    并发: 多个进程在一个CPU下接纳进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发。
    https://i-blog.csdnimg.cn/direct/417c00e8071040368653866d67432a04.png
3.4 进程的切换

CPU上下文切换:其实际寄义是任务切换, 或者CPU寄存器切换。当多任务内核决定运行另外的任务时, 它生存正在运行任务的当前状态, 也就是CPU寄存器(CPU内部的临时空间)中的全部内容。这些内容被生存在任务自己的堆栈中, 入栈工作完成后就把下一个将要运行的任务的当前状况从该任务的栈中重新装⼊CPU寄存器,并开始下⼀个任务的运行, 这⼀过程就是context switch。
https://i-blog.csdnimg.cn/direct/744098f259894efdb76368e0ce417940.png
死循环进程不会使系统瓦解,因为时间片进程不会一直占据CPU去运行。
进程自己的上下文数据是生存在test_struct中的TSS:任务状态段。


[*] Linux真实调度算法:O(1)调度算法

[*] Linux2.6内核中进程队列的数据布局runqueue—— 一个CPU拥有一个runqueue
https://i-blog.csdnimg.cn/direct/82e66188b79942dcb2d322ba8fa08850.png

[*] 时间片还没有竣事的全部进程都按照优先级放在活动队列
[*] nr_active: 记载统共有多少个运行状态的进程
[*] queue: 一个元素就是一个进程队列,雷同优先级的进程按照FIFO规则进行排队调度,以是,数组下标就是优先级!是实时优先级我们不必要关心,只需关注这40个普通优先级即可。
[*] 从该布局中,选择⼀个最符合的进程,过程如下:

[*]从0下表开始遍历queue
[*]找到第⼀个非空队列,该队列肯定为优先级最⾼的队列
[*]拿到选中队列的第⼀个进程,开始运行,调度完成!

[*] bitmap:遍历queue时间复杂度固然是常数但照旧太低效了,因此引入bitmap。⼀共140个优先级,⼀共140个进程队列,为了提⾼查找⾮空队列的效率,就可以⽤5*32个比特位表示队列是否为空,这样便可以大大进步查找效率!
                   https://i-blog.csdnimg.cn/direct/b85d483b63594030a221b1f66162ab00.png
[*] 过期进程的queue和活泼进程布局千篇一律,只不过过期队列上放置的进程都是时间片耗尽的进程。当活动队列的进程都被处置处罚完毕之后,对过期队列的进程进行时间片重新计算。
[*] *active指针永久指向活动队列;*expired指针永久指向过期队列;由于活动队列上的进程因为时间片的耗尽会在过期队列越积越多,而活动队列的进程会越来越少。这时就必要*active指针和*expired指针交换一下指向的内容即可。
      
[*] 总结:在系统当中查找⼀个最符合调度的进程的时间复杂度是⼀个常数,不随着进程增多而导致时间本钱增长,我们称之为进程调度O(1)算法!
   
4. 环境变量

4.1 根本概念



[*]环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数。
[*]如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照旧可以链接成功,生成可执行步伐,缘故原由就是有相干环境变量帮助编译器进行查找。
[*]环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性,其意味着可以被子进程继承。而本地变量不会被子进程继承,只在bash内部被利用。
4.2 常见的环境变量



[*]PATH:指定下令的搜刮路径
[*]HOME:指定用户的主工作目次(即用户登陆到Linux系统中时,默认的目次)
[*]SHELL:当前Shell,它的值通常是/bin/bash。
[*]USER:指定当前登录的用户名。
[*]LOGNAME:指定当前登录的用户名,但更方向于 系统登录记载 的原始用户名。因此即利用户通过 su 或 sudo 切换身份,LOGNAME 仍保持最初的登录用户名。
[*]PWD:指定当前工作目次的路径(由 Shell 动态更新)。
[*]OLDPWD:指定上一次所在的工作目次路径(可通过 cd - 切换回去)。
[*]HOSTNAME:指定当前系统的主机名(hostname),用于标识网络中的计算机。影响终端提示符、网络通信(如 SSH)、系统日志等场景。
[*]HISTSIZE:指定 Shell 汗青记载(history 下令)在 内存中 生存的最大下令条数。
4.3 关于环境变量的下令



[*]echo:显示某个环境变量值。如echo $VAR_NAME
[*]export:设置一个新的环境变量,仅对当前 Shell 及其子进程有效,关闭终端后失效。如export VAR_NAME=value
[*]env:显示全部环境变量。
[*]unset:删除环境变量。如unset VAR_NAME
[*]set:显示全部变量(包括环境变量和Shell本地变量)。
[*]内建下令built-in command:不必要创建子进程,而是让bash自己调用函数或是通过系统调用执行。
4.4 通过代码获取环境变量



[*] main函数的第三个参数
       #include <stdio.h>
int main(int argc, char *argv[], char *env[])
{
        int i = 0;
        for(; env; i++){
                printf("%s\n", env);
        }
        return 0;
}

[*] 通过第三方变量environ获取
       #include <stdio.h>
int main(int argc, char *argv[])
{
        extern char **environ;//libc中定义的全局变量environ指向环境变量表,
                                            //environ没有包含在任何头⽂件中,所以在使⽤时 要⽤extern声明。
        int i = 0;
        for(; environ; i++){
                printf("%s\n", environ);
        }
        return 0;
}

[*] 通过系统调用获取
       #include <stdio.h>
#include <stdlib.h>
int main()
{
        printf("%s\n", getenv("PATH"));        //可以用来实现一个加密的小程序
        return 0;
}

4.5 bash的两张表



[*] 每有一个用户登录就会有一个bash,而bash内部会存在两张表。第一张表是下令行参数表char *argv[],第二张表是环境变量表char *env[]。
[*] 下令行参数表是bash对于输入的下令进行的切分,可以用来分析和实现步伐差别子功能(下令参数选项)的方法。
[*] 环境变量表是bash最开始从系统的相干配置文件中拷贝过来的,用来帮助用户和操作系统的运行。其是⼀个字符指针数组,每个指针指向⼀个以’\0’末端的环境字符串。
       https://i-blog.csdnimg.cn/direct/2bf6ffa9ba6543798a31db46a77ba26c.png
5. 步伐地址空间

步伐地址空间并不是真正的内存
5.1 虚拟地址

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int g_val = 0;
int main()
{
        pid_t id = fork();
        if(id < 0){
                perror("fork");
                return 0;
        }
        else if(id == 0){ //child,⼦进程肯定先跑完,也就是⼦进程先修改,完成之后,⽗进程再读取
                g_val=100;
                printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
        }else{ //parent
                sleep(3);
                printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
        }
        sleep(1);
        return 0;
}
输出结果:
child: 100 : 0x80497e8
parent: 0 : 0x80497e8
根据上边的代码我们可以看出,父子进程的输出地址是一致的,但是变量内容却不一样。
结论:
   

[*]变量内容不一样,以是父子进程输出的变量绝对不是同一个变量。
[*]但地址值是一样的,说明,该地址绝对不是物理地址!
[*]在Linux地址下,这种地址叫做 虚拟地址。虚拟地址可以雷同,但物理地址一定差别。
[*]我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理。
[*]OS必须负责将虚拟地址转换成物理地址。
5.2 进程地址空间(紧张)

https://i-blog.csdnimg.cn/direct/601bc2ecdf7346d7be3794316aca4319.jpeg#pic_center


[*]一个进程就会有一个虚拟地址空间(进程地址空间);一个进程就有一套页表,而页表是用来做虚拟地址和物理地址映射的。页表中其实还有一列存储的是内容的访问权限——在地址转换过程中,对你的地址和操作进行合法性判定,进而保护物理内存。
[*]虚拟地址空间本质就是一个数据布局。
[*]其初级作用是为了让每个进程都以为自己有4GB的物理内存,或是让每个进程都以为自己在独占物理内存。
[*]地区划分:对整个虚拟地址空间进行编址,此时地址空间便有了一个个地址。然后只必要记载每块地区的开始地址和竣事地址即可。
5.3 虚拟内存管理第一讲

描述Linux下进程的地址空间的全部的信息的布局体是 mm_struct(内存描述符)。每个进程只有⼀个mm_struct布局,在每个进程的task_struct布局中,有⼀个指向该进程的布局。
struct task_struct
{
        /*...*/
        struct mm_struct *mm; //对于普通的⽤⼾进程来说该字段指向他的虚拟地址空间的⽤⼾空间部分,对于内核线程来说这部分为NULL。
        struct mm_struct *active_mm; // 该字段是内核线程使⽤的。当该进程是内核线程时,它的mm字段为NULL,表⽰没有内存地址空间,可也并不是真正的没有,这是因为所有进程关于内核的映射都是⼀样的,内核线程可以使⽤任意进程的地址空间。
        /*...*/
}
可以说,mm_struct布局是对整个用户空间的描述。每⼀个进程都会有自己独立的mm_struct,这样每⼀个进程都会有⾃⼰独⽴的地址空间才气互不⼲扰。先来看看由task_struct到mm_struct,进程的地址空间的分布环境:
https://i-blog.csdnimg.cn/direct/16828f2bf3b745159a38317fa7cbc3ca.png
struct mm_struct
{
        /*...*/
        struct vm_area_struct *mmap; /* 指向虚拟区间(VMA)链表 */
        struct rb_root mm_rb; /* red_black树 */
        unsigned long task_size; /*具有该结构体的进程的虚拟地址空间的⼤⼩*/
        /*...*/
        // 代码段、数据段、堆栈段、参数段及环境段的起始和结束地址。
        unsigned long start_code, end_code, start_data, end_data;
        unsigned long start_brk, brk, start_stack;
        unsigned long arg_start, arg_end, env_start, env_end;
        /*...*/
}
那既然每⼀个进程都会有⾃⼰独⽴的mm_struct,操作系统肯定是要将这么多进程的mm_struct组织起来的!虚拟空间的组织⽅式有两种:

[*]当虚拟区较少时接纳单链表,由mmap指针指向这个链表;
[*]当虚拟区间多时接纳红⿊树进⾏管理,由mm_rb指向这棵树。
Linux内核利用 vm_area_struct 布局来表示一个独⽴的虚拟内存地区(VMA),由于每个差别质的虚拟内存地区功能和内部机制都差别,因此⼀个进程利用多个vm_area_struct布局来分别表示差别范例的虚拟内存地区。上面提到的两种组织⽅式使⽤的就是vm_area_struct布局来连接各个VMA,方便进程快速访问。
struct vm_area_struct {
        unsigned long vm_start; //虚存区起始
        unsigned long vm_end; //虚存区结束
        struct vm_area_struct *vm_next, *vm_prev; //前后指针
        struct rb_node vm_rb; //红⿊树中的位置
        unsigned long rb_subtree_gap;
        struct mm_struct *vm_mm; //所属的 mm_struct
        pgprot_t vm_page_prot;
        unsigned long vm_flags; //标志位
        struct {
                struct rb_node rb;
                unsigned long rb_subtree_last;
        } shared;
        struct list_head anon_vma_chain;
        struct anon_vma *anon_vma;
        const struct vm_operations_struct *vm_ops; //vma对应的实际操作
        unsigned long vm_pgoff; //⽂件映射偏移量
        struct file * vm_file; //映射的⽂件
        void * vm_private_data; //私有数据
        atomic_long_t swap_readahead_info;
        #ifndef CONFIG_MMU
        struct vm_region *vm_region; /* NOMMU mapping region */
        #endif
        #ifdef CONFIG_NUMA
        struct mempolicy *vm_policy; /* NUMA policy for the VMA */
        #endif
        struct vm_userfaultfd_ctx vm_userfaultfd_ctx;
} __randomize_layout;
https://i-blog.csdnimg.cn/direct/65e5ee18ddba4e10b5c42b1bf2cd4dda.png
https://i-blog.csdnimg.cn/direct/11a709f692dc44609147c8d38da01be0.png
5.4 为什么要有虚拟内存空间

亦或是如果步伐直接可以操作物理内存会造成什么问题?
   在早期的计算机中,要运行⼀个步伐,会把这些步伐全都装⼊内存,步伐都是直接运⾏在内存上的,也就是说步伐中访问的内存地址都是实际的物理内存地址。当计算机同时运行多个步伐时,必须包管这些步伐用到的内存总量要小于计算机实际物理内存的大小。
那当步伐同时运行多个步伐时,操作系统是怎样为这些步伐分配内存的呢?例如某台计算机总的内存大小是128M,现在同时运⾏两个步伐A和B,A需占⽤内存10M,B需占⽤内存110。计算机在给步伐分配内存时会接纳这样的⽅法:先将内存中的前10M分配给步伐A,接着再从内存中剩余的118M中划分出110M分配给步伐B。
这种分配方法可以包管步伐A和步伐B都能运行,但是这种简朴的内存分配计谋问题许多。
比如:
   

[*] 安全⻛险:
每个进程都可以访问恣意的内存空间,这也就意味着恣意⼀个进程都能够去读写系统相干内存地区,如果是⼀个木马病毒,那么他就能随意的修改内存空间,让设备直接瘫痪。
[*] 地址不确定:
众所周知,编译完成后的步伐是存放在硬盘上的,当运行的时候,必要将步伐搬到内存当中去运行,如果直接利用物理地址的话,我们无法确定内存现在利用到哪里了,也就是说拷⻉的实际内存地址每⼀次运⾏都是不确定的,⽐如:第一次执⾏a.out时候,内存当中⼀个进程都没有运行,以是搬移到内存地址是0x00000000,但是第二次的时候,内存已经有10个进程在运行了,那执⾏a.out的时候,内存地址就不⼀定了。
[*] 效率低下:
如果直接使⽤物理内存的话,⼀个进程就是作为⼀个团体(内存块)操作的,如果出现物理内存不够⽤的时候,我们⼀般的办法是将不常⽤的进程拷⻉到磁盘的交换分区中,好腾出内存,但是如果是物理地址的话,就必要将整个进程⼀起拷⾛,这样,在内存和磁盘之间拷贝时间太⻓,效率低。
    有了虚拟地址空间和分⻚机制就能办理了
   地址空间和⻚表是OS创建并维护的!也就意味着,凡是想利用地址空间和⻚表进⾏映射,也⼀定要在OS的羁系之下来进⾏访问!!也趁便保护了物理内存中的全部的合法数据 ,包括各个进程以及内核的相干有效数据。
因为有地址空间的存在和⻚表的映射的存在,我们的物理内存中可以对未来的数据进⾏恣意位置的加载!物理内存的分配 和 进程的管理就可以做到没有关系,进程管理模块和内存管理模块就完成相识耦合。
因为有地址空间的存在,以是我们在C、C++语⾔上new, malloc空间的时候,其实是在地址空间上申请的,物理内存甚⾄可以⼀个字节都不给你。而当你真正进⾏对物理地址空间访问的时候,才执⾏内存的相干管理算法,帮你申请内存,构建⻚表映射关系(延迟分配),这是由操作系统⾃动完成,用户包括进程完全零感知!!
因为页表的映射的存在,步伐在物理内存中理论上就可以恣意位置加载。它可以将地址空间上的虚拟地址和物理地址进⾏映射,在 进程视⻆全部的内存分布都可以是有序 的。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 操作系统 :进程概念