一、进程退出
基本知识储备
1、代码不会执行了——首先可以立刻释放的就是进程对应的程序信息数据退出
2、进程退出,要有退出信息(进程的退出码)保存在自己的task_struct内部
3、管理结构task_struct必须被OS维护起来,方便用户未来进行获取进程退出的信息
task_struct->是进程的PCB
进程=内核数据结构+代码和数据
结构体->成员属性->退出信息(int code,其他信息)
二、僵尸进程
1、僵尸进程是进程的一种状态
在进程死亡后由其父进程进行回收其退出信息,而当进程死亡后其退出信息没有被回收时,就会出现僵尸进程
2、在代码层面:
我们创建子进程,其父进程设计一个循环,使其什么都不做,通过kill命令杀死子进程,此时子进程就会变成僵尸进程
在代码变成僵尸进程后会一直维持自己的task_struct,方便未来父进程读取其退出状态,当父进程读取子进程的退出信息后,子进程会自动退出。
如果没有人回收僵尸进程,就会一直消耗内存,进而造成内存泄漏!
三、孤儿进程
在上述情况中,子进程被杀死,而父进程还在。而如果将父进程杀死,子进程还在就会出现孤儿进程
子进程会被系统自动领养,退出数据也由系统自动回收
四、进程优先级
1、是什么?
获得某种资源的先后顺序,比如:排队的本质就是在确定优先级
2、为什么?
cpu的资源比较少,需要其竞争cpu资源
3、优先级VS权限
优先级是先后问题
权限是能不能的问题
两者从本质上不一样
4、怎么办?
tast_struct->优先级属性->特定的几个int类型的变量表示优先级
优先级越小代表优先级越高
优先级分pri(默认优先级)和nice(调整/修正的数据)
最终优先级=默认优先级+nice
a、文件会记录下拥有者,所属组,和对应的权限
b、linux下一切皆文件
c、所有操作,都是进程操作,进程会自己记录谁启动的自己
权限的控制
为什么有nice?
因为要保证程序在可控范围内
分时OS->进程调度->>>保证程序调用的时间尽量平均
5、修改优先级nice:
(不是高频,不建议修改,在此只是表示可以修改)
a、指令
b、代码
nice的可修改范围是【-20,19】->40个数字
而最终优先级【60,99】
pri(最终优先级)=pri(default 80)+nice(每次修改都会重置)
五、其他概念
竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高 效完成任务,更合理竞争相关资源,便具有了优先级
独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰
并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行(任何时刻)
并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为 并发
六、进程的切换
1、知识储备
a、时间片:时间片到了,进程就要被切换
b、Linux是基于时间片进行调度轮换的
c、一个进程在时间片到了的时候,并不一定跑完了,可以在任何地方被重新调度
2、切换进程过程和理解
cpu中有不同寄存器,他们有不同作用,其中有ir寄存器和pc寄存器
寄存器!=寄存器中的数据
ir寄存器中储存处理器当前要需要处理的代码指令
pc寄存器中储存下一段要处理的代码指令的地址和其代码长度
->在内存中我们的代码不是一行一行进行存储的,而是连续的,pc中保存代码的长度可以帮助找到下一段代码。
当cpu处理完ir寄存器中的代码时,pc中的地址就可以寻找下一段代码,我们写的代码就是这样一句一句的执行下去。
你的进程在运行的时候,会有很多的临时数据,都在CPU的寄存器中保存。
cpu内部的寄存器的数据,是进程执行时的瞬时状态信息数据
基本过程:
1、取指令
2、更新pc
3、分析执行的指令
总结:
CPU调度进程的时候是基于时间片轮换调度的,而在CPU中有加载下一行代码的寄存器pc,有用来加载当前代码的寄存器ir,他们通过不断轮换调度来不断的加载执行代码;
六、进程数据的恢复
进行切换的核心含义就是进程上文的保存和恢复
如果没有保存,进程切换走后,下一次切换回来,上一次数据全部消失,代码就不能继续完成。
所以在进程切换时数据将保存和储存呢?
进程的上下文寄存器数据(进程的上下文数据)被保存到了当前进程的PCB(任务状态段的结构)中。
切走:将相关寄存器的内容保存到内存中。
切回:将历史保存的寄存器数据恢复到寄存器。
当进程被切回的时候接着上次没做完的事情,继续运行
————————>>>这个是所有进程都要做这个工作。
每次切换时,每次保存上下文的时候CPU都是全新的
总结:
进程在进入CPU调度的时候,因为有时间片的原因,可能不会直接跑全部代码,当代码没有全部跑完的时候,这个进程代码数据都被保存到进程的PCB中,当重新切回时再向CPU重新加载数据,CPU是不会保存数据的;
七、Linux真实的调度算法
上面说到 进程的优先级 和 CPU调度时为了进程都可以被调用而出现的分时操作,两者都是进程被调用的规则,但CPU在调用时如何同时体现两种规则??
答:FIFO调度
1、基本知识
在系统中存在这样的一个结构体,叫struct runqueue
上面结构中有三个变量最为重要
其中nr_active意思是活跃进程;
queue数组含有140个元素,前100个数据是实时动态变化的数据,不必理会。后40个数据刚好对应着40个优先级。
相同优先级的数据就会被挂在对应优先级的数组位置,形成一个hash桶,
2、bitmap原理和Linux内核O(1)调度算法
bitmap是queue数组的相位图,其相位图记录了queue中每个位置上是否含有元素
原理:bitmap含有5个int型元素,一个int型是32个比特位,32*5=160,正好覆盖queue140元素,当其对应位置上是1时便代表其对应queue数组中对应位置是否含有元素,这样便构成了相位图。
for(int i=0;i<5;i++)
{
if(bit_map[i]==0)
continue;
else
//32个比特位中确定在哪一个队列
}
上面代码一次就可以检测32个位置
时间复杂度是O(1);
在C语言阶段,我们就学会了 按位与 和 按位或 异或等操作,通过简单的有限次数的while循环就可以判定最低的比特位在int是哪一个
while(x)
{
x&=(x-1);
}
上面程序用来判定最低的比特位在int的哪个位置,时间复杂度也为O(1);
因此整体Linux内核的调度算法就是O(1);
因此叫Linux内核O(1)调度算法
上面的结构
active队列永远都是一个存量进程竞争的情况,即:active队列只会越来越少,不会增多
时间片到了,进程就会退出
调度器要非常均衡的进行进程调度
因此优先级高的一定一直优先吗???????
优先级低的一定一直不会被调度吗????
1、CUP调度只会从active队列中选择进程调度
2、调度有三种情况
a、运行退出
b、不退出但时间片到了
c、有新的进程产生了
swap(&active,&expired);
swap:
1、之前:按照优先级调度
2、之后:给其他优先级的进程的调度机会
struct riunqueue
{
...
struct queue* active;//活跃的
struct queue* expired;//过期的
struct queue* array[2];
}
总结:
简单来说:在相同中存在一种名为struct run queue的结构体
在这个有两个一样的变量组
其中nr_active的意思是不是活跃进程,bitmap是位图,用来记录queue数组中的含有的元素的位置
而queue数组就是用来管理40个优先级。
系统只会调用活跃的那个结构体中的进程
当有新的进程加入时,不管他的优先级多少,都不会直接加入活跃,也就是被系统调用的那个结构体,而是加入不活跃的那个结构体中。
而一个进程被调用结束后也不会放回活跃的结构体中,而是放到不活跃的那个结构体中。
所以活跃的那个结构体中的进程越来越少,当活跃的那个结构体中进程为零时,系统会通过改变指针的指向,将活跃的变为不活跃的,将不活跃的变为活跃的。
此时就完成一轮系统调用;
八、Linux中的链式结构:
Linux中都是双链表结构
1、所有的进程都要用链表链接
2、进程可以在调度队列中,也可以在堵塞队列
其中链表中只有只有指针域,没有数据域
他们的链接方式是上面这样的结构。
那么问题来了,如果我们可以从头开始访问结构体,我们可以找到他的数据域和指针域
如果是上面的这样的结构,我们如何找到他的数据开始的地方呢?????
答:在内存中,我们是由低地址到高地址来进行存储。假设存在这样的一个结构体
//低地址
struct A
{
int a;
int b;
int c;
int d;
}
//高地址
如果我们知道了c的地址,我们怎么找到第一个元素的起始位置。
(struct A*)0->c=偏移量
上面的公式是将地址为0的地方强转成struct A*类型的元素,此时0地址处就是a元素的所在位
我们找到此时c的地址位,这个时候就是a元素到c元素的偏移量
此时再用原先c的地址减去偏移量,就得到了原链表中链表首元素的地址;
这样做意义是什么??
答:这样做一个进程既可以在全局链表中,也可以在任意一个其他数据结构中,只要加节点字段即可;
九、命令行参数
main函数的命令行参数
int main(int argc,char *argv[])
{
printf("argc: %d\n",argc);
for(int i=0;i<argc;i++)
{
printf("argv[%d]:%s\n",i,argv[i]);
}
return 0;
}
命令行参数列表:argc:参数个数,argv[]:参数的清单
为什么要有他:
同一个程序,可以根据命令行参数,根据选项的不同,表现处不同的功能
比如:指令中选项的实现!
命令行参数就是一串字符串,最先被shell拿到,按照空格被打散,形成一张表和元素个数
命令行启动的程序,父进程是谁-------->shell ,对于数据(尤其是只读),子进程也能看见。
十、环境变量
具有全局属性的变量叫做全局变量。
PATH:指定命令的搜素路径
为什么系统知道,命令在/usr/bin路径下,我能更改,让他认识我们的路径吗??
PATH环境变量,告诉shell,你应该去哪个路径下查指令。
如果我不想带路径,让我的程序执行起来,
1、cp/usr/bin---done
2、把自己的路径,添加到PATH中
环境变量不是存在于内存中,在系统的配置文件中
当你在启动shell时,系统会记录你的uid
我们登录后->启动一个shell进程->读取用户和系统相关的环境变量的配置文件->形成自己的环境变量表->子进程
———————————————————————————————————————————
感谢你的观看,如果有什么问题,可以给我留言。