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
#
fork
is 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)kill
andpkill
: Terminates a process if allowed (pkill
finds 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
exec
andfork
, 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
,stdout
for write end, andstdin
for read end. - Close fds you do not need as soon as possible
- Kernel has a buffer for unread data if the
write end
is writing faster than theread end
can handle
Signals #
How the kernel notifies processes of some events and severe errors
Examples #
Interupt (
CTRL+C
):SIGINT
Suspend and Resume:
SIGSTOP
,SIGCONT
Child died/suspended/resumed:
SIGCHLD
Broken pipe:
SIGPIPE
Request for termination (shell ‘
kill
’ default):SIGTERM
Hard request for termination:
SIGKILL
Illegal 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