为什么没有奇数的 Windows 进程 ID?

为什么没有奇数的 Windows 进程 ID?

有很多方法可以检查 Windows 中的进程 ID。

例如,使用 PowerShell 命令:

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

我们看到以下输出:

  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       
...     

所有进程 ID 均为偶数,并且均为4 的倍数

在任何基于 Windows NT 的 Windows 版本上都没有奇怪的进程 ID。

这是什么原因呢?

答案1

“为什么没有奇怪的 Windows 进程 ID?”

分配内核句柄的代码也用于分配进程和线程 ID。由于内核句柄是 4 的倍数,因此进程和线程 ID 也是 4 的倍数。


为什么进程和线程ID都是四的倍数?

在基于 Windows NT 的操作系统上,进程和线程 ID 恰好总是 4 的倍数。这只是巧合吗?

是的,这只是一个巧合,你不应该依赖它,因为它不是编程合同的一部分。例如,Windows 95 进程和线程 ID 并不总是 4 的倍数。(相比之下,内核句柄始终是 4 的倍数的原因是规范的一部分,并且在可预见的未来将得到保证。)

进程和线程 ID 是 4 的倍数,这是代码重用的副作用。分配内核句柄的相同代码也用于分配进程和线程 ID。由于内核句柄是 4 的倍数,因此进程和线程 ID 也是 4 的倍数。这是一个实现细节,因此不要编写依赖于它的代码。我告诉你只是为了满足你的好奇心。

来源为什么进程和线程ID都是四的倍数?


为什么内核 HANDLE 总是四的倍数?

不太为人所知的是,内核句柄的底部两位始终为零;换句话说,它们的数值始终是 4 的倍数。请注意,这仅适用于内核句柄;它不适用于伪句柄或任何其他类型的句柄(用户句柄、GDI 句柄、多媒体句柄……)。内核句柄是可以传递给 CloseHandle 函数的东西。

底部两位的可用性隐藏在 ntdef.h 头文件中:

//
// 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

GetQueuedCompletionStatus 函数暗示内核句柄的最低位至少始终为零,这表明您可以设置事件句柄的最低位以抑制完成端口通知。为了使此功能有效,最低位通常必须为零。

这些信息对于大多数应用程序编写者来说没什么用,他们应该继续将 HANDLE 视为不透明值。对标记位感兴趣的人是那些正在实现低级类库或将内核对象包装在更大的框架内的人。

来源为什么内核 HANDLE 总是四的倍数?


进一步阅读


相关内容