> Further, the common denominator you're seeing goes back much, much further than BSD itself.
Interesting, I didn’t know that! There’s definitely an essay to be written here about the evolution of this “Unix system-call convention” over the decades, going into how this table of calls survived each transition and port mostly-unscathed. (Given that we’re throwing actual source syscall tables back and forth as proofs, there’s definitely some narrativizing to be done here, The Old New Thing-style.)
I assume the syscalls weren’t retained in ports with the goal of “binary compatibility”, given that these descendant Unix ports were on different architectures that couldn’t literally exec(2) binaries from their ancestor. Guesses:
• Toolchain compatibility?
• Some shared cross-compiling assembler/linker that nevertheless had hard-coded syscalls?
• (The perhaps never-achieved-in-practice goal of) emulation-assisted descendant cross-compatibility, ala z/OS?
• The existence of hybrid/transitional minicomputer generations, that had application processors for both the old and new architectures (or application processors that could execute on both ISAs!), such that at least some of the systems being ported to could exec(2) the ancestor’s binaries straight from tape?
• Or just the expectation that, despite Unix and C being so intertwined, there were still enough people writing ASM for these machines — using a non-macro assembler — who had developed reflex-memory for the existing syscall numbers, that it would be a bad idea to change the table out from under them?
> the userland simply linked into kernel symbols and called "read", "readdir" etc directly
So basically, the original Unix was a DOS, rather than a kernel/supervisor. Was that just because the PDP7 didn’t have virtual memory management, or was it a conscious design decision that was later reversed?
Well first of all I was wrong -- the PDP7 did have syscalls, I'm just bad at reading PDP7 assembly and missed the dispatcher. Curiously, it looks like the sequence is entirely different, although there could be some magic that makes the order different than it appears at first glance.
It's all just guessing, but I figure the explanation is much simpler -- for PDP11 UNIX, they just kept using the same syscalls up till V7 / 2BSD, and there should have been a sort of "rolling release" binary compatibility. For the VAX, the first port (32v) probably just retained the original numbering since there was no reason to deviate from it, which colored 3BSD and 4BSD, hence {Net,Free,Open}BSD and Darwin and friends.
Worth pointing out that several versions of Linux have rather different syscall tables. 32 bit ARM and x86 are more-or-less matches, with ARM differing on a few early syscalls, while 64 bit ARM and amd64 differing quite dramatically. The old ABI for 32bit MIPS also matches, but both the n32 and n64 ABIs use slightly variant syscall tables. PowerPC 32/64 bit is also a close match, although it has some impedance (I think it matches closer to AIX by design)
At the end of the day, I think the similarity is mostly a mixture of coincidence, system developers being influenced by their bootstrap system's syscall tables, and no real reason to change them up. No reason to not change them, either, since it's pretty trivial to use different dispatch tables for different types of processes, like how the BSD's handle other-OS compat.
Interesting, I didn’t know that! There’s definitely an essay to be written here about the evolution of this “Unix system-call convention” over the decades, going into how this table of calls survived each transition and port mostly-unscathed. (Given that we’re throwing actual source syscall tables back and forth as proofs, there’s definitely some narrativizing to be done here, The Old New Thing-style.)
I assume the syscalls weren’t retained in ports with the goal of “binary compatibility”, given that these descendant Unix ports were on different architectures that couldn’t literally exec(2) binaries from their ancestor. Guesses:
• Toolchain compatibility?
• Some shared cross-compiling assembler/linker that nevertheless had hard-coded syscalls?
• (The perhaps never-achieved-in-practice goal of) emulation-assisted descendant cross-compatibility, ala z/OS?
• The existence of hybrid/transitional minicomputer generations, that had application processors for both the old and new architectures (or application processors that could execute on both ISAs!), such that at least some of the systems being ported to could exec(2) the ancestor’s binaries straight from tape?
• Or just the expectation that, despite Unix and C being so intertwined, there were still enough people writing ASM for these machines — using a non-macro assembler — who had developed reflex-memory for the existing syscall numbers, that it would be a bad idea to change the table out from under them?
> the userland simply linked into kernel symbols and called "read", "readdir" etc directly
So basically, the original Unix was a DOS, rather than a kernel/supervisor. Was that just because the PDP7 didn’t have virtual memory management, or was it a conscious design decision that was later reversed?