Process Control
Prev: System Call Error Handling
TOC: Exceptional Control Flow
Next: Signals
- Unix provides number of system calls for manipulating processes from C program, some of them are described here with usage
Obtaining Process IDs
- Each process has a unique positive (non zero) process ID, we can use getpid to return PID of calling process and getppid to return PID of it's parent
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
pid_t getppid(void);
Returns: PID or either the caller or the parent
- pid_t is defined as int in Linux's types.h
Creating and Terminating Processes
- A process can be in one of 3 states:
- Running: is either executing on the CPU or waiting to be executed and will be eventually scheduled.
- Stopped: It's suspended usually as a result of receiving SIGSTOP, SIGTSTP, SIGTTIN or SIGTTOU Signals, and remains stopped until it receives SIGCONT
- Terminated: Process is stopped permanently, the reasons could be:
- receiving a signal whose default action is to terminate the process.
- returning from main routine
- calling exit function
- The exit function terminates the process with exit status of status
#include <stdlib.h>
void exit(int status);
- A parent process creates a new running child process by calling fork function
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
Returns 0 to child, PID of child to Parent -1 on error
- The resulting child is almost identical to the parent, it gets copy of parent's user level virtual address psace, from heap to code/data segment to shared libraries the most notable difference being the process ID.
- Example usage:
int main(){
pid_t pid;
int x = 1;
pid = Fork();
if(pid = 0){//child
printf("child: x = %d\n", ++x);
exit(0);
}
//parent
printf("parent: x=%d\n", --x);
exit(0);
}
- There are a few aspects of this function:
- is called once but returns twice, once to the child, once to the parent
- parent and child are separate process that run concurrently
- the parent and child have the same content in address space but it's seperated
- They also share file descriptor, so they share the same open files
Reaping Child Process
- When a process is terminated, the kernel does it immediately remove it from the system, instead it's kept around in terminated state until reaped by it's parent.
- Upon being reaped, the child's exit status is returned to the parent and it gets discarded.
- A terminated process that's not reaped is zombie process.
- The init process becomes parents any child that gets orphaned as a result of a parent process terminated
- A process waits for its children to terminate or stop by calling waitpid function:
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *statusp, int options);
Retunrs PID of child if OK, 0(If WHANG), or -1 on error
- By default the function suspends the calling process until one of the process in the wait set gets terminated
- About it's args:
- if pid > 0, the parent waits for that single child with that pid to terminate
- if pid = -1, then the wait set consist of all the parent's child process
- We can modify the functions default behavior through option parameters;
- WNOHANG: returns immediately with 0 if no child process in wait set is terminated
- WUNTRACED; Suspends execution of the calling process until a process int he wait set either stops or is terminated, PID of the terminated/ stopped child is returned.
- WCONTINUED: Suspends execution of the calling process until a running process in thew ait set is terminated or a stop process in the wait set is resulted by receipt of a SIGCONT signal.
- We can also OR the condition together like:
- WNOHANG | WUNTRACED: returns immediately with 0 if no children in the wait set stopped or terminated, if stopped/ terminated then PID is returned, of the stopped or terminated process
- If non-NULL, the waitpid function encodes the status info about the child that caused the return in status, wait.h has several macros for interpreting this status argument:
- WIFEXITED(status): returns true if the child process exited with exit() or return statement.
- WEXITSTATUS(status): Returns exit status of a normally terminated child, (use only if WIFESTATUS returned true)
- WIFSIGNALED(status): Returns true if child was terminated due to some uncaught signal.
- WTERMSIG(status): Returns number of the signal that caused the child process to terminate, only defined if WIFSIGNALED() returned true.
- WIFSTOPPED(status): Returns true if the child that caused the return is currently stopped
- WSTOPSIG(status): Returns the signal number that caused the child to stop
- WIFCONTINUED(status): Returns true if child process was restarted by SIGCONT signal
- Error Conditions for waitpid()
- If the calling process has no such child as described in wait pid set, then waitpid returns -1 with errno set to ECHILD.
- If waitpid function was interrupted by a signal, waitpid returns -1 with errno set to EINTR
- There's a simpler alternative to waitpid() called the wait() function:
#include <sys/types.h> #include<sys/wait.h> pid_t wait(int *statusp); Returns PID of child if OK, -1 on error- Calling
wait(&status)is equivalent to callingwaitpid(-1, &status, 0) - Example of waitpid() usage:

- The order that the child gets reaped in above program is non deterministic, there is no particular order to it.
- We can eliminate the above non determinism with following "corrections":

Putting Processes to Sleep
- The sleep function suspends a process fora specified period of time
#include <unistd.h>
unsigned int sleep(insigned int secs);
Retunrn secnds left to sleep
- Returns zero if requested amount of time has elapsed or the number of seconds still left to sleep otherwise in case the function returned prematurely.
- We also have
pause()function that puts our process to sleep until a signal is received, the said signal must be handled by some handler for our process to wake up, else it will just ignore the signal, but if the default behavior of the signal is to kill/ terminate then it will do that.
Loading and Running Programs
- The excev function loads and runs a new program in the context of the current process.
#include <unistd.h>
int exceve(const char *filename, const char *argv[], const char *envp[]);
Does not return if OK, return -1 on error
- loads and runs the executable object file, filename with the argument list argv and env variable list envp.
- Only returns to the calling program with -1 if there'san error, otherwise it's called once and never returns
- argv and envp are organized as following data structure:

- At the start of program the user stack is structured in this way:

- The very first thing, at the highest address (bottom of the stack) are a bunch of null terminated environment variable strings, following by command line arg strings.
- This are followed by Null terminated ARRAY (not of strings) of pointers, one for envp and one for argv pointers.
- The GLOBAL VARIABLE in every C program, environ points to envp[0].
- Right after that is the stack frame for system startup function libc_start_main
- Here's how we can call the main routine:
int main(int argc, char **argv, char **envp);
- Linux provides several functions to manipulate environment array:
#include <stdlib.h>
char *getenv(const char *name);
Returns pointer to name if it exists, NULL if not
- getenv searches for env array for string name=value, if found, it returns pointer to value, otherwise NULL.
#include <stdlib.h?
int setenv(const char *name, const char *newvalue, int overwrite);
Returns 0 on success -1 on error
void unsetenv(const char *name);
Returns nothing
Program v/s Process
Program's collection of code that can exist as object file or disk or as segments in an address space.
A process is a specific instance of a program in execution
There was an implementation of simple shell right after this section but don't wanna do it
Prev: System Call Error Handling
TOC: Exceptional Control Flow
Next: Signals