深度理解进程----进程状态

目录

进程的基本操作

1.查看进程

方法一:

方法二:

[top 指令](#top 指令)

[ps 指令](#ps 指令)

2.通过系统调用获取进程标示符(PID)

3.通过系统调用创建子进程

进程状态

僵尸进程

孤儿进程


进程的基本操作

在深度理解进程前,我们需要对进程的操作进行初步的了解,方便我们了解进程的相关属性,随着进程的学习,我们接触到的有关进程的操作越来越多,让我们先慢慢开始吧。


1.查看进程

对于进程的学习,我们肯定需要知道怎么查看进程的相关属性,才能帮助我们理解进程,在展示操作的时候,我会先介绍一些进程的相关属性,没听过没关系,慢慢学。

方法一:

进程的信息通过 /proc 系统文件查看

如:要获取 PID(进程标示符) 为1的进程信息,只需要 ls /proc/1

方法二:

大多数进程信息可以采用 top 和 ps 指令来获取


top 指令

ps 指令

a:显示一个终端所有的进程

x:显示没有控制终端的进程,例如后台运行的守护进程

j:显示进程标示符相关的信息

u: 以用户为中心的格式显示进程信息

ps axj 显示所有用户的进程对应的标示符相关的信息

ps aux 或者 ps -ef 显示所有用户的进程

ps -u root 查看 root 用户的进程

ps ux 查看当前用户的进程

最常用的查看一个进程的方法

ps aux | grep 程序名 | grep -v grep


2.通过系统调用获取进程标示符(PID)

getpid 获取当前进程的 PID

getppid 获取当前进程的 PPID

cpp 复制代码
  1 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <unistd.h>
  4 
  5 int main()
  6 {
  7     printf("我的进程PID:%d\n", getpid());                                                                                        
  8     printf("我的父进程PID:%d\n",getppid());
  9     return 0;
 10 }

3.通过系统调用创建子进程

fork 创建子进程

如果创建子进程成功,给父进程返回子进程的 PID,给子进程返回 0,如果创建失败,给父进程返回-1,子进程不存在

cpp 复制代码
  1 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <unistd.h>
  4 
  5 int main()
  6 {
  7     pid_t id = fork();
  8     if(id < 0)
  9     {
 10         perror("fork faile");
 11         return 1;
 12     }
 13     else if(id == 0)
 14     {
 15         printf("我是子进程,我的PID:%d, 我的父进程PID:%d\n", getpid(), getppid());                                              
 16     }
 17     else
 18     {
 19         printf("我是父进程,我的PID:%d\n", getpid());
 20     }
 21     return 0;
 22 }

思考:

  1. fork 为什么有两个返回值?

  2. 父进程和子进程得到的fork返回值为什么不同?

  3. 为什么一个变量可以使 if 和 else if 同时成立?

首先需要知道一点,进程具有独立性,进程之间互不影响。

fork 创建一个子进程之后,父进程和子进程的task_struct存在两个指针,一个是指向代码的指针,一个是指向数据的指针,它们共享代码段,且"共享数据段",因为进程具有独立性,当一个进程想要修改数据段中的数据,为了不影响其他进程,就会发生写时拷贝,将为修改的数据再开辟一个物理空间来存储它。这样进程间就独立了。

那么,如何理解上述三个问题呢?

父进程调用fork函数,在fork函数内部就创建了子进程,所以fork函数进行返回时父进程和子进程就已经共享代码和数据了,那么fork 进行返回时,由于对父子进程返回值不同,就发生了写时拷贝,在父进程中返回值为子进程PID,在子进程中返回值为0。


进程状态

就绪状态:进程在运行队列中,并没有占用CPU的状态

如图所示,后面4个进程在运行队列中,但没有占用CPU,此时这些进程为就绪状态,在Linux下没有做细分,可以被称为运行状态

阻塞状态:进程等待某种事件的完成,例如 scanf 函数,如果你不向终端输入任何东西,则该进程不会向下执行代码,此时的状态为阻塞状态

挂起状态:内存空间严重不足时,操作系统会将就绪状态或者阻塞状态的代码和数据(唤入)磁盘上的swap分区,在执行该进程时,再将代码和数据从磁盘上的swap分区进行唤出

Linux 中的进程状态

cpp 复制代码
/*
 * 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运行状态:进程在运行中或者在运行队列里

S睡眠状态:进程在等待事件完成(例如:代码中带有scanf, 进程就会从运行队列转到等待队列),这种睡眠状态被称为可中断睡眠

D磁盘休眠状态:进程在等待IO的结束,这种睡眠状态被称为不可中断睡眠

T停止状态:进程收到了信号,被暂停了

t调试跟踪暂停状态:对所写的可执行程序的代码进行调试的进程

X死亡状态:进程结束了,这个状态只是一个返回状态,在任务列表中看不到

Z僵尸状态:子进程退出,父进程不做管理,子进程就会变成僵尸进程


僵尸进程

僵尸状态是一个比较特殊的状态,当进程退出并且父进程没有等待子进程(后面会讲),从而没有读取到子进程退出状态的信息,此时子进程就会变成僵尸状态

僵尸进程的PCB会一直存在进程列表中,(但僵尸进程的其他资源(如代码,数据,虚拟地址空间,页表等)会被操作系统回收) 并且会一直等待父进程读取退出状态的信息,此时就会占有内存空间,从而发生内存泄漏

详谈内存泄漏:

学习语言的时候,我们写的程序向堆区申请的空间,没有手动释放,这不叫内存泄漏,因为当我们进程退出的时候,操作系统会进行对我们进程申请的空间进行管理并释放。

产生内存泄漏的原因有两种:

一种是僵尸进程

一种是常驻进程,长时间甚至一直占用内存空间,不断向堆区申请空间,且忘记了手动释放


孤儿进程

孤儿进程:父进程先退出,子进程后退出,此时子进程被称为孤儿进程

孤儿进程会被1号进程领养,所以子进程退出时,它的相关数据会被1号进程释放掉,不会造成内存泄漏

相关推荐
A小辣椒1 天前
TShark:Wireshark CLI 功能
linux
A小辣椒1 天前
TShark:基础知识
linux
AlfredZhao1 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao2 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334662 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪2 天前
linux 拷贝文件或目录到指定的位置
linux
大树883 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质3 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
bush43 天前
嵌入式linux学习记录十四、术语
linux·嵌入式