Information

If you can see this, JavaScript doesn't appear to be enabled for this domain, and some function of this site may not work properly!

Found below is a number of annotated lists which are intended as a useful, general-purpose reference for assignments in the course.

Syscalls

System calls (often just syscalls) are requests for service made by a process, directed at an operating system. They are usually relatively low-level calls, abstracted away by most libraries (including libc, the C "userspace" library), but we will be using them as critical channels of communication, and also for their implementation of (non-portable) process management.

The following list is valid for both XV6 and POSIX (and the Single Unix Specification); however, it only includes the syscalls they both share (and specifically, the ones XV6 implements--which is a strict subset). Note that there are differences between them, which require some care to maintain portability (if this is desired).

On most Unix hosts (Linux in particular), these are in "manual section 2", and far more detail can be obtained by running man 2 ... in your shell.

pid_t fork(void) int fork(void)
"Forks" a new process, creating two copies: a "child" and a "parent". After returning, the child and parent have copies of the same memory mapping and the same open resouces (e.g., file descriptors created with open). It is important to note that this function essentially "returns twice"--the return value is, by far, the most straightforward way to differ which process the code has continued in. On success, fork return 0 in the child, and the (positive) PID of the child in the parent; on failure, it returns < 0 only to the parent, and no child is created.
void _exit(int status) void exit(void)
Exits the currently running process. This function never returns. All resources held by the process (file descriptors, memory) are recycled back into the system's free pool, so long as they are not shared with other processes; the process entry itself is set to "zombie" state until the parent calls wait to acknowledge the child's death, at which point the PID may be reused. The POSIX specification permits an "exit code" to be propagated back to the parent, usually used to indicate exit success or abortion due to an error condition; mainline XV6 has no such facility.
int wait(int *status) int wait(void)
Waits for a child process (as spawned by fork) to terminate (by calling exit or being killed, possibly by the kernel). This function "blocks"--it returns only when some child dies. Note that this returns after any child dies, and it is up to the caller to determine the significance of which child it was (e.g., identifying by the child PID returned by fork). If this process has no children, this function returns immediately with an error (POSIX specifies ECHLD). On POSIX, the exit code is written to *status if status is not NULL; XV6 has no such facility.
int kill(pid_t proc, int sig) int kill(int proc)
Sends a signal to a process. On POSIX, the signal can be specified, and (for all signals except SIGKILL) the signal can be handled non-fatally by the process, so "killing" can be used as a clumsy system of asynchronous IPC. XV6 acts as if SIGKILL were specified, making death unavoidable. If the process is terminated, any waiting parents are notified normally. (On POSIX, they receive a special error code including information about the fatal signal.) This call will return 0 if it completed successfully, or an error (< 0) if the target process does not exist or the caller does not have permission to send signals to the target.
pid_t getpid(void) int getpid(void)
Returns the PID of the calling process. This call never fails.
libc, uses unsigned alarm(unsigned sec); int sleep(int sec)
Suspends the calling process for sec seconds; assuming all goes well, this call returns after this duration elapses. This function may return earlier than the duration (if, e.g., on POSIX, something else delivers SIGALRM), or never return at all (if the process is killed). Returns error (< 0) if the parameter is out of range, and 0 (some time later) if successful. On POSIX, this function is provided by libc by using the alarm syscall, which delivers a (handleable) signal SIGALRM, and also returns the remaining duration left before SIGALRM would have been delivered (which will occur if it is called within a signal handler during the sleep duration).
int execv(const char *prog, char *const argv[]) and related; see man 2 exec int exec(char *prog, char *argv[])
Executes a new program with arguments. prog is a path (possibly relative) to a loadable program, and argv is an array of the arguments. Per convention, argv[0] should equal prog, and further argv[i] should be the ith argument (as a C string), terminated by a NULL pointer. On success, this function does not return; the current process' loaded image is freed, the program image is loaded from the file into memory, and execution begins at the start address indicated in the loaded image. If the function failed (no program, bad format, too many arguments, etc.), the function does return a value < 0 to the calling process.
void *sbrk(intptr_t increment) void *sbrk(int increment)
Changes the amount of virtual memory allocated to the calling process by adding increment bytes to its "break" (the end of its address space). If increment is positive, this has the effect of increasing the amount of memory available to user programs (and is frequently used internally by malloc et al.); if negative, the size of the process decreases. The kernel may deny any such allocation by returning (void *) -1, which should be treated as being out of memory. Otherwise, on success, it returns a pointer to the previous break, which (if increment > 0) points to the beginning of a newly allocated region of increment bytes. Processes should avoid using this as a general-purpose allocator--it suffers from internal fragmentation.
int open(const char *path, int flags) int open(char *path, int flags)
Opens an object on the filesystem, and returns a file descriptor (a capability to that object, expressed as a small, non-negative integer) at position 0 (the beginning of the file), or -1 if it fails. flags specifies simultaneously the requested access to the object (O_RDONLY for read-only, O_WRONLY for write-only, or O_RDWR for read and write) and a set of binary-or'd (|) with some other "flag" constants to change the behavior. Currently, XV6 only supports O_CREAT, which creates a file if it does not already exist (and the parent directory does). POSIX supports far more (O_TRUNC, O_APPEND, O_CLOEXEC, etc.); see man 2 open for more information.
ssize_t read(int fd, void *buf, size_t bytes) int read(int fd, void *buf, int bytes)
Reads from a file descriptor into a byte buffer. On success, returns the number of bytes read into the buffer, and the descriptor's position is advanced by this amount; values of bytes further in the buffer are undefined. read may return 0, which indicates no further data exists to be read (at the moment), and may be interpreted as "end of file". Returns less than zero indicate an error condition (such as attempting to read from a descriptor without read access). Note that the buffer contains bytes, not characters, and possibly with embedded '\0' characters--it is not a valid C string.
ssize_t write(int fd, const void *buf, size_t bytes) int write(int fd, void *buf, int bytes)
Writes to a file descriptor from a byte buffer. On success, returns the number of bytes written to the file from the buffer, and advances the descriptor's position by this much; note that this may not be the entirety of the buffer, so be prepared to continue from ((char *) buf) + n for a return of n < bytes (and accumulate this correctly--see examples). Returns of 0 are rare and deprecated, but consider it successful, but without any data transfer (e.g., due to a transient error or signal). Returns less than zero indicate errors. Note that the buffer may contain arbitrary bytes, and need not be a C string.
int close(int fd) int close(int fd)
Closes the file descriptor fd. This technically may fail, e.g., if fd does not refer to a valid file descriptor. POSIX additionally warns that errors during the last write may be reported only during a close. In all cases, unless the descriptor itself was invalid, the errors are merely informative--the descriptor is closed anyway.
int dup(int fd) int dup(int fd)
Duplicates the specified file descriptor to a new descriptor, the lowest one currently not allocated. On success, returns the new descriptor, which shares all state with the original descriptor--seeking the position on one is observable in the other--but may be closed and inherited by forked children independently. Returns a negative value on failure, such as if the original descriptor was invalid or the process has exhausted file descriptor resources.
int dup2(int oldfd, int newfd) int dup2(int oldfd, int newfd)
Duplicates the specified file descriptor given as oldfd into the file descriptor newfd. If newfd is already open, it is silently closed; errors encountered while closing it are not reported. (If this is a concern, dup newfd first, and check for errors when closing the duplicated descriptor.) On success, returns newfd; on error, returns < 0.
int pipe(int fds[2]) int pipe(int fds[2])
Creates two file descriptors: the first one (at fds[0]) is open for reading, and the second (at fds[1]) is open for writing. Data written to the second can be read in that order from the first, as if both file descriptors referred to the same file, but without being backed by a filesystem (the kernel passes the data in memory). Additionally, because they have no backing store, pipe descriptors cannot be "seeked" (sought) arbitrarily--data must be read exactly in the order written, and only once. (XV6 has no facility for seeking anyway.) The kernel dictates the amount of memory available to buffer writes ahead of reads; a write to a full buffer, or a read from an empty buffer, will usually block the respective process until such conditions change. pipe returns 0 on success, and < 0 on failure. Example:
					#include <unistd.h>
					#include <string.h>
					#include <stdlib.h>
					#include <assert.h>

					int main() {
						int p[2];

						if (pipe(p) < 0)
							abort();

						write(p[1], "hello", 5);

						char buf[6];

						read(p[0], buf, 5);
						buf[5] = '\0';

						assert(strcmp(buf, "hello") == 0);
					}
				
int chdir(const char *path) int chdir(char *path)
Changes the working directory of the current process to the given path, which must refer to an existing directory. Further syscalls with relative paths (those not starting with /) are resolved relative to this directory, until the next call to chdir. On success, return 0; on an error, such as an invalid path or access denied, returns < 0.
char *getcwd(char *buf, size_t size) char *getcwd(char *buf, int size)
Returns the current absolute working directory of the calling process. The caller must allocate a buffer of sufficient size to hold the path, and pass it in as buf, and the permissible size as size (including space reserved for the null terminator). If the buffer is too small, the path is truncated. While this path should be absolute, some systems (notably Linux) may return paths which do not start with '/' in unusual circmstances, such as if the current working directory has been unlinked (and so no path exists from / to that directory). On success, returns buf; otherwise, returns NULL (a 0 value).
int mkdir(const char *path, mode_t mode) int mkdir(char *path)
Creates a directory at the given path. All components but the last must exist, and the last component must note exist. The POSIX version accepts permission bits in mode; the XV6 version lacks permission support. Both return 0 on success, or < 0 on failure.
int mknod(const char *path, mode_t mode, dev_t dev) int mknod(char *path, int major, int minor
Creates an object in the filesystem at the given path. For POSIX, mode must specify the kind of node to create (S_IFREG for a regular file, S_IFBLK for a block device, S_IFCHR for a character device, S_IFIFO for a named pipe, S_IFSOCK for a UNIX socket), and should also contain permission bits; dev contains a device description, consisting of a major and minor device number (major usually identifies a driver, while minor identifies a device "owned" by that driver), which is most portably made using makedev(major, minor) (a macro on most systems). On XV6, no permissions are possible, the object is always a character device, and major and minor are specified explicitly. In any case, UNIX sockets should be opened with socket (not available on XV6), and FIFOs act like pipes, but have an explicit location in the filesystem (but do not store data--they merely serve as a rendezvous point for disparate processes to set up a pipe). Regular files are created empty and can be opened later. Device files are managed by a kernel-mode driver which handles all reads and writes on its own; various drivers will implement these in different ways, and you should check their documentation for details. mknod returns 0 on success and < 0 on failure.
int fstat(int fd, struct stat *st) int fstat(int fd, struct stat *st)
Returns information about an open file descriptor, such as the file size, number of hard links, actual inode number, etc.. The fields in this structure are too numerous to describe here; for details, see man 2 stat on a POSIX system, or stat.h in the XV6 distribution.
int link(const char *oldpath, const char *newpath) int link(char *oldpath, char *newpath)
Creates a "hard link", a new path for the same inode. Removal of one file does not affect the data contained by the other, and the data is not released until all such links are gone (creating a file is considered the "first link") and all file descriptors to that file no longer refer to it. Returns 0 on success, and < 0 on error.
int unlink(const char *path) int unlink(char *path)
Removes a hard link which refers to an inode, and decreases its reference count. If this was the last link to the file (usually, most files only have one link) and no processes have an open file descriptor to that file, the inode is deleted and the space on the underlying storage freed. Returns 0 on success, and < 0 on error.