num = fork([true])
num = getpid()
num = getppid()
t/f = kill(pid[,signal])
str = wait([pid])
t/f = daemon()
num = nice(increment)
The terms "program" and "process" are often used interchangeably but they are not the same thing. A program is the machine code which comprises the set of instructions to be executed by the processor. A process is a single running instance of a particular program. There may be many such instances, as the same program may be started over and over again. In fact, programs often start new instances of themselves, using the fork() function. And even within the same process a program may replace itself with another program, perhaps written in a different language (jump()). This page deals with functions that control processes.
New processes are created with the fork() function. In fact, all processes are created by forking, starting from "init", which is the very first process (and program) started when the operating system is booted up. Basically, fork() creates a new process as a figment of its own imagination. Before a fork() there is just one process called the parent. When fork() completes there are two process instances of the same program — the parent and its new child. The program remains the same but the two processes now operate on different sets of data. Initially, both processes share exactly the same pages in virtual memory but, if either process modifies any data, it is given a new, private copy of just those pages that have been written to. This is called "copy on write" and helps to minimize the amount of storage duplicated in multiple processes. Because compiled machine code is rarely modified by a running program Linux is able to take this a step further. Pages with executable instructions and other "constant" data are loaded directly from the program's binary image, on disk. Effectively, there is only one copy of the large JSUS shell program shared by all of the JavaScript processes running on the system.
Both parent and child continue executing with the next program statement, immediately following the fork(). In the new, child process fork() returns zero, so that it knows it has just been born. In the parent, fork() returns an integer which is the "process id" of the child. (In the unlikely event fork() fails, it returns a value of -1, to the parent only, and the child is not forked). At this point, both processes share most of the same state, including variable contents, opened files, file positions and even memory mappings. Both processes may change or keep any part of this state according to their needs. The one glaring exception is that file locks are not inherited across a fork(). These locks must be reapplied by the child and failure to do so has led to many obscure bugs in many programs. (There are a few other exceptions, but they don't apply to most JavaScript programs).
Any process can obtain its own process id (pid) with the getpid() function. And any process can get the pid of its parent with getppid(). (A parent receives the pid of each child it creates as the return value from the fork() function). Given a pid, and sufficient permissions, one process may kill() another, causing it to die a horrible death. And, biblically, every parent has the right to kill() its own children. Just kidding, of course. What kill() really does is to send a "signal" to the identified process, as the optional second argument to the function. For example, SIGTERM is often sent to a "server" process to signal it's time to "terminate". The target process can intercept this signal and proceed with an orderly shutdown. If a server process is blocked, waiting for a get() to receive a request from a client, SIGTERM will interrupt the waiting function and then return null (with an error code in errno). If the signal argument is omitted, SIGKILL is the default, and this really does immediately liquidate the target (because it cannot be intercepted by the program). Signals are handled automatically within JSUS programs and their names are returned as strings by strerror(). They are specified to kill() as integer values, some of which are (for Intel systems):
01: SIGHUP (hang up)
02: SIGINT (interrupt)
03: SIGQUIT (quit operating)
06: SIGABRT (abort program)
09: SIGKILL (kill, cannot intercept)
14: SIGALRM (interrupt from a timer)
15: SIGTERM (terminate program)
10: SIGUSR1 (user defined)
12: SIGUSR2 (user defined)
17: SIGCHLD (child has stopped or terminated).
18: SIGCONT (continue a stopped process).
19: SIGSTOP (stop, cannot intercept)
If the second argument to fork() is omitted, or evaluates to false, the ending of a child process is ignored (which is usually desirable). Otherwise, the parent must wait() for the child to complete. If it fails to do so, the child process will hang around for ever, as a so-called "zombie", much to the consternation of the system administrator. The pid of the child to wait for is specified as the argument to wait(). A zero requests to wait() on any waitable child forked by the parent (i.e. those where the second argument to fork() evaluated as true). To be precise, wait() blocks the process until any signal is received, not just SIGCHLD. When interrupted, wait() returns a string containing the name of the signal and any other known information. For example, after a child ends, the string would hold "SIGCHLD process-id=exit-code". When there are no waitable children, wait() is often used to suspend the process indefinately, until some outside event occurs (such as the expiration of a timer).
The daemon() function is a special form of fork() used to place a "server" program into the "background" state. After forking, the parent process immediately exits, forcing all its open files to be closed. The "daemon" child then follows the standard initialization procedure for daemons. It sets its umask to zero, creates a new session and process group for itself and changes the current working directory (cwd) to the root. Finally, if the standard files are attached to a terminal device, stdin, stdout and stderr are closed and re-opened on /dev/null. Uniquely, JSUS then marks those files as "closed". This causes all future JavaScript output to stdout or stderr to be sent to the system log (USER.INFO or USER.ERR, respectively). However, with the debug option (-d) all redirection is suppressed and stdout and stderr are left connected to the terminal, where messages can still be displayed. This greatly simplifies the design and testing of server programs from a terminal, especially when run-time errors are thrown by JSUS. On success, daemon() returns true to the child. The parent receives null if errors are detected but, on success, the parent process is normally terminated.
It is important to note that any stdio file opened to a pipe, socket or regular file is never redirected. Also, if any of these files files are redirected, they will be inherited as /dev/null in any program subsequently started with a jump(). If a redirected file is redirected again, with mkpipe(), then /dev/null will be closed and the file's Fing will be marked as open. Conversely, in any process, stdout or stderr can be directed to the system log simply by close()ing the file or descriptor.
The nice() function changes the execution priority of a process. Its increment argument value is added to the process priority (which ranges from -20 (high) to +19 (low). A negative value (increasing priority) can only be given by a process running as root. A successful change returns the new priority, which may legitimately be -1 or zero. A null is returned if any errors are detected.