守护进程
守护进程
复习定位
守护进程是在后台运行、不与任何终端关联的进程——通常在系统启动时启动——在后台持续提供服务(SSHD、Nginx、MySQL)。守护进程通过setsid创建新会话脱离控制终端——fork两次确保不再持有终端。现代Linux使用systemd管理守护进程——提供自动启动、看门狗、日志收集等功能。
守护进程的特征
守护进程(Daemon)是:
- 在后台运行——没有控制终端。
- 通常以root或其他专用系统用户身份运行。
- 独立于所有会话——即使用户退出登录也不受影响。
- 通常在系统启动时由init/systemd自动启动。
常见的守护进程——sshd(SSH服务器)、ntpd(时间同步服务)、httpd(Nginx/Apache)、cron(定时任务)、systemd-journald(日志服务)。
守护进程的创建步骤
从普通进程变为守护进程的基本步骤:
- fork()——父进程退出(exit)——子进程继续。子进程成为孤儿被init收养→后台运行。
- setsid()——子进程中调用——创建一个新的会话并使其成为会话首进程——脱离原有终端控制。因为新会话没有控制终端——所以进程不再与任何终端绑定。
- 再次fork()——再次fork——确定进程不是会话首进程——从而失去调用打开终端设备的能力——确保了后续不可能意外获得控制终端。
- chdir("/")——改变当前工作目录为根目录——避免占用挂载点而导致文件系统无法卸载。
- umask(0)——清除文件创建掩码——确保守护进程创建的文件具有所需的权限。
- 关闭所有文件描述符——关闭从父进程继承的0/1/2(stdin/stdout/stderr)及其他打开的fd——避免占用父进程的资源。
- 重定向0/1/2到/dev/null——防止守护进程错误地写入到终端的stdout。
现代Linux——systemd
在systemd出现之前——sysVinit通过/etc/rc.d/init.d/中的启动脚本来管理守护进程——用service daemon start/stop/restart控制。
systemd使用单元文件(unit files)管理守护进程——/etc/systemd/system/xxx.service:
[Unit]
Description=My Daemon
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/my_daemon
Restart=on-failure
User=daemon_user
[Install]
WantedBy=multi-user.targetsystemctl enable daemon——设置开机启动——systemctl start daemon——启动服务。systemd维护了所有服务的依赖和启动顺序——提供统一的管理接口。
日志与监控
systemd-journald统一收集守护进程的stdout/stderr到日志中——使用journalctl -u daemon.service查看。服务异常崩溃时——systemd可以自动重启(Restart=on-failure)。systemd也通过cgroups跟踪所有进程资源——防止守护进程的内存泄漏影响到整个系统。
复习检查
守护进程为什么需要fork一次后父进程退出——子进程被init收养后成为后台进程而不再有前台任务的牵涉——原父进程是交互进程的普通身份转为后台。
setsid()的作用——创建一个新会话——使进程成为新会话的首进程——脱离原控制终端——即使关闭原来的终端ssh会话、守护进程也不会收到SIGHUP。
为什么要再次fork(第二次fork)——进程从setsid后会取得会话首进程的身份——可能重新获得控制终端——再次fork后子进程不是会话首进程——永远无法再获得终端。
关闭文件描述符0/1/2(标准输入/输出/错误)并重定向到/dev/null的原因——防止守护进程在read端时从stdin读取到终端输入——或向stdout写数据写到用户终端(终端被关闭时write出错)——重定向到/dev/null后忽略这些输入/输出。
systemd的自动重启机制如何工作——当服务进程因异常退出——systemd根据Restart策略(always/on-failure/on-abnormal)决定是否重新启动——比起传统sysVinit需要人工监测并启停的做法具有更好的自动化。