关于程序地址空间详解

本文重点:

1.环境变量;
2.程序地址空间;
3.进程优先级;

一.环境变量:

环境变量:指在操作系统中用来指定操作系统中的运行环境的一些参数;
1.常见的环境变量:

PWD:当前系统的工作路径;
LANG:当前系统的工作语言;
HOME:当前的家目录下;
PATH:以冒号间隔的路径;
SHELL:命令行解释器;
(PATH环境变量是一个重点;它的作用是指定一个可执行程序的默认搜索路径)

2.查看环境变量:env ; set ; echo;
(1)用env进行查看:在这里插入图片描述

在env查看时,我们可以看到所有的环境变量,以及每个环境变量下的内容;

(2)set可以查看环境变量以及所有常量;
(3)echo查看环境变量;(指定查看某个环境变量下的内容)

echo $+环境变量名
在这里插入图片描述

3.设置环境变量:export
4.删除环境变量:unset grep | 变量名
5.环境变量的继承特性:

通过程序来演示一个环境变量的获取演示:

//第一种方法:通过环境变量名来获取内容:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(){
   //接口:char * getenv(const char* name);通过环境变量来获取变量内容;
   char * ptr=getenv("PWD");
   printf("ptr: %s\n",ptr);
   return 0;
}
     

执行结果:
在这里插入图片描述

//第二种方法:获取所有环境变量;
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char * argv[],char* env[]){   
   int i;
   for(i=1;env[i]!=NULL;i++){
      printf("env[%d]=[%s]\n",i,env[i]);
   }
 
   return 0;
}

程序执行结果:
在这里插入图片描述

//第三种获取方法:获取所有环境变量----extern关键字;
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char * argv[],char* env[]){   
   int i;
   extern char** environ;         
   //标准库中environ是一个全局变量,该全局变量就是一个局部变量;
   //extern 是一个声明,声明environ是一个环境变量;
   
   for(;environ[i]!=NULL;++i){
      printf("env[%d]=[%s]\n",i,environ[i]);
   }
   
   return 0;
}

程序执行结果为:
在这里插入图片描述

总结:环境变量的特性(继承特性):

1.shell终端下所运行的进程能够获取所有的环境变量,但是获取不到普通变量;
2.在程序中获取环境变量:char * getenv(char*name)
3.声明全局变量:extern关键字-----》extern char** environ;

6.环境变量的全局特性:在子进程中获取继承于父进程的环境变量信息;

main(int argc,char * argv[],char *env[]) 参数获取
extern char **environ; 全局变量获取
char * getenv(const char *env_name) 接口获取

二.程序地址空间:(非常重要)

在这里插入图片描述
以上是一个内存空间示意图,也是一个程序地址空间的示意图-------虚拟内存空间;

1.什么是内存地址??

内存地址就是内存中的一个编号;

2.什么是程序地址空间??

程序地址空间即虚拟地址空间;
我们通过这段代码来进行验证:

#include <unistd.h>
#include <stdio.h>
int g_val=100;     //设置一个全局变量g_val为100;
int main(){
   printf("hello world ------pid:%d\n,getpid());
   pid_t pid=fork();    //创建一个进程;
   if(pid<0){
      printf("fork error\n");
   }
   else if(pid==0){
      g_val=200;     //当为子进程时输出200;
      printf("----i am child-----%d   g_val:%d-----%p\n,getpid(),g_val,&g_val);
   }
   else{
      sleep(1);
      printf("----i am parent----%d   g_val:%d-----%p\n,getpid(),g_val,&g_val);
   }

   printf("nihao~~  pid:%d\n",getpid());
   while(1)
      sleep(1);
   }
   
   return 0;
}

程序的输出结果为:
在这里插入图片描述
我们发现父子进程拥有相同的地址,但输出的值不一样;因此肯定用的不是一块相同的内存区域;
为什么不同的内存区域会有相同的地址呢??----这块内存区域不是一块物理内存地址;而是一块虚拟地址;

我们申请一块变量,给他分配一块内存地址,实际上是给他分配了一块虚拟内存地址;

3.虚拟内存地址空间该如何存储数据??

程序地址空间是一个虚拟地址空间 mm_struct的结构体;(内存描述符,该结构体描述了一个虚拟地址空间)

mm_struct
{
  long int size      (size 描述了当前的地址空间有多大)
//描述各个区域;
  long int code_start  (描述代码段的起始位置信息)
  long int code_end

  long int data_start  (描述数据段的起始位置信息) 
  long int data_end  
}
......
4.为什么需要使用虚拟地址空间??

每个程序都需要一块连续的内存地址空间;进程在通过访问虚拟地址进而获得变量数据,最终还是要去访问物理内存的,因为数据是存储在物理内存中的;

在虚拟地址和物理地址之间通过页表进行地址映射,转换得到物理地址,进而访问物理内存区域;通过映射之后的物理地址不一定连续,通过映射转换的方式,实现数据的离散存储,提高内存的利用率;

原理如下图所示:
在这里插入图片描述

5.写时拷贝技术:

父进程有一个虚拟地址空间,然后父进程fork()了一下创建了一个子进程,该子进程父进程完全一样;他们指向的位置是完全相同的,但当这块内存区域的数据发生改变的时候,为了互不影响,因此为子进程重新开辟了一个内存空间,更新页表的映射关系;-------------数据独有,代码共享
在这里插入图片描述
写时拷贝技术的优点:
(1)节省资源;
(2)提高子进程创建效率;

6.虚拟地址空间的作用:

(1)提高内存利用率;
(2)增加内存访问控制;
(3)保持进程独立性;

7.页表的功能:

(1)记录虚拟地址和物理地址之间的映射关系;
(2)并且对虚拟地址进行内存访问控制;

8.页表是如何将虚拟内存转换为物理内存地址的????

(1)分段式内存管理:
内存地址的构成:段起始地址(段号)+段内偏移;
段表:在操作系统中做了记录—这块内存分了多少段;

在段表中有很多的段表项,每一项都有一个段的起始地址(物理段的起始地址);通过段号找到物理段的起始地址,然后加上段内偏移就是段的物理地址;

总结:通过地址中的段号去段表找到段表项,通过段表项中的物理起始地址加上地址中的段内偏移获取物理地址;

(2)分页式内存管理:
内存地址的构成:页号+页内偏移;
内核中----页表(有很多页表项)—32位操作系统下有2^20个页表项;
页号在虚拟页号中找,然后找到对应的物理页号;物理页号+页内偏移----》内存中的物理地址;

总结:通过地址中的页号去页表中找到页表项,通过页表项中的物理页号加上页内偏移进而获取到物理地址;
在我们当前的虚拟地址空间中采用的是段页式内存管理:

我们的内存是通过分段式进行管理的,因为分段式更加易于编程;而在段内使用的是分页式;首先通过取地址得到段号,在段表中进行查找;在段表中有一个段内页表起始地址,找到段内页表起始地址就可以找到页表了;

段页式内存管理:内存地址:段号+段内页号+页内偏移;
段表项中有段内页表起始地址;段内页表项中又包含物理页号;

总结:通过段号在段表中找到段表项,通过段表项中的段内页表地址找到段内页表,通过地址中的段内页号在段内页表中找到页表项,通过页表项中的物理页号与页内偏移组成物理地址;
9.内置换算法:

实例:当一个操作系统的内存只有4G,但是要处理5G的数据,怎么办???

swap分区也叫交换内存:内存不够时,将内存中的数据置换到交换分区中,腾出内存处理数据;

如何来进行置换:三种方法
(1)FIFO:先进先出,核心原则:如果一个数据最先进入缓存中,则应该最早被淘汰;
(2)LFU:最近最少使用算法;基于访问次数;
(3)LRU:最近最久未使用算法;

三.进程优先级概念:

1.优先级的概念:

优先级就是决定资源优先分配权的等级划分;

2.设定进程优先级的好处:

(1)配置进程优先权对多任务环境的linux可以改善系统性能;
(2)可以把进程运行到指定的CPU上,把不重要的进程安排到某个CPU,可以大大改善系统整体性能;

因为在操作系统上多个进程完成的工作都不一样,为了使CPU处理数据更具有效率性,所以划分产生交互式进程和批处理进程;

3.关于进程优先级的几个重要信息:

我们通过进程查看可看到在第一行有一些信息:
在这里插入图片描述
UID : 代表执行者的身份;
PID : 代表这个进程的代号;
PPID :代表这个进程是由哪个进程发展衍生而来的,即父进程的代号;
PRI :代表这个进程可被执行的优先级,其值越小越早被执行;
NI :代表这个进程的nice值;

4.如何修改进程的优先级:

在Linux下调整进程的nice值就是调整进程的优先级;
PRI (优先级是一个数字,无法直接设置,可设置NI值,进而进行调整PRI);
NI (nice值,PRI无法直接设置,但可通过设置NI值,进而调整)
优先级的修改:PRI=PRI+NI;

5.关于优先级的几个概念:

并行:CPU资源足够多,多个进程可同时工作;

并发:CPU资源不够多,多个程序切换运行,同时推进;

独立性:进程间相互独立;

竞争性:系统进程数目众多,但CPU只有一个,所以进程之间就具有了竞争属性。为了高效完成任务,更合理竞争相关资源,便具有了优先级;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值