进程退出后占用的内存都去哪儿了?

时间:2024-03-23 13:03:52

本文是《深入理解操作系统》第四章,从本章开始将开启第一个重要的主题:进程,彻底理解进程对程序员来说是极为重要的,本章就从程序员的角度来讲解到底什么是进程、操作系统是如何实现进程的。本文承接上篇《进程是如何运行的》,以下为本篇目录:

  • 进程自愿退出

  • 操作系统终止进程

  • 进程终止进程

  • 进程退出后占用的内存去哪了?

  • 总结

本节是进程三部曲的最后一篇,在经历了进程创建和进程运行之后,进程迎来终结,是的,任何进程都不会一直运行,当进程终止后,程序不再运行,进程所占用的资源(内存,打开的文件等等)将被操作系统回收。

进程退出一般出现在以下几种情况:

  • 正常退出,进程执行完任务

  • 错误退出,进程遇到不可继续运行的错误(非进程本身的问题)

  • 被操作系统终止,进程本身有问题,比如进程企图访问不属于自己的内存地址

  • 被其它进程终止,比如通过资源管理器我们可以选择终止掉某个进程

在这四种情况下,只有前两种情况是进程自愿退出的,因此,总体上可以分为三类:进程自愿退出,操作系统终止进程以及进程终止进程。接下来我们就详细的看一下这几种情况。

 

进程自愿退出

大多数情况下,进程退出是因为进程完成了自己要执行的任务,比如使用Windows时你不想听音乐了就关闭音乐播放器(进程),Linux下程序员写完后代码后退出vim(进程)等等。在这种情况下进程可以直接调用系统调用来退出,在Linux下这个系统调用叫exit,在Windows下叫ExitProcess。

写过C/C++程序的同学可能会问,我写的程序在main函数结束后就退出了,没有显示的调用exit系统调用啊,确实是这样的,原来我们的编译器在main函数之后自动加上了exit系统调用,所以作为程序员,我们在main函数中无需显示的调用exit系统调用。

但是当我们的程序遇到不可继续运行的外在错误时情况就不一样了,比如我们的程序需要从网络中接收数据,但是已经判断出来其实已经断网了,这时我们的进程就没有必要继续运行了,但是注意,在判断出断网时程序还没有执行到main函数,而我们又不想程序继续运行,因此可以通过显示的调用exit来退出进程。也就是说当进程遇到了不可继续运行的错误(不是进程本身的问题)时,作为程序员我们可以显示的调用exit来退出进程。注意在程序的任何部分调用exit都会导致进程退出。

另外需要注意的一点是return和exit系统调用的区别,return仅仅表示return语句所在的函数执行完成,当return执行后该函数的栈帧就会被回收,return和进程本身没有关系,只有当main函数return后进程才会退出,而此时进程退出也不是return的原因,是因为编译器自动在main函数的return之后加上了exit系统调用。与return不同,exit是系统调用,当执行该系统调用后,无论exit处于程序的那个函数中,只要执行exit系统调用整个进程就都退出了,我们的程序将不再继续运行,进程占用的整个内存空间将被操作系统回收。

 

操作系统终止进程

当进程被操作系统终止掉的情况出现后,作为程序员,你应该知道,一般情况下这其实是程序中的bug引起的,当进程被操作系统终止后,一些情况下操作系统会非常贴心的将进程被终止时刻的内存状态保存下来供程序员调试使用。在Linux下进程的内存状态是通过Core文件来保存的,这就是常见的Core Dump,dump就是倾倒,倾卸的意思,把进程内存中的状态倾倒(dump)出来,倾倒到core文件中,程序员就可以通过gdb之类的调试工具来查找bug了。

比如著名的Segmentation Fault段错误,这个错误就是由于我们的进程试图越狱访问不属于自己的内存空间时而被操作系统kill掉了。一般这种错误同样是程序的bug引起的。

 

进程终止某个进程

同进程可以创建进程一样,一个进程同样可以终止掉另一个进程。在Windows中我们可以通过打开任务管理器,找到某个进程,然后右击选择“结束任务”就可以将某个进程退出了。在Linux下,一般我们可以用过ps命令找到某个需要终止的进程的进程号,然后使用kill命令就可以终止掉某个进程。

其实,这种方式本质上是这样的,杀手进程(Windows中的资源管理器进程,是的,资源管理器也是一个进程,Linux中的kill进程)通过系统调用向操作系统发起请求,请求操作系统终止掉某个进程,操作系统响应该杀手进程的请求,将该进程终止掉。这个系统调用在Linux下是kill,在Windows下叫TerminateProcess。不过,杀手进程一定要有相应的权限才可以完成该操作。

 

进程退出后占用的内存去哪了?

作为程序员我们需要执行,当进程结束后,进程占用的所有的内存空间将被操作系统回收,包括函数调用使用的栈、动态内存分配使用的堆、代码段中的机器指令、数据段中的全局变量等。所谓回收,就是将进程所占用的内存空间标记为可用,可以用于其它用途了。而当进程运行时这些所占用的内存是不可以用作其它用途的。因此,我们不用担心堆区上动态分配的内存在程序结束前没有释放,这是不会造成内存泄漏的,因为进程退出后包括堆区在内所有占用的内存都会被操作系统回收,如图所示:

进程退出后占用的内存都去哪儿了?

因此如果程序存在内存泄漏但是又一时找不出原因的话,可以通过不断重启进程来解决内存不断上涨的问题,直到找到问题的原因。

 

总结

作为进程三部曲的最后一篇,我们了解了各种进程退出的情况,一般有三种:程自愿退出、操作系统终止进程以及进程终止进程。作为程序员,我们需要知道进程终结后所占用的内存将会被操作系统回收,因此无需担心内存泄漏的问题。

经过本章前几节的铺垫之后,我们已经对进程有了较为深刻的理解,接下来的章节中我们学习一下进程是如何实现的

更多精彩内容,欢迎关注公众号“码农的荒岛求生”。

进程退出后占用的内存都去哪儿了?