Mostly a subsystem is a part offering a specific functionality with a defined interface and can have multiple different implementations. On the technical side subsystems are basically just a convenient way to pack certain parts of the code into makefiles and add a few configuration options to them. (See conf/autopilot/subsystems/...) So these replaced the old raw makefile section in the airframe file. This makes it easier to put an airframe file together and also allows us to change the code and move/rename files behind the scenes without breaking everyones airframe files.
List of Subsystems
Differences to Modules
So the difference is that with subsystems you "package" some of the "normal" code and you have to explicitly call the functions (e.g. control loops) from the main ap loop. This gives you tighter control over what gets called when. The event and periodic functions of the Modules get called at the end of event_task_ap and periodic_task_ap respectively.
This means that modules are perfect to add stuff like extra sensors, but not so much for e.g. estimation and control where you want to exactly control when and in what order things happen.
But you can also wrap some subsystem code as a modules (e.g. infrared), or the other way round (e.g. imu_ppzuav). As you can see with the imu_ppzuav module it also still expects a direct ImuEvent call from main_ap, so you could say it's not a "pure" module.