Linux探秘坊-------8.进程详解

1.概念详解

在这里插入图片描述

1.运行&&阻塞&&挂起

在这里插入图片描述
内容基础:方框中的就是调度队列,是一个 双向队列,每一个元素是PCB+其对应的代码数据

1.运行

只要进程 在调度队列中,进程的状态就是运行(running).

2.阻塞

阻塞:等待某种硬件资源就绪(例如网卡,键盘…)

在这里插入图片描述
在这里插入图片描述

  • 操作系统中不只有调度队列(runqueue),还有 设备队列(devicequeue) 用来管理设备。
  • 设备队列中还有等待队列(wait queue)

如果某一个进程在调度队列中,而调度其需要从键盘上读取数据,但键盘上没响应,所以cpu会把这个进程从调度队列中拿下来,并把其链接到对应硬件的等待队列中。这就是一个阻塞的过程
在这里插入图片描述
在这里插入图片描述

3.挂起

挂起的本质是--------将数据唤入或唤出到磁盘中的交换区(内存资源不足时)

在这里插入图片描述

内存资源不足时,如果进程处于阻塞状态,那么会将进程的数据交换到硬盘的swap分区,PCB依然保留,内存资源充足时,数据会从swap分区唤出和PCB重新组合为进程--------------------阻塞挂起

运行挂起同理。

2.PCB内核链表的理解

在这里插入图片描述
这是普通的双端队列,可以看到next指针是指向下一个元素的头,prev也是指向上一个元素的头。

在这里插入图片描述

  • PCB中的next是直接指向下一个元素的next,prevent也是直接指向上一个元素的prev。

在这里插入图片描述

一个PCB可以隶属于多个数据结构,可以属于调度队列,可以属于等待队列,也可以属于设备队列。所以有多个next,prev

2.进程状态

在这里插入图片描述

在这里插入图片描述

  • r表示running,表示正在运行
  • s表示浅睡眠,(例如输出一个字符只要1毫秒,但如果进程持续1(sleep(1))秒,那剩余的时间都处于浅睡眠时间,【可以被kill杀掉进程
  • d表示深度睡眠,这类状态基本与硬盘数据交换有关【不可被kill杀掉进程
  • Z表示僵尸状态,即子进程在运行完后不会直接消失,而是会先保留信息以留给父进程,这个状态就是僵尸状态信息保留在PCB中

1.僵尸进程

如果子进程结束,父进程还没结束,而且父进程还没有接收完子进程的信息,这个状态的子进程就是僵尸进程
在这里插入图片描述
在这里插入图片描述
那如果一直存在,那么子进程的PCB就一直不会消失,那么就会一直占用内存,可能会导致内存泄漏。

2.孤儿进程

如果子进程还没结束但父进程已经结束了,那么子进程就被称为孤儿进程。

在这里插入图片描述

  • 测试代码如上,可以看到id为0的是子进程,处于无限循环中,父进程则于5次循环后结束,可见五次循环后子进程就会变为孤儿进程
  • 但子进程不能没有父进程,所以这个子进程会被1号进程“领养”(一号进程会变为它的父进程)一号进程就是操作系统,如果不领养,子进程就会一直处于僵尸状态,导致内存泄漏

3.小知识

在这里插入图片描述

  • 进程退出了,内存泄漏的问题就不在了(例如**malloc,**进程结束后,申请的内存会被系统回收)
  • 常驻内存例如:window系统,一些软件(开机后一直运行都如此,会导致卡机)

BASH为一号进程。

4.进程优先级

1.是什么

本质是进程获得CPU资源的先后顺序(与权限不一样)

在这里插入图片描述

2.为什么

因为CPU资源是有限的,需要通过优先级来确认谁先谁后

3.怎么办

优先级也是一种数字

  • 值越小,优先级越高;反之,越低

4.查看优先级

在这里插入图片描述

  • 黄色方框中的“ps -al”用来显示进程优先级,绿色方框中的用于限制只显示 myprocess中的进程优先级
  • 蓝色方框中在这里插入图片描述

在这里插入图片描述

5.修改优先级

⽤top命令更改已存在进程的nice
在这里插入图片描述
注意:

在这里插入图片描述
PRI(显示)=PRI(默认)(80)+ NICE

5.补充概念-竞争、独⽴、并⾏、并发

在这里插入图片描述

6.进程切换

1.死循环进程如何运行

在这里插入图片描述

2.cpu与寄存器

在这里插入图片描述

  • 红色方框内的就是 寄存器,蓝色的就是 进程

在这里插入图片描述

  • 寄存器中的内容可以随时改变,这为系统进程切换提供基础

3.如何切换

在这里插入图片描述

  • 运行进程A时会把A中的数据保存到cpu中的寄存器中
  • 若达到时间片(一个进程不能一直占有cpu),寄存器中的数据会被拷贝到对应进程的PCB中,进程A被拿走,进程B开始占用cpu
    在这里插入图片描述
  • 再次轮到进程A占用cpu时,只需要把拷贝的数据覆盖到寄存器,就可以从上一次运行的结尾处继续运行,不需要重新从头开始

在这里插入图片描述

7.进程调度---------大O(1)调度算法

1.queue【140】

在这里插入图片描述

  • queue存储的就是PRI(进程调度),其中0-100都是实时优先级,基本不会调用,剩余的40就是之前【60-99】的范围,PRI(显示)=PRI(默认)(80)+ NICE;经常调用,其本质就是一个哈希表:
    在这里插入图片描述

2.bitmap【5】

在这里插入图片描述

在这里插入图片描述

  • 每一个比特位是1代表存在该优先级的进程,0代表不存在。

3.active与expired指针

看到最初的图,不禁疑惑,为什么会有红蓝两份一样的内容呢?
在这里插入图片描述

  • 1.其中 active指向正在调动的活跃进程的头expired则指向另一个过期进程的头,
  • 2.先对活跃进程分析,活跃进程 运行时,会根据bitmap选出优先级最高的进程,并在queue队列中找到对应的进程去占用cpu,若时间片到了,则会把该进程放到 过期进程 的queue中
  • 3.在活跃进程中的进程 (没调用完/被放到过期进程) 之前会一直在活跃进程中执行前两条
  • 4.当活跃进程的内容全部结束后,active和expired交换(swap),重复之前的内容,直至所以进程全部运行完毕

在这里插入图片描述

8.环境变量

环境变量(environmentvariables)⼀般是指在操作系统中⽤来指定操作系统运⾏环境的⼀些参数

1.命令行参数

在这里插入图片描述

在这里插入图片描述

  • main函数是有参数的,argc表示命令行的子串个数argv【】用来存储字串
  • 在这里插入图片描述

在这里插入图片描述

  • 会依次打印命令行的字串,这就是指令选项调用的前身,之前在VS上写代码时用不上选项,所以默认main函数是没参数的

2.环境变量初识

  • 要执行一个程序,首先要找到他,例如上文的./code中的“./”表示程序在当前路径下
  • 但ls这种指令就不需要找路径,因为 环境变量 ----PATH 会帮助系统找到这些指令的路径(user/bin)

3.环境变量操作

1.env指令------查询

在这里插入图片描述

  • PATH变量也在其中
2.echo $环境变量----------查询

在这里插入图片描述

  • 用来查询单独一个环境变量

在这里插入图片描述

  • 环境变量存储在bash中,bash中有两张表,一张是环境变量表(上图)存储所有环境变量–主串,另一张是命令行参数表,存储所有主串对应的选项–子串
3.export XXX=… ----------------- 插入环境变量

在这里插入图片描述

4.unset XXX ----------------- 删除环境变量

在这里插入图片描述

4.环境变量进阶

1.代码获取环境变量
#include<stdio.h>
    2 #include<stdlib.h>                                                                                                                           
    3 #include<string.h>                             
    4 //main函数最多有三个参数,前两个是之前说的指令行参数,最后一个是从父进程继承而来的环境变量
W>  5 int main(int argc,char *argv[],char *env[])    
    6 {
    
                                                  
    7   for(int i=0;env[i];i++)                      
    8   {
    
                                                
    9     printf("env[%d]->%s\n",i,env[i]);          
   10   }                                            
   11   return 0;                                    
   12 }   

在这里插入图片描述

  • 运行程序后打印父进程给的环境变量
  • 也可以使用getenv("环境名称“) 来获取!!!!!!
2.创建只有自己能运行的代码
  • 只有bash————一号进程才知道当前登录的账号是谁,而USER环境变量会被继承给子进程
 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<string.h>
  4 //main函数最多有三个参数,前两个是之前说的指令行参数,最后一个是从父进程继承而来的环境变量
  5 int main(int argc,char *argv[],char *env[])
  6 {
    
    
  7   (void)argc;
  8   (void)argv;
  9   (void)env;
 10 
 11  const char *name=getenv("USER");//获取环境变量中的USER变量
 12  if(name==NULL)
 13    return 1;
 14 
 15    if(strcmp(name,"root")==0)                                                                                                                  
 16     {
    
    
 17       printf("执行者满足条件,可以执行\n");
 18     }
 19     else{
    
    
 20       printf("执行者不满足条件,不可执行\n");
 21     }
 22  return 0;
 23 }

5.环境变量的特征

  • 环境变量通常是具有全局属性的
  • 在这里插入图片描述
    在这里插入图片描述

9.程序地址空间

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.小示例

#include<stdio.h>
  2 #include<unistd.h>//getpid和getppid的头文件
  3 int gval=100;
  4 
  5 int main()
  6 {
    
    
  7   pid_t id=fork();
  8   if(id==0)
  9   {
    
    
 10     while(1)
 11     {
    
    
 12       printf("子进程:gval: %d, gval的地址:%p, pid: %d, ppid: %d\n",gval,&gval,getpid(),getppid());
 13       sleep(1);
 14       gval++;
 15     }
 16   }
 17            
 18   else                                                                                 
 19   {
    
                                                              
 20       while(1)                          
 21       {
    
                
 22          printf("父进程:gval: %d, gval的地址:%p, pid: %d, ppid: %d\n",gval,&gval,getpid(),getppid());
 23          sleep(1);                                                                                                                                                                                                                                                                                
 24      }                                  
 25   }                    
 26 }

在这里插入图片描述

  • 可以看到父子进程中的gval有同一个地址但值却不一样

2.概念引入

在这里插入图片描述

在这里插入图片描述

  • 可以看出可以通过页表中的虚拟地址找到物理地址并进行修改数据,(父子进程gval地址相同(虚拟地址),但值却不同的原因)
  • 在这里插入图片描述
  • 因为子进程的PCB与数据都是先拷贝父进程的,所以虚拟地址也一样
  • 当子进程要修改gval的值时,虚拟地址不变,但会通过写实拷贝,新开一个物理地址,源代码打印的是虚拟地址而不是物理地址,所以不一样。
  • 在这里插入图片描述

在这里插入图片描述
总结:
——————————虚拟地址是一个结构体。

10.虚拟地址空间的意义

1.将无序变有序

  • 当磁盘把文件和代码放到物理内存中的时候,基本是无序的,但页表中的虚拟地址可以让用户认为他们是有序的,用户看到的栈,堆,代码层。。。。。。都是虚拟地址,用户修改内容也是系统通过虚拟地址映射到物理地址再修改

2.地址转换时,判断操作,保护物理内存

在这里插入图片描述

  • 页表中也会有权限,防止野指针。

例:
在这里插入图片描述

因为页表中字符常量区的权限只有r(read),如果进行w(write),系统就会阻止。进而崩溃