文章目录
- 前言
- 一、Linux 进程管理基本知识
- 1、Systemd 的进程管理
- 2、父子进程与僵尸进程
- 2.1 父子进程
- 2.2 僵尸进程
- 3、Linux 系统间的通信信号
- 二、验证 Linux 进程管理机制
- 1、子进程后台运行
- 2、子进程前台运行
- 三、Docker 容器管理与 Linux 进程机制的联系
- 1、容器错误启动方式:后台启动应用
- 2、容器正确的启动方式:前台运行进程
- 3、官方推荐的容器启动方式
- 总结
前言
容器化技术以其轻量级、便携性和高度可扩展性,正在引领软件开发和运维的新潮流。 Docker 作为容器化技术的先驱,其设计和实现深度依赖于 Linux 内核的 进程控制机制
。这些机制不仅为 Docker 提供了 隔离性
和 安全性
的基础,还直接影响了容器的生命周期管理、资源分配和 进程调度
。本文将简要探讨 Linux 进程控制机制对 Docker 容器进程管理的影响。
大家好,我是技术界的小萌新,今天要和大家分享一些干货。在阅读之前请先点赞👍,给我一点鼓励吧!这对我来说很重要 (*^▽^*)
一、Linux 进程管理基本知识
1、Systemd 的进程管理
Systemd
是 Centos 7 发行版开始启用的系统服务 初始化
管理器,用于替代传统的 System V init ,优势是以服务的异步并行启动代替了 init
的脚本串行启动服务,显著减少了系统启动时间。
Systemd 使用 Unit 单元
来管理服务,每一个单元代表一个服务或系统资源,单元的类型包括服务单元(.service)、挂载单元(.mount)、设备单元(.device)等。
每个服务单元都有自己的配置文件,通常位于 /etc/systemd/system/ 或 /usr/lib/systemd/system/
目录下,配置文件后缀名为 .service。配置文件中定义了服务的启动、停止、重启等行为。我们可以通过 Systemd start | stop | restart |
命令的的方式来管理服务。
2、父子进程与僵尸进程
2.1 父子进程
Systemd 为 上帝进程
,它在系统启动时作为用户空间的第一个进程(PID 为 1)运行,作为其他所有用户空间的父进程负责启动他它们所辖的子进程,父子进程机制是操作系统中进程管理的一个重要概念。
它描述了进程之间的层级关系以及它们之间的 相互作用
,它规定所有子进程在创建时需要 继承
父进程的许多属性,如 用户权限
、文件描述符、环境变量等。
父子进程间还存在如下机制:
- 当子进程终止,父进程存活时,它会向父进程发送
SIGCHLD
信号。父进程可以通过 wait() 或 waitpid() 系统调用来等待子进程的终止,进行“收割”
,回收资源,并获取其退出状态。 - 当子进程存活,父进程消亡时,子进程成为
“孤儿进程”
,但会认 Systemd 为父进程,交给上帝进程托管进而后台运行
,例如 httpd 服务后台启动时,会生成一个 httpd 父进程,它生成众多子进程后会把自己杀死,从而让上帝进程托管
这些子进程,达到后台运行的目的。所有孤儿进程的默认
父进程为 Systemd。 - 由 Systemd 命令管理启动的子进程,都必须
前台运行
,因为后台运行的进程只能通过杀掉父进程的方式被 Systemd 托管,然而由上帝进程直接管理的程序只会被分配到一个
父进程,没有第二个,所以后台运行的结果只能是都被 Systemd 杀死,可以通过查看 Service 服务文件获知。这一点与容器的进程管理
联系十分紧密
。
2.2 僵尸进程
僵尸进程
(Zombie Process)是类Unix操作系统中的一个特殊进程 状态
。当一个子进程完成其执行并退出时,它的进程描述符(包括进程ID、退出状态等信息)并不会立即被操作系统完全释放,而是保留在系统中,虽然 释放
了内存,CPU 等 资源
,但会占用 进程表项
,若被占满则不能启动新进程。
承接上述情况,当子进程终止,父进程存活时,它会成为 “失效进程”
,即僵尸进程,倘若父进程 回收机制
不完善 (如 Redis daemon 进程) ,它就不会被 “收割”,直到父进程也死亡时,统一被 Systemd 上帝进程回收。(Systemd 先回收父进程,子进程成为孤儿进程,Systemd 同时负责对所有孤儿进程的回收)
3、Linux 系统间的通信信号
Linux定义了多种信号,如 SIGINT(中断信号,通常由用户通过Ctrl+C产生)、SIGTERM(终止信号,用于请求进程正常退出)、SIGKILL(杀死信号,用于立即终止进程,不能被忽略或捕获)、SIGALRM(闹钟信号,用于定时提醒)、SIGCHLD(子进程状态改变信号,通知父进程子进程已经改变状态)等。
进程可以通过kill 系统调用向另一个进程发送信号。例如, kill -9(SIGKILL) pid
会向进程ID为pid 的进程发送终止信号。
二、验证 Linux 进程管理机制
我们通过使得子进程在前台和后台运行,可以观察到父子进程的启动机制,示例如下:
1、子进程后台运行
我们需要把子进程放入后台运行,有两种方式,都是以父进程自杀的方式,来使得子进程被 systemd 托管的方式。如下, 直接执行
httpd 程序,可以看到 httpd 被放在了后台执行,这时候你也许没有看到 httpd 父进程被杀死的过程,因为 速度
太快了。同时使用 &
放入后台同样也是会杀死父进程,详细可见如下验证。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z7tTeNTz-1720453881932)(https://i-blog.csdnimg.cn/direct/08bf3b8287df4b44806328c5d3fd5309.gif#pic_center)]
这里我们通过 循环启动
进程的方式,可以看到当前 bash 下,确实通过我们的命令产生了 httpd 的主进程,且被杀死,所以“孤儿进程”产生了,此时 systemd 上帝进程就成为了其他 httpd 进程的父进程。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wbk6X2Kp-1720453881937)(https://i-blog.csdnimg.cn/direct/c754a64d9bc7452281278c2b330743e9.gif#pic_center)]
& 同理,通过执行 命令 &
启动进程的时候,可以看到,另开了一个 bash 终端运行进程,随后子进程杀死父进程成为后台进程,如上述 逻辑一致
,当然 & 也可以使进程挂在当前 bash 后台而不杀死 bash,但它也不会被 systemd 托管,这取决于子进程的 行为
。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l1jMsgbR-1720453881938)(https://i-blog.csdnimg.cn/direct/d68ad795097b44d9b75ebfa3e13b0137.gif#pic_cente =110%x)]
如果使用上帝进程启动后台运行 httpd 会是什么情况呢?为了验证这个问题,我们可以修改 httpd 的 服务文件
,它通过 $OPTIONS DFOREGROUND
的参数前台运行程序,去掉后再用 systemctl 的方式启动进程检查结果。
[root@ECS-PROXY ~] vim /usr/lib/systemd/system/httpd.service
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1P4JTBot-1720453881939)(https://i-blog.csdnimg.cn/direct/0eff9cbb73864574ae0c532c29c1d207.png#pic_center =70%x)]
可以看到,只有当用 systemd 快速启动
进程时,你才能观察到 httpd 进程被杀死,说明在 systemd 进程启动模式下,只会给进程 分配
一个父进程,当父进程被杀死,它不会 接管
孤儿进程,这与用户启动进程的行为不同。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-opbEPeq5-1720453881940)(https://i-blog.csdnimg.cn/direct/61fbde392cf54b358c29d41a7dca6ceb.gif#pic_center)]
2、子进程前台运行
当子进程前台运行时,就会阻塞父进程 bash 界面,用户 不能输入
命令,子进程就会一直运行,直至终止。而当子进程后台运行时,结果就截然不同了。
通过 -DFOREGROUND
参数可以实现进程前台运行的目的,如下可以看到当进程前台运行时, 阻塞
界面,父进程不会被杀死,当父进程退出,子进程会收到父进程的信号,跟着退出,不会成为后台进程。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8FYhufdn-1720453881940)(https://i-blog.csdnimg.cn/direct/390456730a6b43d490d1c0f511f59f5a.gif#pic_center)]
结论:
- 子进程需要杀死父进程,首先成为“孤儿进程”,从而被 systemd 托管才能后台运行。
- 由 systemd 上帝进程运行的进程必须
前台执行
,因为 systemd 只会分配一个父进程给它,如果子进程为了后台运行把父进程杀死,那么 systemd 不会继续分配,而是直接杀死子进程进行回收。 - 子进程前台运行会阻塞界面,但是父进程不会被杀死,父进程消亡,子进程跟着消亡,
不会进入
后台。
三、Docker 容器管理与 Linux 进程机制的联系
在Docker 容器中进程管理引用了 Linux 的 PID 命名空间
管理方式,每一个容器都是一个 独立
的 PID 命名空间,每个空间内的 PID 号可以 重复
,以这种方式 隔离容器应用
。
在容器内启动的 第一个
进程就会成为上帝进程 (PID=1) ,他的行为与 Linux 系统的 systemd 行为一致,如上述结论。Docker 的守护进程会时刻 监视
PID=1 的容器进程,只要它停止了,容器就会停止运行,所以需要保持根进程 活跃
,不能停止。
如果使用 Docker 的新手不了解上述的 Linux 进程管理机制,在使用容器时,就会出现容器无论如何启动不了的问题。这都是因为在 Docker 中,容器应用的根进程 必须
在前台运行,否则根进程后台启动完子进程后会自杀,导致容器无法正常启动,进入容器使用 bash 后台启动应用同理,一旦退出容器,bash 就会自动消亡,与上述逻辑一致。
下面进行演示:
1、容器错误启动方式:后台启动应用
当你使用 bash 进入容器后,后台启动应用,用户在容器内应用正常运行,一旦退出容器或者 bash 因为其他原因而消亡,应用也不会由于父进程的消亡去找 systemd 托管,因为每个 PID 命名空间都是独立管理程序的,在这里 bash 就是上帝进程,之后 Docker 守护进程检测不到 PID = 1
的进程存在,也会终止容器。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-51Nm8Zza-1720453881941)(https://i-blog.csdnimg.cn/direct/0c56651c07ed4ae98a211076cc47f446.gif#pic_center)]
这里使用了 watch -n 1 docker exec test ps -ef
监视了容器内的应用,可以注意到其 PPID
父进程 ID 为 0 ,说明其父进程不在容器内。以 bash 后台启动应用后退出容器就会停止。
2、容器正确的启动方式:前台运行进程
正常创建容器进入,以 bash 运行前台进程 nginx,可以看到 bash 被阻塞,此时按 crtl + p,crtl + q
可以退出容器,退出后可以看到容器内进程前台运行中,容器没有被 Docker 守护进程
停止,这才是容器正确的打开方式。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r6vcnzpZ-1720453881941)(https://i-blog.csdnimg.cn/direct/3e934dcb27b84367ab14891a06dc8772.gif#pic_center)]
3、官方推荐的容器启动方式
apache 官方 Dockerfile 推荐写法:
ENTRYPOINT [ "/usr/sbin/apache2" ]
CMD ["-D", "FOREGROUND"]
# 让 apache 以前台方式运行
nginx 官方 Dockerfile 推荐写法:
ENTRYPOINT [ "/usr/sbin/nginx", "-g", "daemon off;" ]
# daemon off; 即关闭 nginx 后台运行
在官方的推荐 Dockerfile
文件中,都表明了自动化 构建
Docker 镜像的过程中,需要让根进程以前台的方式运行,这就不会导致 Docker 容器的 错误停止
。
总结
本文科普了 Linux 的 进程管理机制
,特别是 Systemd 的进程管理、 父子进程关系
、僵尸进程问题以及信号通信,进而研究了一些机制在 Docker 容器化技术中的 应用
,指出了容器进程管理的 最佳实践
,包括前台运行进程的重要性和避免 错误停止
容器的方法。
文章到这里就结束了,希望我的分享能为你的技术之旅增添一抹亮色。如果你喜欢这篇文章,请点赞收藏支持我,给予我前行的动力!🚀