Cyhist Apr 16 1997 A
Date: Wed, 16 Apr 1997 18:54:54 +1200
Reply-To: ljk@cs.mu.oz.au
Sender: "CYHIST Community Memory: Discussion list on the History of
Cyberspace" <CYHIST@MAELSTROM.STJOHNS.EDU> From: ljk@CS.MU.OZ.AU
Subject: CM> sixth edition unix + PDP-11
In-Reply-To: Message of "Wed, 16 Apr 97 01:01:35 EDT."
______________________________________________________________________
Community Memory: Discussion List on the History of Cyberspace ______________________________________________________________________
>Date: Tue, 15 Apr 1997 14:48:59 -0400
>From: andrew stellman <roo@ANON.RAZORWIRE.COM> Subject: sixth edition unix (was Re: An interesting URL...)
>
>ever since a friend of mine got his PDP-11 up and running a few years ago, i've been curious about PDP-11 UNIX. how much memory did it require? how close was it to today's UNIX that we know and love? i'd love some info -- anything, really -- but i never managed to dig anything up.
I worked on a PDP-11 running sixth-edition Unix in the Computer Vision Lab at the University of Maryland, starting in late 1977. So it wasn't *really* early in Unix days, but fairly early. I can't clearly remember "sixth edition" Unix, but I distinctly remember running version 6 C, because I later had to convert a lot of programs to version 7 C. (Change all those =+ to +=.) This is all based on recollections, so will have a quite a few holes in it:
I'm pretty sure it was PDP-11/45. It had a full load of nominal 128k bytes of core memory, with memory management. The PDP-11 had a 16-bit address space, so could address 64k bytes, but knew whether it was fetching data or instructions, so you effectively had an extra address bit, giving you a 64k data address space (D) and a 64k instruction address space (I). Some of the address space was taken up by device registers (memory mapped) about 4k, and of course some by the resident kernel. If I remember right, about 48k of each was available for user programs, and that implies that the resident kernel took up about 24k. (I never then thought explicitly about how much space the kernel took up -- but I did care about how big a program I could write.)
We had two disk drives with removable cartridges -- I can't remember exactly, but I think they were about 5 meg each, plus a 2 meg fixed disk for the root device. One cartridge was always mounted for the users' home directories. So all the real Unix stuff fitted onto the fixed disk.
I think I still have a PDP-11 processor handbook around somewhere, but from memory, I think most instructions took at least about one microsecond -- that was mostly for the instruction fetch. Longer if you had to fetch additional addresses and operands, and longer for exotic operations like multiplication and division. We had a floating point unit. I could say a lot more about the good ole PDP-11 hardware, but that'd be getting off the Unix topic. This is enough to set the context for our usage of Unix.
We had about twenty active users and eight CRT terminals, which were pretty nearly all in use most working hours. I can visualize them, but can't remember their brand name. Plus a DEC Writer as the console. And a modem for dial-up access and uucp connections (300 baud). We used the Rand editor for most of our editing.
About Unix: Really, the amazing thing is how little the fundamentals have changed -- which is a tribute to how the original Unix developers got it right, from the beginning.
Almost all the shell stuff you regularly use was there: command invocation, redirection (< > >>), pipes (|), shell variables ($), background processes (&), shell control structures. No job control, no history, and a very limited set of signals (maybe INT, QUIT, HUP -- can't remember for sure). Pretty much all the basic Unix utilities were there in their essentials: ls, cp, mv, mkdir, rm, make, cc, wc, nice, nroff, man pages, etc., though with less options than now.
The memory management gave you virtual memory, which Unix supported, but only up to the limit of the address space (64k each for I and D). But it did mean that every process could have its own full address space. So often single programs that were too big to fit got split up into several processes that talked to each other through pipes.
No sockets and IP stuff, but there was uucp, which provided remote copy (uucp), remote execution (uux), mail, and eventually news, so long as you could wait till the next time the machine dialled out.
The file system was hierarchical, just like now: inodes, directories, mounting. I think there was a 14-character limit on names. No special opendir(), etc.: You could open and read a directory just like any other file, but then you had to cope with the internal binary layout of the directory yourself. Hard links, but no symlinks.
You had to repair the file system by hand if the system crashed. There were some utilities (ncheck? fcheck?) to check for inode and directory entry consistency, but a lot of it was manual, including copying specific blocks off the back-up disk (using dd), to fix the broken bits. Not everybody had the nerve for this, but the skill was pretty much a necessity if you wanted to work late or weekends. Overall, the system was pretty reliable. No statistics, but my impression is it would stay up for days on end at least.
The C compiler was version 6. Like I said, the assignment operators were around the opposite way from now. (Actually the old way made more sense, "assign with the operation", but caused problems with the unexpected tokenization of something like x=-2, which was x =- 2, rather than x = -2. Well, I guess it would have been unexpected for F O R T R A N programmers...)
Version 6 had structs, but you couldn't pass them by value to functions, or return them. Did everything by pointers. Also, there was only one name space of struct field names (distinct from variables and functions). So, more-or-less every field had to have a unique name across all structs -- usually done by putting some kind of struct-specific prefix on the name. This probably explains some of struct field names that still persist to this day.
Actually, the truth was that struct field names were just offsets, and C didn't care at all if you used one struct's fields with a different struct altogether -- you just got whatever was at the same offset. Sometimes this was useful, like for do-it-yourself object inheritance.
C then was much less strict about types. Many things, like functions, were by default int, unless explicitly declared otherwise, and programs tended to use that default. No prototypes, function argument type checking, no typedefs, no enums. No malloc() or free(), but for dynamic memory there was an sbrk() call that would grow your data space by so many bytes at the end and return you a pointer to the old end (the beginning of the new stuff). Then you were on your own. But for most things, it worked quite well enough.
No stdio. Just read() and write(). There were simpler versions of printf() and scanf(). Printf() could take an optional *first* argument, which was the file descriptor. The way it worked was that printf() could recognize it was a file descriptor, because it was a small integer (I think maximum number of file descriptors was 15). A format string, being an address, would appear to be a bigger number. Scanf() must have worked much the same way, but I never used it much. Mostly I was reading binary image data. When I had decimal data, I just called atoi() or atof(), to avoid the overhead. Even linking in the scanf() code made your binaries objectionably bigger.
The C compiler took a number of passes, maybe five. My overall impression, though, is that things didn't feel all that much slower. Even with most terminals occupied, response was fine for interactive editing. I guess the real story is we wrote effectively smaller programs in those days. Today, even when you write what appears to be a small program, it ends up pulling in a relatively enormous amount of #includes and libraries.
One other oddity was that the compiler, to save time, didn't invoke the preprocessor unless it thought it needed it, which it did by looking for preprocessor directives at the beginning of the file. For that reason, we had the convention that every program started with a lone hash sign on the first line, even before the intro comments. This was to force the compiler to run the preprocessor, even when our real directives occurred too much later in the file to be noticed.
The compiler had a peep-hole optimizer, but you still had to take care with your code. Running through arrays with pointer arithmetic rather than indexing was the done thing. And since the PDP-11 had post-increment instructions and pre-decrement instructions (but not the other ways round), you knew you were better off doing things in terms of p++ or --p. Even to this day, I still superstitiously avoid writing p-- or ++p, unless I really need those values and can't recast my algorithm. Remember we were doing computer vision, which meant a lot of image processing.
We even had a full LISP system, with interpeter and compiler, called U-LISP (similar to Wisconsin LISP, with an inheritance from Mac-LISP), written by my fellow Ph.D. student, Robert Kirby. It actually got used for a lot more than AI, since it was the only language on the system from which you could interactively issue system calls, so it also got used sometimes for debugging hardware interfaces live. The compiler was especially important, not only for speed, but because compiled code got moved across into I space, freeing up D space for your real data structures.
One of the interesting quirks of the PDP-11 was that its bus was totally asynchronous. This did make it much easier to hook up random hardware of varying speeds. But if you brought up the system before you'd switched on all the peripherals, or if you switched off a peripheral while the system was running, then it would hang, right in the middle of a memory operation to that device's registers (devices were memory mapped).
And just to contribute to another thread: We had the game adventure running, but only the binary, not the source. In fact, the binary was for the DEC operating system (was that RSX-11?), but Unix could run these too, as well as its own a.out binaries. I did manage to get carried away by the elves at last.
Anyway, perhaps I've gone on too long. I hope some of this at least is interesting to Andrew Stellman and the list.
Les.
Les Kitchen, Senior Lecturer <URL:http://www.cs.mu.oz.au/~ljk> Computer Science Department ljk@cs.mu.oz.au
The University of Melbourne phone: +61-3-9344-9101,-9104
Computer Vision & Machine Intelligence Lab fax: +61-3-9348-1184
______________________________________________________________________
Reply-To: ljk@cs.mu.oz.au
Sender: "CYHIST Community Memory: Discussion list on the History of
Cyberspace" <CYHIST@MAELSTROM.STJOHNS.EDU> From: ljk@CS.MU.OZ.AU
Subject: CM> sixth edition unix + PDP-11
In-Reply-To: Message of "Wed, 16 Apr 97 01:01:35 EDT."
______________________________________________________________________
Community Memory: Discussion List on the History of Cyberspace ______________________________________________________________________
>Date: Tue, 15 Apr 1997 14:48:59 -0400
>From: andrew stellman <roo@ANON.RAZORWIRE.COM> Subject: sixth edition unix (was Re: An interesting URL...)
>
>ever since a friend of mine got his PDP-11 up and running a few years ago, i've been curious about PDP-11 UNIX. how much memory did it require? how close was it to today's UNIX that we know and love? i'd love some info -- anything, really -- but i never managed to dig anything up.
I worked on a PDP-11 running sixth-edition Unix in the Computer Vision Lab at the University of Maryland, starting in late 1977. So it wasn't *really* early in Unix days, but fairly early. I can't clearly remember "sixth edition" Unix, but I distinctly remember running version 6 C, because I later had to convert a lot of programs to version 7 C. (Change all those =+ to +=.) This is all based on recollections, so will have a quite a few holes in it:
I'm pretty sure it was PDP-11/45. It had a full load of nominal 128k bytes of core memory, with memory management. The PDP-11 had a 16-bit address space, so could address 64k bytes, but knew whether it was fetching data or instructions, so you effectively had an extra address bit, giving you a 64k data address space (D) and a 64k instruction address space (I). Some of the address space was taken up by device registers (memory mapped) about 4k, and of course some by the resident kernel. If I remember right, about 48k of each was available for user programs, and that implies that the resident kernel took up about 24k. (I never then thought explicitly about how much space the kernel took up -- but I did care about how big a program I could write.)
We had two disk drives with removable cartridges -- I can't remember exactly, but I think they were about 5 meg each, plus a 2 meg fixed disk for the root device. One cartridge was always mounted for the users' home directories. So all the real Unix stuff fitted onto the fixed disk.
I think I still have a PDP-11 processor handbook around somewhere, but from memory, I think most instructions took at least about one microsecond -- that was mostly for the instruction fetch. Longer if you had to fetch additional addresses and operands, and longer for exotic operations like multiplication and division. We had a floating point unit. I could say a lot more about the good ole PDP-11 hardware, but that'd be getting off the Unix topic. This is enough to set the context for our usage of Unix.
We had about twenty active users and eight CRT terminals, which were pretty nearly all in use most working hours. I can visualize them, but can't remember their brand name. Plus a DEC Writer as the console. And a modem for dial-up access and uucp connections (300 baud). We used the Rand editor for most of our editing.
About Unix: Really, the amazing thing is how little the fundamentals have changed -- which is a tribute to how the original Unix developers got it right, from the beginning.
Almost all the shell stuff you regularly use was there: command invocation, redirection (< > >>), pipes (|), shell variables ($), background processes (&), shell control structures. No job control, no history, and a very limited set of signals (maybe INT, QUIT, HUP -- can't remember for sure). Pretty much all the basic Unix utilities were there in their essentials: ls, cp, mv, mkdir, rm, make, cc, wc, nice, nroff, man pages, etc., though with less options than now.
The memory management gave you virtual memory, which Unix supported, but only up to the limit of the address space (64k each for I and D). But it did mean that every process could have its own full address space. So often single programs that were too big to fit got split up into several processes that talked to each other through pipes.
No sockets and IP stuff, but there was uucp, which provided remote copy (uucp), remote execution (uux), mail, and eventually news, so long as you could wait till the next time the machine dialled out.
The file system was hierarchical, just like now: inodes, directories, mounting. I think there was a 14-character limit on names. No special opendir(), etc.: You could open and read a directory just like any other file, but then you had to cope with the internal binary layout of the directory yourself. Hard links, but no symlinks.
You had to repair the file system by hand if the system crashed. There were some utilities (ncheck? fcheck?) to check for inode and directory entry consistency, but a lot of it was manual, including copying specific blocks off the back-up disk (using dd), to fix the broken bits. Not everybody had the nerve for this, but the skill was pretty much a necessity if you wanted to work late or weekends. Overall, the system was pretty reliable. No statistics, but my impression is it would stay up for days on end at least.
The C compiler was version 6. Like I said, the assignment operators were around the opposite way from now. (Actually the old way made more sense, "assign with the operation", but caused problems with the unexpected tokenization of something like x=-2, which was x =- 2, rather than x = -2. Well, I guess it would have been unexpected for F O R T R A N programmers...)
Version 6 had structs, but you couldn't pass them by value to functions, or return them. Did everything by pointers. Also, there was only one name space of struct field names (distinct from variables and functions). So, more-or-less every field had to have a unique name across all structs -- usually done by putting some kind of struct-specific prefix on the name. This probably explains some of struct field names that still persist to this day.
Actually, the truth was that struct field names were just offsets, and C didn't care at all if you used one struct's fields with a different struct altogether -- you just got whatever was at the same offset. Sometimes this was useful, like for do-it-yourself object inheritance.
C then was much less strict about types. Many things, like functions, were by default int, unless explicitly declared otherwise, and programs tended to use that default. No prototypes, function argument type checking, no typedefs, no enums. No malloc() or free(), but for dynamic memory there was an sbrk() call that would grow your data space by so many bytes at the end and return you a pointer to the old end (the beginning of the new stuff). Then you were on your own. But for most things, it worked quite well enough.
No stdio. Just read() and write(). There were simpler versions of printf() and scanf(). Printf() could take an optional *first* argument, which was the file descriptor. The way it worked was that printf() could recognize it was a file descriptor, because it was a small integer (I think maximum number of file descriptors was 15). A format string, being an address, would appear to be a bigger number. Scanf() must have worked much the same way, but I never used it much. Mostly I was reading binary image data. When I had decimal data, I just called atoi() or atof(), to avoid the overhead. Even linking in the scanf() code made your binaries objectionably bigger.
The C compiler took a number of passes, maybe five. My overall impression, though, is that things didn't feel all that much slower. Even with most terminals occupied, response was fine for interactive editing. I guess the real story is we wrote effectively smaller programs in those days. Today, even when you write what appears to be a small program, it ends up pulling in a relatively enormous amount of #includes and libraries.
One other oddity was that the compiler, to save time, didn't invoke the preprocessor unless it thought it needed it, which it did by looking for preprocessor directives at the beginning of the file. For that reason, we had the convention that every program started with a lone hash sign on the first line, even before the intro comments. This was to force the compiler to run the preprocessor, even when our real directives occurred too much later in the file to be noticed.
The compiler had a peep-hole optimizer, but you still had to take care with your code. Running through arrays with pointer arithmetic rather than indexing was the done thing. And since the PDP-11 had post-increment instructions and pre-decrement instructions (but not the other ways round), you knew you were better off doing things in terms of p++ or --p. Even to this day, I still superstitiously avoid writing p-- or ++p, unless I really need those values and can't recast my algorithm. Remember we were doing computer vision, which meant a lot of image processing.
We even had a full LISP system, with interpeter and compiler, called U-LISP (similar to Wisconsin LISP, with an inheritance from Mac-LISP), written by my fellow Ph.D. student, Robert Kirby. It actually got used for a lot more than AI, since it was the only language on the system from which you could interactively issue system calls, so it also got used sometimes for debugging hardware interfaces live. The compiler was especially important, not only for speed, but because compiled code got moved across into I space, freeing up D space for your real data structures.
One of the interesting quirks of the PDP-11 was that its bus was totally asynchronous. This did make it much easier to hook up random hardware of varying speeds. But if you brought up the system before you'd switched on all the peripherals, or if you switched off a peripheral while the system was running, then it would hang, right in the middle of a memory operation to that device's registers (devices were memory mapped).
And just to contribute to another thread: We had the game adventure running, but only the binary, not the source. In fact, the binary was for the DEC operating system (was that RSX-11?), but Unix could run these too, as well as its own a.out binaries. I did manage to get carried away by the elves at last.
Anyway, perhaps I've gone on too long. I hope some of this at least is interesting to Andrew Stellman and the list.
Les.
Les Kitchen, Senior Lecturer <URL:http://www.cs.mu.oz.au/~ljk> Computer Science Department ljk@cs.mu.oz.au
The University of Melbourne phone: +61-3-9344-9101,-9104
Computer Vision & Machine Intelligence Lab fax: +61-3-9348-1184
______________________________________________________________________