I was reading an article about the RISC OS operating system and the fact that it doesn’t support multiple CPU cores. Then I got to thinking, how would one implement multi-CPU support on such an operating system?
Between 1989 and 1994 I used a British computer running the operating system RISC OS. This operating system is still alive, and is still in use. It was produced by Acorn Computers and runs on the ARM processor (“Acorn Risc Machine”), now found in many computing platforms such as the iPhone. It has many innovative features which haven’t made it yet to other operating systems (although is the topic for a post of its own!)
Back then there was obviously no need to support multiple CPUs. So today one has the choice of either finding single-core CPUs (will become increasingly difficult) or using a multi-core CPU with all but one core inactive (which is a waste). Various fora speculate on the technical possibility or impossibility of utilizing the extra cores. I don’t think it would be especially difficult. This article describes how I would go about it.
Constraints and information about RISC OS:
- RISC OS can run multiple programs at once but yet has no concept of pre-emptive multi-tasking, threads, processes, etc.
- It’s extremely old, therefore extremely fast on modern processors (even running on only one core)
- Many parts of the operating system, such as those to do with the filesystem, are marked as “non re-entrant” in the documentation, that means they would need to be re-written or altered to work with pre-emptive multi-tasking or simultaneous execution (multi-core), for example locks would need to be introduced.
For me at least, I would say that the way the system currently operates on a single core is already very fast (when running at less than full speed via emulation on an x86 processor – it runs faster than it did on native hardware in the 1990s, it runs faster than Windows ran in the 1990s, and faster than Windows runs today.) So I think for most tasks, there is no need to use additional cores.
I am used to working in companies with limited human resources, and due to the fact that RISC OS isn’t a mainstream operating system, I am going to assume that this would also be a constraint with any solution to introduce multi-core capabilities to RISC OS. That means a complete re-write of RISC OS and applications, or even visiting every single part of the operating system and introducing locking and the expectation that any function can be called at any time due to pre-emptive multi-tasking and simultaneous execution by multiple cores, is also out of the question.
So my solution would be to assume that those extra cores should do “pure CPU” work, for example video processing, rendering fractals, or other CPU-intensive work. All work like accessing the filesystem, interacting with the user, which is already fast enough, would not be given the opportunity to work even faster by being executed in parallel on multiple cores.
I would write a very simply layer, similar to a hypervisor, which would run under RISC OS, which would simply handle allocation of CPU and memory resources. This supervisor would allow processes to run, which would not share memory (and thus not need to support shared-memory primitives such as locking). Pipes would allow processes managed by the supervisor to communicate.
The first, and often the only process, which this supervisor would manage, would be the entire existing RISC OS and all applications. This process would manage the user-interface, network, filesystem, printers and communication between all running RISC OS applications. So this “RISC OS + applications” process would run largely unaltered from the existing software (saving much development time; but also providing no benefit in terms of speed increase).
But there would be an extra API available to applications to spawn new processes managed by the supervisor, which would execute asynchronously, and primitives to pass/receive messages from those spawned processes. The supervisor would run these processes on the different cores, and pre-emptively multi-task the processes in the case of more processes than cores.
I would take the approach to concurrency of “message-passing with no shared memory” as opposed to “shared memory (threads)” for two reasons:
- Existing RISC OS lacks the primitives to deal with shared memory and pre-emptive concurrency (such as locking), so it would not be possible to allow the spawned processes to interact with the main memory of the RISC OS applications. (Although it would be theoretically possible to allow the spawned processes to themselves have threads, although that would require introducing extra concepts such as locking to the supervisor.)
- It might be argued one would need to support shared-memory computation to allow existing software to be easily ported to the platform, however this isn’t really a concern as porting software to RISC OS is next to impossible for other reasons: there is no POSIX, and the concepts provided by POSIX such as streams unifying filesystem access, inter-process communication, network access, are not available on RISC OS (and I would not advocate building them in the supervisor.)
I would allow “pipes” to be created between the processes and the pipes store an ordered set of “messages”, each message is simply a bunch of bytes. RISC OS software is generally written in C so just passing the byte-contents of a “struct” is easy and convenient. (This is the Erlang philosophy: Facebook say “Erlang… approaches concurrency with three iron rules: no shared memory even between processes on the same computer, a standard format for messages passed between processes, and a guarantee that messages are read in the order in which they were received.”)
This proposal has the following consequences:
- The supervisor would be reasonably easy to write
- RISC OS and applications would not need to be re-written. The application developer can, at a time of their choosing, decide to take advantage of the new feature, and surgically alter their code by altering only that part of the code where the speed improvement is desired (i.e. processing logic, not the GUI)
- The supervisor as described would not consume many resources (i.e. the system would not suddenly become radically slower due to the introduction of this layer.)
- The system would work as well on single-CPU systems as on multi-CPU systems (so software written with the new system can work on older single-core hardware)
- One could envisage such software also running across multiple networked computers, so if a colleague’s computer was not in use (e.g. they were on holiday) ones own computer could become faster.
Admittedly there although there is no performance loss, there is also no performance gain until apps are extended. However I think spending the work to add pre-emptive multi-tasking to RISC OS (a huge undertaking) simply isn’t necessary: RISC OS works well as it is, and arguably is a lot more reliable (in terms of repeatable and therefore testable behaviour) due to its cooperative nature. The original designers of RISC OS could have created a pre-emptive multi-tasking operating system instead of RISC OS — the hardware supported it (RISC/iX ran on it) — but they actively decided not to do that.
- Hydra card for RiscPC – Supports shared memory and access to filesystem etc., so necessarily more complex
- Discussion of adding multi-core capability to “RISC OS Open”
- Adding multi-processor support to Mac OS classic, in many ways a similar situation.