识别内核线程

时间:2012-08-31 10:24:46

标签: linux process

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

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

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

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

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

4 个答案:

答案 0 :(得分:12)

内核线程与用户空间线程之间存在 的一些明显差异:

  • /proc/$pid/cmdline对于内核线程是空的 - 这是pstop用来区分内核线程的方法。

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

    更具体地说,readlink()系统调用返回ENOENT“没有这样的文件或目录”),尽管链接本身存在,表示事实这个过程的可执行文件不存在(从来没有)。

    因此,检查内核线程的可靠方法应该是在readlink()上调用/proc/$pid/exe并检查其返回码。如果成功,则$pid是用户进程。如果它与ENOENT一起失败,则stat()上的额外/proc/$pid/exe应该告诉刚刚终止的进程内核线程的情况。

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

答案 1 :(得分:4)

正如您在上面的评论中指出的那样,所有用户进程都是init进程的后代(pid = 1)。内核线程不是init进程的后代,因为init是用户进程,而用户进程无法创建内核线程。因此,要检查进程p是否是用户进程而不是内核线程,需要对流程图进行操作并评估init dom p dom是否为Dominator运算符。具体在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)

答案 2 :(得分:0)

这是一个在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。

答案 3 :(得分:0)

您可以从flagssee proc(5) manpage)中读取线程/proc/[pid]/stat的值,并检查它是否设置了PF_KTHREAD位标志。

PF_KTHREAD常量本身就是available since 2.6.17,大约十年了,它的值是hasn't changed since then

#define PF_KTHREAD      0x00200000  /* I am a kernel thread */

即使包含此常量(include/linux/sched.h)的头文件也不会导出到用户空间,并且在源代码中具有此定义的副本,并且在运行时从用户空间进行了内核版本检查(例如,使用{{3 }}系统调用)应该非常健壮。