我的另一篇有关进程概念的博客:Linux 进程概念
目录
一、操作系统进程状态
1.1 运行状态(R)
1.2 阻塞状态(S)
1.3 挂起状态(S)
二、Linux操作系统内核中的进程状态
2.1 进程状态种类
2.2 查看R和S进程状态
2.3 T和t状态
2.3.1 T状态
2.3.2 t状态(说明此进程正被追踪)
2.4 僵尸状态(Z)
2.4.1 模拟实现僵尸进程
2.4.2 僵尸进程的危害
2.5 孤儿进程
三、进程优先级(了解)
编辑 3.1 进程优先级标志(PRI) 和 修正符(NI)
3.2 top工具手动修改nice(了解)
四、进程其他概念
4.1 并发/并行
4.2 进程切换
一、操作系统进程状态
进程状态是进程的一种属性,task_struct中有数据描述进程的状态!
下面我们谈谈普遍操作系统下的进程状态!
1.1 运行状态(R)
每个进程都有一个PCB,描述进程的所有属性!同一时间,有很多进程需要CPU处理!CPU中有一个运行队列(run_queue),运行队列中放着一个个进程PCB,它们通过链表链接!CPU通过队列顺序去读取相应的进程代码!CPU很快,所以队列中这些进程的命令很快就能被处理!所以这些在运行队列中的进程,进程状态称为运行!
1.2 阻塞状态(S)
根据冯诺依曼体系,CPU很快,相比之下硬件设备很慢!
OS通过描述硬件的结构体管理硬件
比如键盘:struct dev_keyboard
显示器:struct dev_display
可能有很多进程都要访问同一个硬件!CPU能很快读取进程代码并向OS发出进程申请使用硬件的信息!操作系统通过驱动调用底层硬件。外设启动和数据加载到内存中需要时间!比如我们需要读取磁盘文件信息!但文件加载很慢!CPU很快,CPU不会等外设!OS此时会将这个进程状态改为阻塞,然后将它放到对应的硬件结构体的等待队列(wait_queue)中!
我们称这些在外设等待队列中等待的进程为阻塞状态!
当一个阻塞进程完成外设任务以后,会不会立即被CPU运行呢?
答案是不会,完成以后OS会先将它的进程状态改为R,然后将它放置运行队列中。
1.3 挂起状态(S)
如果队列中有很多进程,短时间内不会执行该进程,但是它的进程数据和代码依然在内存中白白占据空间!万一内存空间不够了怎么办!这样会影响进程的有效运行!那些短期不被执行的进程代码和数据浪费了内存空间。OS的任务是有效管理进程!此时它会出手,它会在磁盘开辟一块空间,将这些进程的数据和代码置入到这个临时空间中!这样减少内存空间的占据!此时这个进程状态称为挂起!我们称这个过程叫做内存数据的唤出,同样OS将它拿回到内存中,称为内存数据的唤入!
二、Linux操作系统内核中的进程状态
2.1 进程状态种类
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) : 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠
(interruptible sleep))。
D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态
2.2 查看R和S进程状态
#include<stdio.h>
#include<unistd.h>
#include <sys/types.h>
int main()
{
pid_t id=fork();
//子进程执行代码
if(id==0)
{
while(1)
{
//子进程访问外设
printf("我是子进程,我的pid=%d\n",getpid());
sleep(1);
}
}
//创建子进程失败
else if(id<0)
{
printf("Creat process error\n");
}
//父进程执行代码
else
{
//父进程不访问外设
while(1)
{}
}
return 0;
}
2.3 T和t状态
2.3.1 T状态
同样是上面代码,我们可以用kill -19 + 进程pid 将运行的进程状态修改为T状态!此时该进程会被暂停!kill -18 又可以让这个进程继续跑起来!
暂停子进程结果:
暂停父进程结果:
对比两者,不同之处在于一个+号,这里有加号说明进程在前台,没有则是在后台!后台进程不能通过ctrl + C 结束进程,必须用kill -9 指令来结束进程!
2.3.2 t状态(说明此进程正被追踪)
2.4 僵尸状态(Z)
僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面讲)没有读取到子进程退出的返回代码时就会产生僵死(尸)进程
僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态。
2.4.1 模拟实现僵尸进程
下面我们来测试一下:
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>//exit
int main()
{
pid_t id=fork();
//子进程提前5秒结束
if(id>0)
{
printf("id=%d parent process is sleeping\n",getpid());
sleep(10);
}
else
{
sleep(5);
printf("id=%d child process has entered Z...\n",getpid());
exit(0);//子进程退出
}
return 0;
}
2.4.2 僵尸进程的危害
1.进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态
2.维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说, Z状态一直不退出, PCB一直都要维护!
3.那一个父进程创建了很多子进程,就是不回收,就会造成内存资源的浪费,因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间!
4.内存泄漏?是的!
2.5 孤儿进程
父进程如果提前退出,那么子进程后退出,进入Z之后,那该如何处理呢?
父进程先退出,子进程就称之为“孤儿进程”!孤儿进程被1号init进程领养,结束后被init进程回收喽。
下面我们来测试一下:
下面我们再看一下这个pid为1的进程:它负责回收子进程的进程信息!
三、进程优先级(了解)
3.1 进程优先级标志(PRI) 和 修正符(NI)
PRI :代表这个进程可被执行的优先级,其值越小越早被执行
NI :代表这个进程的nice值①.PRI也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高
②.那NI呢?就是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值
③.PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice
(老的优先级默认为80,nice默认为0)。这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行所以,调整进程优先级,在Linux下,就是调整进程nice值!nice其取值范围是-20至19,一共40个级别
3.2 top工具手动修改nice(了解)
进入top后按“r”–>输入进程PID–>输入nice值!
四、进程其他概念
竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级
独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰
4.1 并发/并行
并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行
并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发。
4.2 进程切换
我们绝大多数的笔记本或者电脑只有一个CPU!但是我们可以“同时”使用很多软件(进程),一个CPU如何“同时”处理多个进程? 答案是进程切换!
CPU很快,但CPU不是处理完一个进程才去处理另一个进程!它会来回切换进程,并处理!每个进程有固定时间片周期去占用CPU资源!
CPU内有一套寄存器!它负责存储当前进程的数据!CPU读取内存中进程PCB信息,将信息存储到寄存器中!其中一个寄存器内存放着下一段代码的地址便于CPU读取下一段代码!
进程占有CPU不是一直到进程结束!它有一个时间片,这个时间片期间,如果进程没跑完就会进程切换!那个存放下一段代码地址的寄存器内的数据会被写入进程PCB!下一次进程再来的时候,CPU读取PCB,也就能找到上次中断的位置并开始一个时间片周期!需要强调的是,寄存器是被所有进程所共享的,寄存器内的数据是被进程所私有的!