18

I'd like to know how I can distinguish a kernel-thread from a user-thread for a process-scanner I'm building. I'm having a hard time finding a good definition of both types.

I found that kernel-threads don't have memory of their own, so no Vm* values in /proc/$pid/status, and that a stat on /proc/$pid/exe does not return anything.

So, I figured I could identify kernel threads if a process has no Vm* values and no inode number. I figured wrong... my script sees php-cgi processes that are identified as kernel processes sometime.

If found that most of those wrongly identified processes are zombies that are gone a second later. So I implemented a simple check to see if the status is "Z". If so, ignore it. That saved me a lot of false positives, but still I receive messages about php-cgi kernel-processes.

Can anyone tell me how I can distinguish a kernel-thread from a user-thread the right way?

4
  • 1
    Using Google I understand all kernel-threads are a child of kthreadd... but on some CentOS 5 servers I don't see a kthreadd process. I do see a kthread process but some kernel threads I see in the processlist (using ps auxf) are not a child of that process. Commented Aug 31, 2012 at 12:53
  • 1
    kernel threads by convention have names in square brackets, e.g: [ksoftirqd/0]. Also, by convention, a number after a slash indicates a CPU number. Commented Aug 31, 2012 at 14:30
  • I wish that was true... on my system I see a kswapd0 which in ps is listed with brackets around it. In /proc/$pid/status however, it's name has no brackets. Also, there is no slash in the name. Commented Aug 31, 2012 at 15:03
  • 2
    I think I was on to something before: On newer kernels (CentOS 6.x has 2.6.32) all kernel-threads seem to be a child of kthreadd. On older kernels (CentOS 5.x has 2.6.18) I see kernel-threads are a child of kthread (without the extra d) OR are a child of init. It would be nice if someone could confirm this though. :-) Commented Sep 26, 2012 at 13:13

4 Answers 4

16

There are some visible differences between a kernel thread and a user-space thread:

  • /proc/$pid/cmdline is empty for kernel threads - this is the method used by ps and top to distinguish kernel threads.

  • The /proc/$pid/exe symbolic link has no target for kernel threads - which makes sense since they do not have a corresponding executable on the filesystem.

    More specifically, the readlink() system call returns ENOENT ("No such file or directory"), despite the fact that the link itself exists, to denote the fact that the executable for this process does not exist (and never did).

    Therefore, a reliable way to check for kernel threads should be to call readlink() on /proc/$pid/exe and check its return code. If it succeeds then $pid is a user process. If it fails with ENOENT, then an extra stat() on /proc/$pid/exe should tell apart the case of a kernel thread from a process that has just terminated.

  • /proc/$pid/status is missing several fields for most kernel threads - more specifically a few fields related to virtual memory.

Sign up to request clarification or add additional context in comments.

22 Comments

In my original post you see that the virtual memory related lines in /proc/$pid/status cannot be trusted as processes that are shutting down don't have those as well. That is the biggest part of my problem... :-P I'll look into /proc/$pid/cmdline, but since some processes rewrite that I don't think its a very good idea to rely on that.
@HighKing: True, cmdline can be affected by threads changing argv[0], although I am not sure if it can become completely empty. So I found another difference for you :-)
The /proc/$pid/exe I had already found, see my first post. ;-) Well, I've added a check on /proc/$pid/cmdline, so am now checking if cmdline is empty, if there's no VmSize and if a stat on exe returns zero results. Still I get php-cgi processes that are identified as kernel-threads. :-P
@HighKing: uh, 1 sec... if you do a stat on /proc/$pid/exe and the executable is removed it will error-out, since stat will attempt to dereference the link. What you need is to check the output of readlink(2)...
I don't know... they are gone 1/10th of a second later so I don't have the time to check the ps output. I guess I'll just have to recheck all kernel threads at the end of the loop. Not very efficient, but it's better this. Edit: Damn you're fast... ;-)
|
6

As you pointed out in your own comment above, all user processes are descendants of the init process (pid=1). Kernel threads are not descendants of the init process, since init is a user process, and user processes cannot create kernel threads. Therefore, to check if process p is a user process and not a kernel thread, one needs to operate on the process graph and evaluate if init dom p where dom is the Dominator operator. Concretely in 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)

1 Comment

There are simpler and faster alternatives - i.e. checking the process flags or checking if PPID equals 2.
4

You can read thread flags value from /proc/[pid]/stat (see proc(5) manpage) and check if it has PF_KTHREAD bit flag set.

PF_KTHREAD constant itself has been available since 2.6.17, which is roughly for 10 years, and it's value hasn't changed since then:

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

Even through the header file containing this constant (include/linux/sched.h) is not exported to userspace, having a copy of this definition in your source code combined with kernel version check done from userspace at runtime (e.g. using uname(2) system call) should be pretty robust.

1 Comment

1

Here is a version that works under 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
}

To use it do

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

This was first useful solution for me at least, thanks for er0 of the python version.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.