|CS 321 Spring 2013 > Lecture Notes for Monday, January 23, 2013|
Concurrency means logically (as opposed to physically) doing multiple operations at the same time. Modern OSs support concurrency far in excess of the number of processors in the machine. They do this by rapidly switching a single processor between multiple tasks. This is called multitasking (an older term, time sharing, means essentially the same thing). Such switching may be done with or without the explicit permission of the programs being executed concurrently. In the latter case, the OS is doing preemptive multitasking.
A process is a program in execution. This is an abstraction of the processor. A typical modern computer runs hundreds of processes concurrently.
We also use the term “process” to refer to the operating facility that supports a running program. Modern OSs generally allow this facility to support multiple concurrent executions, all of which have access to the same memory. These are called (kernel) threads.
Each process has an address space: its abstraction of the memory. This looks like a simple sequence of bytes, accessed via the usual assembly-language instructions. In practice, parts of the address space may correspond to physical memory, while other parts are actually on disk. When the latter are accessed, they are loaded into memory (this is virtual memory). If memory is full, then portions may need to be swapped out to storage.
is another abstraction that looks like a sequence of bytes,
but it is accessed via a very different interface:
which is optimized for slow connections.
Conceptually, a file is an abstraction of storage;
however, as we have seen, essentially anything can be behind
the file interface.
Input/output, or I/O, means communication with anything other than the processor and the memory.
All modern OSs provide various kinds of protection: ensuring that all access (to memory, files, etc.) is authorized.
A shell is a low-ish level interface to operating-system functionality. Traditionally, a shell uses a command-line interface.
Some things an OS does without being directly told to do so: switch between processes, swap a portion of memory out to disk, etc. Other OS operations are done when requested: output something, start a new process, wait a while, grab some memory, etc. These requests are done via system calls.
System calls happen a lot: essentially everything a program does, aside from mucking about with its own variables, will involve system calls.
Nonetheless, in a typical program
we rarely deal directly with actual system calls.
Typically we use higher-level library routines that make system calls.
For example, in C++, “
cout << ...”
is (in part) a wrapper around the
sys_write system call.
In C/C++, system calls are usually made through thin wrapper functions. These are bare-bones interfaces to the assembly-language system calls. The main reason for a wrapper is calling conventions: C/C++ functions are not called the same way that system calls are made.
We looked at the following Unix system call wrappers.
All are usually declared in
(not an ANSI standard header).
sys_write. Write to a (virtual?) file. Takes file descriptor, pointer to character buffer, number of characters to write. Returns number of characters actually written. A file descriptor is a nonnegative integer that identifies an open file. File descriptors are returned by the
sys_opensystem call. Standard file descriptors that are opened for us are
2(standard error output).
sys_exit. (The underscore avoids name conflict with a Standard Library function.) End current process. Takes integer parameter, which is used as status of process. By convention, a status of zero means no error, while nonzero signals an error. Does not return.
sys_sleep; may be wrapper around
sys_nanosleep, which allows for more fine-grained timing. Put process to sleep for given number of seconds. Takes integer parameter: number of seconds to sleep. Returns number of seconds the process actually slept.
sys_fork. Clone the current process. No parameters. Return value is integer of type
pid_t. For child process, returns zero. For parent process, returns non-zero. If return value is positive: process ID (PID) of child. If negative: fork failed.
for C++ code that makes system calls.
Note: These will probably work on most Unix-derived OSs;
they may not work under Windows.
System Calls will be continued next time.