This document offers a brief conceptual overview of Components and the Component Framework.
In Fuchsia, [component][glossary.component] is the term for the common abstraction that defines how all software[^1] (regardless of source, programming language, or runtime) is described, sandboxed, and executed on a Fuchsia system.
[^1]: With the exception of early-boot software necessary to run components.
Sandboxing is a security mechanism to isolate programs from each other at runtime. In Fuchsia, all software is sandboxed. When a program is initially created, it does not have the ability to do anything -- not even to allocate memory. The program relies on its creator to provide the capabilities needed for it to execute. This isolation property allows Fuchsia to employ the principle of least privilege: programs are provided only the minimal set of capabilities needed to execute.
The Component Framework (CF) consists of the core concepts, tools, APIs, runtime, and libraries necessary to describe and run components and to coordinate communication and access to resources between components.
The Component Framework includes:
component_manager
process, which coordinates the communication and sharing of resources between components.component_manager
, or implemented by other components and used by component_manager
, for the purposes of coordination.Since Fuchsia is a capability-based operating system, components interact with each other through the use of capabilities. A capability combines access to a resource and a set of rights, providing both a mechanism for access control and a means by which to interact with the resource.
To support the complex composition of software present in today's products, the Component Framework provides distinct capability types built upon Zircon kernel objects. A common representation of a capability is a channel that speaks a particular FIDL protocol.
The Component Framework assembles the namespace for a component using component declarations that describe the capabilities the component requires to function. Components can discover the available capabilities in their namespace using the fuchsia.io.Directory
protocol.
At runtime, every component receives its namespace as well as a handle to the server end of a Directory
channel. This Directory
channel is called the the outgoing directory. Through the outgoing directory, the component's executable makes discoverable any capabilities that it provides.
The Component Framework brokers discovery from a providing component‘s outgoing directory to a consuming component’s namespace through a process called capability routing. While most capabilities are routed to component instances, runner and resolver capabilities are routed to environments. Environments configure the behavior of the framework for the realms to which they are assigned.
Note: In the Fuchsia process layer, “having a capability” means the process holds a handle to the kernel object capability in its handle table. In the Component Framework, we often use “having a capability” to mean that the capability is discoverable through the component's namespace at runtime.
Further reading:
Components are the foundational building blocks of software running in Fuchsia. Each component is a composable, sandboxed module that interacts with other components through capabilities.
At its core, a component consists of the following:
The Component Framework relies on component resolvers to retrieve components from their origin. Resolvers take a component URL as an input and produce a component manifest and (optionally) an access mechanism to the bytes of a software package as output.
Components that include an executable program may specify any runtime (such as a raw process or a virtual machine) provided to the Component Framework through a component runner. Runners consume parts of the manifest and the package, and provide the component's binary with a way to execute.
Note: Components without an executable program may still route capabilities and host children, but no code will be executed for the component.
Resolvers and runners are themselves capabilities and interact directly with the framework to extend its functionality. Components can implement these capabilities to add support for new component origins and runtimes.
Note: To bootstrap the system, component_manager
includes a built-in resolver, the boot-resolver
, which resolves fuchsia-boot://
URLs to manifests on the boot image, as well as a built-in runner, the ELF runner, which executes ELF binaries stored in signed Fuchsia packages.
Further reading:
A component together with its children are referred to as a realm. The collective parent and child relationships of all individual components are referred to as the component instance tree. A moniker is a topological path that identifies a specific component instance within a component instance tree. You will often see monikers represented as POSIX-like path strings.
Component topology is the term for the component instance tree and the collective capability routes over that tree.
Further reading:
Components move through the following lifecycle states:
Components are discovered either a) by virtue of being statically declared as a child of another component in a component manifest, or b) by being added to a component collection at runtime. Similarly, components are destroyed implicitly by being removed from the list of static children in a component manifest, or explicitly by being removed from a component collection at runtime.
When a component is started or stopped, component_manager
coordinates with the appropriate runner to execute or terminate the component's executable.
Further reading: