Newsgroups: alt.os.multics From: jim@thinkage.on.ca (James Alan Gardner) Subject: UNIX vs. Multics Message-ID: Sender: Jim Gardner Organization: Thinkage Ltd. Date: Mon, 19 Apr 1993 18:31:46 GMT Lines: 906 A year ago, my company had reason to prepare a general comparison between Unix and Multics, mostly to explain Unix to an audience more familiar with Multics. However, the document also gives a quick and dirty, somewhat simplified, explanation of the way Multics handles basic tasks. ------------DOCUMENT BEGINS----------------------- A COMPARISON OF THE MULTICS AND UNIX OPERATING SYSTEMS - prepared by - Thinkage Ltd. 85 McIntyre Drive Kitchener, Ontario N2R 1H6 (519) 895-1860 April 24, 1992 Introduction: This paper provides a brief comparison of the Multics and UNIX operating systems. It looks at underlying structures rather than the actual services provided by the two systems. Note that we are only comparing and contrasting the systems. We don't claim that one system is better than the other, only that the two are different. History: Both Multics and UNIX owe their start to a series of papers written in the late 1960's. That paper described an operating system that supported both separately running asynchronous processes and dynamic linking from a process to subroutines. In practice, however, the hardware of the day couldn't run such an operating system efficiently enough to provide a satisfactory level of service. To make a system that could provide satisfactory service, something had to be discarded. The development team disagreed on which direction to take, and eventually the team split into groups. One group (based at AT&T Bell Labs) decided to discard dynamic linking and concentrate on separately running asynchronous processes; this led to the development of UNIX. Another group discarded multiple processes and designed a system where a user had a single process which used dynamic linking to invoke commands; this was the origin of Multics. Over time, as hardware improved, it became possible for both systems to support a wider level of services. Thus UNIX System V now offers features which provide a form of dynamic linking, although less sophisticated than that found on Multics. Similarly, Multics was adapted to support features that gave users the impression of multiple processes running simultaneously. However, even though it became possible to offer similar kinds of services, the underlying operating system structures remain quite different. One important historical difference between the two systems is their degree of penetration into colleges and universities. Beginning in the early 1970's, AT&T made UNIX available to colleges and universities for a nominal fee. UNIX also had the advantage that it was adapted for use on inexpensive mini-computers like the PDP-11, while Multics was only found on mainframes. As a result, UNIX knowledge spread through the academic community and the UNIX computing model became familiar to most computer science students and researchers. These days, most graduating computer science students have a thorough grounding in UNIX principles, but have never been exposed to Multics. Consequently, they have difficulty grasping the way that Multics works and how Multics features can best be used in programming. Multics Process Structure: When you start a Multics session, the system creates a single process for you. Everything you do during the session is part of that process, and everything is all part of a single (large) address space. To invoke a program, your process dynamically links to that program, bringing it into memory if it isn't already there. Programs may be made up of separate parts, tied together by dynamic linking. For example, programs may make use of their own (local) code and data, but may also link to shared libraries, system software services, and so on. Flow of execution is controlled by a pushdown stack. For example, if program A wants to invoke program B, it stores linkage information and program data on the stack, then starts B executing. B has access to the data that A has stored on the stack. When B finishes execution, it pops the linkage information off the stack and execution returns to A. If desired, B can pass information back to A by leaving it on the stack. In this way, A and B can communicate with each other through the stack. Since all programs share a single address space (owned by your single process), they can also communicate by reading and writing data into the shared address space. All of this shows that software execution is based on the subroutine model. One software unit calls another as a subroutine. The subroutine executes, then returns to the caller. Thus there is an execution hierarchy, based on the stack. A calls B which calls C; C returns to B, which returns to A. When a program or subroutine terminates execution, it often remains in memory so that it is more quickly available to be used again later in the session. The program stays with all its data objects set to the values they had at program termination. UNIX Process Structure: When you start a UNIX session, the system creates a shell process for you. The shell serves as a command interpreter, reading the commands that you enter and figuring out how to invoke those commands. To invoke a program, the shell creates a new and entirely separate process. This establishes a parent-child relationship, with the shell as the parent and the new process as its child. The child process operates in a separate memory address space from its parent. The child can inherit data from its parent, but this data is set up at the time that the child is created. Once the child is created, parent and child cannot affect each other's memory space. Parent and child execute independently and asynchronously. It is common for a parent to start a child process executing, then wait for the child to finish before the parent resumes its own execution. However, a parent could also choose to resume execution as soon as the child has been invoked, so that the two processes are active simultaneously. Children may invoke children of their own, and all processes operate independently. For example, a parent may terminate but its children keep running. In particular, you can start up processes during a session that keep on running after you terminate the session (i.e. after you terminate the shell process that is created when you log in). In some cases, a single user command may initiate a number of processes. For example, database managers are often implemented as a number of separate processes which all cooperate to work with a single database file. When a process terminates, it is immediately removed from memory and ceases to exist. Unlike Multics, there is no way that a UNIX process can remain in memory after termination. If you want a process to stay in memory, that process has to stay continuously in execution. Process Structure Terminology: UNIX and Multics use the same words for certain concepts, but because of the differences in process structure, the meanings of those words differ between the two systems. In Multics, a program is executed in response to a user command. The program is invoked by means of a call that is similar to a subroutine call in a stack-based programming language. Indeed, program calls and subroutine calls use the same stack, so the terms "command", "program" and "subroutine" are more or less interchangeable in Multics. When programs end, they often remain in memory so that they are more readily available if they're needed again. In UNIX, a program is initiated by a system call which creates a separate address space for the program. A subroutine, on the other hand, is invoked within a program, and simply transfers control within that program's address space. Therefore UNIX "programs" are substantially different from "subroutines", since the two are invoked in completely different ways. A UNIX program has a definite start and end, and once it ends, it is completely removed from memory. In Multics, each user has only one process, and all programs are executed within that process's address space. In UNIX, users often make use of many separately processes operating simultaneously, and each process has its own address space. Dynamic Linking on UNIX System V: UNIX System V supports a form of dynamic linking, but it is much less versatile than Multics dynamic linking. A UNIX process can submit a system call saying that it is willing to share a block of memory within that process. The system call specifies various permissions for that block of memory and also gives the memory a handle (basically a name). Other processes can ask to have this block of memory attached to their own memory spaces. They issue a system call specifying the handle of the sharable memory. The system then attaches this sharable memory to the process, at exactly the same address as in the first process. For example, process A can say that it is willing to share memory locations 5000-8000 with other processes. If process B asks to share that memory, the memory from A will be loaded as a block into locations 5000-8000 of B. Anything currently in the same range of addresses within B is lost. This is the process used, for example, in sharing libraries, including the standard run-time library. Obviously, this approach has weaknesses compared to the true dynamic linking in Multics. A process must take all of the shared block or none of it. If the shared block is a complete library containing many routines, the process can't choose to take most of the library as is but substitute its own version of the some of the routines. Furthermore, serious difficulties arise if a program changes the contents of the shared memory; since the other program is running asynchronously, the change happens at a "random" time and can "pull the rug out from under" other programs sharing that memory. Finally, the shared memory must have the same range of addresses in all the processes that share itjrelocation isn't possible. With Multics dynamic linking, it is possible to substitute your own routine for any other routine in a shared library. Furthermore, if a program is running and it finds that a particular routine is missing, you can stop the program, write the routine, compile it, then link it into the program and the program can keep on going. There is no comparable ability with any version of UNIX. Communication Between Programs: In Multics, all programs run in the same address space. Therefore, programs can communicate with each other simply by reading/writing the address space. For example, when program X calls program Y, it can pass a pointer indicating a data area where Y should write data. When Y terminates and X resumes execution, X can simply check the data area to see what Y has written. With UNIX, each program is a separate process and therefore has a separate address space. Processes cannot communicate with each other in the direct manner used under Multics. Indirect means are required. For example, process X might write data to a file which is later read by process Y. It is also possible to ask the operating system to set up a pipe channel between two simultaneously running processes. A pipe is a communication link. One process writes data to the pipe, in the same way that it would write data to a file. The other process reads this same data from the pipe. By using pipes, two processes running simultaneously can coordinate their efforts toward a common end. As a typical example, one process might produce raw data while a second process formats that data and presents it to a user in a form that is easier to read. On some other systems, the same application might be written as a single program that produces and formats the data; but UNIX has traditionally encouraged the approach of several small cooperating processes instead of one larger process. Although UNIX System V processes can share memory (as discussed earlier), most UNIX interprocess communication takes place through pipes. Often pipes are used to let processes alternate actions on files or shared memory. Basically, the processes use pipes to say to each other, "I'm done my work for a while, it's your turn now." Static Data: On Multics, program units can have an indefinite lifetime. For example, consider the case of a library of run-time routines which may be shared by many different commands. When a command is invoked, it can dynamically link to these routines in order to use them. Even after the command has terminated, the routines typically stay resident in memory because they may be needed by future commands (or future invocations of the same command). Multics programs and routines may have static data: data which is preserved from one invocation to the next. Suppose, for example, that program A invokes a run-time routine X. Later, after A is finished, program B invokes routine X as well. It is possible for X to set up static data that is remembered from one invocation to the next, so that data used in connection with A remains available for use in connection with B. This arrangement is not possible on UNIX. Remember that each UNIX process is separate from all others. Each time a program is invoked, a clean copy of that program is created with its own memory space. Suppose then that process A invokes X and that later B invokes X again. Both A and B create entirely separate copies of X, and these copies of X cannot directly access each other. They could, of course, communicate indirectly; for example, the first invocation of X could write data to a file which the second invocation of X later reads. When it is necessary for data to be preserved in memory over time, UNIX systems keep the same process running indefinitely. This leads to the concept of server programs. For example, suppose that a number of programs are likely to invoke a particular program X and that X should retain data from one program to the next. In this case, X would simply be kept running as a separate process, as long as it is needed. When another program wanted services from X, it could catch X's attention by sending an interrupt, by creating a pipe, or by writing to a file that X is watching. Once X notices that its services are required, it would go ahead and do whatever is needed. In between times, X would still be an active process, but it would just be "spinning its wheels" until called on. As an example, consider the UNIX init process. This process spends most of its time waiting for someone to try to connect to the system. When someone does try to connect, init goes through a sequence of operations to validate the connection (for example, it might ask for the user's name and password). Once init is satisfied that the user is authorized to use the system, init typically creates a shell process which then interacts with the user as described earlier. Once init has started up an appropriate shell, it goes back to wait for someone else to try to connect. The init process usually runs as long as the system is running; init starts at boot-time and just keeps going. If init does terminate for some reason, no new user will be able to sign on. However, existing sessions may still continue. The use of static data is one of the most important areas of difference between Multics and UNIX. In Multics, software expects to be invoked through dynamic linking, and typically expects to retain static information from one invocation to the next. UNIX programs, on the other hand, always expect to be called "clean", with all data areas initialized afresh on each invocation. The idea of "clean" invocation is reflected in all standards that have evolved out of the UNIX environment. For example, the ANSI standard for the C programming language expects C programs to start up "clean", thereby complicating the use of C on Multics systems. The same problem arises with implementing Fortran 77 and Cobol 84 on Multics, since both of those standards assume "clean" program start-up. Files: Under Multics, the operation of opening a file attaches all the contents of that file to the Multics process's address space. If any command wants to read or write to the file, the command simply accesses the appropriate location in memory. Since the entire file is assumed to be in memory, a program can directly access any location in the file just by specifying the address. Under UNIX, a file is considered to be separate from the program. The file can only be accessed through system calls that open, read, and write the file. The file is considered to be a sequential stream of bytes, and at any moment, there is a read/write pointer indicating a position in that stream. It is possible to issue system calls that can make the read/write pointer jump about within a file, but it is far more common to write UNIX programs in such a way that they read or write the file sequentially, from beginning to end. UNIX programs sometimes store portions of files directly in memory, but they seldom keep whole files in memory. Multics files are automatically shared by all programs, since all programs run in the same address space and the files are stored in that address space. The situation is different with UNIX files. When a parent process creates a child, it can set up the child with appropriate information so that parent and child share the same file. In this case, both parent and child will share the same read/write pointer. Suppose, for example, that parent and child alternate reading a byte at a time. The parent reads the first byte of the file and the read/write pointer automatically advances to the second byte. When the child submits a service call to read a byte from the file, it will read this second byte and the read/write pointer automatically advances to the third byte of the file. In this way, parent and child can alternate their way through the file. (Important: remember that parent and child run asynchronously. If they really want to alternate bytes, they need some way to signal each other, saying, "Okay, I've read my byte, you can go ahead and read yours.") The example we've just talked about showed a parent sharing a file with a single child. Parents can share files with any number of their children, the children can share files with their grandchildren, and so on. Thus any number of processes can share a file, all of them using the same read/write pointer. It is also possible for a different set of processes to access the same file, using their own read/write pointer, thereby working with the file concurrently. UNIX systems offer various file-locking facilities to support different kinds of concurrent access. File Permissions: Multics files have access control lists (ACLs) through which you can specify different permissions for different individuals. UNIX has a less versatile permission structure. Each file has owner permissions, group permissions, and other permissions: (a) The owner of a file is usually the person who created that file in the first place. However, the owner of a file can assign ownership to someone else if desired. Every file has a set of permissions (read, write, execute) which apply only to the owner. (b) A UNIX system administrator can assign each user to one or more system groups. For example, the administrator might create a system group for each project on the system, and the members of the group would be the people who are authorized to work on that project. A user can belong to several groups at once; one of these is considered the users primary group and the rest are secondary groups. Each file is affiliated with a group, usually the primary group of the person who created the file. Every file has a set of permissions which apply to everyone who belongs to the file's group. (c) The final type of permissions are other permissions. These apply to all other users of the system (anyone who is not the owner and is not in the file's group). As this shows, it's not possible to assign different permissions to specific users. On every UNIX system, there is one userid designated as root or the superuser. This userid can access any file on the system for reading, writing, or execution. The superuser userid should only be used by system administrators and only to perform administrative tasks like software upgrades, adding/deleting other userids, and so on. UNIX files also have a second type of permissions associated with them. The most important of these is called setuid permission. This permission is relevant when the file contains an executable program. Normally, an executing program runs in the name of the person who invoked the program. However, if the owner of the program file puts setuid on the file, the program can choose to run under the name of the invoker or the owner. For example, suppose a program is owned by jsmith and invoked by mjones; if jsmith has given the program file setuid permission, the program can choose to run under either jsmith or mjones. The name that the program chooses is called the program's effective userid. If a program file is owned by root, a program with setuid can change its effective userid to root. Once the program's effective userid becomes root, the program can access any file on the system. This is a common technique for implementing privileged system software: the superuser installs the program with setuid permission under the ownership of root, and the program can then become root whenever it wants to perform privileged operations. Note that the setuid concept can't work satisfactorily on Multics. Since the basic unit of software on Multics is the subroutine, setuid permissions would have to be associated with specific subroutines. However, the vast majority of subroutines work by calling other subroutines; thus a setuid subroutine would soon call a non-setuid subroutine, and the permission would vanish. If a called function wanted to find out if one of its callers had setuid permission, it would have to walk back through the stack checking each subroutine. The only secure way to check a subroutine's permissions would be to make a system call to a privileged ring, and the whole process would be prohibitively expensive. The UNIX File Bias: Multics tends to view everything as part of the user's address space. For example, a file is regarded as part of the address space, the operating system is also part of the address space, and so on. UNIX, on the other hand, tends to view everything as a file. All I/O devices, for example, are treated like files in the normal file system, with file names that look just like the names of normal disk files. Similarly, the actual memory of the computer can be referred to as files named /dev/mem (physical memory), /dev/kmem (memory occupied by the kernel), and so on. Since the superuser has permission to read/write any file, the superuser can access these files to read/write anything currently in the computer's memory. Thus the superuser really has completely unhindered access to all system resources. UNIX user privileges are implemented through file permissions. As noted in the previous section, the setuid file permission lets a program run under the root userid. Thus, every program with setuid permission must be considered privileged. If the system administrator wants to grant privileges to a particular user, it's just necessary to give that user execute permission on a program file that has setuid permission. As a simple example, consider an electronic mail package. This package must be able to access mailbox files owned by many different people; to do this, it must be able to become root, since root is the only userid which is not controlled by normal file permissions. Thus the system administrator can give the mail program file setuid permission so that the mail program can do its job. Assuming that the administrator trusts the mail program, the administrator would usually set up execute permission as part of the file's other permissions, so that anyone on the system could invoke the mail program. In general then, UNIX associates privileges with programs, and controls privileges by restricting access to the files that contain the programs. In addition, UNIX can control privileges by restricting access to certain types of data files. For example, the file /etc/passwd on a UNIX system specifies the names of all users authorized to sign on to the system and encrypted versions of their passwords. Anyone with permission to write to /etc/passwd can add or delete users and change passwords. For this reason, a user with permission to write to /etc/passwd has a certain type of privileges on the system. Links: Both UNIX and Multics support the concept of links, i.e. multiple names for the same file. On Multics, links are essentially files that contain the name of another file. When you try to open up the first file, the file system finds the name of the linked file and opens up the referenced file. Thus links are implemented by indirect references. To understand how links work on UNIX, you need to understand the underlying structure of the UNIX file system. On UNIX every file is associated with a unique integer, called an inode (eye-node) number. Some of files in the file system represent directories. Directory entries associate file names with inode numbers. For example, the directory file for directory dir might say that the file abc under that directory was associated with inode 100. Thus the real identifier for a file is its inode number; a file name gives you a way to look up a directory entry and find out the inode number associated with the name. Roughly speaking, UNIX has a linear file system (the inode numbers) with a hierarchical naming structure imposed on it. Any number of names may be associated with the same inode number and therefore the same UNIX file. These files are links of each other because they all refer to the same file. All of these file names have equal status, because they all are direct references to the same inode. Thus this type of link is more direct than the Multics concept. UNIX also supports Multics-style links, where a file just contains a pathname that provides an indirect reference to another file. UNIX calls these symbolic links. UNIX File Systems: UNIX uses inodes to identify files. In early implementations of UNIX, inodes were 16-bit numbers and therefore the file system had a maximum of about 65,000 files and directories. This soon became inadequate, and the designers of UNIX were forced to devise a way to expand file system capacity. Their solution was to allow multiple file systems. Each file system is disjoint and has its own independent set of inode numbers. For example, file system /u would have inodes running from 0 to 65595 and file system /u1 would also inodes running from 0 to 65595, but the inodes refer to entirely different files. An administrator might choose to configure each disk attached to the system as a separate file system. A single large capacity disks might contain several file systems, while several small capacity disks might be joined into a single file system. This shows that separate file systems do not have to be on separate physical media, although that's usually the most sensible way to organize the system. To make a new file system accessible to users, the superuser must mount that system. Basically, the mount operation associates a particular directory name with the new file system. Whenever you ask to access a file or directory under that directory, the system looks in the file system associated with that directory. You cannot establish true UNIX links between different file systemsjremember that UNIX links are based on inode numbers and each file system has its own inode numbers. You can, however, establish symbolic links between different file systems, since symbolic links are based on the file name, not on inodes. Resuming a Program Already in Execution: With both UNIX and Multics, it is possible that you might have program A call program B which calls program C, and then you want to resume A again (without terminating B and C). Under UNIX, this kind of operation is simple. A, B, and C are all active processes running simultaneously. If you are currently talking to C, C is considered the foreground process which means it is currently attached to the user terminal. The other processes are called background processes, which means they are not currently receiving input from the terminal. (They may still issue output to the terminal if they choose to do so.) To switch from C to A, you simply issue a signal to the UNIX system (typically by pressing CTRL-Z) which suspends C. You can keep C as a suspended process (which means that it is in memory but not active) or you can switch C to the background (in which case it will continue active execution but no longer receives input from the terminal). Finally, you can make A your foreground process and resume working with A. Basically then, you just detach the terminal from C and attach it to A. All processes continue as separately running processes in memory. On Multics, the situation is more complicated. Remember that Multics programs are structured in a strict hierarchy according to a pushdown stack. This means that C is on top of B which is on top of A in the stack. If you want to resume interacting with A, you could terminate C and B and pop them off the stack so that you could get to A. However, if A is the Multics editor, it has built-in facilities that let you resume an editing session without terminating B or C. Suppose you issue a BREAK as you are working with C. This adds a new break processor routine to the stack. The break processor invokes a command interpreter, and you can ask the command interpreter to call the Multics editor. The editor checks back through the programs on the stack to see if there is another invocation of the editor on the stack. If so, the editor copies down any stack information that the new invocation might need to pick up where the old editing session left off. From the user's point of view, it may look like the old editing session has resumed execution, since the new invocation of the editor has access to all the old invocation's files, memory, and so on. However, the stack contains the following hierarchy: -- the new invocation of the editor -- the command interpreter -- the break processor -- the old C -- the old B -- the old invocation of the editor While this set-up is more or less transparent to the user, it has a strong effect on the way that programs must be constructed internally. Note too, that the editor is specially constructed to make it possible to resume an editing session. Most programs cannot be resumed in this way. Environment Variables: Both UNIX and Multics support environment variables: variables which provide information to running software. However, the technique of implementation is different. In Multics, environment variables are true variables, available to all running programs. For example, if a particular program wants to use variable X, the program simply links in X dynamically. If one program changes X, the change is automatically received by all other programs, since the same X is shared by all programs. UNIX environment variables are not variables in the same sense. When a parent creates a child, the parent can deposit environment data into the child as part of the process creation process. This data is just a block of memory containing text of the form name=value name=value ... The child process can then read through this data to find the value associated with a particular name. The child can also change the value or add/delete entries in this list, if it chooses to do so. However, all of this takes place within the child's own address space. Changes that the child makes in the environment block do not affect the environments of other processes. (Note: if a UNIX parent process changes an environment variable, it normally passes on the changed value to any child processes that it creates after that point. However, a change in the parent doesn't affect any children that were created before the change.) Command Lines: When you issue a command to a UNIX shell (command interpreter), the shell may process the command line in various ways before invoking the command. The result of this processing is a character string which specifies options and arguments for the command. The shell then breaks the command string into tokens. Next, the shell invokes the command by creating a new process, and during process creation, the shell deposits the command line token strings into the new command's memory space. The command can then examine these tokens to extract the arguments, options, etc. specified by the user. Note that it is the command itself which interprets the tokens, often being forced to break up tokens into smaller component parts. On Multics, the command interpreter takes a larger part in interpreting command line arguments. Essentially, the command interpreter processes and parses the command line, breaking it up into arguments. These arguments are then passed on to the command in the same way that arguments are passed to a subroutine. Furthermore, this provides a wider range of features on command lines. For example, it is possible to pass a pointer to a command, and the command can then use this pointer to put values into the memory associated with another piece of software. This kind of operation is not possible under UNIX. Exceptions and External Interrupts: With both Multics and UNIX, exception handling takes place at the process level. However, in Multics there is only one process while in UNIX, there are many processes. Because a Multics user only has one process, exception handling can be relatively simple. When an exception or interrupt is triggered, an exception handler is invoked (and therefore pushed onto the stack). This handler has many choices: doing nothing (i.e. just popping itself off the stack), terminating the current program/routine on the stack, popping many programs/routines off the stack, or even killing the whole session. If the handler chooses to pop a number of units off the stack, each unit still has the chance to do wrap-up processing before it dies. A unit can also set flags saying, "Don't kill me if there's an exception." In this case, the popback operation stops at this unit and the unit resumes execution. With UNIX, each process can control its own destiny. Different programming languages may have different conventions for exception handlingjC, for example, generally has one set of exception handlers for the entire program, while in PL/I, different routines within the program can have different exception handlersjbut these are merely conventions. Each UNIX process may take its own approach to exception handling; there is no shared handler, as in Multics. Furthermore, the exception handling in one UNIX process doesn't affect what happens in other processes. For example, when a parent process terminates, it typically sends signals (interrupts) to each of its children saying, "I'm terminating now. Please terminate yourselves." Children can choose to ignore this signal if they wish; but even if one child chooses to ignore the signal, the other children still receive the signal and may decide to terminate. Contrast this with the Multics situation where one program saying, "I don't want to die," saves the life of everything left on the stack. Privileged Operations: On Multics, privileges are handled through the use of rings. As noted earlier, the user session has a single address space, containing all the code and data required during the session. In particular, this address space contains the code and data for privileged system operations. In the interest of security, this part of the address space must be hidden from ordinary user programs. Thus Multics divides the address space into a number of rings which have increasing degrees of privilege. Each section of the address space has an associated ring number, running from zero to eight. A program running in a low-privilege ring cannot directly access memory belonging to a higher-privilege ring; such a program is not allowed to reach across the ring boundary. You can only cross ring boundaries at designated entry points, using the equivalent of a subroutine call. Thus if a program wants to perform a privileged action, it must make a subroutine call to a ring with higher privilege. There are eight possible rings and therefore eight degrees of privilege. A high-privilege ring can access memory allocated to a lower-privilege ring. For example, a low-privilege ring could call a high-privilege routine, passing a pointer to an area in the low-privilege ring. The high-privilege routine could then write data into the area indicated by the pointer. Files containing Multics programs may have an associated ring level. When a user invokes such a program, the program is automatically loaded into the associated ring and acquires the given privileges. System administrators must guard the ability to assign high-privilege ring levels to files, since administrators must be able to trust programs that are granted such privileges. With UNIX, there are really own two degrees of privilege: normal user processes and processes making up the operating system itself (called the operating system kernel). In Multics terminology, therefore, UNIX has two rings. UNIX user programs can obtain services from the kernel through system calls. In essence, a system call sends a message to a kernel process, requesting a particular service. UNIX also supports privileges through file permissions, discussed in an earlier section. A program from a file with setuid permission can change its effective userid and can therefore access any file in the system. Users with permission to write to certain key system files can reconfigure the system or affect the system in some other way. As an example of the difference between UNIX and Multics, consider the situation where a normal user (jsmith) creates a database and wants to let other people use this database, but only if they do so through the user's own database management program. On UNIX, this is easy for jsmith to set up: -- create the database file; thus jsmith becomes the owner of the database -- set the owner permissions on the database file so that the owner can read and write the file -- remove all group and other permissions on the database file so that only the owner can access the file -- create a file containing the database management program; again, jsmith becomes the owner of this file -- turn on the setuid bit in the program file, so that the program can become jsmith when appropriate -- set the group and other permissions of this file to allow anyone to execute the program When someone executes the program, the program runs under the effective userid of jsmith. Therefore the program can access the database file as the owner of the file. If someone tries to access the database file without going through the authorized program, the system prevents the operationjonly the owner can access the file. Multics, on the other hand, has difficulty with this situation. A program operates under the userid of the person who executes the program, not the program owner. The database owner could use file permissions to let anyone read/write the database file, but then they wouldn't have to go through the authorized database management program. The only other approach would be to make the database management program privileged, by giving it a high-privilege ring level. However, the system administrator would be reluctant to do this for a normal program written by a normal user, since privileges should only be granted to highly trusted programs. As another useful comparison, consider how the two systems implement electronic mail. On UNIX, the mail program has setuid permission. It can therefore become root and read/write any file. On Multics, mailbox files are given general read/write permission but are also given a high-privilege ring level. Thus the file can only be read by a program with equal or higher privilege, so normal user programs can't read these files.