建行手机网站网址是多少钱,团队网站怎么做,wordpress回到顶端插件,前端开发工程师是什么专业目录
前言#xff1a;
一、进程状态#xff1a;
1.运行状态(时间片)
2.阻塞状态
3.阻塞挂起状态
二、Linux进程状态#xff1a;
1.运行状态(R)和阻塞状态(S)
2.深度睡眠状态(D)
3.停止状态(T)
3.1使进程在后台运行
4.追踪暂停状态(t)
5.死亡状态(X)和僵尸状态…目录
前言
一、进程状态
1.运行状态(时间片)
2.阻塞状态
3.阻塞挂起状态
二、Linux进程状态
1.运行状态(R)和阻塞状态(S)
2.深度睡眠状态(D)
3.停止状态(T)
3.1使进程在后台运行
4.追踪暂停状态(t)
5.死亡状态(X)和僵尸状态(Z)
5.1进程退出信息
三、孤儿进程
四、命令总结
总结 前言
我们已经知道了进程的一些属性和如何创建子进程那么接下来我们需要了解更多关于进程的概念。
一、进程状态
我们主要讲述Linux的进程状态。 我们先来了解什么是并行和并发。 并发是指多个任务或者事件在一段时间内交替执行。它强调的是在宏观上看起来这些任务是同时在进行但在微观层面实际上在某个瞬间可能只有一个任务在执行。还是用交通来类比在一个单车道的道路上有多辆车任务需要通过。由于只有一个车道车辆不能同时通过但是通过合理的调度让每辆车都有机会前进在一段时间内看起来所有的车辆都在前进。例如在一个单核处理器的计算机系统中同时运行多个程序。由于只有一个核心这些程序不能真正同时执行。操作系统会在这些程序之间快速切换在一段时间内每个程序都能得到执行时间就好像它们在同时运行一样。 并行是指多个任务或者多个事件在同一时刻同时执行。这就好比有多个车道的高速公路不同的车辆任务可以在不同的车道上同时前进它们在物理时间上是重叠的。例如在一个拥有多核处理器的计算机系统中不同的核心可以同时处理不同的计算任务。假设我们有一个四核处理器当执行四个独立的计算任务如对四个不同的数据集进行数学运算时这四个任务可以同时在四个核心上运行它们真正地在同一时间点都在执行这就是并行处理。 Linux/Windows民用级别的操作系统都是分时操作系统(调度追求公平)。
实时操作系统实时操作系统一般要将一个特定的进程彻底执行完(在特定的领域会使用)不追求调度公平。
操作系统内会提供一个runqueue的运行队列 而我们一般就是并发也就是这样 1.运行状态(时间片)
所以我们在理解运行状态时就是当进程在运行队列中该进程就处于运行状态。
此时CPU调度进程时直接在runqueue队列中拿到一个进程的PCB即可。之后在CPU上运行完以后(也就是该进程的时间片到达以后)直接根据FIFO算法(先进先出)将时间片到达的进程再尾插到进程链表最后即可。
2.阻塞状态
操作系统有时如何管理硬件的呢——先描述在组织
struct device
int type;
//比如1代表键盘
int status;//状态
//管理时间
//其他属性
}struct device
{int type;//比如1代表键盘int status;//状态//管理时间//其他属性
} 此时如果有代码执行到了scanf时CPU不可能一直让其在CPU上等待。其实struct device结构体中还有一个属性就是task struct* wait_queue: 所以当代码运行到scanf时用户没有输入时就会把该进程放入该设备的wait_queue中等待。当用户输入数据后会再把该进程链入到runqueue中。
当进程在键盘的等待队列中也被称为阻塞状态。所以进程都是在队列中的状态只是看在那些队列中而已。
运行和阻塞的本质就是让不同的进程处在不同的队列中。等待的本质链入目标外部设备CPU不调度。
3.阻塞挂起状态
因为即使在等待队列中也会占据内存可此时内存已经严重不足了。此时操作系统为了自身的安全会把该进程代码和数据换出到磁盘中当然PCB还保留着。对于阻塞的进程此时该用户又输入了内容但是阻塞状态都是运行状态给定的所以会直接把该进程再链入到运行状态的队列中此过程为换入。
磁盘中有一个专门为此存在的分区——swap分区。当有很多这样处于阻塞的进程操作系统内存严重不足时可能会把所有处于阻塞状态的进程都换出到swap分区上。 以上是阻塞挂起状态。当然还有运行挂起也就是正在运行时的进程加载到swap分区中。但是这个风险较大一般操作系统不会开启。
这个过程就会变慢也就是时间换空间。swap分区一般设计为内存的等量大小根据工程师分配。 一般云服务器swap功能会被禁用系统一般对时间要求更高。如果此时操作系统内存快要占满就会杀死正在运行的进程以保证自身安全这也就是我们有时可能会遇到的闪退。 二、Linux进程状态
我们之前说的都是进程的状态但是接下来我们要说的是Linux进程的状态 1.运行状态(R)和阻塞状态(S)
我们写一个代码
#includestdio.hint main()
{int cnt 0;while(1) {printf(hello world, cnt: %d\n, cnt);}return 0;
}
运行该进程在另外一个窗口中查看该进程的运行状态 此时可以看到我们有时能查到S状态有时能查到R状态(后面的是指的在前台下跑的)。这是为什么
其实S状态就是阻塞状态。你可能不相信此时我们把代码更改一下 #includestdio.hint main()
{int cnt 0;while(1) {scanf(%d, cnt);printf(hello world, cnt: %d\n, cnt);}return 0;
} 此时我们再修改代码此时我们先把打印语句给注释掉再次观察状态 此时为运行状态。 这是为什么
一个进程的时间片是非常短的而print是往显示器中写的也就是IO所以大部分时间都是阻塞状态之后偶尔是R状态。
加入你使用的是云服务器所以还要从你服务器所在地传入到你所在的地方也就是网络IO所以你可能会很难查到R状态。
在阻塞状态的进程可以被kill发送信号给杀掉。这种可中断睡眠也称为浅睡眠。
2.深度睡眠状态(D)
阻塞等待状态的一种不可中断睡眠深度睡眠。 此时有10w条银行数据需要往磁盘中写入进程A会处于阻塞状态知道在磁盘中写完这些数据。此时内存资源严重不足了操作系统把这个进程A给干掉了。此时磁盘在写入第8w条数据时出现了错误于是向进程A汇报错误结果进程A已经挂掉了于是磁盘就把这些数据清空了。 此时就出现了问题因为这10w条数据很重要于是操作系统就把向磁盘中写入数据进程状态的阻塞又分出了一种D状态也就是不可终止的深度睡眠状态。 这里不做演示。当在公司中看到该状态有两种可能
磁盘可能要挂掉了磁盘空间不足或者磁盘老化了操作系统可能要挂掉了
3.停止状态(T)
我们再次修改code.c代码让其死循环打印 这里相当于发送暂停信号。 之后再对其发送-18SIGCONT继续信号 可以看到刚才停止的程序又继续运行了。这时我们在程序运行的Xshell窗口上CtrlC发现无法终止程序你可能也发现了当我们查看code这个运行程序时发现状态栏后面的消失了之前说过这里的代表在前台运行此时已经转到后台运行了而CtrlC只能杀死前台运行的程序所以此时只能通过信号终止进程。
发送-9信号杀死code程序。 3.1使进程在后台运行
我们当然也可以让进程在后台进行。先将code.c代码修改为每隔一秒打印一次 在调用程序时在后面加上即可让程序在后台运行 也就是Windows上的最小化。但是此时还是在前台打印了因为我们并没用和终端文件做脱离如果脱离就看不到了。
T状态一般是进程做了非法但不致命的操作被OS暂停了。
4.追踪暂停状态(t)
我们需要使用gdb来观察t状态我们更改makefile生成一个debug版本的可执行程序。 为了方便动态查询我们编写一个简单的shell脚本这里死循环每隔一秒打印关于code进程的信息。
while :; do ps ajx | head -1 ps ajx | grep code | grep -v grep;sleep 1; done 此时运行代码会在断点位置停下 所以ttracing stop追踪的暂停状态的本质就是当前进程被暂停了。此时我们n执行一步程序。 可以看到由t状态变为S状态。 当进程被追踪的时候断点停下进程状态就是t。
5.死亡状态(X)和僵尸状态(Z)
dead状态和Zzombie状态。
我们首先要知道进程为什么要被创建出来进程创建出来是为了完成用户任务的。但是进程结束任务到底完成没有这是通过进程执行的结果告知父进程/操作系统的。
5.1进程退出信息
我们这里来了解一个命令$?
代表上一个进程退出时的退出信息一般0代表程序正常退出也就是完成任务而其他就是任务就是出错的。 我们平时写的.c文件在结尾都会有return我们修改code.c代码让其正常执行完并使用echo $?来查看退出结果(更加具体的细节会在环境变量和进程替换中讲解)。 关于X(dead)和Z(zombie)状态我们先举一个例子 有一个大爷跑的很快忽然倒下了。你在旁边经过作为新时代的三好少年你不会袖手旁观于是你拨打了110之后警察封锁了现场法医进行了鉴定之后对外宣布大爷是如何死亡的。这里我们就可以把大爷理解为进程其状态如下 对于Linux为什么有Z状态因为我们要维持退出信息方便父进程和操作系统来进行查询。
当一个进程退出的时候
1.代码不会执行---首先可以立即释放进程对应的程序信息数据 2.进程退出要有退出信息(进程的退出码)保存在tastk_struct(int exit_code)内部 3.管理结构task_struct必须被OS维护起来方便用户未进行获取进程退出的信息。
所以一个进程在创建的时候第一步是先创建内核数据结构(struct_task)之后加载代码和结构。在销毁的时候先释放代码和结构之后OS维护内核数据结构最后根据情况释放。
接下来就用代码证明我们先创建子进程此时父子同时存在之后让子进程退出父进程什么都不做。 #includestdio.h
#includeunistd.hint main()
{printf(父进程运行,pid: %d, ppid: %d\n\n,getpid(), getppid());pid_t id fork();if (id 0) {//子进程int cnt 5;while(cnt){printf(我是子进程,我的pid: %d, ppid: %d, cnt: %d\n, getpid(), getppid(), cnt);sleep(2);cnt--;}}else {//父进程while(1){printf(我是父进程,我的pid: %d, ppid: %d\n, getpid(), getppid());sleep(1);}}return 0;
}
我们再次开启一个窗口持续观察进程和进程状态还是使用这个代码进行观察
while :; do ps ajx | head -1 ; ps ajx | grep myprocess | grep -v grep; sleep 1; done defunct : 失灵的不再使用的死的 当然我们还可以使用另外一种方法观察僵尸状态我们修改myprocess.c代码将子进程代码修改为死循环 我们再次新开一个窗口发送信号将子进程杀掉 僵尸状态的进程也称僵尸进程维护自己的task_struct方便父进程读取状态。也就是说没人管它它就是一直僵尸task_struct也就一直占用内存资源即使对应的代码和数据已经释放。此时存在的问题就是内存泄漏。
我们以前一般写的程序存在内存泄露我们一般发不现问题。是因为我们很快就把程序执行完了但是在一个大项目中不会很快就执行完程序所以我们要避免内存泄漏。
最后我们要回收这个进程需要父进程读取子进程信息子进程才会自动退出此时task_struct也就会释放。
此时我们已经将子进程杀掉了再次发送信号是无法杀掉的因为你无法杀掉一个在概念上已经死掉的进程。
如何回收我们后面再讲不过这里先抛砖引玉一下wait方法回收(在2号系统调用手册中)。 X状态是瞬时状态我们捕捉不到但是确实存在。
三、孤儿进程
我们已经知道了僵尸进程也就是父在子退而还有另一种情况就是父退子在。我们还用刚才的代码进行演示这次我们将父进程杀死。 我们可以看到子进程成为了后端进程所以先发送信号将其杀死。
这就奇怪了在之前我们把子进程杀掉其会变成僵尸状态而我们把父进程杀掉他却没有变成僵尸状态这是为什么因为父进程的父进程是bash当我们杀死父进程时bash会自动回收。而子进程没有了父亲PID为1的进程会将其领养。
接下来我们看看PID为1的进程时谁。执行top命令我们可以先将其理解为任务资源管理器。 因为操作体统必须对所有的进程进行管理当一个子进程没有父亲时系统进程就会将其领养。
四、命令总结
top命令相当于任务资源管理器。
可执行程序 该进程在后台运行。
echo $?查看上次进程退出信息。
死循环查看进程信息脚本
while :; do ps ajx | head -1 ps ajx | grep code | grep -v grep;sleep 1; done
总结
我们知道了进程的状态并且知道如何观察但是这只是冰山一角我们目前只是初窥门径欲知后事如何且听下回分解(记得追剧啊)。