【Linux】进程状态

一.task_ struct内容分类

标示符(pid): 描述本进程的唯一标示符,用来区别其他进程
状态(status): 任务状态,退出代码,退出信号等
优先级(PRI): 相对于其他进程的优先级;
程序计数器: 程序中即将被执行的下一条指令的地址;
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针上下文数据:                  进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器;
I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表;
记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。

其他信息

 在抢占式多任务处理中,进程被抢占时,所有cpu寄存器的内容,页表指针,程序计数器会被保存下来。


二.通过系统调用获取标识符

linux中可以通过 系统调用接口:getpid 获取该进程的PID,getppid可以获取父进程的PID 

例:

#include <stdio.h>
#include <unisted.h>

int main()
{
      while(1)
      {
          printf("我是一个进程  pid: %d  ppid: %dn",getpid(),getppid());  //获取进程的pid和 
                                                                         //其父进程的pid
          sleep(1);
      }
  
      return 0;                                                                         
 }


三.fork函数的认识与理解

fork认识

 linux输入 man fork 可以看到以上信息:

      1.fork包含在头文件 <unisted.h> 中;

      2.返回值是 pid_t  (这个 pid_t 是有符号整型);

      3.作用是创建一个新的进程;

      4.当fork调用成功时会返回0给子进程,返回子进程的 pid 给父进程

         当fork调用失败时返回一个负值;

几个问题:

     A.为什么要返回两个值?

    B.一个函数怎么可以有两个返回值?

    C.一个变量怎么会有两个不同的内容?

A:

  fork多创建了一个进程, 返回两个值是为了区分不同的执行流,执行不同的代码块

B:

其实fork之后的代码是父子进程共享的,fork函数既然是函数,且有返回值,那么内部一定有return 语句,一般一个函数执行到return时,那么就意味着它的核心任务完成了,要准备返回了,那return语句是代码吗?答案当然是的!既然父子进程代码是共享的,那么return也会被执行两次,所以fork函数就有两个返回值。

C:

我们知道:进程=PCB+代码和数据

fork创建的子进程的PCB里的内容其实和父进程的大部分是相同的,但是子进程只有PCB是不行的,子进程和父进程共享代码,那数据呢?一般情况下,子进程和父进程也是共享数据的,但是一直共享数据也不现实,因为当我们要修改数据时,会把两个进程的数据都改了,这并不是我们想要的,但是重新开一块空间拷贝父进程的数据又有点浪费,所以linux就使用了一种叫写时拷贝的技术。

即在要修改数据时,给这个要修改的数据单独拷贝一份,你要修改多少就拷贝多少,这样就解决了上面的问题。

那么 return 一个值算是对数据的修改吗?当然算!return 会写入数据,也就是修改了数据,所以一个变量会有两个不同的内容。

示例

我们可以使用 ps 指令 观察

ps ajx |head -1&&ps ajx|grep mypro
int main()
{
     printf("我是一个进程n");
  
     pid_t id =fork();
     if(id==0)
     {
         //子进程                                                      
         while(1)
         {
             printf("我是子进程  pid: %d  ppid: %dn",getpid(),getppid(    ));
             sleep(1);
         }    
     }
     else if(id>0)
     {
         //父进程
         while(1)
         {
             printf("我是父进程  pid: %d   ppid: %dn",getpid(),getppid    ());    
             sleep(1);
         }
  
     }
      else 
     {
          //error
     }

     return 0;
}

 其实先执行父进程还是子进程这跟你的调度器有关。

我们再来看看父进程的父进程是谁:

我们发现,父进程的父进程是bash进程,bash进程就是我们的命令行解释器。 


三.进程状态

操作系统学科的状态

        我们先来认识以下操作系统学科上的状态:运行,阻塞,挂起

运行:

        其实内存中有一个叫运行队列的结构体,凡是放在这里面的进程,都处于运行状态;运行队            列里的PCB之间使用双链表组织起来,所以运行队列中分别有一个 head指针指向双链表的            头,一个tail指针指向双链表的尾。

        一个进程把自己放到CPU上就开始运行了,但不会一直运行,有一个时间片的概念,它规定         了一个进程在CPU上运行的时间,当超过这个时间时,这个进程就会被拿下来,大量的从               CPU上拿下进程,运行进程,这个称为进程切换

阻塞:

        阻塞可以说是处于一种等待的状态,大多会涉及到外设,外设的速度是毫秒级的,CPU的速          度是纳秒级的,相差6个数量级,所以一般涉及外设的访问,大多数会处在阻塞状态;

        处于阻塞状态的进程会被放到等待队列中,需要注意的是,内存中有非常多的等待队列,而            运行队列只有一个(有几个CPU就有几个运行队列)

挂起:

        当内存严重不足时,系统会把一些进程的代码和数据换出到外设中(通常是磁盘),只留              PCB在内存中,需要的时候再把代码和数据换入到内存中,处于此状态的进程称为挂起状              态。

        所以重装系统的时候,除了会看到C盘,D盘什么的,还会看到一个叫swap的分区,这个就           是交换分区

linux中进程的状态

linux中的进程状态分为这几种

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 */
};

这个S状态和D状态就算是阻塞状态了,S状态又叫浅度睡眠,D状态又叫深度睡眠处于深度睡眠的进程不会响应任何请求,你只能慢慢等它结束,或是断电。

下面通过一些代码演示

int main()
{
    while(1)
    {
        printf("我是一个进程  pid: %d  ppid: %dn",getpid(),getppid());
        sleep(1);
    }

    return 0;
}

可以看到,使用printf需要访问外设,此时进程是处在睡眠状态的;

这个 + 号表示是在前台运行,没有 + 号就是在后台运行,后台运行的进程只能使用 kill 命令发送 -9 信号加进程的PID杀掉这个进程。

kill -9 PID

kill -l  可以查看kill 都可以发送哪些信号

其中,-19 信号可以暂停一个进程,即让进程处于T状态,-18可以继续这个进程,但此时该进程就在后台运行了,要用 - 9  信号才能杀掉它;

 僵尸进程

     僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用)没有读取到子进程退出的返回代码时就会产生僵死(尸)进程
     僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
     所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态

僵尸进程会一直占用系统资源,还会导致内存泄漏,所以要尽量避免僵尸进程。

孤儿进程

 如果父进程先退出,子进程就称之为“孤儿进程”,此时孤儿进程在后台运行

孤儿进程会被1号init进程领养,最后由init进程回收

孤儿进程退出不会成为僵尸进程,因此也不会资源泄露。

守护进程&精灵进程

这两种是同一种进程的不同翻译,是特殊的孤儿进程,不但运行在后台,最主要的是脱离了与终端和登录会话的所有联系,也就是默默的运行在后台不想受到任何影响 。


??本篇文章到此就结束了, 若有错误或是建议的话,欢迎小伙伴们指出;?️?

??希望小伙伴们能支持支持博主啊,你们的支持对我很重要哦;??

??谢谢你的阅读。??