Unix System Calls for Process Handling

Birth and Death

  1. Include files:
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <signal.h>
  2. pid_t fork()
    Creates a new process that is a memory copy of the calling process. On error return –1. On success returns 0 to the new child process and the process id of the new child to the parent process. Parent and child will have different address spaces, but will share offsets of open files. The child inherits the entire U-area of the parent.
  3. pid_t vfork()
    Similar for fork, used when process creation is followed immidiately by exec() or exit(). Parent and child will share address space and U-area. Also, child is ensured to run first until it executes exec() or exit() – may cause deadlock!
  4. int exit(int status);
    int _exit(int status);
    Terminate the current process. exit() flushes and closes all open files, but _exit() doesn’t.
  5. An example program for fork() versus vfork() and exit() versus _exit():
    int global = 0;
    pid_t pid;
    int main() {
    printf(“before fork\n”);
    if ((pid = fork()) < 0)
    { perror(“fork”); exit(1); }
    else if (pid == 0) {
    global++;
    exit(0);
    }
    printf(“after fork, global = %d\n”, global);
    }
    If we use fork() both lines are printed, and the global will be zero. If we use vfork() and _exit() both lines will be printed, and global will be 1. If we use vfork() and exit(), only the first line will be printed because the child will close the parent’s standard output.
  6. int atexit(void (*func)(void));
    Registers a function to be performed when the process exits. Up to 32 functions can be registered, functions may be registered more than once, and are executed in reverse order of registration.
  7. pid_t getpid(void);
    pid_t getppid(void);
    Get process id or the parent process id of the current process.
  8. uid_t getuid(void); uid_t geteuid(void);
    gid_t getgid(void); gid_t geteguid(void);

    Get the real user id, effective user id, real group id or effective group id of the process.
  9. pid_t wait(int *statloc);
    Waits for one of the child processes of the current process to stop or terminate. When someone does, it returns termination status (not exit status!) in statloc, and the process id as a return value. Returns –1 on error. If statloc is null, nothing is inserted to it.
  10. pid_t waitpid(pid_t pid, int *statloc, int options);
    Similar to wait(), but can be either blocking (options = 0) or non-blocking (options = WHOHANG), and can wait for a specific pid (wait for anyone if pid = -1, wait for a specific process if pid > 0). Same return value as wait(), termination status in statloc.
  11. int execl(const char *pathname, const char *arg0, ..., (char*)0);
    int execv(const char *pathname, char *const argv[]);
    int execle(const char *pathname, const char *arg0, ..., (char*)0,
    char *const envp[]);
    int execve(const char *pathname,char *const argv[],char *const envp[]);
    int execlp(const char *filename, const char *arg0, ..., (char*)0);
    int execvp(const char *filename, char *const argv[]);
    Replaces the current process with a new program. No new process is created! ‘l’ stands for arguments list, ‘v’ for vector. ‘e’ stands for environment variables list. ‘p’ stands for “look in the PATH of the process if you don’t file the file in the current directory”.
    All these functions return –1 on error, otherwise they never return.
  12. int system(const char *cmdstring);
    Forks a new process and executes a shell to process the given command string. This is a library function that calls fork(), exec() and waitpid() in the backgroud.
  13. Signals

  14. Signals are messages sent to processes. It can be a program event (child died, broken pipe), a hardware error (devision by zero), or a message from another process. Signals are sent and handled by either a default handler or a specified handler. There is no order in which signals are sent to a process, and in most systems signals are not queued while inside the signal handler for that signal.
  15. Sigfunc *signal(int signo, Sigfunc *func);
    Defines func as a signal handler for signal number signo. Sigfunc is defined as:
    typedef void Sigfunc(int);
    func can also be one of the special constants SIG_ERR, SIG_DFL or SIG_IGN.
  16. There are constants for all signal numbers: SIGCHLD, SIGALRM, SIGABRT, SIGKILL, SIGFPE, SIGSEGV, SIGPIPE, SIGUSR1, SIGUSR2 and others.
  17. int kill(pid_t pid, int signo);
    int raise(int signo);

    Send a signal to another process, or to yourself. Returns –1 on error or 0 on success.
  18. int pause(void);
    Pause the process until a signal (any signal) is sent to it.
  19. int alarm(unsigned int seconds);
    The process will receive a SIGALRM signals after the specified number of seconds. Note that the process may handle the signal later, when the scheduler wakes it up. The default behavior of the signal is to terminate the process. There is only one alarm per process, so calling the function while a previous alarm is set will cancel the previous one; calling alarm(0) cancels the alarm. Note a race condition in this code (error handling removed):
    signal(SIGALRM, SIG_IGN); alarm(10); pause();
  20. void abort(void);
    Sends SIGABRT to the process. This function never returns – the signal handler should do process clean-up and then call exit().
  21. int sleep(int seconds);
    The process will be suspended for the specified number of seconds, or until it receives a signal. Return value is zero if no signal was received, or the number of unslept seconds otherwise.
  22. int sigemptyset(sigset_t *set);
    int sigfillset(sigset_t *set);
    int sigaddset(sigset_t *set, int signo);
    int sigdelset(sigset_t *set, int signo);
    int sigismember(sigset_t *set, int signo);

    First four functions empty, fill, add a signal or delete a signal from a signal set. The fifth function returns 1 if and only if signo is a member of the set, 0 otherwise.
  23. int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
    Blocks or unblocks signals – useful for critical sections. If oset is not null, returns current block mask in it. If ‘how’ is SIG_BLOCK, blocks the signals in ‘set’ from now on. If ‘how’ is SIG_UNBLOCK, unblocks the signals in ‘set’. If how is SIG_SETMASK, sets the block mask to be ‘set’.
  24. int sigpending(sigset_t *set);
    Returns in ‘set’ the set of signals that are blocked from delivery and currently pending for the current process.
  25. Shell Redirection

  26. Using the shell, you can ask programs to redirect their standard input, standard output or standard error to specific files instead of using the keyboard and screen:
    ls > my_output_file1
    date >> my_appended_output_file2
    sort < my_input_file3
    sort < my_input_file3 > my_output_file4
    ls >& my_error_file
    ls >>& my_appended_error_file
  27. You can also create “pipes”, in which the output of one program becomes the input of the next one in the pipe:
    ls | sort
    cat < input_file | sort | head –20 > output_file
    Pipes and redirection can be as long as use wish. The only thing you can’t do is redirect a file to more than one place, as in:
    ls | sort < my_file
    ls > my_file | sort
  28. Pipes and redirection characters are interpreted by the shell, and the programs don’t see them. So redirection and pipes will work for any program.
    However, to make life easier, it is a convention that filter programs such as sort or head process the list of files they are given as arguments, or the standard input if they were executed with no arguments. This convention also works well for wildcards.
  29. Wildcards are also interpreted by the shell: ‘*’ stands for ‘any zero or more characters’, ‘?’ stands for ‘exactly one character, and ‘[list]’ stands for ‘exactly one of the characters in the list’. Examples:
    ls *.a , ls file??.tmp , ls temp[123].old , ls temp[A-Z].old
  30. Implementing Redirection

  31. The pipe() system call and the dup2() system call can be used together to implement redirection as done by the shell. If, for example, we would like to execute a program called ‘prog’ but make it think that its standard input is not the screen but a file we write to, then we call pipe() and fork() as in the previous example, but the child process code will now be:
    close(fd[1]);
    if (f[0] != STDIN_FILENO)
    if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO)
    { perror(“dup2”); exit(1); }
    close(fd[0]);
    if (execl(“prog”, (char*)0) < 0)
    { perror(“execl”); exit(1); }
    Redirecting the standard output or standard error can be done in the same way: Duplicate a file descriptor of your choice to STDOUT_FILENO or STDERR_FILENO.