问题 识别内核线程


我想知道如何区分内核线程和用户线程,以便我正在构建一个进程扫描器。我很难找到两种类型的好定义。

我发现内核线程没有自己的内存,因此/ proc / $ pid / status中没有Vm *值,而/ proc / $ pid / exe上的stat也没有返回任何内容。

因此,我认为如果进程没有Vm *值且没有inode编号,我可以识别内核线程。我想错了......我的脚本看到了php-cgi进程,有时会被识别为内核进程。

如果发现大多数错误识别的进程都是僵尸,这些僵尸会在一秒钟之后消失。所以我实施了一个简单的检查,看看状态是否为“Z”。如果是这样,请忽略它。 这为我节省了很多误报,但我仍然收到有关php-cgi内核进程的消息。

谁能告诉我如何以正确的方式区分内核线程和用户线程?


1434
2017-08-31 10:24


起源

使用Google我理解所有内核线程都是kthreadd的孩子......但是在某些CentOS 5服务器上我没有看到kthreadd进程。我确实看到了一个kthread进程,但我在进程列表中看到的一些内核线程(使用ps auxf)不是该进程的子进程。 - HighKing
按照惯例,内核线程的名称在方括号中,例如: [ksoftirqd/0]。此外,按照惯例,斜杠后面的数字表示CPU编号。 - ninjalj
我希望这是真的...在我的系统上,我看到一个kswapd0,在ps中列出了括号。在/ proc / $ pid / status中,它的名称没有括号。此外,名称中没有斜线。 - HighKing
我想我之前有过这样的事情:在更新的内核(CentOS 6.x有2.6.32)上,所有内核线程似乎都是kthreadd的孩子。在较旧的内核(CentOS 5.x有2.6.18)上,我看到内核线程是kthread的子代(没有额外的d)或者是init的子代。如果有人能证实这一点会很好。 :-) - HighKing


答案:


那里  内核线程和用户空间线程之间的一些明显差异:

  • /proc/$pid/cmdline 对于内核线程是空的 - 这是使用的方法 ps 和 top 区分内核线程。

  • /proc/$pid/exe 符号链接没有内核线程的目标 - 这是有道理的,因为它们在文件系统上没有相应的可执行文件。

    更具体地说, readlink() 系统调用返回 ENOENT (“无此文件或目录”),尽管链接本身存在,但表示该进程的可执行文件不存在(并且从未这样做)。

    因此,检查内核线程的可靠方法应该是调用 readlink() 上 /proc/$pid/exe 并检查其返回码。如果成功那么 $pid 是一个用户进程。如果它失败了 ENOENT,然后一个额外的 stat() 上 /proc/$pid/exe 应该从刚刚终止的进程中分辨出内核线程的情况。

  • /proc/$pid/status 缺少几个字段  内核线程 - 更具体地说是与虚拟内存相关的几个字段。


12
2017-09-01 20:13



在我的原始帖子中,您看到/ proc / $ pid / status中与虚拟内存相关的行无法被信任,因为正在关闭的进程也没有这些行。这是我问题的最大部分...... :-P我会调查/ proc / $ pid / cmdline,但由于某些进程重写,我认为依靠它不是一个好主意。 - HighKing
@HighKing:是的, cmdline 可以通过线程更改来影响 argv[0]虽然我不确定它是否会变得完全空洞。所以我发现了另一个不同之处:-) - thkala
我已经找到/ proc / $ pid / exe,看到我的第一篇文章。 ;-)好吧,我在/ proc / $ pid / cmdline上添加了一个检查,所以现在检查cmdline是否为空,如果没有VmSize,并且exe上的stat返回零结果。我仍然得到被识别为内核线程的php-cgi进程。 :-P - HighKing
@HighKing:是的 ps 还将它们显示为内核线程? - thkala
@HighKing:呃,1秒......如果你做了 stat 上 /proc/$pid/exe 并且可执行文件被删除它将错误输出,因为 stat 将尝试取消引用该链接。你需要的是检查输出 readlink(2)... - thkala


正如您在上面的评论中指出的那样,所有用户进程都是init进程的后代(pid = 1)。内核线程不是init进程的后代,因为init是用户进程,而用户进程无法创建内核线程。因此,要检查进程p是否是用户进程而不是内核线程,需要对流程图进行操作并评估是否 init dom p 在哪里是dom 统治者 运营商。具体在Python中:

def is_user_process(p):
  if (p=='1'):
    print 'User process'
  else:
    pstat = open('/proc/%s/stat'%p).read().split()
    parent = pstat[3]
    if (parent=='1'):
      print 'User process'
    elif (parent=='0'):
      print 'Kernel thread'
    else:
      is_user_process(parent)

4
2017-12-21 04:21





这是一个在bash下运行的版本:

# check if pid is user process and not a kernel thread
is_user_process() {
  if [[ $1 -eq 1 ]]; then
    return 0
  else
    parent=$(grep -e '^PPid:' /proc/$1/status | cut -c6-)
    if [[ $parent -eq 1 ]]; then
      return 0
    elif [[ $parent -eq 0 ]]; then
      return 1
    else
      is_user_process $parent
    fi
  fi
}

要用它呢

~$ is_user_process `pgrep kthreadd` || echo "kthreadd is kernel process"

至少这对我来说是第一个有用的解决方案,感谢python版本的er0。


0
2018-06-10 21:57