Wait.... no malloc or sbrk? That means all space has to be stack allocated? That's a pretty serious limitation and would probably make it hard to do anything really interesting.
You don't want your space ship flight to be "interesting".
Note that it says "after task initalization". What this really implies is that you must use O(1) heap space, and you should get what you need ahead of time.
Having to deal with out of memory conditions 4 seconds after main engine turn-on is not a fun party. Neither is blocking on malloc() so you can prepare your struct course_adjustment to send to another task.
You don't want to do anything interesting in a space flight mission. Code affecting a space mission only has one chance to get it right in many cases. In general, memory management in these systems is extremely serious business. Think of all the ways that manual memory management in C has damaged the reliability of software you have written in the past.
In addition, a lot of the rules are created to make the task of reading source code easier, both for humans and machines. I remember Dr. Gerard Holzmann once half-joked in a meeting that he wanted to disallow any declaration of pointers except at static initialization. I sort of thought he was joking, but then he assured me that it was a serious consideration. He reminded me of the gravity of the situation and explained that $2 billion of public funds were on the line.
Disallowing pointer indirection would make the task of certain automated analysis techniques much, much simpler to perform. Adding a pointer indirection can really conflate matters sometimes.
But without pointer indirection and dynamic memory allocation, why even use C? The big idea of C is pointers. Aren't there languages designed for mission critical and embedded environments (Ada for instance) ?
This is a much larger discussion, akin to most religious wars in software ;-). There is a huge argument for what you're saying, but there's other forces at play. One aspect that plays into it is that C is a really nice layer on top of assembly, and there are a lot of extremely talented embedded C software engineers, (I'm assuming) moreso than the number of available Ada engineers. Also, keep in mind this is a pretty conservative domain. What has worked in the past is trusted much more than what might work better. Up until a decade or so, there was no operating system to speak of and most development used hardware controllers.
Also, C is not the only horse in town. In fact, I hear that Ada is actually pretty popular in space-flight. Other missions have successfully leveraged FORTH even. Using a Lisp read-eval print loop from many millions of miles away once saved the Voyager mission.
In my experience, I have never come across Ada in the spaceflight domain.
Among the big players in spacecraft flight software, there seems to be a divergent east-coast/west-coast preference for C and C++, respectively. In my estimation, this is the reason: there is a wide variety of target hardware and OSes and the need for FSW to be reused across all of them (embedded linux, VxWorks, QNX on PowerPC, SPARC, intel architectures). In terms of development environments and compiler toolchains, only ISO C (and to a slightly lesser degree C++) is supported by all of them.
Edit: Various instruments on spacecraft may be programmed in Forth or other nifty languages, for example, and there's a growing effort to make some of the more "interesting" challenges in spaceflight (autonomy, fault management, guidance-and-control, etc..) to be coded in custom domain-specific languages or other scripting languages like Lua.
What do pointers have to do with memory allocation? Pointers don't discriminate against the otherwise allocated.
That said, this system still uses a dynamic memory system - its somewhat non-optional when you use an operating system which has to maintain memory for stacks and its own resources.
These guidelines just forbid you to use the memory allocation routines after the task initialization phase to make memory allocation and usage completely predictable.
I brought up pointer indirections as a way of illustrating how stringent some of these rules are and what sorts of forces are at play. Sorry for muddying matters.
The point is that you can still do all of that stuff even on staticly allocated memory.
The other limitation in a lot of these systems is the underlying virtual memory system and some times there isn't one. Memory fragmentation issues are a huge problem when you have a couple kB to a few MB of physical RAM and a limited VM subsystem.
> That's a pretty serious limitation and would probably make it hard to do anything really interesting.
No it's not, and no it doesn't.
No dynamic allocation is a pretty standard precaution for safety critical software. It requires careful coding and design, but it eliminates an entire class of runtime errors and makes it relatively easy to put an upper bound on memory usage.
It's very common in embedded systems. It's not so limitating since you need to plan the exact size of your buffers anyway, to make sure the system has enough memory; might as well just allocate them statically. It's also rare to use linked lists or other structures that grow dynamically in that kind of software.
It doesn't mean you have to allocate on the stack. It means you have to declare buffer sizes at compile-time rather than at runtime. Instead of saying char ∗buf = malloc(size_of_my_data()), you just say char buf[MAX_SIZE_OF_MY_DATA].
would probably make it hard to do anything really interesting
The Rockbox music player firmware has the same restriction and it doesn't seem to prevent it from running Doom or decoding FLACs.
It just means no dynamic allocation. This is not an uncommon restriction for certain classes of embedded systems (engine controls, avionics, medical, etc.) I've worked primarily in this environment and haven't found it to be too much of a nuisance.
But that's AFTER startup. I think this is a very nice pattern for servers. Do as much work as possible at startup time. Do anything that can fail, including allocating memory.
And then in the request path, be miserly about what you are willing to do. "Modern" web servers like nginx seem to be designed this way. Node JS's HTTP parser makes a point of not allocating any memory.
I think you'd be surprised how far this pattern can go.