进程状态与管理详解
参考:
https://www.cnblogs.com/satire/p/14759216.html(进程的7种状态)
https://blog.csdn.net/qq_24016309/article/details/127632218(操作系统中进程有哪些状态)
进程状态的分类:按ps或者top命令
R:运行状态,并不意味着程序在运行中,它表明进程要么是在运行中要么在运行队列里。
S:可中断休眠状态,意味着进程在等待事件完成。
D:不可中断休眠状态,在这个状态中进程通常会等待IO结束。
T:停止状态,可以通过发送SIGSTOP信号来停止进程,这个被暂停的进程可以通过发送SIGCONT信号让进程继续运行。
Z:僵尸状态,一种濒临死亡的状态。
X:死亡状态,这个状态只是一个返回状态,你不会在列表中看到这个状态。
进程状态从操作系统来看:
就绪态:R
运行态:R:正在使用cpu
阻塞态:S、D、T
新建态:还不算进程:还未进入主存(为分配内存)
终止态:X、Z:进程正在释放:不能回到正常状态了
新建态:
创建一个进程一般要通过一下两个两个步骤
1,为一个新进程创建PCB,并填写必要的管理信息.、
2,把该进程转入就绪状态并插入就绪队列之中。
当一个新进程被创建时,系统已为其分配了PCB,填写了进程标识等信息,但由于该进程所必需的资源或其它信息,如主存资源尚未分配等,一般而言,此时的进程已拥有了自己PCB,但进程自身还未进入主存,即创建工作尚未完成,进程还不能被调度运行,其所处的状态就是创建状态。
引入创建状态,是为了保证进程的调度必须在创建工作完成后进行,以确保对进程控制块操作的完整性。同时,创建状态的引入,也增加了管理的灵活性,操作系统可以根据系统性能或主存容量的限制,推迟创建状态进程的提交。对于处于创建状态的进程,获得了其所必需的资源,以及对其PCB初始化工作完成后,进程状态便可由创建状态转入就绪状态。
终止态:
等待操作系统进行善后处理,然后将其PCB清零,并将PCB空间返还系统。
当一个进程到达了自然结束点,或是出现了无法克服的错误,或是被操作系统所终结,或是被其他有终止权的进程所终结,它将进入终止状态。
进入终止态的进程以后不能再执行,但在操作系统中依然保留一个记录,其中保存状态码和一些计时统计数据,供其它进程收集。一旦其它进程完成了对终止状态进程的信息提取之后,操作系统将删除该进程
就绪态:
进程所有的执行条件都满足,只等着cpu来调度;
当前是就绪态的话,要么是要等待运行,要么是运行时间片用完又回到了就绪态。
阻塞态:
进程不具备执行条件,比如需等待I/O;
阻塞态是运行过程中,条件不满足了被阻塞,等到条件满足了,回到就绪态。
可中断睡眠态S和不可中断休眠态D的区别
1,Linux 中的进程睡眠状态有两种:一种是可中断的睡眠状态(S状态),其状态标志位为TASK_INTERRUPTIBLE;另一种是不可中断的睡眠状态(D状态),其状态标志位为TASK_UNINTERRUPTIBLE。
2,可中断睡眠状态的进程在睡眠状态等待某特定事件发生,即使其等待的某特定事件没有发生,可中断休眠状态的进程也是可以被唤醒的,如:用户空间等待条件变量,在条件满足之前,也是可以被信号唤醒的。产生一个中断、释放进程正在等待的系统资源或是传递一个信号都可以唤醒可中断休眠状态的进程。用户空间的条件变量和互斥锁、sleep函数、内核空间的互斥锁mutex_lock_interruptible和信号量down_interruptible函数等都可以导致进程进入可中断休眠状态。
3,不可中断睡眠状态与可中断睡眠状态类似,但是不可中断休眠的进程必须等到某特定事件发生后才能被唤醒。将信号(如:kill函数发出的信号)传递到这种睡眠状态的进程不能改变它的状态(因为发送给处于不可中断休眠状态的进程的信号会被丢弃掉),也就是说它不响应信号的唤醒或者被调试(如:gdb,pstack等)。等待IO响应(如:磁盘IO,网络IO,其他外设IO等)、内核空间的互斥锁mutex_lock函数等都可以导致进程进入不可中断休眠状态。
挂起状态
就绪态和阻塞态都可以被挂起,如图
进程挂起,阻塞和睡眠的区别
从触发机制上:
睡眠是主动触发的,
挂起也是主动触发
而阻塞是被动的
从资源占用角度来说:
阻塞进程不再占用CPU资源,但还在占用调度器和内存资源
挂起进程不再占用CPU和内存资源了:wait()
睡眠进程既占用内存资源又占用CPU资源:sleep()
从恢复上来看:
睡眠恢复是自动完成的,因为睡眠有一个睡眠时间,睡眠时间到则恢复到就绪态。
阻塞是被动的,是在等待某种事件或者资源的表现,一旦获得所需资源或者事件信息就自动回到就绪态。
挂起情况最多,如果是用户将进程挂起的话,那么只有用户才能解除进程的挂起状态,而如果是被操作系统挂起的,这要根据系统资源情况,优先级,挂起中的状态来判定什么时候解除挂起状态。
原因不同:
导致进程被挂起的原因一般是内存不足或者是系统、用户的请求,协调、修改进程,研究进程的状态等; 进程阻塞是进程正在等待某一事件发生,可能是等待资源或者响应等(eg.等待I/O完成等)而暂时停止运行 1、Ctrl-Z 挂起当前正在运行的进程。 2、kill -STOP <pid> 挂起有控制权限的进程。 3、fg %<jobid> 恢复挂起的进程
sleep() 和wait()函数的区别:
两者比较的共同之处是:两个方法都是使程序等待多少毫秒。
最主要区别是:
sleep(2000)表示:占用CPU,程序休眠2秒,没有释放锁。
wait(2000)表示:不占用CPU,程序等待2秒,释放锁。
进程管理:
了解僵尸进程Z态:
一个进程在调用exit命令结束自己的生命的时候,其实它并没有真正的被销毁, 而是留下一个称为僵尸进程(Zombie)的数据结构。
top命令可以查看
Tasks: 278 total, 1 running, 277 sleeping, 0 stopped, 0 zombie
父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起。
如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler,因为子进程结束后, 父进程会收到该信号,可以在handler中调用wait回收。
如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD,SIG_IGN) 通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收, 并不再给父进程发送信号。
还有一些技巧,就是fork两次,父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收 还要自己做。
守护进程
实现一个守护进程
function daemonize() { \umask(0);
//第一次 $pid = \pcntl_fork(); if (-1 === $pid) { throw new Exception('Fork fail'); } elseif ($pid > 0) { exit(0); } if (-1 === \posix_setsid()) { throw new Exception("Setsid fail"); }
//第二次 // Fork again avoid SVR4 system regain the control of terminal. $pid = \pcntl_fork(); if (-1 === $pid) { throw new Exception("Fork fail"); } elseif (0 !== $pid) { exit(0); } }