32
A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then a division of AT&T) in 1969. In early 70s, Dennis Ritchie (also at Bell Labs) designed and implemented C. Quickly C was used to write most of the UNIX kernel. In the mid 70s, AT&T released UNIX into universities for a low cost, which made UNIX very popular, and that also encouraged lots of UNIX variants. The most important UNIX “flavors” are: BSD (UC Berkeley) and AT&T’s System V Release 4 (SVR4). Then standards emerge: POSIX.1 (IEEE 1998), Spec 1170 (X/Open Foundation 1994), and finally the Single Unix Specification, version 3 (SUSv3, the Austin Group, 2001).

A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

  • Upload
    others

  • View
    12

  • Download
    0

Embed Size (px)

Citation preview

Page 1: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

A briefer history of UNIX

• UNIX (a pun on MULTICS) originated from Ken Thompson atthe Bell Labs (then a division of AT&T) in 1969. In early 70s,Dennis Ritchie (also at Bell Labs) designed and implementedC. Quickly C was used to write most of the UNIX kernel.

• In the mid 70s, AT&T released UNIX into universities for alow cost, which made UNIX very popular, and that alsoencouraged lots of UNIX variants.

• The most important UNIX “flavors” are: BSD (UC Berkeley)and AT&T’s System V Release 4 (SVR4).

• Then standards emerge: POSIX.1 (IEEE 1998), Spec 1170(X/Open Foundation 1994), and finally the Single UnixSpecification, version 3 (SUSv3, the Austin Group, 2001).

Page 2: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

Then there comes GNU and Linux

• In 1984, Richard Stallman (rms) started the GNU project tocreate a “free” (in a legal sense) UNIX implementation(GNU’s not UNIX) and founded the Free Software Foundation(FSF) to promote free software (the GPL license).

• By Early 90s, the GNU project produced an almost completesystem (Emacs, GCC, Bash, glibc, etc.) except for the OSkernel (named GNU/HURD, still under development).

• In early 90s, Linus Torvalds, then a student in Finland,developed an OS kernel based on Minix (an educational OS),and the system was eventually named Linux. Linux as weknow today is a combination of the Linux Kernel and softwarefrom the GNU project: GNU/Linux.

• None of the free UNIX implementation has passed the officialUNIX conformance tests (thus cannot officially call themselvesUNIX), but nonetheless, they look and behave like classicUNIX systems . . .

Page 3: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

The Unix process life-cycle

1 fork(): process creation

2 exit(): process termination

3 wait(): process monitoring

4 execve(): process execution

Page 4: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

Process creation by fork()

The fork() system call creates a new process, the child, which is analmost exact duplication of the calling process, the parent.

fork() specification

#include <unistd.h>

pid_t fork(void);

In parent: returns process ID of child on success,

or -1 on error;

In successfully created child: always returns 0

Page 5: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

fork() semantics detail

• After fork(), two processes exist, and, in each process,execution continues from the point where fork() returns.

• The two processes are executing the same program text, butthey have separate copies of the stack, data, and heapsegments. The child’s copies are initially duplications of theparent’s. But after fork(), each process can modify its owndata without affecting the other process.

• For parent, fork() returns the id of the child process; for thechild, fork() always returns 0. If needed, the child process canuse getpid() to obtain its own id, and getppid() to query itsparent process’s id.

• Important: after a fork(), it is indeterminate which of the twoprocesses is next scheduled to use the CPU.

Page 6: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

File sharing between forked parent and child

After fork(), the child duplicates all of the parent’s file descriptors,which means the corresponding descriptors in parent and childrefer to the same open file. Thus if one of the process modifies anopen file, the other process will also see the change!

descriptor x

descriptor y

descriptor x

descriptor y

entry m

entry n

Parent file descriptors

Child file descriptors

Open file table

fork()

Page 7: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

Distinguishing the parent and child after fork()

The following idiom is sometimes employed when callingfork():

/* used in parent to record child process id */

pid_t child_pid;

switch(child_pid=fork()) {

case -1:

/* fork() failed, handle error */

case 0:

/* child comes here after fork() */

default:

/* parent comes here after fork() */

}

Page 8: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

A fork() example

static int data = 111;

int

main(int ac, char* av[]) {

int local = 222;

pid_t child_pid;

switch(child_pid=fork()) {

case -1:

perror("fork failed");

return 1;

case 0:

data *= 3;

local *= 3;

break ;

default:

sleep(3);

break;

} /* end of switch */

/* both parent and

child come here */ printf

("PID=%ld %s data=%d \

local=%d\n",(long)getpid(),

(child_pid==0)?

"(child) ":"(parent) ",

data, local);

return 0;

}

Sample output:

PID=3829 (child) data=333 local=666

PID=3828 (parent) data=111 local=222

Page 9: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

Process termination using exit()

When a process can terminate normally using the exit() or exit()system calls.

exit() specification

#include <unistd.h>

void _exit(int status);

exit() specification

#include <stdlib.h>

void exit(int status);

In general, performing an explicit “return n;” from the “main()”function is equivalent to calling “exit(n)”.

Page 10: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

Difference between exit() and exit()

The exit() call is built on top of the exit() call. When exit() isinvoked:

1 Exit handlers are called, in reverse order of their registration.

2 The stdio stream buffers are flushed.

3 The exit() call is then invoked, using the value supplied instatus.

Page 11: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

Exit handler

Exit handlers are registered using the atexit() system call.

atexit() specification

#include <stdlib.h>

int atexit(void (*func)(void));

Returns 0 on success, or nonzero on error

An example:

static void handler(void) {

printf("exit handler called\n");

}

int main(int ac, char* av[]) {

if(atexit(handler) != 0) {

perror("handler registration error");

exit(1);

}

exit(0);

}

Page 12: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

Monitoring child processes

A parent process can use the wait() system call to find out whenand how one of its child process terminate. The wait() call waitsfor one of the children of the calling process to terminate andreturns the termination status of that child in the buffer pointed toby status.

wait() specification

#include <sys/wait.h>

pid_t wait(int* status);

Returns process ID of terminated child,

or -1 on error

Page 13: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

Semantics of the wait() call

1 If no (previously unwaited-for) child of the calling process hasyet terminated, the call blocks until one of the childrenterminates. If a child has already terminated by the time ofthe call, wait() returns immediately.

2 If status is not NULL, information about how the childterminated is returned in the integer to which status points.

3 The kernel adds the process CPU times and resource usagestatistics to running totals for all children of this parentprocess.

4 wait() returns the process ID of the child that has terminated.

Note: wait() only waits for one of the children if there are multiplechild processes. Also wait() cannot specify which child process towait for. A single call to wait will return once any child processterminates.

Page 14: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

How to wait for all children?

On error, wait() returns -1. One possible error is that the callingprocess has no (previously unwaited-for) children, which isindicated by the error number (errno) value ECHILD. We can usethis property to repeatedly wait for all children until no one is left:

A common method to wait for all children#include <errno.h>

while( (child_pid=wait(NULL)) != -1)

continue;

if(errno != ECHILD) /* make sure no other error occurred */

/* handle or report error */

Page 15: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

A fork/wait example (waitall.c)

int main(int argc, char* argv[]) {

int num_dead, j;

pid_t child_pid;

if(argc < 2) {

printf("Usage: %s sleep-time...\n", argv[0]);

exit(0);

}

setbuf(stdout,NULL); /* disable stdio buffering */

/* create one child for each argument */

for(j=1;j!=argc;++j) {

switch(fork()) {

case -1:

perror("fork error");

exit(1);

case 0: /* child process */

printf("child %d started with PID %ld, sleeping %s "

"seconds\n", j, (long)getpid(), argv[j]);

sleep(atoi(argv[j]));

_exit(0);

Page 16: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

waitall.c continued

default: /* parent continues to loop */

break ;

} /* end of switch */

} /* end of for loop */

num_dead = 0;

for(;;) {

if( (child_pid=wait(NULL)) == -1) {

if(errno == ECHILD) {

printf("No more children - parent quit!\n");

exit(0);

} else {

perror("wait error");

exit(1);

}

}

++num_dead;

printf("wait() returned child PID %ld (num_dead=%d)\n",

(long)child_pid, num_dead);

}

} /* end of main */

Page 17: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

waitall.c continued

A possible shell session log of the program could be:

$ ./waitall 7 2 4

child 1 started with PID 34093, sleeping 7 seconds

child 2 started with PID 34094, sleeping 2 seconds

child 3 started with PID 34095, sleeping 4 seconds

wait() returned child PID 34094 (num_dead=1)

wait() returned child PID 34095 (num_dead=2)

wait() returned child PID 34093 (num_dead=3)

No more children - parent quit!

Page 18: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

Orphans and zombies

The lifetimes of parent and child processes are usually not thesame — either the parent outlives the child or vice versa. Thisgives us two situations:

• If the parent process terminates before one of its children,then that child process becomes an orphan process. The initprocess, the ancestor of all processes (with a PID 1), willadopt orphan processes.

• If a child process terminates before a parent process, then itsresources will be released back to the system. But the kernelstill keeps the child process’s ID, termination status, statistics,etc. in the kernel process table so that in the future a parentcan still perform a wait() to find out the status of this child.However if the parent forgets to wait for this child, then it isforever remaining in the kernel’s process table and cannot bekilled by normal means, effective becomes a zombie.

Page 19: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

Program execution by execve()

The execve() system call loads a new program into a process’smemory. During this operation, the old program is discarded, andthe process’s stack, data, and heap are replaced by those of thenew program. After some system initialization, the new programstarts its main() function.

The most frequent use of execve() is in the child produced by afork() so that the child process starts executing a new program.

By default, all file descriptors opened by a program that callsexecve() remain open and are available for use by the new program.

Page 20: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

execve() details

execve() specification

#include <unistd.h>

int execve(const char* pathname,

char* const argv[], char* const envp[]);

Never returns on success; returns -1 on error

• pathname contains the pathname of the new program to be loaded.It can be absolute (indicated by an initial “/”) or relative to thecurrent working directory of the calling process.

• The argv argument specifies the command-line arguments to bepassed to the new program. It is a NULL-terminated list of pointersto character strings, just as the argv argument to a C main()function.

• The envp argument specifies the environment list for the newprogram. It is a NULL-terminated list of pointers to characterstrings of the form name=value.

Page 21: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

A family of exec() library functions

exec() functions specification#include <unistd.h>

int execle(const char* pathname, const char* arg, ...

/* , (char *) NULL, char* const envp[] */);

int execlp(const char* filename, const char* arg, ...

/* , (char *) NULL */);

int execvp(const char* filename, char* const argv[]);

int execv (const char* pathname, char* const argv[]);

int execl (const char* pathname, const char* arg, ...

/* , (char *) NULL */);

None of the above returns on success; all return -1 on error

Page 22: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

Differences in the exec() function family

Essentially the various exec() functions are all built on top of theexecve() function. They primarily differ in the argument formatand whether or not to include an environment.

Function Specification ofprogram file

Specification of ar-guments

Source of environ-ment

execve() pathname array envp argument

execle() pathname list envp argument

execlp() filename (searchedusing currentPATH)

list caller’s environ-ment

execvp() filename (searchedusing currentPATH)

array caller’s environ-ment

execv() pathname array caller’s environ-ment

execl() pathname list caller’s environ-ment

Page 23: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

exec() examples

Both of the examples run “ls -l” but each uses a different exec()function.

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

int main() {

char* args[]

= {"ls", "-l", NULL};

execve("/bin/ls",

args,

NULL);

/* if we are here, then

execve has failed */

perror("execve failed");

return 1;

}

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

int main() {

execl("/bin/ls",

"ls",

"-l",

(char*)NULL);

/* if we are here, then

execl has failed */

perror("execl failed");

return 1;

}

Page 24: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

IPC using pipes

Pipes are the oldest method of Inter-process communication (IPC)on the UNIX system. It provides an elegant solution to theproblem: how to pass data between related processes?

A pipe example in a UNIX shell

$ ls | wc -l

The output of the “ls” program was directly used by the “wc”program as input. There is no intermediate files or results createdon the disk.

Pipes can used for communication between any number of relatedprocesses, as long as the pipe was created by a common ancestorbefore the series of fork() calls that led to the existence of theprocesses.

Page 25: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

The semantics of pipe

Unidirectionalbyte stream

pipe

write end

read end

• A pipe is a byte stream: there is no concept of message or messageboundaries when using a pipe. Also reading the pipe can obtain blocks ofdata of any size regardless of the size of blocks written to the pipe.Furthermore, bytes are read in the exact order they were written —access is strictly sequential.

• Attempts to read from an empty pipe will block until at least one bytehas been written to the pipe. If the write end of a pipe is closed, then aprocess reading from the pipe will see end-of-file (i.e., read() returns 0)once it has read all remaining data in the pipe.

• write() to a pipe will block if the size of data exceeds current space in apipe. write() will attempt to transfer as much data as possible to fill thepipe, and then blocks until data has been removed from the pipe.

• A pipe has limited capacity. E.g., since Linux 2.6.11, the pipe capacity is65,536 bytes. But theoretically a pipe can be a single-byte buffer.

Page 26: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

Creating a pipe

pipe() specification#include <unistd.h>

int pipe(int pfd[2]);

Returns 0 on success, or -1 on error

direction of

data flow

pipe

calling processpfd[1] pfd[0]

After a process called pipe()

• pipe() returns two file descriptorsin the array pfd: pfd[0] is thepipe read end and pfd[1] is thepipe write end.

• As normal file descriptors, we canuse the write() call to fill thepipe, and use the read() call toget data from the pipe.

• A pipe has few uses within asingle process. Normally we use apipe to allow communicationbetween two processes.

Page 27: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

IPC using a pipe (i.e., fork/pipe)

direction of

data flow

pipe

parent processpfd[1] pfd[0]

child processpfd[1] pfd[0]

A pipe after fork()

direction of

data flow

pipe

parent processpfd[1] pfd[0]

child processpfd[1] pfd[0]

After closing unused pipe descriptors

It is very important to close unused pipe ends:• If the reading process doesn’t close the write end of the pipe, then after

the other process closes its write descriptor, the reader will not seeend-of-file, even after it has read all data from the pipe. Instead read()will block waiting for data since theoretically it is still possible to write tothe pipe.

• Likewise, if the writing process doesn’t close the read end of the pipe,then, even after the other process closes the read end of the pipe, thewriting process will still be able to write to the pipe, eventually filling thepipe and blocks.

Page 28: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

Steps to create a pipe from parent to child

direction of

data flow

pipe

parent processpfd[1] pfd[0]

child processpfd[1] pfd[0]

int pfd[2];

if(pipe(pfd) == -1)

/* pipe creation error */

switch(fork()) {

case -1: /* fork error */

case 0: /* child */

if(close(pfd[1]) == -1)

/* close error */

/* child reads from pipe */

break ;

default: /* parent */

if(close(pfd[0]) == -1)

/* close error */

/* parent writes to pipe */

break ;

}

Page 29: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

Basic I/O routines — open()

open() specification

#include <sys/stat.h>

#include <fcntl.h>

int open(const char* pathname, int flags, ... /* mode_t mode */);

Returns file descriptor on success, or -1 on error

• The name of the file to be opened is indicated by thepathname argument.

• The flags argument is a bit mask that specifies the accessmode for the file.

• The mode bit-mask is optional and if specified, sets thepermission to be placed on the file.

Page 30: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

Some open() examples

open() examples

/* open existing file for reading */

int fd = open("reader.c", O_RDONLY);

/* open new or existing file for writing; truncating

the file to zero bytes; file permisions read+write

for owner, nothing for all others */

fd = open("writer.c", O_WRONLY | O_CREAT | O_TRUNC,

S_IRUSR | S_IWUSR);

Page 31: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

read() and write()

read() and write() specifications

#include <unistd.h>

ssize_t read(int fd, void* buffer, size_t count);

Returns number of bytes read, 0 on EOF, or -1 on error

ssize_t write(int fd, void* buffer, size_t count);

Returns number of bytes written, or -1 on error

• fd is the file descriptor (e.g., returned by open(), or a pipe end);

• buffer is the data buffer (the target place of read, or, the sourceplace of write).

• count is the number of bytes to read into the buffer, or, in the caseof write, the number of bytes in the buffer to write to the file.

Page 32: A briefer history of UNIXweb.cse.msstate.edu/~ioana/Courses/CSE6733/SLIDES/... · A briefer history of UNIX UNIX (a pun on MULTICS) originated from Ken Thompson at the Bell Labs (then

An example of read() and write()

Assume fd1 and fd2 are two file descriptors. The followingprogram repeatedly reads from fd1 and writes to fd2 until all datain fd1 are read.

#define BUF_SIZE 10

char buf[BUF_SIZE];

int num_read;

for(;;) {

num_read = read(fd1, buf, BUF_SIZE);

if(num_read == -1)

/* read error */

if(num_read == 0) /* end-of-file */

break;

if(write(fd2, buf, num_read) != num_read)

/* write error */

}