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),系统就会阻止。进而崩溃