0
点赞
收藏
分享

微信扫一扫

Linux 进程概念 (下)

陆佃 2022-06-02 阅读 105

                       

进程概念完了吗?不,还没有!今天博主就来和大家接着学习后半部分的概念~

今天博主主要讲优先级、环境变量、进程地址空间。尤其是进程地址空间的知识,这是我们学习操作系统由量变到质变的一个重要的小转折!

目录

进程优先级

基本概念

查看系统进程

 PRI and NI

PRI vs NI

用top命令更改已存在进程的nice

注意

其他补充概念

环境变量

基本概念

常见的环境变量

PATH

HOME

SHELL

 和环境变量相关的命令

环境变量的组织方式

通过代码获取环境变量

命令行第三个参数

 通过第三方变量environ获取

通过系统调用获取或设置环境变量

环境变量通常是具有全局属性的

总结

补充

 进程地址空间

一个重要现象

什么是虚拟地址?

虚拟地址和PCB的关系

struct mm_struct里面描述什么? 

进一步理解虚拟地址

进程地址空间有什么用?

作用一

作用二

作用三

总结

回顾一下


进程优先级

基本概念

为什么要有优先级?

查看系统进程

在linux或者unix系统中,用ps –l或ps -al命令来查看进程优先级:

[cyq@VM-0-7-centos test]$ ps -al

我们来观察一下:

我们发现会出现几个重要的名词信息:

 PRI and NI

PRI vs NI

有了上面概念理解,我们有几个问题:

为什么要有NI值,我们,要想修改PRI直接去修改PRI不就好了吗?为啥还要通过修改NI值来间接修改优先级呢?

nice值为什么是一个相对比较小的范围呢?

用top命令更改已存在进程的nice

我们先写一个死循环的C程序(这样可以得到它的pid来修改nice值):

int main()
{
  while(1);
  return 0;
}

我们运行程序后,先获取对应进程的pid:

 接着我们输入指令top,打开任务管理器:

然后我们再按r(不要按回车): 

这时候出现了一个让我们输入进程pid的命令行,我们把对应pid输入进去:

 

输入对应进程pid按下回车键之后,会出现让我们输入nice值的命令行: 

[cyq@VM-0-7-centos test]$ ps -al

注意

其他补充概念

环境变量

基本概念

常见的环境变量

我们先思考一个问题:为什么我们在运行自己的可执行程序时要加上./来指定路径,而执行系统的命令不需要加./呢?

实际上这和环境变量(PATH)有关。

PATH

我们来查看一下,系统中默认的搜索路径:

[cyq@VM-0-7-centos test]$ echo $PATH //注意,这里一定要加$

那我们怎么去把我们自己的可执行程序加到环境变量中?

方法一:把自己的可执行程序路径拷贝到系统文件中

方法二:使用export设置一个新的环境变量,仅在本次登录有效。

步骤一:我们先使用pwd指令把当前工作目录的路径显示出来

 步骤二:我们使用export命令把当前工作目录路径拷贝进去

[cyq@VM-0-7-centos test]$ export PATH=$PATH:/home/cyq/practice/test

这时候我们可以查看我们是否导入成功了:

 这时候我们就可以不加./来运行一下该目录下的可执行文件:

注意:

注意步骤二:export PATH=$PATH:hello程序所在路径,其中$一定要加,否则这时候环境变量就乱了,我们举个栗子:

HOME

我们来举个栗子:

[cyq@VM-0-7-centos test]$ echo $HOME

比如我们换几个用户身份来观察一下:

 

 

SHELL

我们来查看一下:

[cyq@VM-0-7-centos test]$ echo $SHELL

 和环境变量相关的命令

上面的命令博主就不演示了,但是老铁们要下去试试来感受一下哦~

环境变量的组织方式

通过代码获取环境变量

命令行第三个参数

我们介绍第三个参数前,也顺便先介绍前两个参数。

我们用代码来演示一下:

int main(int argc, int* argv[])
{
  for(int i = 0; i < argc; i++)
  {
      printf("argv[%d]-> %s\n", i ,argv[i]);
  }
  return 0;
}

运行一下:

好了,介绍完前两个参数后,我们继续。

代码:

int main(int argc, int* argv[], int* env[])
{
    for(int i = 0; env[i]; i++) //env为NULL就结束条件
    {
      printf("%s\n",env[i]);
    }
    return 0;
}

运行一下(部分截图):

 通过第三方变量environ获取

我们在上面的环境变量的组织方式中可以发现environ指向指针数组的第一个指针。我们通过man手册来了解一下:

[cyq@VM-0-7-centos test]$ man environ

代码演示:

int main()
{
  extern char** environ;
  for(int i = 0; environ[i]; i++)
  {
    printf("%s\n", environ[i]);
  }
  return 0;
}

运行结果(部分截图):

通过系统调用获取或设置环境变量

我们通过man手册来查看一下getenv:

举个栗子:

int main()
{
  printf("%s\n",getenv("PATH"));
  printf("%s\n",getenv("HOME"));
  return 0;
}

运行结果:

环境变量通常是具有全局属性的

补充知识,先做个小实验:

[cyq@VM-0-7-centos test]$ env_string=wmm
[cyq@VM-0-7-centos test]$ echo $env_string
wmm

我们先写一个不fork的程序,看看它的父进程是谁:

代码测试:

int main()
{
  printf("i am process pid: %d  ppid: %d\n", getpid(),getppid());
  return 0;
}

测试结果:

      假设一个场景,证明环境变量具有全局属性:我们写一个代码,打印环境变量(我们手动导入的)中的一个字符串。对比一下把这个字符串导入到env前后,程序的运行结果。

测试代码:

int main()
{
  printf("my_env_string: %s\n", getenv("my_env_string"));
  return 0;
}

我们先在本地变量设置一个字符串:

[cyq@VM-0-7-centos test]$ my_env_string="wmmandcyq"

        这时候我们发现my_env_string字符串在本地变量中,环境变量中还没有,这时候,我们运行程序,看一下打印my_env_string的情况: 

当我们把my_env_string导入环境变量后再来打印:

总结

补充

了解一下即可:

1、系统的环境变量在~/.bash_profile 系统默认执行的环境变量。

[cyq@VM-0-7-centos test]$ vim ~/.bash_profile

2、我们发现上面的环境变量会调用另一个文件,我们来看一下~/.bashrc下的内容 

[cyq@VM-0-7-centos test]$ vim ~/.bashrc

 3、/etc/bashrc配置文件,获取各种环境变量信息

[cyq@VM-0-7-centos test]$ vim /etc/bashrc

部分截图:

 进程地址空间

一个重要现象

在这里博主讲的内容是32位平台的~

我们先来回顾一下曾经学习的空间分布图:

                                  

 我们先抛出一个问题,我们曾经写的代码定义的变量打印的地址是物理地址吗??

先不多说,来段代码演示一个现象:

int g_val = 100;
int main()
{
  pid_t fd = fork();
  if(fd == 0)
  {
    //child
    int count = 5;
    while(count)
    {
      printf("i am child, &g_val = %p, g_val = %d\n", &g_val, g_val);
      sleep(1);
      count--;
      if(count == 2)
      {
        printf("##########child 修改ing##############\n");
        g_val = 200;
        printf("##########child 修改done#############\n");
      }
    }
  }
  else if(fd > 0)
  {
      //parent
      while(1)
      {
        printf("i am parent, &g_val = %p, g_val = %d\n", &g_val, g_val);
        sleep(1);
      }
  }
  return 0;
}

 我们看一下运行结果:

那么,我们打印的地址是什么地址的??答案是虚拟地址!

什么是虚拟地址?

虚拟地址和PCB的关系

struct mm_struct里面描述什么? 

进一步理解虚拟地址

我们运行代码,实际上还是要找到物理内存上的实际代码,可是PCB看向的是虚拟地址,那么CPU调度PCB时,怎么能找到对应的物理地址上的代码和数据呢?答案是:页表映射

进程地址空间有什么用?

作用一

老铁们会不会这么想,让PCB直接指向物理内存不就行了吗?为什么还要在中间添加虚拟地址、页表,这样不是好麻烦? 

所以有了虚拟地址之后,由有了页表,我们每个段地址通过页表映射时,OS会检查一定的权限,检查该进程做法是否合理。

作用二

作用三

总结

回顾一下

       最后我们就能解释最开始的演示现象了,子进程继承了父进程的大部分PCB数据,所以g_val的地址也是一样的,当子进程的g_val值被修改后,在物理内存中发生了写时拷贝!虚拟地址中没有变化,所以最后出现了打印变量的地址一样,但是打印出来变量的值不一样的结果。这个打印的地址就是虚拟地址!

写时拷贝前:

写时拷贝后: 

看到这里,支持博主一下吧~

举报

相关推荐

0 条评论