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

目录

进程的基本操作

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号进程释放掉,不会造成内存泄漏

相关推荐
艾莉丝努力练剑1 小时前
【Linux网络】Linux 网络编程入门:TCP Socket 编程(下)
linux·运维·服务器·网络·c++·tcp/ip
yuezhilangniao1 小时前
Ansible基础 ansible入门 针对不同python3版本 - 含 Terraform 入门联动
运维·自动化·ansible
宵时待雨1 小时前
linux笔记归纳4:进程概念
linux·运维·服务器·c++·笔记
零K沁雪1 小时前
OpenV_X_N 2.5.x 配置文件选项详解
linux
一勺菠萝丶1 小时前
如何在 Linux 服务器上使用 Speedtest 官方 CLI 测试带宽(小白教程)
java·服务器·前端
原来是猿1 小时前
TCP Echo Server 深度解析:从单进程到线程池的演进之路(中)
linux·服务器·数据库
leoZ2312 小时前
Linux 环境常用服务一键部署文档(Docker 版)
运维·docker·容器
fTiN CAPA2 小时前
Linux系统离线部署MySQL详细教程(带每步骤图文教程)
linux·mysql·adb
落魄实习生2 小时前
Jenkins安装及使用
运维·jenkins