eekee wrote:I know why systems written in C or certain other languages are much nicer with an MMU. What I'm looking for is opinions on developing an OS in other languages without an MMU, or using certain programming techniques to minimize the risk.
Actually, I'm wondering why a Lisper would only take hardware seriously if it has one, Schol-R-LEA.
It isn't so much that I don't take systems without an MMU seriously, personally, so much as that I would expect most of the others here wouldn't.
OTOH, Lisp systems have a
long history of accelerated hardware and hardware memory support; the various
Lisp Machines all had MMUs, for example, as well as tagged memory. I would certainly love to see tagged memory architectures return for that very reason (as well as hoping that hardware-based capability security will finally reach the mainstream). While the usual approach to memory management doesn't play well with the usual approaches to garbage collection, that doesn't necessarily have to be the case if the two are designed to work together. However, I intend to flexible enough that, if a given system doesn't have an MMU, I can do without it.
On the gripping hand, most 32-bit systems today - even microcontrollers - have MMUs. Most 8-bit ones don't, of course, but I doubt I could fit enough my design into a 64K memory space to make it work. I might have to look into that... but no, I mean to focus on 64-bit systems, actually.
As for
Arduino: while the majority of Arduinos use an 8-bit
AVR CPU, there are a number of official Arduino-branded SBCs (and
Arduino-compatible SBCs) with microcontroller-class ARM core SoCs. There was also a plan for a RISC-V Arduino called the
Arduino Cinque, but AFAICT that never happened. The
HiFive 1 isn't an 'official' Arduino, but it is in the same form factor as Arduino Uno, IIUC, and compatible with at least some shields; while the US$60 price point is high for a maker-class microcontroller, it isn't nearly as extreme as the HiFive Unleashed's $1000 price is.
eekee wrote:I thought Lisp was a safe language.
Setting aside the question of whether there is such a thing, I am guessing that this ties into the misunderstanding of Lisp being primarily an interpreted language, as well as the matter of garbage collection implementations. Thing is, there's not really such as thing as 'an interpreted language' vs 'a compiled language' - with enough effort and run-time sleight-of-hand, pretty much any language can be compiled for even code which explicitly accesses the code translator. While many, many Lisp interpreters have been written as class projects (because it is dead easy, and a useful example project), most 'serious' Lisp systems are compiled (often to native code), and usually can mix-and-match interpreted and compiled code transparently in ones designed after the early 1970s (see the
Lambda Papers for the breakthroughs that made this feasible). For some systems, even the Lisp Listeners (the interactive REPL) are compile-and-go rather than interpreted.
(As I understand it, most Forth implementations also mix interpretation and compilation, but in a different manner, with the interpreter walking through the words of a given named operation until it finds a built-in or compiled word, and then calling it as native code. Comments and corrections welcome.)
As for garbage collection, that helps a lot in minimizing the possibilities of trivial memory leaks and wild pointers, no question. However, it comes with trade offs. First, you are replacing a large number of potential failure points with (conceptually at least) one much bigger one, and debugging garbage collectors is notoriously difficult. The bigger issue is that while the most common classes of memory problems are avoided, several less common ones still remain, most notably the problems that come when a very long running process doesn't release the memory it is using somehow - as long as the memory still has live references, it won't be freed by the collector, and while most memory references go stale as a matter of course, if the process is recursing heavily such that prior memory used in prior calls is still 'live' it can result in a something akin to a memory leak. There are some ways around this, but that's just one possible problem.
Also, naively implemented garbage collection does not interact well with naively implemented paging. However, this wasn't a problem with the Lisp Machines (for example), because they used a more sophisticated approach which let the two facilities work together (and the systems had hardware support for GC that tweaked the performance of this even more). In my system, too, I intend for the paging and the garbage collectors (plural, as I mean to have a hierarchical memory manager) will be coordinated with each other, rather than fighting against each other.
Mind you, I mean to do some seriously odd things in my OS, such as using
runtime code synthesis in a high-level language,
Xanalogical storage instead of a conventional file system, and using a combination of a portable AST, code templates, and compiler hints as the fuel for a JIT compiler, rather than the more common bytecode approach for portability. Some of this may not work out. It is more a series of experiments than anything else.