【Linux】进程状态

一.task_ struct内容分类

标示符(pid): 描述本进程的唯一标示符,用来区别其他进程;
状态(status): 任务状态,退出代码,退出信号等;

优先级(PRI): 相对于其他进程的优先级;

程序计数器: 程序中即将被执行的下一条指令的地址;

内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器;

I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表;

记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。

其他信息

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


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

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

例:

cpp 复制代码
#include <stdio.h>
#include <unisted.h>

int main()
{
      while(1)
      {
          printf("我是一个进程  pid: %d  ppid: %d\n",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 指令 观察

bash 复制代码
ps ajx |head -1&&ps ajx|grep mypro
cpp 复制代码
int main()
{
     printf("我是一个进程\n");
  
     pid_t id =fork();
     if(id==0)
     {
         //子进程                                                      
         while(1)
         {
             printf("我是子进程  pid: %d  ppid: %d\n",getpid(),getppid(    ));
             sleep(1);
         }    
     }
     else if(id>0)
     {
         //父进程
         while(1)
         {
             printf("我是父进程  pid: %d   ppid: %d\n",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中的进程状态分为这几种

cpp 复制代码
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状态又叫深度睡眠,处于深度睡眠的进程不会响应任何请求,你只能慢慢等它结束,或是断电。

下面通过一些代码演示

cpp 复制代码
int main()
{
    while(1)
    {
        printf("我是一个进程  pid: %d  ppid: %d\n",getpid(),getppid());
        sleep(1);
    }

    return 0;
}

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

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

bash 复制代码
kill -9 PID

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

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

僵尸进程

僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用)没有读取到子进程退出的返回代码时就会产生僵死(尸)进程;

僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。

所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态;
僵尸进程会一直占用系统资源,还会导致内存泄漏,所以要尽量避免僵尸进程。

孤儿进程

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

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

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

守护进程&精灵进程

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


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

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

😍😁谢谢你的阅读。😸😼

相关推荐
大树886 小时前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠6 小时前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质7 小时前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
bush47 小时前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5207 小时前
Linux 11 动态监控指令top
linux
小宇宙Zz7 小时前
Maven依赖冲突
java·服务器·maven
Inhand陈工8 小时前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
酣大智8 小时前
ARP代理--工作原理
运维·网络·arp·arp代理
不会C语言的男孩8 小时前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言
shushangyun_8 小时前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化