FSKit: two extension processes after mount, one survives unmount — expected behavior?

Hi,

I'm developing an FSKit extension and noticed a consistent process lifecycle that also reproduces with Apple's official passthrough sample.

Environment:

  • macOS: [26.5.2]
  • Xcode: [26.6]

Observed behavior:

  • After a successful mount, Activity Monitor shows two processes with the same executable path (the embedded .appex inside the host app).
  • After umount, one process exits; one remains.
  • A subsequent mount again results in two active processes (one lingering + one new mount-serving instance, depending on timing).

What I've ruled out:

  1. Not caused by Instruments / lldb attach (reproduces after reboot, with no debugger attached).

  2. Not specific to our project: Apple's official passthrough FSKit demo shows the same pattern.

  3. Not duplicate pluginkit registrations at different paths — both processes report the same .appex executable path.

  4. Not requiring the host app to be running — reproduces with command-line mount only (UDrive app and Xcode quit).

Questions:

  1. Is this the intended FSKit lifecycle?
  2. After umount, is it expected that one extension process remains idle rather than exiting completely?

Thanks!

Based on what I've seen via a debugger, the process spawned to probe resources is separate from the process(es) spawned to manage each container you mount.

I've just been treating those idle processes similar to how I would an idle on-demand launch agent or launch daemon which is able to terminate when it's inactive. That is, I ignore it since the system should manage their lifecycle and the process should use very little resources on idle, but I don't depend on them staying alive for any specific period of time (as maybe the behavior could change in some OS update). Maybe an Apple employee can give a more definitive answer, but I'd be surprised if this weren't intentional, as if a user mounts many volumes in close succession, there would be worse performance for no reason if the probing process had to constantly be relaunched and initialized again for each request.

In any case, on my own system, I've definitely used my FSKit extension since I last rebooted my machine, but I no longer see any running processes related to that extension. So it does seem that the process is going to terminate on its own eventually, although I don't know the exact criteria for that.

I can provide a little bit of background here. FSKit modules are packaged as app extensions (appexen). An appex runs in a process whose lifetime is managed by the system. There are two factors involved here:

  • The host app, that is, the app who’s calling on the appex’s services (this is distinct from the container app, that is, the app in which the appex is embedded)
  • The requests made to the appex

If the host app terminates, the system terminates the appex. The tricky thing here is that for an FSKit appex — and various other things that use appex packaging, like Network Extensions — the host app is the system itself, so this factor isn’t relevant here.

The host app makes requests to the appex. While those requests are inflight, the system will keep the appex’s process alive.

If either of those factors apply, the system is free to terminate the appex process. Whether it actually does terminate the process depends on a variety of factors. For example, iOS is much more enthusiastic about terminating appex processes than macOS. macOS is more likely to leave the process lying around, especially if the system is not under memory pressure.

Oh, one last thing: Attaching with the debug is another factor that keeps the process alive.

I've just been treating those idle processes similar to how I would an idle on-demand launch agent or launch daemon

That’s a good way to look at it. Notably, launchd use to be much more enthusiastic about terminating idle jobs but nowadays it tends to be based on memory pressure. This similar behaviour isn’t a coincidence. Under the covers, all of this stuff (appexen, sysexen, XPC services, and launchd daemons and agents) are all actually just different flavours of launchd job.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

FSKit: two extension processes after mount, one survives unmount — expected behavior?
 
 
Q