前言
进程与我们息息相关,我们打开QQ就是一个进程,微信就是一个进程,所以,对于进程的理解十分重要。而想要更深层次的理解进程,就需要从操作系统的角度来认识进程。
一、操作系统简单概述
1.什么是操作系统?
操作系统就是用来管理记算计软硬件资源的程序,也就是说操作系统也是由代码构成的。
操作系统=操作系统内核(组成操作系统的代码)+一堆应用
2.操作系统都做什么事情?
操作系统的作用就是用来管理计算机的软硬件资源。而它管理资源的方式是组织+描述。
组织:使用双向链表或者其他高级的数据结构将结构体组织起来的过程。
描述:使用struct结构体来管理进程。
3.系统调用接口和库函数的概念?
系统调用接口:在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口,叫做系统调用接口。
库函数:系统调用在使用上,功能比较基础,对用户的要求相对也比较高,
因此一些行业大佬为了方便使用者使用,将系统调用又封装了一遍形成了库函数。
二、冯诺依曼体系结构
冯诺依曼体系结构是美籍匈牙利数学家冯·诺伊曼于1946年提出存储程序原理,我们常见的计算机,服务器等,大部分都遵守冯诺依曼体系结构,结构如下图
冯诺依曼体系结构两个要点:
1.所有的数据都采用二进制进行存储(二进制中只有数字0和1,而我们使用的机器是需要进行通电,与电流只有高低电平之分正好契合)
2.数据都保存在存储器当中(内存)
三 、进程
1.什么是进程
首先区别程序和进程的概念:
程序:一个程序就是一个文件,是静态的。
进程:程序的一个执行实例,是动态的,正在执行的程序就是一个进程,同时进程也是担当分配系统资源(CPU时间,内存)的实体。
2.进程描述(PCB)
之前说过操作系统管理进程的方式是组织+描述。描述进程的信息被放在一个叫做进程控制块(PCB)的数据结构中,可以理解为进程属性的集合,在Linux中,PCB的名字叫task_struct,组织方式是使用双向链表。
在task_struct结构体内部,到底有哪些变量呢?
首先,使用ps aux命令可以查看当前操作系统所有的进程信息,如下图
1.PID(进程号)
在当前操作系统中唯一标识一个进程。(注意是当前操作系统,同一进程在不同操作系统中进程号是不同的)
2.进程状态
从操作系统调度层面分为三种状态
①就绪:准备资源准备就绪,等待cpu分配资源的状态。
②运行:进程拿着cpu资源进行运算的状态。
③阻塞:需要等待某种资源到来之后才能进行运算的状态。
细分来讲:通过ps aux命令显示的进程状态有四种
①R:运行状态
②S:可中断睡眠状态
③D:不可中断睡眠状态
④T:暂停状态,一个正在运行的进程通过ctrl+z就进入暂停状态,使用fg命令就又回到之前状态
⑤t:当进行gdb调试的时候,会产生t状态
⑥X:死亡状态(通过ps aux看不到死亡状态,因为进程的task_struct结构体即将被释放,释放的前一刻被置为X状态)
⑦Z:僵尸状态
补充:之前在ps aux命令看到的进程状态会有+存在,如S+,其中有+的进程代表前台进程,没有的代表后台进程。
3.内存指针:
指向了程序地址空间的首地址
4.程序计数器:
保存了程序即将要执行的下一条汇编指令(如一个进程在运行过程中被切换出去,程序计数器就保存了即将要执行的下一条汇编指令,知道这个进程被切换回来时应该从哪条指令继续开始执行)
5.上下文信息:
保存了程序运行时寄存器的当中的内容(如一个进程在运行过程中被切换出去,上下文信息就保存了寄存器的信息,直到这个进程重新拥有cpu资源)
6.IO信息:
7.记账信息:
cpu使用率,内存使用率,cpu使用时长等等
四、fork函数(创建子进程)
先用man命令查看fork函数如下图
fork函数比较简单,函数原型:pid_t fork(void)
1.从返回值角度看fork函数:
①创建子进程成功:fork函数会将返回值返回两次
pid_t == 0 返回给子进程
pid_t > 0 返回给父进程,返回值为子进程的pid
②创建子进程失败:pid_t < 0
2.从代码角度看fork函数:
一个进程调用了fork函数就是给这个进程创建了一个子进程。原来的进程称作父进程,新创建的称作子进程,可以通过getpid()和getppid()函数查看,getpid()函数作用是返回调用者的进程号,getppid()函数是返回调用者父进程的进程号。子进程也算是一个进程,它是拷贝了父进程的PCB ,内存指针指向的代码段中的内容也是一样的,子进程的代码段是从fork函数之后开始运行的。
3.fork函数的几个问题总结:
①父进程创建出来一个子进程之后,子进程与父进程的地位是平等的,父子进程都是PCB,被放到双向链表中由操作系统管理。
②父子进程谁先运行取决于操作系统调度。
③父子进程是抢占式执行的。
④父进程的父进程是bash(命令行解释器)
五、僵尸进程与孤儿进程
1.僵尸进程
1.什么是僵尸进程?
僵尸进程是子进程比父进程先退出,而父进程没有回收子进程的资源,此时子进程就会变成一个僵尸进程。
2.模拟一下僵尸进程
执行结果
通过ps aux命令观察进程发现
3.kill [进程号] 可以结束一个进程
kill -9 [进程号] 可以强行结束一个进程
这两个命令可以结束父进程,但是不能结束掉刚刚产生的僵尸进程,因此,僵尸进程有一个严重的危害:内存泄漏
4.如何解决产生的僵尸进程呢?
①重启操作系统(不推荐)
②结束僵尸进程的父进程(此时僵尸进程就变成了孤儿进程,资源会被1号进程回收)
③进程等待
5.产生僵尸进程的原因?
父进程创建出一个子进程,子进程比父进程先退出,子进程在退出的时候会向父进程发送一个信号(SIGCHILD),而父进程对这个信号是忽略处理的,因此造成没有进程回收子进程的资源,子进程变成僵尸进程。
2.孤儿进程
1.什么是孤儿进程?
父进程比子进程先退出,此时子进程就变成孤儿进程,孤儿进程将被init进程(1号进程)所收养,并由init进程对它们完成状态收集工作。
2.模拟一下孤儿进程
执行结果
执行ps aux命令后