开发者

How to keep track of all descendant processes to cleanup?

开发者 https://www.devze.com 2023-03-24 23:07 出处:网络
I have a program that can fork() and exec() multiple processes in a chain. E.g.: process A --> fork, exec B --> fork, exec C --> fork, exec D.So A is the great-great-grandparent of C.

I have a program that can fork() and exec() multiple processes in a chain. E.g.: process A --> fork, exec B --> fork, exec C --> fork, exec D. So A is the great-great-grandparent of C.

Now the problem is that I do not have any control of processes B, C and D. So, several things can happen.

  1. It might so happen that a descendant process can do setsid() to change its process group an开发者_如何转开发d session.
  2. Or one of the descendant process dies (say C) and hence its child (D) is parented by init.

Therefore, I can't rely on process group id or parent id to track all descendants of A. Is there any reliable way of keeping track of all descendants? More specifically, I would like to kill all the descendants (orphans and otherwise).

It would be also great if its POSIX compliant.


The POSIX way to do this is simply to use process groups. Descendant processes that explicitly change their process group / session are making a deliberate decision not to have their lifetime tracked by their original parent - they are specifically emancipating themselves from the parent's control. Such processes are not orphans - they are adults that have "flown the nest" and wish to exert control over their own lifetime.


I agree with caf's general sentiment: if a process calls setsid, it's saying it wants to live on its own, no matter what . You need to think carefully as to whether you really want to kill them.

That being said, sometimes, you will want some form of “super-session” to contain a tree of processes. There is no tool that provides such super-sessions in the POSIX toolbox, but I'm going to propose a few solutions. Each solution has its own limitations, so it's likely that they won't all be applicable to your case, but hopefully one of them will be suitable.

A clean solution is to run the processes in their own virtualized environment. This could be a FreeBSD-style jail, Linux cgroups, or any other kind of virtualization technology. The limitations of this approach are that virtualization technologies are OS-dependant, and the processes will run in a somewhat different context.

If you only have a single instance of these processes on the system and you can get root involved, run the processes as a dedicated user. The super-session is defined as the processes running as the dedicated user. Kill the descendants with kill(-1, signum) (note that this will kill the killer process itself unless it's blocked or handled the signal).

You can make the process open a unique file, making sure that the FD_CLOEXEC flag is set on the file descriptor. All child processes will then inherit the open file unless they explicitly remove the FD_CLOEXEC flag before calling execve or close the file. Kill the processes with fuser -k or by obtaining the list of process IDs with fuser or lsof (fuser is in POSIX, but not fuser -k.) Note that there's a race condition: a process may fork between the time you call fuser and the time you kill it; therefore you need to call fuser in a loop until no more processes appear (don't loop until all processes are dead, as this could be an infinite loop if one of the processes is blocking your signal).

You can generate a unique random string and define an environment variable with that name, or with a well-known name and that unique string as a value. It will be inherited by all descendant processes unless they choose to change their environment. There is no portable way to search for processes based on their environment, or even to obtain the environment of another process. On many unix variants, you can obtain the information with an option to ps (such as ps -e on *BSD or ps e on Linux); the information may not be easy to parse, but the presence of the unique string is a sufficient indicator. As with fuser above, note the need for a loop to avoid a race condition if a descendant calls fork too late for you to notice its child but before you could kill the parent.

You can LD_PRELOAD a small library that forks a thread that listens on a communication channel, and kills its process when notified. This may disrupt the process if it expects to know about all of its own threads; it's only a possibility on architectures where the standard library is always thread-safe, and you'll miss statically linked processes. The communication channel can be anything that allows the master process to broadcast the suicide order; one possibility is a pipe where each descendant process does a blocking read and the ancestor process closes the pipe to notify the descendants. Pass the file descriptor number through an environment variable.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号