When does a process ID become available for reuse?

A customer wanted some information about process IDs:

I'm writing some code that depends on process IDs and I'd like to understand better problem of process ID reuse.

When can PIDs be reused? Does it happen when the process handle becomes signaled (but before the zombie object is removed from the system) or does it happen only after last handle to process is released (and the process object is removed from the system)?

If its the former, will OpenProcess() succeed for a zombie process? (i.e. the one that has been terminated, but not yet removed from the system)?

The process ID is a value associated with the process object, and as long as the process object is still around, so too will its process ID. The process object remains as long as the process is still running (the process implicitly retains a reference to itself) or as long as somebody still has a handle to the process object.

If you think about it, this makes sense, because as long as there is still a handle to the process, somebody can call WaitForSingleObject to wait for the process to exit, or they can call GetExitCodeProcess to retrieve the exit code, and that exit code has to be stored somewhere for later retrieval.

When all handles are closed, then the kernel knows that nobody is going to ask whether the process is still running or what its exit code is (because you need a handle to ask those questions). At which point the process object can be destroyed, which in turn destroys the process ID.

What happens if somebody calls OpenProcess on a zombie process? The same thing that happens if they call it on a running process: They get a handle to the process. Why would you want to get a handle to a zombie process? Well, you might not know that it's a zombie yet; you're getting the handle so you can call WaitForSingleObject to see if it has exited yet. Or you might get the handle, knowing that it's a zombie, because you want to call GetExitCodeProcess to see what the exit code was.

Comments (16)
  1. missing the question says:

    That doesn't explain when the id becomes available for reuse. How long does the process have to be terminated before a new process can reuse the same id? If a id is reused immediately then other processes doesn't know the process has terminated. Maybe it (the id) has terminated twice.

  2. asdf says:

    Like he said, the pid can be reused when all handles to that process are closed.

  3. Rangoric says:

    That's the point. If you are tracking it, it won't go away.

  4. Henke37 says:

    Or we could just use the "What if this was true" approach.

    The statement to be proven is "process ids are reused for zombie processes". Let's assume that it is true. Now imagine the scenario that another process wants to know the exit code by looking up the process id. Ops, not possible, you'd get the wrong process.

  5. DWalker says:

    @missing:  When the THREADS go away, there can still be handles.  When the handles go away, the process goes away and then the process ID can be reused.  At least that's my understanding.

  6. Ben Voigt [Visual C++ MVP] says:

    @Henke37: Yes, you'd get the wrong process.  That's a risk you run by using PIDs instead of handles.  If you don't already own a handle to the process, the last existing handle could be closed, the zombie could be reaped, and the PID could be reused.  Preventing PIDs of zombies from being reused doesn't actually help much.

  7. Rob Kennedy says:

    I think it's clear that a pid becomes eligible for re-use when the process it previously identified ceases to exist.

    But what's still not clear is precisely when a process ceases to exist. We know it's not when the process terminates — the process can continue to exist afterward if there are still handles open on that process. During that time, it's a "zombie process." But once all the handles are closed, you only say the kernel knows the process object CAN be destroyed. Does that mean the kernel destroys it immediately, or can the process object continue to exist even after the last handle is closed? And if it can continue to exist, can somebody call OpenProcess on that pid and get a handle to that not-yet-destroyed process, even though there were no other handles open at the time?

    However, for an application developer, I don't see how it really matters. Suppose you're writing application A and it has a handle to process B. All that's important is to know that as long as A has an open handle, the pid will continue to identify B. Once A closes its last handle to B, it should assume that the pid is invalid and stop using it. It should not assume that B continued running afterward, nor that any other program C still has a handle to B.

    [When most people ask the question, it's not with the goal of forcing process IDs to be recycled faster; it's how to delay the recycling. And the answer is "To delay the recycling, convert the process ID to a handle." Even if all application handles to the process are closed, kernel components may have a reference to the process. -Raymond]
  8. dave says:

    Just to add my 2 cents worth – this seems to be a case of "just like everything else".  While you've got an open handle to file FOO.BAR, it will continue to be the same FOO.BAR.  If you close your open handle and open a new handle based on the file name, it may not be the same file.  If that matters to you, don't do it :-)

  9. Alex Grigoriev says:

    The question is from the category "if you're asking this, you're doing it wrong". It's the same as asking "when a freed memory becomes reused" or "how to check if the memory address is valid".

  10. Vilx- says:

    I'm wondering – was this really necessary? Why the duality – PID and handle, which identify the same thing? Couldn't we have just one unique identifier, like a GUID?

    [All that does is change the noun in the question. "How long is a process ID valid?" becomes "How long is a process GUID valid?" -Raymond]
  11. Joshua says:

    This is one of the few places where Windows actually did it better than UNIX. Under UNIX there's no way to get a handle to a process so no way other than being the parent to prevent the pid from being recycled on you.

  12. Gabe says:

    Vilx: Handles are not unique identifiers, while PIDs are. Think of it this way: PID:process::filename:file. Processes need both PIDs and handles just as much as files need names and handles. Since a process doesn't otherwise have a unique name it gets assigned a number, essentially allowing you to talk about a process that you don't have a handle to.

    Perhaps you are wondering why you even need handles to processes at all (UNIX doesn't). UNIX has only two permissions on processes (if you own the process or you're root, you can do anything to the process, otherwise you can do nothing to it), meaning that for anything you want to do to a process (debug, signal, renice, etc.) it is sufficient to just pass the PID. Windows has a dozen different permissions (msdn.microsoft.com/…/ms684880.aspx) and handles are used to manage those. In this respect processes are just like any other object on the system (threads, mutexes, files, registry keys, etc.). The big difference is their namespace (PIDs).

  13. Killer{R} says:

    Just think about process as any other kernel object (like mutex, event etc) and PID in this analogie is a process object's "name". While there're any opened to existing object handles – you may call Open*** (OpenMutex, OpenEvent, OpenProcess) to get another handle to to. While all handles closed – Open*** will fail. But there is 2 differences between process and other objects name lifetime:

    1) Process holds reference to itself while its not terminated -so its possibe to open 'running' process even there're no external handle on it yet.

    2) If after process terminated there still remained any references (handles) for any of its (already dead too) thread(s) – its possible to 'open' process even if there're no other already opened handles to it.

    When exactly process id will be release is a complicated question, cause threads deferencing performed asynchronously by backround 'reaper' thread. So on one side – when thread terminates it pushes itself to 'reap' queue that processed asynchronously. On another side – when you calls 'final' CloseHandle related to already exited process – it will dereference and destroy process object synchonousy if all its thread alrready reaped asynchronously until this moment.

    So practical conclusion: you may relay that process ID will not be reused while process alive and/or while there any opened handle(s) (in other processes) referencing this process or any its thread. But you may not be sure that PID will be reused after you closed everything related to process.

    PS: BTW the story about handle count is even more complicated, cause every kernel object has actually 2 ref counters – 'handles' and 'pointers' count – second always same or greater than first. And kernel object 'deleted' when its pointer count reaches zero, while object's name removed from namespace when its handles count zeroed. Actually I don't know if this same (thinking like PID=="name") for process/thread handles, but while object refcounter may be modified directly only from kernelmode its out of this article's scope but interesting for me too.

  14. 640k says:

    I'm writing some code that depends on process IDs and I'd like to understand better problem of process ID reuse.


  15. Ray Trent says:

    Hmmm… this leads to an interesting question: Is there's any way to open a process and know that it's the process you were trying to open (other than to arrange to have created the process yourself, I suppose)? It would seem there's an inherent race condition in finding the process ID you want to open and then calling OpenProcess.

  16. Gabe says:

    Ray Trent: Yes, there's an inherent race condition, and it happens everywhere. For example, let's say a user tells a program to open file FOO.TXT; you have no way of knowing if FOO.TXT has been deleted or renamed and had a new file of the same name placed in its directory between the time the user chooses it and you actually open the file. It is only once you open the file that you can control what happens to it.

    Of course, this race condition isn't limited to computers. I could give you my phone number, and by the time you call me I could have closed my account with the phone company and they could have assigned my number to somebody else.

Comments are closed.

Skip to main content