本篇文章进行操作系统中进程状态的学习!!!
凡是学进程,都要想到进程控制块(PCB) – task_struct
在学习OS中的进程状态时,书上的描述为了在Linux、Windows、Android中都能说的通,往往加深了理解的难度
我们可以先学习具体的Linux进程状态,然后再介绍然后再介绍OS学科的状态如何理解
操作系统是计算机中的一门“哲学”,要考虑很多情况
贴几张不同的进程状态图
具体的进程状态可以分为四种:
如何理解进程被加载到运行队列中?
运行队列也是一个对象,也可以通过描述再组织进行管理
运行队列的属性其中有PCB指针,可以通过队列的性质或复杂的数据结构进行管理
调度器的主要作用是在就绪队列中选择优先级最高的任务运行,如果优先级最高的任务不止一个,则选择队头的任务运行
当运行队列中的进程被调度时,CPU会执行该进程的代码
进程都已经终止了,为什么不立刻释放资源,而要维护一个终止态呢?
因为释放进程的资源需要时间
CPU不可能一直盯着这个进程,可能还会在做其他事情,所以要维护一个终止态
例子:比如你去吃饭,吃完后,叫老板结账,老板可能没回应你,他可能在给别人点餐
一个进程在被CPU执行的时候,用的不仅仅是CPU的资源
进程可能申请更多的资源,如:磁盘、网卡、显卡、显示器资源和声卡资源等等…
申请对应的资源无法满足时,是需要排队的,比如:CPU资源在运行队列中排队
申请其他慢设备的资源在对应的队列中进行排队
Linux内核源代码中的状态
/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] =
{
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};
- R (running)运行状态:进程是在运行中或在运行队列中
R状态一般是看不出来的,因为CPU的运算速度非常快,当你运行程序时,CPU就已经执行结束
比如在死循环中打印hello world,进程状态一般为"S",因为CPU太快了
CPU一般在等待显示器资源就绪,所以状态一直为S
[lyh_sky@localhost lesson11]$ ls
makefile test test.cpp
[lyh_sky@localhost lesson11]$ cat test.cpp
#include
#include
#include
using namespace std;
int main()
{
while (1)
{
cout << "hello world!!!" << endl;
sleep(3);
}
return 0;
}
[lyh_sky@localhost lesson11]$ cat makefile
test:test.cpp
g++ test.cpp -o test
.PHONY:clean
clean:
rm -rf test
[lyh_sky@localhost lesson11]$ ./test
通过不断刷新ps指令来查看进程状态的变化:while :; do ps ajx | head -1 && ps ajx | grep ‘test’ | grep -v grep; sleep 2; echo “#############################################”; done
可以通过不断的死循环来让CPU一直不断的工作,因为死循环没有访问外设资源
[lyh_sky@localhost lesson11]$ ls
makefile test test.cpp
[lyh_sky@localhost lesson11]$ cat test.cpp
#include
#include
#include
using namespace std;
int main()
{
while (1)
{
}
return 0;
}
[lyh_sky@localhost lesson11]$ cat makefile
test:test.cpp
g++ test.cpp -o test
.PHONY:clean
clean:
rm -rf test
[lyh_sky@localhost lesson11]$ ./test
- S (sleeping)睡眠状态:意味着进程在等待事件完成
这里的睡眠也叫做:可中断睡眠(浅睡眠),因为它可以通过指令进行中断
可以使用 kill 【-9】 【进程PID】进行中断
睡眠状态是:该进程申请的资源已经被其他进程使用,该进程在对应的“资源等待队列中进行等待,进程中的代码没有执行
[lyh_sky@localhost lesson11]$ ls
makefile test test.cpp
[lyh_sky@localhost lesson11]$ cat test.cpp
#include
#include
#include
using namespace std;
int main()
{
while (1)
{
cout << "hello world!!!" << endl;
sleep(3);
}
return 0;
}
[lyh_sky@localhost lesson11]$ cat makefile
test:test.cpp
g++ test.cpp -o test
.PHONY:clean
clean:
rm -rf test
[lyh_sky@localhost lesson11]$ ./test
D状态也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束
D状态不能通过指令进行中断,必须等它由D状态变成S状态时才能杀死
经过上图可以发现,如果堆磁盘进行读写时,发生中断,会导致严重的问题
所以才有了D状态,D状态就算是OS也不能对其进行释放
D状态不好模拟,但可以使用"dd指令"进行模拟
可以通过发送 SIGSTOP 信号或快捷键【ctrl+z】给进程来停止(T)进程
这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行
[lyh_sky@localhost lesson11]$ ls
makefile test test.cpp
[lyh_sky@localhost lesson11]$ cat test.cpp
#include
#include
#include
using namespace std;
int main()
{
while (1)
{
cout << getpid() << endl;
sleep(2);
}
return 0;
}
[lyh_sky@localhost lesson11]$ cat makefile
test:test.cpp
g++ test.cpp -o test
.PHONY:clean
clean:
rm -rf test
[lyh_sky@localhost lesson11]$ ./test
可以通过kill 【-19】 【进程PID】将指定进程发生停止运行信号
kill 【-18】【进程PID】可以恢复重新运行
还有一种t (tracing stop)状态:针对gdb调试的状态
[lyh_sky@localhost lesson11]$ ls
makefile test test.cpp
[lyh_sky@localhost lesson11]$ cat test.cpp
#include
#include
#include
using namespace std;
int main()
{
while (1)
{
cout << getpid() << endl;
sleep(2);
}
return 0;
}
[lyh_sky@localhost lesson11]$ cat makefile
test:test.cpp
g++ test.cpp -o test -g
.PHONY:clean
clean:
rm -rf test
[lyh_sky@localhost lesson11]$ make
g++ test.cpp -o test -g
[lyh_sky@localhost lesson11]$ gdb test
概念:
注意:僵尸进程是不能被杀死的,因为已经是僵尸了,不能再死第二次!!!
当一个进程退出的时候,一般不会直接进入X状态(死亡,资源可以立马被会回收),而是进入Z状态
僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用)没有读取到子进程退出的返回代码时就会产生僵死(尸)进程
僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码
只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
进程被创建出来是因为用户有任务要交给它执行,当进程退出的时候,我们不知道这个进程完成的怎么样了,一般需要将进程的执行结果告知给父进程和OS
模拟Z状态:创建子进程,子进程退出,父进程不退出(还在运行),子进程退出之后的状态就是Z
[lyh_sky@localhost lesson11]$ cat test.cpp
#include
#include
#include
#include
using namespace std;
int main()
{
pid_t id = fork();
int cnt = 3;
if (id == 0)
{
while (cnt)
{
cout << "我是子进程,我还有" << cnt-- << "秒时间结束" << endl;
sleep(1);
}
cout << "进入僵尸状态" << endl;
// 结束子进程,并且返回0
exit(0);
}
else
{
while (1)
{}
}
return 0;
}
[lyh_sky@localhost lesson11]$ cat makefile
test:test.cpp
g++ test.cpp -o test -g
.PHONY:clean
clean:
rm -rf test
[lyh_sky@localhost lesson11]$ ./test
僵尸进程危害:
如果没有人回收子进程的僵尸,该状态会一直被维!该进程的相关资源(task_struct)不会被释放
那一个父进程创建了很多子进程,就是不回收,就会造成内存资源的浪费!因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量,是要在内存的某个位置进行开辟空间!
这样就会造成内存泄漏!!!
init进程:它是内核启动的第一个用户级进程
父进程如果提前退出,那么子进程后退出,进入Z之后,那该如何处理呢?
父进程先退出,子进程就称之为“孤儿进程”
孤儿进程被1号init进程领养,当然要有init进程回收喽
[lyh_sky@localhost lesson11]$ cat test.cpp
#include
#include
#include
#include
using namespace std;
int main()
{
pid_t id = fork();
int cnt = 3;
if (id != 0)
{
while (cnt)
{
cout << "我是父进程,我还有" << cnt-- << "秒时间结束" << endl;
sleep(1);
}
cout << "父进程退出" << endl;
exit(0);
}
else
{
while (1)
{}
}
return 0;
}
[lyh_sky@localhost lesson11]$ cat makefile
test:test.cpp
g++ test.cpp -o test -g
.PHONY:clean
clean:
rm -rf test
[lyh_sky@localhost lesson11]$ ./test
进程优先级和权限的区别?
进程优先级是:进程获取外设资源的先后顺序问题
权限是:拥有者、所属组和other是否可以进行读写执行操作
它们的区别是:在学校中,我们去吃饭,食堂分教师和学生,我们不能去教师食堂打饭,这就是权限。我们打饭时,需要排队,排在前面可以先打到饭,后面的也一样可以打到饭,这就是优先级
概念:
cpu资源分配的先后顺序,就是指进程的优先权(priority)
优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能
还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能
为什么要存在进程优先级呢???
排队的本质就是确认进程的优先级
内存里面永远都是进程占大多数,而资源是少数(外设:磁盘、网卡、显卡…)
进程竞争资源是常态,OS要确认进程的先后循序,不然就乱套了
使用【ps -al】指令查看进程更多的属性(包含进程优先级):
top指令
进入top后按“r”–>输入进程PID–>输入nice值
[lyh_sky@localhost lesson11]$ ls
makefile test test.cpp
[lyh_sky@localhost lesson11]$ cat test.cpp
#include
#include
#include
#include
using namespace std;
int main()
{
while (1)
{
cout << "hello world!!!" << endl;
sleep(2);
}
return 0;
}
[lyh_sky@localhost lesson11]$ cat makefile
test:test.cpp
g++ test.cpp -o test -g
.PHONY:clean
clean:
rm -rf test
[lyh_sky@localhost lesson11]$ ./test
我们很容易注意到其中的几个重要信息,有下:
UID : 代表执行者的身份
PID : 代表这个进程的代号
PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
PRI :代表这个进程可被执行的优先级,其值越小越早被执行
NI :代表这个进程的nice值
PRI也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高
NI就是nice值,其表示进程可被执行的优先级的修正数值
PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为: PRI(new) = PRI(old) + nice
当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行
调整进程优先级,在Linux下,就是调整进程nice值
nice其取值范围是-20至19,一共40个级别,调整NI时,会重新变成初始值80,然后+NI值
例如PRI:70 NI:-10,把NI更改成10时,PRI = 80 + 10 = 90
在root用户下,使用top指令修改进程优先级:
[lyh_sky@localhost lesson11]$ ls
makefile test test.cpp
[lyh_sky@localhost lesson11]$ cat test.cpp
#include
#include
#include
#include
using namespace std;
int main()
{
while (1)
{
cout << "hello world!!!" << endl;
cout << "我的pid是: " << getpid() << endl;
sleep(2);
}
return 0;
}
[lyh_sky@localhost lesson11]$ cat makefile
test:test.cpp
g++ test.cpp -o test -g
.PHONY:clean
clean:
rm -rf test
[lyh_sky@localhost lesson11]$ ./test
进入top指令后输入r,然后回车,然后输入需要修改进程优先级的pid
输入pid后回车,接着输入NI值,就能修改进程优先级了
需要强调一点的是,进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进程的优先级变化
可以理解nice值是进程优先级的修正修正数据
比如:我们在下载东西时,同时打开浏览器进行浏览,当浏览器挂掉了,是不会影响下载的,反之下载挂掉了,也不会影响浏览器
进程具有独立性,不会因为一个进程挂断或者出现异常,而导致其他进程出现问题
操作系统,就是简单的根据队列来进行先后调度的吗?有没有可能突然来了一个优先级更高的进程?
操作系统中有二种内核,分为“抢占式内核”和“非抢占式内核”
当正在运行的低优先级进程,如果来了一个优先级更高的进程,调度器会直接把正在执行的进程从CPU上剥离,放上优先级更高的进程,这就是“进程抢占”(抢占式内核)
当进程被剥离:需要保存上下文数据
当进程恢复的时候:需要将曾经保存的 上下文数据恢复到寄存器中