In computer science, bare machine (or bare metal) refers to a computer executing instructions directly on logic hardware without an intervening operating system. Modern operating systems evolved through various stages, from elementary to the present day complex, highly sensitive systems incorporating many services. After the development of programmable computers (which did not require physical changes to run different programs) but prior to the development of operating systems, sequential instructions were executed on the computer hardware directly using machine language without any system software layer. This approach is termed the "bare machine" precursor to modern operating systems. Today it is mostly applicable to embedded systems and firmware generally with time-critical latency requirements, while conventional programs are run by a runtime system overlaid on an operating system.
For a given application, in most of the cases, a bare-metal implementation will run faster, using less memory and so being more power efficient. This is because operating systems, as any program, need some execution time and memory space to run, and these are no longer needed on bare-metal. For instance, any hardware feature that includes inputs and outputs are directly accessible on bare-metal, whereas the same feature using an OS must route the call to a subroutine, consuming running time and memory.
For a given application, bare-metal programming requires more effort to work properly and is more complex because the services provided by the operating system and used by the application have to be re-implemented regarding the needs. These services can be:
- System boot (mandatory)
- Memory management: Storing location of the code and the data regarding the hardware resources and peripherals (mandatory)
- Interruptions handling (if any)
- Task scheduling, if the application can perform more than one task
- Peripherals management (if any)
- Error management, if wanted or needed
Debugging a bare-metal program is difficult since:
- There are no software error notifications nor faults management, unless they have been implemented and validated.
- There is no standard output, unless it has been implemented and validated.
- The machine where the program is written cannot be the same where the program is executed, so the target hardware is either an emulator / simulator or an external device. This forces to setup a way to load the bare-metal program onto the target (flashing), start the program execution and access the target resources.
Bare-metal programming is generally done using a close-to-hardware language, such as Rust, C++, C, assembly language, or even for small amounts of code or very new processors machine code directly. All the previous issues inevitably mean that bare-metal programs are very rarely portable.
Early computers, such as the PDP-11, allowed programmers to load a program, supplied in machine code, to RAM. The resulting operation of the program could be monitored by lights, and output derived from magnetic tape, print devices, or storage.
Bare machine programming remains common practice in embedded systems, where microcontrollers or microprocessors often boot directly into monolithic, single-purpose software, without loading a separate operating system. Such embedded software can vary in structure, but the simplest form may consist of an infinite main loop, calling subroutines responsible for checking for inputs, performing actions, and writing outputs.
The approach of using bare machines paved the way for new ideas which accelerated the evolution of operating system development.
This approach highlighted a need for the following:
- Input/output (I/O) devices to enter both code and data conveniently:
For example, programs were loaded into the PDP-11 by hand, using a series of toggle switches on the front panel of the device. Keyboards are far superior to these vintage input devices, as it would be much faster to type code or data than to use toggle switches to input this into the machine. Keyboards would later become standard across almost every computer, regardless of brand or price.
Computer monitors can also easily display the output of a program in a user friendly manner. For example, one would have to be intimately knowledgeable about a specific early computer and its display system, consisting of an array of lights, to even begin to make sense of the status of the computer's hardware. In contrast, anybody who can read should be able to understand a well-designed user interface on a modern system, without having to know anything about the hardware of the computer on which the program is being run.
- Faster, cheaper, more widely available secondary storage devices to store programs to non-volatile memory. This was needed, as it was cumbersome to have to type code in by hand in order to use the computer in a useful way, which would be lost upon every reboot due to the system saving it to volatile memory.
- The requirement for a convenient high-level language and a translator for such a high-level language to the corresponding machine code.
- Linkers to link library modules, which may be written by the user or already available on the system.
- Loaders to load executables into RAM from the secondary storage.
- Suitable I/O devices, such as printers for producing a hard copy of the output generated by programs.
- Gordon, Abel; Amit, Nadav; Har'El, Nadav; Ben-Yehuda, Muli; Landau, Alex; Schuster, Assaf; Tsafrir, Dan (2012). "ELI". ACM SIGPLAN Notices. 47 (4): 411–422. doi:10.1145/2248487.2151020.