The best explanation I could give you is in this article.
Anyway, in short:
All modules are merged during the compilation phase.
When eagerly loaded Angular Compiler put all services in a rootInjector making available the service for the entire app.
If more than one module provides a service with the same token, the provider defined in the module that imports other modules always win.
The provider from the last imported module overrides providers in the preceding modules except for the module that imports them.
When lazyLoaded every module is still merged into one in compilation, but an injector for each module is created. From this, a Hierarchy of Injector exists and the way a component looks for the injected token is climbing the hierachy looking for the closer provider for that token.
forRoot()* it is only a convention used when your module has services that you want to provide for the entire app and others just for the children of some module.