Home
Dave

Blog
Archives
By Subject

Recent:

Nixie Tubes
Memory
Scavengers
Nightshade
Cobras
Magical Feedback of Oz
Harp Case
Reading List 2023
Moonrise
Local Wildlife
Centipede
Recent Photos
Mouse Teeth
Reading List 2022
Connection Machine Photo
Engraved Photos
Pad One
Whisker
Black Rice
Gabe Serbian

Multics versus Unix

Unix started as an experimental response to Multics. The initial Unix developers were previously involved in the Multics project, and they were inspired both by wanting a sophisticated timesharing environment like Multics, but also by the thought that Multics had grown monsterously out of hand, and the desire to experiment with simpler solutions.

The main philosophical difference between Unix and Multics is that Multics provides facilities which are carefully engineered to solve specific well-defined problems, while Unix is more like a metal shop. You are given a bunch of very simple tools, and a first-aid kit: do what you want, suffer the consequences if you do something stupid.

Unix is anarchy, Multics is newspeak.

A good example of how Unix copied a Multics feature, but redesigned it with a "simple & sloppy" philosophy is command output substitution. This did not show up in the earliest versions of Unix (not until the Bourne Shell, 7th Edition Unix, 1979), but it is still clearly a copy of a very key Multics feature.

In Multics, this is called an "active function", and it looks something like:

    command arg arg [af-command arg arg] arg

"af-command arg arg" is executed and the results put back in the command line string before it is further parsed & executed.

The Unix version looks almost exactly the same:

    unix$ command arg arg `command arg arg` arg

But the important thing is that the Unix developers managed to duplicate this Multics feature using a completely different internal mechanism.

In Multics, a program has to be specifically written to support being invoked as an active function, and the code used to output is completely different from a normal program. A Multics program that is to be used as an active function calls the "cu_$af_return_arg" subroutine which provides a "return string" pointer. The program writes its output to that string, and then exits, instead of using the normal output routines (PUT, WRITE, iox_, etc.).

For example, this is the PL/I code from the "calc" standard utility that determines if it is currently being used as an active function, gets the return string, and sets some flags ("af_sw" (active function switch) is set to true (1) if calc is being called as an active function, false (0) otherwise):

      call cu_$af_return_arg (arg_count, return_ptr, return_len, code);
      if code = error_table_$not_act_fnc then do;
         if arg_count > 1 then do;
	  call com_err_$suppress_name (0, "calc", "Usage:  calc {expression}");
	  return;
         end;
         else if arg_count = 1 then expr_arg_sw = "1"b;
         else expr_arg_sw = "0"b;
         af_sw = "0"b;
      end;
      else do;
         if arg_count = 0 | arg_count > 1 then do;
	  call active_fnc_err_$af_suppress_name (0, "calc", "Usage:  [calc expression]");
	  return;
         end;
         af_sw, expr_arg_sw = "1"b;
      end;

When calculations are complete, it checks if af_sw is true, and if so "outputs" the results to return_string and exits:

      if af_sw then do;
         ip = 1;
         call ffop (out, ip, fval);	/* convert value to char string */
         return_string = rtrim (ltrim (substr (out, 1, ip - 1)));
         return;
      end;

Otherwise it formats the results differently, and outputs using the iox_$put_chars call:

      ip = 5;
      substr (out, 1, 5) = "=   ";	/* set up output line */
      call ffop (out, ip, fval);	/* convert value to char string */
      substr (out, ip, 1) = "
";					/* append NL to output line */
      call iox_$put_chars (iox_$user_output, addr (out), ip, (0));

The Unix approach is to simply redirect a program's standard output to a pipe before executing it, then read the data from that pipe. The program doesn't even generally know that it's being called this way. (A Unix program can go out of its way to detect whether standard output is a pipe, but few programs do. A rare example is ls, which won't default to multi-column if output is a pipe, since that would obviously screw up pipeline filters.)

Aside from a different mechanism for output, any Multics program that can be called as either a command or an active function must handle formatting its output differently for either case. In Unix, the shell simply strips all newlines from the output of the called program if it is used for command output substitution. No other program needs to worry about the difference.

The Unix approach is much simpler, and also much less safe. The Multics approach ensures that the command makes sense, and the program being called expects to be called in that way. It prevents you from making insane calls. The Unix approach cuts out all the work required to make special forms of commands, and lets you do anything you want, even if it makes no sense and will likely screw up your login session.

The Multics approach is safe, but extremely limited. Many commands do not support active function use. For example, the Multics equivalents of who and ls ("how_many_users" and "list") cannot be used in this way. Given the ability to use anything for command output substitution, Unix users have found ways to use it that could never have been predicted by the original designers. This is the point where the Multics approach fails - if they didn't forsee it, you aren't allowed to do it.

Again: anarchy versus newspeak.

One obscure but amusing side effect of the Unix "simple & sloppy" approach to this is that none of the mainstream Unix shells correctly cancel a command when the process invoked for command output substitution fails. Bourne, Bash, Korn, Z-, all output something like this:

    Unix$ echo This should not print, really. `aslkdjfaslkdfjlk`
    aslkdjfaslkdfjlk: not found
    This should not print, really.
    Unix$

Note: I am describing one particular case where I think the "simple & sloppy" approach turned out to work beautifully. This should not be extrapolated to a complete dismissal of Multics. There are many features of Multics that are impressive even by today's standards, and we would probably be better off if Unix had copied them more closely.

(Sample PL/I code is copyright Honeywell Bull Inc.)