Week 9 #
Processes and Redirection #
Launching a New Process #
- Clone the process
pid_t fork(void); // child gets return value 0, parent gets child's pid- Both processes (child and parent) run the same code
- The child can switch to running another program
execlp(path, arg0, arg1, ..., (char *)NULL); // 'exec' family of system calls- Things such as environment variables, pid, fd, current dir, etc. are preserved
- File descriptors can be closed before exec by marking them as
close on exec
Why seperate fork and exec?
#
- Some use cases do not require
exec - The child process can do some prep before
exec(file redirection and pipelining)
The only ‘parent-less’ process: init
#
forkis the only way to launch new processes- As the kernel boots, it launches
init, with pid 1
Process Commands #
ps: List processespgrep: Find processes by name, users, etc.top: Process list that periodically refreshes (think of it as a terminal task manager)htop:top, but better (more features)killandpkill: Terminates a process if allowed (pkillfinds likepgrep)
Waiting for a Child process #
pid_t wait(int *status); // status is for child's exit code
pid_t waitpid(pid_t pid, int *status, int options);
// pid > 0 -> wait for the given child
// pid == -1 -> wait for any child
// options == WNOHANG -> don't hang waiting
Useful macros #
- Normal Termination:
WIFEXITED,WEXITSTATUS
- Killed by signal:
WIFSIGNALED,WTERMSIG,WCOREDUMP
- Stopped and continued by signal:
WIFSTOPPED,WIFCONTINUED
Termination of Parent before Child #
- If the child terminates first and the parent process is still running and does not call
wait- A ‘zombie’ process of the child retains the entry of the child, but is not actually running
- If the parent terminates but the child is still running
- The child is now an ‘orphan’ process and gets the parent pid of
init
- The child is now an ‘orphan’ process and gets the parent pid of
- If the child terminates and the parent calls
wait- Just regular termination and no ‘zombie’
File Redirection #
- Before
execandfork, open the file - Duplicate the file descriptor with
dup2(curr_fd, new_fd) - Close the file descriptor or request
close on exec - Then call
exec
Pipes #
int pipe(int pipefd[2]); // creates a unidirectional pipe
// pipefd[0] is for read end
// pipefd[1] is for write end
- This is how shells do pipelining
- Usually, only one process at both ends
- For
dup/dup2,stdoutfor write end, andstdinfor read end. - Close fds you do not need as soon as possible
- Kernel has a buffer for unread data if the
write endis writing faster than theread endcan handle
Signals #
How the kernel notifies processes of some events and severe errors
Examples #
Interupt (
CTRL+C):SIGINTSuspend and Resume:
SIGSTOP,SIGCONTChild died/suspended/resumed:
SIGCHLDBroken pipe:
SIGPIPERequest for termination (shell ‘
kill’ default):SIGTERMHard request for termination:
SIGKILLIllegal memory access (two types:
SIGBUS,SIGSEGV)Application-specific:
SIGUSR1,SIGUSR2
Signal Life Cycle #
- Some events generate a signal
- Kernel tries to deliver the signal, and is pending until delivered
- Normal exec resumes if signal ignored or handler returns normally
- Shell Command:
$ kill -SIGKILL 31337 $ kill -9 31337 - System Calls:
int kill(pid_t pid, int sig); int raise(int sig); // to self
Signal Actions/Handlers #
int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact)
sig: The signal typeact: The new action you wantoldact: The old action- on
fork: Signal actions cloned - on
exec: Handlers are replaced by default
struct sigaction
#
struct sigaction {
void *sa_handler(int sig); // pointer to handler function, SIG_IN, or SIG_DFL
sigset_t sa_mask; // mask these signals when running handler
int sa_flags; // options
void *sa_restorer(void); // not for application use
}
sigset_t Operations
#
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set); // add all signals
int sigaddset(sigset_t *set, int sig);
int sigdelset(sigset_t *set, int sig);
int sigismember(const sigset_t *set, int sig);
sa_flags Flags
#
// If you install handler
SA_NODEFER // don't mask signal when running handler
SA_RESETHAND // reset action to default before running handler
SA_RESTART // auto-restart most syscalls before running handler
// For SIGCHLD
SA_NOCLDSTOP // don't signal for child stop/cont.
SA_NOCLDWAIT // don't turn terminated child into zombie