百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 优雅编程 > 正文

linux 进程退出 泄露的内存会释放吗

sinye56 2025-02-15 01:05 3 浏览 0 评论


Linux体系架构

推荐视频:

360度无死角讲解进程管理,调度器的5种实现

工程师的圣地—Linux内核, 谈谈内核的架构

c/c++ linux服务器开发学习地址:C/C++Linux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂

基础概念

进程是个什么玩意?

进程是一个资源分配的单位。每个进程在操作系统中都有一个“进程控制块PCB*”来描述一个进程,在linux中使用 task_struct 这个结构体描述一个进程/线程。

线程和进程的异同在后续文章再讲,明确一个概念,linux内核中线程和进程都是同一个结构。

如何描述一个进程呢?

1、有一个pid,用作进程的唯一标识

2、有自己的内存空间 *mm

3、文件系统资源(进程的可执行文件路径等)*fs

4、进程打开的文件资源数组 *files

5、信号相关资源 *signal

所以我们自然的可以想到 task_struct 结构应该是这样的结构:

task_struct结构

linux系统中开启了N个进程,linux该怎么组织一堆 task_struct 结构呢?

先想下,我们有哪些场景会需要找进程的task_struct?

1、kill -9 pid 杀掉指定pid。

2、pstree 命令查询进程的树状父子关系

3、linux内核需要组织管理所有进程

所以,linux底层用3种结构维护所有 task_struct 结构:

1、hash结构 应对直接查询pid的场景

2、tree 结构 描述进程间父子关系

3、链表组织管理 task_struct

这是典型的空间换时间的思想,在不同的场景下使用不同的结构。

关于pid:在系统中的pid的数量是有限的

[root@dev ~]$ cat /proc/sys/kernel/pid_max32768

进程的生命周期

linux的一个进程是被fork() 出来的,这里主要讲这六种状态

想一想,理论上只需要三个状态(就绪、运行、睡眠)就可以描述进程状态了吧?为什么是6个状态呢?

按照自己的思路想一下:

一个进程被fork() 出来之后处于“就绪”状态,当进程拿到cpu之后,就进入“执行”态。因为cpu不可能被一个进程一直占用,所以执行态的进程也可能会切回就绪态。

那么什么情况下可能切回就绪态呢?

我们知道linux内核是抢占式的,那么有可能有优先级更高的任务要执行,cpu被抢占。

还有一种可能,分配给进程的cpu时间片用完了(分时调用)。

当进程在运行的时候经常会等资源,比如等网络发包、串口等,那么等资源的时候不应该再占用cpu,这时候进程应该主动让出cpu,进入睡眠态。

【文章福利】需要C/C++ Linux服务器架构师学习资料加群812855908(资料包括C/C++,Linux,golang技术,内核,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等)

僵尸态

那么僵死态是什么情况呢?

当一个子进程死亡,父进程还没有waitpid()的时候,子进程的状态就是僵死态。

僵死是一个非常短的临界状态,一旦父进程waitpid()后,子进程就“人间蒸发”消失了。

进程死亡进入僵死态后,进程名下的资源已经释放了,不可能出现内存泄露。

Linux内核中,父进程可以通过内核提供的wait4得到子进程的退出码(死亡原因),具体可查看linux的wait_task_zombie()*函数:

来做个实验:

?  ~ ./a.outppid = 58111child process pid:58118// 在另一个终端中执行 kill -9 58118child exited with killed by signal 9?  ~ ./a.outppid = 58181child process pid:58182// 在另一个终端中执行 kill -2 58182child exited with killed by signal 2

如果父进程不对子进程做waitpid() 清理操作,那么会怎么样?

我们将上述代码的第34行之前加入死循环,父进程fork子进程后,在死循环不去清理子进程。会发生什么情况?

?  ~ ./a.outppid = 58436child process pid:58437// 将子进程kill之后,父进程不回收子进程,子进程状态就变成Z+?  ~ kill -9 58437?  ~ ps aux | grep 58437Root          58445   0.0  0.0  4285692    700 s004  S+    2:33下午   0:00.00 grep --color=auto 58437Root          58437   0.0  0.0        0      0 s003  Z+    2:33下午   0:00.00 (a.out)

这时候即便对僵尸进程 kill -9 也杀不掉僵尸进程

一个进程已经是僵尸了,即便再杀它也杀不死它(死透了),当一个进程变成僵尸进程,它所占有的资源全部被linux释放掉了,除了对应的task_struct,父进程靠子进程的task_struct获取子进程的死亡原因,所以不必担心内存泄露。

那么怎么才能将僵尸进程彻底杀掉呢?

将僵尸进程的父进程杀掉,僵尸进程就会消失。

如果僵尸进程太多,会占用系统的pid资源,所以僵尸太多也不好。

一般来说,只有父进程没有回收(waitpid)子进程的情况,才会导致僵尸进程存在。

关于内存泄露:

内存泄露从来不是进程死了导致内存泄露,永远是一个进程运行随着时间的流失,所占内存越来越多,这种情况必有内存泄露。我们观察一个进程是否存在内存泄露,可以用连续多点采样法,对进程在不同的时间点采集进程的内存消耗情况,只有拉长采样时间才可以确定进程存在内存泄露。如果进程消耗内存在很长一段时间内是上下震荡收敛的,这种是正常情况,并不存在内存泄露。

停止态

什么叫做停止态?

进程在运行的时候不去睡眠,人为干扰进程,让进程进入停止态。

怎么可以让进程进入停止态呢?

1、给进程发STOP信号 kill -20 (STOP信号)

2、ctrl + z

3、gdb 对进程调试

如何从停止态恢复到运行态?

1、发送STOP信号后,执行 kill -18 (CONT信号)进入运行态

2、ctrl +z 进入停止态后,执行 fb或bg进入运行态

3、gdb 使用 continue 进入运行态

注意:STOP/CONT 信号除了暂停/回复之外,还会将进程挂起后变后台进程的作用,就像bg命令

linux下有一个cpulimit 命令可以控制进程的cpu的占用率

cpulimit -l -p

这个命令的原理就是不断的将进程停止->运行->停止->运行….

深睡眠和浅睡眠

深睡眠:必须等到资源才会醒

浅睡眠:资源来了可以醒,信号来了也会醒

信号就是针对进程的异步打断机制

linux下信号有:

$ kill -l 1) SIGHUP     2) SIGINT     3) SIGQUIT     4) SIGILL     5) SIGTRAP 6) SIGABRT     7) SIGBUS     8) SIGFPE     9) SIGKILL    10) SIGUSR111) SIGSEGV    12) SIGUSR2    13) SIGPIPE    14) SIGALRM    15) SIGTERM16) SIGSTKFLT    17) SIGCHLD    18) SIGCONT    19) SIGSTOP    20) SIGTSTP21) SIGTTIN    22) SIGTTOU    23) SIGURG    24) SIGXCPU    25) SIGXFSZ26) SIGVTALRM    27) SIGPROF    28) SIGWINCH    29) SIGIO    30) SIGPWR31) SIGSYS    34) SIGRTMIN    35) SIGRTMIN+1    36) SIGRTMIN+2    37) SIGRTMIN+338) SIGRTMIN+4    39) SIGRTMIN+5    40) SIGRTMIN+6    41) SIGRTMIN+7    42) SIGRTMIN+843) SIGRTMIN+9    44) SIGRTMIN+10    45) SIGRTMIN+11    46) SIGRTMIN+12    47) SIGRTMIN+1348) SIGRTMIN+14    49) SIGRTMIN+15    50) SIGRTMAX-14    51) SIGRTMAX-13    52) SIGRTMAX-1253) SIGRTMAX-11    54) SIGRTMAX-10    55) SIGRTMAX-9    56) SIGRTMAX-8    57) SIGRTMAX-758) SIGRTMAX-6    59) SIGRTMAX-5    60) SIGRTMAX-4    61) SIGRTMAX-3    62) SIGRTMAX-263) SIGRTMAX-1    64) SIGRTMAX

在一个进程进入深睡眠的时候,是不会对外界信号有任何反应,即便kill -9 也杀不掉进程。这里的深度睡眠和僵尸进程是有区别的!这里深度睡眠不是指TASK_KILLABLE(可杀的深度睡眠)

为什么要有深度睡眠?都用浅睡眠不行么?

Linux下特定情况必须使用深度睡眠的,举个栗子:

比如进程有个代码段在硬盘上,当进程想要执行这个代码段上的函数时,发生了page fault*,然后需要将缺失的代码段读到内存中,这时候linux会将进程置为深度睡眠。如果被置成浅度睡眠,那么进程就可以接收信号,如果这时候接收到信号,信号处理函数还可能执行刚刚缺失的代码段,那么就会又发生page fault… 禁止套娃!

如果发生page fault之后读取硬盘时,硬盘挂了,那么这时候进程就会一直处在深度睡眠态。什么时候能活过来呢?

1、硬盘恢复

2、电脑重启

总结:

深度睡眠和浅度睡眠

1、睡眠是一种阻塞状态

2、睡眠是“主动”设置为睡眠。

3、进程在等待资源的时候,将自己设置为睡眠态。比如等网络回包、磁盘操作

4、睡眠是内核态操作进入的

5、睡眠需要进程在用户态显示的调用Linux内核的api

6、无法强制进程进入深度睡眠,这个由linux内核控制

停止态是外部人为将进程设置为暂停,是被动的,属于作业控制。

fork函数执行后,父子进程执行的先后顺序是不确定的,除非调整
kernel.sched_child_runs_first 内核配置,人为控制父子进程先后顺序。

进程从睡眠态醒来变成就绪态,为什么不直接进入执行态?

进程醒来之后,因为进程执行优先级问题,不一定能被系统调度到。可能有更优先的程序需要先执行。

子进程变成僵尸后,父进程如果不清理,子进程才会一直处于僵尸状态。僵尸态是个过渡态,一般存在时间很短。

父进程变成僵尸,父进程也有父进程的,所有进程都是以linux内核启动的第一个进程init为root结点,在树上不断fork出来的进程。

所以,一旦init进程被杀掉,内核就会触发panic。

相关推荐

程序员:JDK的安装与配置(完整版)_jdk的安装方法

对于Java程序员来说,jdk是必不陌生的一个词。但怎么安装配置jdk,对新手来说确实头疼的一件事情。我这里以jdk10为例,详细的说明讲解了jdk的安装和配置,如果有不明白的小伙伴可以评论区留言哦下...

Linux中安装jdk并配置环境变量_linux jdk安装教程及环境变量配置

一、通过连接工具登录到Linux(我这里使用的Centos7.6版本)服务器连接工具有很多我就不一一介绍了今天使用比较常用的XShell工具登录成功如下:二、上传jdk安装包到Linux服务器jdk...

麒麟系统安装JAVA JDK教程_麒麟系统配置jdk

检查检查系统是否自带java在麒麟系统桌面空白处,右键“在终端打开”,打开shell对话框输入:java–version查看是否自带java及版本如图所示,系统自带OpenJDK,要先卸载自带JDK...

学习笔记-Linux JDK - 安装&配置

前提条件#检查是否存在JDKrpm-qa|grepjava#删除现存JDKyum-yremovejava*安装OracleJDK不分系统#进入安装文件目...

Linux新手入门系列:Linux下jdk安装配置

本系列文章是把作者刚接触和学习Linux时候的实操记录分享出来,内容主要包括Linux入门的一些理论概念知识、Web程序、mysql数据库的简单安装部署,希望能够帮到一些初学者,少走一些弯路。注意:L...

测试员必备:Linux下安装JDK 1.8你必须知道的那些事

1.简介在Oracle收购Sun后,Java的一系列产品就被整合到Oracle官网中,打开官网乍眼一看也不知道去哪里下载,还得一个一个的摸索尝试,而且网上大多数都是一些Oracle收购Sun前,或者就...

Linux 下安装JDK17_linux 安装jdk1.8 yum

一、安装环境操作系统:JDK版本:17二、安装步骤第一步:下载安装包下载Linux环境下的jdk1.8,请去官网(https://www.oracle.com/java/technologies/do...

在Ubuntu系统中安装JDK 17并配置环境变量教程

在Ubuntu系统上安装JDK17并配置环境变量是Java开发环境搭建的重要步骤。JDK17是Oracle提供的长期支持版本,广泛用于开发Java应用程序。以下是详细的步骤,帮助你在Ubuntu系...

如何在 Linux 上安装 Java_linux安装java的步骤

在桌面上拥抱Java应用程序,然后在所有桌面上运行它们。--SethKenlon(作者)无论你运行的是哪种操作系统,通常都有几种安装应用程序的方法。有时你可能会在应用程序商店中找到一个应用程序...

Windows和Linux环境下的JDK安装教程

JavaDevelopmentKit(简称JDK),是Java开发的核心工具包,提供了Java应用程序的编译、运行和开发所需的各类工具和类库。它包括了JRE(JavaRuntimeEnviro...

linux安装jdk_linux安装jdk软连接

JDK是啥就不用多介绍了哈,外行的人也不会进来看我的博文。依然记得读大学那会,第一次实验课就是在机房安装jdk,编写HelloWorld程序。时光飞逝啊,一下过了十多年了,挣了不少钱,买了跑车,娶了富...

linux安装jdk,全局配置,不同用户不同jdk

jdk1.8安装包链接:https://pan.baidu.com/s/14qBrh6ZpLK04QS8ogCepwg提取码:09zs上传文件解压tar-zxvfjdk-8u152-linux-...

运维大神教你在linux下安装jdk8_linux安装jdk1.7

1.到官网下载适合自己机器的版本。楼主下载的是jdk-8u66-linux-i586.tar.gzhttp://www.oracle.com/technetwork/java/javase/downl...

window和linux安装JDK1.8_linux 安装jdk1.8.tar

Windows安装JDK1.8的步骤:步骤1:下载JDK打开浏览器,找到JDK下载页面https://d.injdk.cn/download/oraclejdk/8在页面中找到并点击“下载...

最全的linux下安装JavaJDK的教程(图文详解)不会安装你来打我?

默认已经有了linux服务器,且有root账号首先检查一下是否已经安装过java的jdk任意位置输入命令:whichjava像我这个已经安装过了,就会提示在哪个位置,你的肯定是找不到。一般我们在...

取消回复欢迎 发表评论: