Linux进程概念(二)

一.Linux下进程状态

  • 运行态
  • 终止态
  • 阻塞态
  • 挂起态

二.进程状态的理解
进程在不同的队列中,表示不同的状态。

1.什么是运行队列
1个CPU只有一个运行队列,进程的执行需要排队。让进程入队列,本质上是将task_struct结构体对象放入运行队列中。

这个进程队列中的指针可以找到需要加载的进程,将其PCB对象的信息加载到CPU中,CPU可以根据PCB对象的信息找到进程对应的代码进行执行。

在CPU的运行队列中的进程状态就叫做运行状态

状态,是进程的内部属性,存放于task_struct中。这个状态可以理解为一个整数,例如这个整数为1代表运行;2代表停止,3代表死亡状态等······

2.对于CPU和硬件的速度差异,系统如何调度?
进程不仅仅会占用CPU资源,也会占用硬件资源。对于CPU,它可以很快的处理进程的请求;但是对于硬件,速度很慢,例如网卡,可能同时有迅雷、百度网盘、QQ等进程需要获取网卡的资源,所以每一个描述硬件的结构体中也有一个task_struct* queue运行队列指针,指向排队中的PCB对象的头结点。

那么CPU和硬件的速度差异巨大,系统该怎么平衡这种速度?当CPU发现运行状态的进程需要访问硬件资源时,会让该进程去所需访问的硬件的运行队列中排队,CPU继续执行下一个进程。

那么这个被CPU剥离至硬件运行队列中的进程状态被称为阻塞状态。当进程对硬件的访问结束后,进程的状态将会被修改为运行状态,即该进程重新回到CPU的运行队列。

3.对于过多的阻塞进程,内存占用如何处理?
硬件的速度较慢,但是大量的进程需要访问硬件,势必会产生较多的阻塞进程,这些阻塞进程的代码和数据在短期内不会被执行,如果全部存在于内存中将会导致内存占用。

对于这个问题,如果内存中有过多的阻塞状态的进程导致内存不足,操作系统会将其的代码和数据先挪动至磁盘,仅留PCB结构体,以节省内存空间,这种进程状态被称为挂起状态。将进程相关数据,加载或保存至磁盘的过程,称为内存数据的换入和换出。

进程的阻塞状态不一定是挂起状态,部分操作系统可能会存在新建状态挂起或运行状态挂起等。

4.如何理解终止状态?
终止态:进程还在,只不过永远不会被调度器调度运行了,它随时等待被释放。
释放对应的资源也需要成本和时间,所以说,退出程序系统直接退出,只是理想状态罢了。既然操作系统不一定能立马释放,那我们就必须得维护一个终止态,来告诉操作系统自己已经退出了,等操作系统不忙的时候再来释放。

三.Linux进程状态 

为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在Linux内核里,进程有时候也叫做任务)。
1、进程状态在kernel中的定义

/*
* 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) : 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
  • S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠(interruptible sleep))。
  • D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
  • T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
  • X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
     

2.进程状态的运行状态

 #include <stdio.h>
 int main()
 {
     while(1);  
     return 0;                                                                                                                 
 }


 

R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
状态后面有+号的表示前台进程,没有+号表示后台进程。前台进程在执行时,用户无法继续输入指令除非ctrl+c终止程序;后台进程在执行的过程中,用户可以输入指令且ctrl+c无法杀掉该进程。可以使用kill -9 PID的指令杀掉该进程。

 #include <stdio.h>
 int main()
 {
     while(1)
        priintf("hello pid);
     return 0;                                                                                                                 
 }

 虽然数值一直在打印,但是printf函数需要访问显示器,大部分时间在等显示器IO就绪,只有小部分时间在执行打印代码。所以该代码呈现睡眠状态。
需要访问外设的,一般属于睡眠状态。
S睡眠状态(sleeping):意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠 【interruptible sleep】)。
 

D磁盘休眠状态(Disk sleep)(阻塞状态的一种)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。在该状态下的进程,无法被操作系统杀掉,只能通过断电或者进程自己醒来的方式中断深度睡眠状态。
只有在高IO的情况下才会发生(Linux中有一个dd命令可以模拟高IO的状态)。




T暂停状态(stopped) : 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。(kill -18 PID)

t追踪暂停状态(tracing stop) : 我们在使用gdb对一个可执行文件调试的时候,程序运行到断点处,程序会进入t追踪暂停状态(tracing stop):表示该进程正在被追踪。

Z僵尸状态(zombie):为什么会存在僵尸状态?因为进程在退出的时候,不会立即释放该进程对应的资源,会保存一段时间,让父进程或操作系统来读取子进程的返回代码。
那么进程怎样进入僵尸状态?模拟子进程正常退出,父进程不回收子进程(不读取子进程的返回信息)的场景(也可以使用kill -9 PID杀掉子进程):
 

#include <stdio.h>    
#include <unistd.h>    
#include <sys/types.h>    
#include <stdlib.h>    
int main()    
{    
    
    pid_t id=fork();    
    if(id==0)    
    {    
        //子进程    
        while(1)    
        {    
            printf("子进程,PID=%d,PPID=%d,%d\n",getpid(),getppid(),id);    
            sleep(1);    
            exit(1);    
        }    
    }                                                                                                                        
    else if(id>0)      
    {                          
        //父进程    
        while(1)      
        {  
            printf("父进程,PID=%d,PPID=%d,%d\n",getpid(),getppid(),id);
            sleep(1);   
        }
    }
    else 
    {
        perror("fork");
        exit(-1);                                                                                                            
    }
    return 0;
}  

 

 

 

通过该命令循环打印进程信息,发现子进程已经变成了僵尸状态。旁边的<defunct>译为死者,进程已经死亡,但是还未被回收,这就是僵尸状态。僵尸进程的退出结果会写在PCB中,一个进程退出了,它的代码和数据会被释放,但是它的PCB是不会被释放,如果父进程不回收这块资源,那么会造成系统的内存泄漏。那我能不能手动杀掉这个僵尸进程来手动释放僵尸资源?不可以,因为僵尸进程已经死亡,无法手动杀掉进程。在Z状态的进程被回收后,进程状态变为X死亡状(dead):父进程读取完子进程的返回信息后,收尸速度太快了,我们看不到,进程死亡状态立马被它的父进程回收。进程Z,就是维护退出信息,可以让父进程和操作系统读取的!

 僵尸进程危害

  • 进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态?是的!
  • 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说, Z状态一直不退出, PCB一直都要维护?是的!
  • 那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的!因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间!
  • 内存泄漏?是的!

孤儿进程
前台创建的子进程,父进程被杀掉后,父进程立马被bash回收。子进程被1号进程(操作系统)领养,同时切换到后台运行(使用kill -9 PID杀掉后台进程)。因为不领养子进程的话,那么子进程退出时呈现的僵尸状态就没有谁能回收了(内存泄漏)。这种被领养的进程称为孤儿进程
 

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
int main()
{
 
    pid_t id=fork();
    if(id==0)
    {
        //子进程
        while(1)
        {
            printf("子进程,PID=%d,PPID=%d,%d\n",getpid(),getppid(),id);
            sleep(1);
        }    
    }    
    else if(id>0)    
    {
        //父进程    
        while(1)                                                                         
        {    
            printf("父进程,PID=%d,PPID=%d,%d\n",getpid(),getppid(),id);    
            sleep(1);    
        }    
   }    
    else 
    {
        perror("fork");
        exit(-1);                                                                        
    }
    return 0;
}

  •  父进程如果提前退出,那么子进程后退出,进入Z之后,那该如何处理呢?
  • 父进程先退出,子进程就称之为“孤儿进程”
  • 孤儿进程被1号init进程领养,当然要有init进程回收喽。

四。进程优先级

1.基本概念

  • cpu资源分配的先后顺序,就是指进程的优先权(priority)。
  • 优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。
  • 还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。

2.查看系统进程
在linux或者unix系统中,用ps –l命令则会类似输出以下几个内容:

我们很容易注意到其中的几个重要信息,有下:

  • UID : 代表执行者的身份
  • PID : 代表这个进程的代号
  • PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
  • PRI :代表这个进程可被执行的优先级,其值越小越早被执行
  • NI :代表这个进程的nice值

 

PRI(priority)代表优先级的意思(priority默认是80);NI(nice)用于调整优先级(nice默认是0)。
进程最终优先级=默认优先级(固定80)+nice值。
Linux支持进程在运行过程中调整优先级,调整的方式是修改nice值。

3.nice值的修改

用top命令更改已存在进程的nice:
top
进入top后按“r”–>输入进程PID–>输入nice值

注:1、需要root权限2、使用r调出修改nice值的命令栏。
nice的取值范围为【-20,19】,数字输的再小再大也没用。
所以Linux中进程的权限范围为【80-20,80+19】,数字越小,优先级越高。
进程优先级不要人为的调整,如果一个进程的优先级较高或较低,可能会造成其他进程获取操作系统资源不均,造成操作系统自身的调度失衡。

五.其他进程特性

  • 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级 。
  • 独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰 。
  • 并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行 。
  • 并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值