8

上下文

我有一个 linux[1] 系统,它管理一系列第三方守护进程,与这些守护进程的交互仅限于 shell[2] 初始化脚本,即只有 {start|restart|stop|status} 可用。

问题

进程可以假定先前运行的进程的 PID,通过使用它的 PID 检查正在运行的进程的存在来检查进程的状态。

示例

进程 A 使用 PID 123 运行,随后死亡,进程 B 使用 PID 123 进行初始化,并且状态命令以不真实的(错误的)“OK”响应。换句话说,我们只从它的 PID 中检查一个进程的存在以验证该进程是否正在运行,我们假设如果存在具有此 PID 的进程,它就是有问题的进程。

建议的解决方案

  1. 使用 PID 询问进程,以确保命令/守护程序按预期运行。这个方案的问题是命令和PID都需要匹配;因此需要维护和保持多位信息同步,并增加错误/边缘条件的复杂性。
  2. 将 PID 文件的创建时间与进程的启动时间相关联,如果进程在 PID 文件创建时间的某个增量内,我们可以相当肯定命令/守护程序运行是预期的。

除了存在使用该 PID 运行的进程之外,是否有一种标准方法来批准进程/PID 文件的真实性?即我(作为系统)想知道您(进程)是否正在运行,以及您是否是我认为的您(A 而不是 B)。

假设我们选择实施上面提出的第二种解决方案,PID 创建时间和进程启动时间之间的置信区间/增量是多少是合理的?在这里,合理意味着类型 1 / 类型 2 错误之间可接受的折衷。

[1] CentOS/RHEL [2] 重击

4

3 回答 3

5

文件内容:

/proc/{PID}/cmdline

是用于启动进程的命令行。那是你需要的吗?

于 2012-09-07T12:56:21.883 回答
0

我正在寻找问题的答案如何确保一个进程仍然是同一个进程,我想到了这个问题的两个解决方案,即一个进程是否可以由元组(pid,命令)或(pid,进程开始时间)。但遗憾的是,这两种选择似乎都不够。

  1. (pid, command) 是不够的,因为 pid 重用,例如,原始进程可能已经被杀死,并且 pid 空闲以供重用,具有相同命令行的另一个进程可能已经使用该 pid 启动。

  2. (pid, process start time) 似乎有时会出现启动时间少量变化的问题。

现在,另一个选项来自能够更改进程标题,例如,我们可以在您的进程标题中放入一个随机数,并将随机数与 pid 一起存储在 pidfile 中。那么当我们要检查进程是否还是同一个进程时,例如要杀死它,我们可以检查pid文件的pid的进程标题是否仍然以pid文件中的随机数开头。

为了便于说明,请考虑这个简短的 python 片段,类似的功能应该可以通过其他语言的库获得:

#!/usr/bin/env python3
import os, setproctitle
nonce = bytes.hex(os.urandom(8))                      # create hex nonce
setproctitle.setproctitle(nonce + " " + setproctitle.getproctitle()) # set title
with open("run.pid", "w"): f.write(pid + " " + nonce) # store pid and nonce in pidfile

连同这个shell脚本一起杀掉进程,如果还是一样。

#!/bin/sh
PID=$(cat run.pid | cut -f1 -d" ")     # get pid from pidfile
NONCE1=$(cat run.pid | cut -f2- -d" ") # get nonce from pidfile
NONCE2="$(ps -p "$PID" -o command= 2> /dev/null | cut -f1 -d" ")" # get nonce from process title
if [ "$NONCE1" = "$NONCE2" ]; then     # if nonces equal
  kill "$PID"                          # kill process
  echo "killed"
else                                   # otherwise the process you wanted to kill
  echo "was already dead"              # has been dead anyway
fi
于 2020-06-17T23:08:35.493 回答
0

我的解决方案是捕获命令(通过/proc/PID/cmdline)以及相对开始时间。使用绝对开始时间(通过ps -p PID -o lstart=)可能会起作用,但如果您的系统时钟发生变化(例如来自 NTP 更新或夏令时),您会得到令人困惑的结果。

这是我的实现:

# Prints enough detail to confirm a PID still refers to the same process.
# In other words, even if a PID is recycled by a call to the same process the
# output of this command should still be different. This is not guaranteed
# across reboots.
proc_detail() {
  local pid=${1:?Must specify PID}
  # the process' commandline, if it's running
  # ensures a non-existant PID will never have the same output as a running
  # process, and helps debugging
  cat "/proc/$pid/cmdline" 2> /dev/null && echo
  # this is the number of seconds after boot that the process started
  # https://unix.stackexchange.com/a/274722/19157
  # in theory this could collide if the same process were restarted in the same
  # second and assigned the same PID, but PIDs are assigned in order so this
  # seems acceptably unlikely for now.
  echo "$(($(cut -d. -f1 < /proc/uptime) - \
           $(ps -p "$pid" -o etimes= 2> /dev/null || echo "0")))"
}

我还决定存储此输出,/dev/shm以便在关机时自动为我清除。还有其他可行的选项(例如@rebootcronjob),但对于我的用例而言,写入a 既tmpfs简单又干净。

于 2016-04-07T05:17:33.420 回答