设为首页 加入收藏

TOP

Linux守护进程、sighup与nohup详解(一)
2018-06-04 08:51:20 】 浏览:447
Tags:Linux 守护 进程 sighup nohup 详解

前端时间帮忙定位个问题。docker容器故障恢复后,其中的keepalived进程始终无法启动,也看不到Keepalived的日志。


strace 查看系统调用之后,发现了原因所在


socket(PF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 3
connect(3, {sa_family=AF_LOCAL, sun_path="/dev/log"}, 110) = -1 ENOENT (No such file or directory)
close(3)                                = 0
open("/var/run/keepalived.pid", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=1, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe85ab1b000
read(3, "\n", 4096)                    = 1
read(3, "", 4096)                      = 0
close(3)                                = 0
munmap(0x7fe85ab1b000, 4096)            = 0
kill(0, SIG_0)                          = 0
socket(PF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 3
connect(3, {sa_family=AF_LOCAL, sun_path="/dev/log"}, 110) = -1 ENOENT (No such file or directory)
close(3)                                = 0
exit_group(0)                          = ?
+++ exited with 0 +++


这就是一个典型的Linux单例守护进程启动做的事情:检测进程是否已经存在(判断记录文件是否存在以及对应pid进程是否还在执行),并通过syslog套接字文件向syslog服务端发送日志。


很显然,Keepalived无法正常启动是故障宕机时,相应的pid文件没有清理干净,如果仅仅如此,Keepalived应该可以启动,一般守护进程启动都会覆盖残留的锁文件,问题关键在read(3, "\n", 4096) : 锁文件Keepalived.pid是空的!! 而kil 向进程0 发送信号0,执行成功,则Keepalived认为已经有Keepalived进程正在运行。所以问题出在锁文件存在且内容为"\n",故依次清理 keepalived.pid vrrp.pid checkers.pid文件后,Keepalived正常启动。至于定位为何锁文件内容为"\n",那是后话了。


经此一事,笔者想写一写Linux 守护进程


守护进程特点与相关概念


并非运行时间长的程序即是守护进程,笔者并未找到守护进程最标准的定义,但守护进程都有下面几个特点:


1、没有控制终端,终端名设置为?号:也就意味着没有 stdin 0 、stdout 1、stderr 2


2、父进程不是用户创建的进程,init进程或者systemd(pid=1)以及用户人为启动的用户层进程一般以pid=1的进程为父进程,而以kthreadd内核进程创建的守护进程以kthreadd为父进程


3、守护进程一般是会话首进程、组长进程。


4、工作目录为 \ (根),主要是为了防止占用磁盘导致无法卸载磁盘


这里涉及到一些概念,是unix为了更好管理进程间的关系提出的概念和方法,稍做说明下


控制终端


通过网络登录或者终端登录建立的会话,会分配唯一一个tty终端或者pts伪终端(网络登录),实际上它们都是虚拟的,以文件的形式建立在/dev目录,而并非实际的物理终端。


在终端中按下的特殊按键:中断键(ctrl+c)、退出键(ctrl+\)、终端挂起键(ctrl + z)会发送给当前终端连接的会话中的前台进程组中的所有进程


在网络登录程序中,登录认证守护程序 fork 一个进程处理连接,并以ptys_open 函数打开一个伪终端设备(文件)获得文件句柄,并将此句柄复制到子进程中作为标准输入、标准输出、标准错误,所以位于此控制终端进程下的所有子进程将可以持有终端


与控制终端相连的会话首进程也叫控制进程


进程组


进程组是一个或者多个进程的集合。一般由某个程序fork出一个家族来构成进程组,或者由管道命令建立作业构成进程组。


同一个进程组中的所有进程接收来自同一终端的信号。


进程组中的第一个进程作为进程组的首长,进程组id取首长进程的id。在各个进程中,通过函数getpgrp获取其所属进程组id


孤儿进程组


一个进程的父进程终止后,进程变成了孤儿进程,将被pid为1的进程(init进程或者systemd)收养。


而对孤儿进程组的定义是:进程组中每个进程的父进程要么在组中,也么不在该组所在会话中。


换言之,如果一个进程组中进程的父进程如果是组中成员,或者是init、systemd进程的话,这个进程组就一定是孤儿进程组。这样的进程组是很常见的,下图就是一个简单且典型的孤儿进程组



很显然,只有一个进程的进程组,并且是孤儿进程的话,进程组将变成孤儿进程组(哪怕它只有一个进程)。


典型的例子是一个父进程fork子进程之后,父进程立即退出,这样子进程所在的进程组将变为孤儿进程组。这样的孤儿进程组中的每个停止(Stopped)状态的每个进程都将收到挂断信号(SIGHUP),然后又立即收到继续信号(SIGCONT)。所以fork子进程之后,退出父进程,如果子进程还需要继续运行,则需要处理挂断信号,否则进程对挂断信号的默认处理将是退出。


此时的孤儿进程组并没有变为后台进程,一些博客将后台进程说成是孤儿进程组的一个特点,笔者认为是不正确的,在他们的示例中,孤儿进程组变为后台进程的原因是:父进程退出后,子进程在运行时向自身发送了SIGTS

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Reactor三种线程模型与Netty线程.. 下一篇Python的urllib和urllib2模块

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目