163

There are many ways to examine process IDs in Windows.

For example, using the PowerShell command:

ps | select Id, ProcessName  | Sort Id | ft -AutoSize

We see the following output:

  Id ProcessName         
  -- -----------         
   0 Idle                
   4 System              
 264 svchost             
 388 smss                
 476 csrss               
 536 wininit             
 580 winlogon                      
 620 services            
 628 lsass                          
 728 svchost             
 828 dwm                                     
1060 chrome              
1080 rundll32            
1148 vmms                                        
1620 spoolsv                                                
2912 taskhostex          
3020 explorer       
...     

All the process IDs are even numbers and, in addition, they are all multiples of 4.

There are no odd process IDs on any version of Windows that is based on Windows NT.

What is the reason for this?

DavidPostill
  • 153,128
  • 77
  • 353
  • 394
Peter Hahndorf
  • 13,370
  • 9
  • 51
  • 67

1 Answers1

168

"Why are there no odd Windows process Ids?"

The same code that allocates kernel handles is also used to allocate process and thread IDs. Since kernel handles are a multiple of four, so too are process and thread IDs.


Why are process and thread IDs multiples of four?

On Windows NT-based operating systems, process and thread IDs happen always to be a multiple of four. Is this just a coincidence?

Yes, it's just a coincidence, and you shouldn't rely on it since it is not part of the programming contract. For example, Windows 95 process and thread IDs were not always multiples of four. (By comparison, the reason that kernel handles are always a multiple of four is part of the specification and will be guaranteed for the foreseeable future.)

Process and thread IDs are multiples of four as a side-effect of code re-use. The same code that allocates kernel handles is also used to allocate process and thread IDs. Since kernel handles are a multiple of four, so too are process and thread IDs. This is an implementation detail, so don't write code that relies on it. I'm just telling you to satisfy your curiosity.

Source Why are process and thread IDs multiples of four?


Why are kernel HANDLEs always a multiple of four?

Not very well known is that the bottom two bits of kernel HANDLEs are always zero; in other words, their numeric value is always a multiple of 4. Note that this applies only to kernel HANDLEs; it does not apply to pseudo-handles or to any other type of handle (USER handles, GDI handles, multimedia handles...) Kernel handles are things you can pass to the CloseHandle function.

The availability of the bottom two bits is buried in the ntdef.h header file:

//
// Low order two bits of a handle are ignored by the system and available
// for use by application code as tag bits.  The remaining bits are opaque
// and used to store a serial number and table index.
//

#define OBJ_HANDLE_TAGBITS  0x00000003L

That at least the bottom bit of kernel HANDLEs is always zero is implied by the GetQueuedCompletionStatus function, which indicates that you can set the bottom bit of the event handle to suppress completion port notification. In order for this to work, the bottom bit must normally be zero.

This information is not useful for most application writers, which should continue to treat HANDLEs as opaque values. The people who would be interested in tag bits are those who are implementing low-level class libraries or are wrapping kernel objects inside a larger framework.

Source Why are kernel HANDLEs always a multiple of four?


Further reading


Justine Krejcha
  • 2,207
  • 2
  • 16
  • 27
DavidPostill
  • 153,128
  • 77
  • 353
  • 394
  • 3
    One quote says *"you shouldn't rely on it since it is not part of the programming contract,"* but then the next claims ntdef.h says the bits are *"available for use by application code as tag bits."* Documentation in a public header file is about as close to a _"programming contract"_ as you can get, so the first claim is incorrect. – BlueRaja - Danny Pflughoeft Jul 06 '15 at 10:44
  • 2
    @BlueRaja, "Not very well known is that the bottom two bits of **kernel HANDLEs are always zero; in other words, their numeric value is always a multiple of 4.**" The code in ntdef.h applies to other kinds of handles as well ( USER handles, GDI handles, multimedia handles...) – DavidPostill Jul 06 '15 at 10:45
  • I don't see how that's relevant. If the claim in ntdef.h is true of all handles, then it's certainly true for a specific type of handle _(kernel handles)_. – BlueRaja - Danny Pflughoeft Jul 06 '15 at 10:47
  • @BlueRaja "you shouldn't rely on it since it is not part of the programming contract," this means it **could** change in the future. – DavidPostill Jul 06 '15 at 10:47
  • @BlueRaja **Low order two bits of a handle are ignored by the system** - you can do something with them yourself in an application hence the reference to "tag bits" – DavidPostill Jul 06 '15 at 10:48
  • @BlueRaja Perhaps we should ask [Raymond Chen](http://stackoverflow.com/users/902497/raymond-chen) to clarify - but unfortunately he is not a member of [su] :/ – DavidPostill Jul 06 '15 at 11:01
  • Wow, my guess would have been that deep in their soul a `HANDLE` is an `int *` – Hagen von Eitzen Jul 06 '15 at 11:10
  • @HagenvonEitzen `typedef PVOID HANDLE;` and`typedef void *PVOID;` in `WinNT.h` So it actually a `void *` not an `int *` – DavidPostill Jul 06 '15 at 11:14
  • 39
    @BlueRaja: kernel handles are multiple of four and that's contractual, so you can rely on it; *process ids* (which are *not* the same as process handles), instead, *happen* to be multiple of four, but that's just an implementation detail, so you shouldn't rely on it. – Matteo Italia Jul 06 '15 at 11:16
  • 7
    @BlueRaja I think Raymond would tell you that whoever wrote the documentation looked at the world through *kernel-colored glasses* and thus only referred to kernel handles and not other kinds of handles. – CodesInChaos Jul 06 '15 at 19:53
  • @BlueRaja: Keep in mind "USER" and GDI handles are also kernel handles... – user541686 Jul 07 '15 at 05:45
  • 1
    @Mehrdad: well, USER and GDI handles aren't normally called "kernel" handles (although they are generated by components running in so-called kernel mode). – Matteo Italia Jul 07 '15 at 16:27