个人主页:点我进入主页
专栏分类:C语言初阶 C语言进阶 数据结构初阶 Linux C++初阶 算法
欢迎大家点赞,评论,收藏。
一起努力,一起奔赴大厂
目录
一.冯诺依曼体系
二.操作系统
2.1概念
2.2结构示意图(不完整)
2.3尝试理解操作系统
三.进程理解
3.1进程
3.2指令查看进程
3.3代码查看进程
3.4fork创建子进程
3.5进程退出指令
四.进程的状态
4.1休眠状态 (S)
4.2运行状态(R)
4.3暂停状态 (T/t)
4.4D状态编辑
4.5僵尸状态(Z)
4.6孤儿状态
4.6阻塞和挂起
4.6.1阻塞态
4.6.2挂起
五.优先级
5.1什么是优先级
5.2为什么要有优先级
5.3优先级的查看与修改
一.冯诺依曼体系
在冯诺依曼体系之前设备主要包括输入,cpu,输出,这三部分,没有存储器,冯诺依曼体系多了一个存储器,冯诺依曼体系的设备分类,冯诺依曼体系包括输入输出,存储器,运算器和控制器,输入设备包括鼠标,键盘,摄像头,网卡,磁盘等,输出设备有显示器,网卡,磁盘,声卡等。存储器主要是内存,cup指的是运算器和控制器。我们可以看下面的图,是冯诺依曼体系的具体图例:
在冯诺依曼体系中用户数据是从输入设备流入,流到存储器中,再从存储器流到cpu中,然后再从cup到存储器中,最后输出,数据在这些硬件中的流动实质是一种拷贝,因此设备间拷贝的速率决定了计算机的效率。在cpu中硬件可以看下面图片
从上到下容量越来越大,运算速率越来越低,成本越来越低。冯诺依曼体系和以前最大的区别就是增加了存储器,增加存储器有什么作用呢?存储器可以将数据预先加载到内存中,需要的时候取出来,这样计算机中运算速率最低的由输入设备改到了存储器这一步骤,这样也就加快了设备运行的速率。在程序运行时,为什么要加载到内存中呢?我们需要知道程序运行需要cpu进行处理,而cpu只和内存打交道,所以程序要加载到内存中时冯诺依曼体系规定的。下面我们还有一个场景,我们和别人用软件进行交流,这是怎么进行的?我们可以将我们看成一个冯诺依曼体系,另一个人也看成一个冯诺依曼体系,我们输入信息,信息加载到内存中,经过cpu处理,传回内存经过网卡传到另一台设备,在加载到内存中,经过cpu处理,回到内存中,输出到屏幕。这就是冯诺依曼体系的应用场景。
二.操作系统
2.1概念
操作系统是软件,对软硬件资源进行管理,在广义上是操作系统的内核+系统的外壳和周边,狭义上是操作系统的内核。
2.2结构示意图(不完整)
根据这张图我们可以进行理解,操作系统进行进程管理,文件管理,内存管理,驱动管理等对我们的软硬件资源进行管理,在硬件中遵循冯诺依曼体系,当我们想要修改硬件或一些资源时,我们要保证操作系统正常运行就得修改操作系统的一些信息,我们一换就得更新操作系统,这样显然是不可取的,这时候我们的驱动就出来了,这些硬件会出现对应的驱动来对这些硬件进行处理,保证操作系统的正常运行。我们还可以看到操作系统的体系是一种层状结构。那为什么要有操作系统呢?操作系统对下进行软硬件资源管理(手段),对上提供一个良好的,稳定的,高效的环境(目的)。
2.3尝试理解操作系统
操作系统一个重要的作用就是管理,随着数据的增多,管理的难度也大大增加,我们可以定义一个结构体,将数据转换为结构体,但是由于数据多,且数据独立,因此操作系统中的信息以链表的形式进行存储,这样操作系统只需要知链表头的指针就可以进行管理,对数据的管理也就变成了对链表的增删查改。
在操作系统的上方还有其他的,最重要的就是用户,我们需要知道为了防止操作系统的信息被修改,用户不可以直接访问操作系统,但是我们可以通过一些接口来访问我们的操作系统,比如我们在c语言时学的scanf和printf这两个函数就是可以访问操作系统的接口。
三.进程理解
3.1进程
在进程中会有一个 struct PBC结构体,里面是进程的数据,进程就是PCB+代码和数据,在linux中进程的PCB时task_struct结构体。
进程运行时会在内存中形成一个调度队列,进程被调度时会被加载到内存中,在一段时间后进程没有运行完会再次加载的调度队列的后面等待被调度。
3.2指令查看进程
我们先写一段代码
#include<stdio.h>
int main()
{
while(1)
{
printf("I am a process!!\n ");
sleep(1);
}
return 0;
}
我们运行我们的代码可以看到
我们输入指令
ps axj | head -1 &&ps axj | grep process
其中./process.exe是我们代码的信息,PID是进程的进程标识符,PPID是这个进程的父进程下面那个是我们使用grep产生的进程。
3.3代码查看进程
使用代码查看进程时我们需要一个预备知识getpid函数和gitppid的用法,我们可以通过man来查看。
我们看代码:
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
pid_t id=getpid(),pid=getppid();
while(1)
{
printf("I am a pid: %d,ppid: %d\n ",id,pid);
sleep(1);
}
return 0;
}
我们就可以看到代码运行时的进程信息。
3.4fork创建子进程
我们先看fork函数
fork函数返回失败时返回-1,子进程返回0,父进程返回1。我们写代码来进行实验,我们打开饿哦们的监控脚本
while :; do pa axj |head -1&&ps axj | grep process; sleep 1; done
我们运行程序可以看到
我们可以看到第二个是子进程,第一个是父进程,子进程的ppid是父进程的pid.注意,fork后的代码父子进程共享代码。
我们进入/proc目录下,可以查看进程,
我们可以看到
我们输入指令
ll 21389 -a
可以看到
3.5进程退出指令
进程退出的指令为
kill -9 进程的pid
四.进程的状态
进程的状态分为休眠状态,运行状态,暂停壮观,D状态,死亡状态,孤儿,僵尸这几种,我将会给大家用代码来演示这些状态的形成,由于D状态会引发xshell的卡顿,所以这里不做演示。
4.1休眠状态 (S)
我们看代码:
#include<stdio.h>
int main()
{
while(1)
{
printf("I am a process\n");
}
return 0;
}
我们启动我们的监控脚本可以看到
其中STAT时进程的状态,.process.exe的状态是s+,这个+号是在后台运行,和我们的进程目前无关。如果运气好的话进程也可能是R状态,这个是由于由printf函数的原因,在执行printf语句时,会等待计算机资源,计算器进入输出设备,不在进程中,而且计算机运行很快,但是进入输出却比较慢,所以大部分时间在进行输出,所以进程大部分在S状态。
4.2运行状态(R)
我们看代码
#include<stdio.h>
int main()
{
while(1)
{
//printf("I am a process\n");
}
return 0;
}
我们只需要将print语句著时掉,就不会调用其他资源,一直是R状态,启动监控脚本可以看到
进程的STAT是R状态。
4.3暂停状态 (T/t)
这个状态需要我们输入指令,我们用上面的状态来演示,我们输入指令
kill -19 pid
我们可以看到是T状态
我们输入指令
kill -19 pid
进程就取消了暂停状态。
4.4D状态
在进程进程传输数据时,一旦内存不够,会导致操作系统杀掉进程,由于进程杀掉,会导致数据丢失,造成很严重的损失,所以出现了D状态,这样就不会导致进程没有执行完被杀掉。
4.5僵尸状态(Z)
僵尸状态就是子进程运行结束,父进程还在运行,子进程必须由父进程结束,我们知道进程时由task_struct以及代码和数据,这时候由于子进程结束,操作系统会将代码和数据进行释放,只剩下task_struct,这时候子进程就是僵尸状态。我们看下面代码:
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
pid_t id=fork();
if(id==0)
{
int cur=5;
while(cur--)
{
printf("I am a childprocess , pid: %d ppid: %d\n",getpid(),getppid());
sleep(1);
}
}
else
{
while(1)
{
printf("I am a parentprocess , pid: %d ppid: %d\n",getpid(),getppid());
sleep(1);
}
}
return 0;
}
我们可以看到子进程的STAT是Z状态
4.6孤儿状态
孤儿状态是父进程结束,子进程没有结束,由于子进程需要父进程结束,父进程结束,父进程的task_struct以及代码和数据被释放,子进程不会结束,子进程会被bash领养。我们看代码:
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
pid_t id=fork();
if(id==0)
{
while(1)
{
printf("I am a childprocess , pid: %d ppid: %d\n",getpid(),getppid());
sleep(1);
}
}
else
{
int cur=5;
while(cur--)
{
printf("I am a parentprocess , pid: %d ppid: %d\n",getpid(),getppid());
sleep(1);
}
}
return 0;
}
我们可以看到子进程的ppid显示父进程,然后父进程结束后ppid变成bash。
4.6阻塞和挂起
4.6.1阻塞态
我们知道cpu有一个运行队列,当一个进程拥有cpu时会一直占据cpu吗?显示不是 会有一个基于时间片的调度,一定时间后若进程没有结束,会回到调度队列的后面,等待再次被调度
当运行到scanf时会将scanf给操作系统中的调度队列,这时候就会进入阻塞状态。
4.6.2挂起
在磁盘中有一个swap分区,当处在阻塞态时,内存不足会让进程的task_struct以及代码和数据, 全部拷贝到swap分区(唤入),需要时在唤出,其中唤入的过程就是阻塞挂起。
五.优先级
5.1什么是优先级
优先级是指进程获取资源的先后顺序,在Linux中优先级数字越小,优先级越高,优先级和权限有什么区别?优先级是可以访问资源,权限是是否可以访问资源。
5.2为什么要有优先级
进程访问的资源是有限的,而进程总是很多的,为了维持操作系统的正常运行,需要有优先级,如果进程长时间不被调度,会造成节问题。
5.3优先级的查看与修改
我们可以通过指令
ps -al |head -1 &&ps -al |grep process
进优先级的访问,其中的PRI就是优先级,我们运行一个进程,然后查看它的优先级
我们输入指令可以看到:
其中的PRI就是优先级,NI是nice值,新优先级等于优先级+nice值。对于渡河修改优先级,需要我们的top指令,我们输入
sudo top
再输入
r
再输入进程的pid
然后输入想要修改的nice值
输入q返回。
再次输入指令
ps -al |head -1 && ps -al |grep process
可以看到
我们想要再次修改需要重复上面的过程
我们想要再次修改,nice为-10,可以看到
上次是90,这次-10不应该是80吗?其实不然,我们新的进程的优先级=进程的优先级+nice值,其中进程的优先级为80,每一次都按80去计算。其中nice值的范围是[-20,19],我们超过这个范围它就取到最大值,例如我们把nice修改为-100,可以看到
这样我们就可利用指令来查看与修改进程的优先级。