Professional Documents
Culture Documents
Instructors:
Randal E. Bryant and David R. O’Hallaron
Today
Shells
Signals
Nonlocal jumps
init [1]
… …
Daemon …
e.g. httpd Login shell Login shell
Shell Programs
A shell is an application program that runs programs on behalf
of the user.
sh Original Unix shell (Stephen Bourne, AT&T Bell
Labs, 1977)
csh/tcsh BSD Unix C shell
bash “Bourne-Again” Shell (default Linux shell)
int main()
{ Execution is a
char cmdline[MAXLINE]; /* command line */ sequence of
read/evaluate
while (1) {
/* read */ steps
printf("> ");
Fgets(cmdline, MAXLINE, stdin);
if (feof(stdin))
exit(0);
/* evaluate */
eval(cmdline);
}
}
shellex.c
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition 5
Carnegie Mellon
strcpy(buf, cmdline);
bg = parseline(buf, argv);
if (argv[0] == NULL)
return; /* Ignore empty lines */
if (!builtin_command(argv)) {
if ((pid = Fork()) == 0) { /* Child runs user job */
if (execve(argv[0], argv, environ) < 0) {
printf("%s: Command not found.\n", argv[0]);
exit(0);
}
}
Today
Shells
Signals
Nonlocal jumps
Signals
A signal is a small message that notifies a process that an
event of some type has occurred in the system
Akin to exceptions and interrupts
Sent from the kernel (sometimes at the request of another process) to a
process
Signal type is identified by small integer ID’s (1-30)
Only information in a signal is its ID and the fact that it arrived
pid=10
pgid=10
Shell
Background Background
process group 32 process group 40
Child Child
linux> ps
Examples PID TTY TIME CMD
/bin/kill –9 24818 24788 pts/2 00:00:00 tcsh
Send SIGKILL to process 24818 24818 pts/2 00:00:02 forks
24819 pts/2 00:00:02 forks
24820 pts/2 00:00:00 ps
/bin/kill –9 –24817 linux> /bin/kill -9 -24817
linux> ps
Send SIGKILL to every process
PID TTY TIME CMD
in process group 24817 24788 pts/2 00:00:00 tcsh
24823 pts/2 00:00:00 ps
linux>
Background Background
process group 32 process group 40
Child Child
pid=21 pid=22
pgid=20 pgid=20
Foreground
process group 20 17
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Carnegie Mellon
Receiving Signals
Suppose kernel is returning from an exception handler
and is ready to pass control to process p
Process A Process B
user code
user code
Receiving Signals
Suppose kernel is returning from an exception handler
and is ready to pass control to process p
If (pnb == 0)
Pass control to next instruction in the logical flow for p
Else
Choose least nonzero bit k in pnb and force process p to receive
signal k
The receipt of the signal triggers some action by p
Repeat for all nonzero k in pnb
Pass control to next instruction in logical flow for p
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition 21
Carnegie Mellon
Default Actions
Each signal type has a predefined default action, which is
one of:
The process terminates
The process stops until restarted by a SIGCONT signal
The process ignores the signal
int main()
{
/* Install the SIGINT handler */
if (signal(SIGINT, sigint_handler) == SIG_ERR)
unix_error("signal error");
return 0;
} sigint.c
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition 24
Carnegie Mellon
Time
kernel code
Inext
user code (main)
Supporting functions
sigemptyset – Create empty set
sigfillset – Add every signal number to set
sigaddset – Add signal number to set
sigdelset – Delete signal number from set
Sigemptyset(&mask);
Sigaddset(&mask, SIGINT);
Async-Signal-Safety
Function is async-signal-safe if either reentrant (e.g., all
variables stored on stack frame, CS:APP3e 12.7.2) or non-
interruptible by signals.
Posix guarantees 117 functions to be async-signal-safe
Source: “man 7 signal”
Popular functions on the list:
_exit, write, wait, waitpid, sleep, kill
Popular functions that are not on the list:
printf, sprintf, malloc, exit
Unfortunate fact: write is the only async-signal-safe output function
int ccount = 0;
void child_handler(int sig) { Correct Signal Handling
int olderrno = errno;
pid_t pid; Pending signals are
if ((pid = wait(NULL)) < 0)
Sio_error("wait error"); not queued
ccount--; For each signal type, one
Sio_puts("Handler reaped child ");
Sio_putl((long)pid); bit indicates whether or
Sio_puts(" \n"); not signal is pending…
sleep(1);
errno = olderrno;
…thus at most one
} pending signal of any
particular type.
void fork14() {
pid_t pid[N];
You can’t use signals
int i;
ccount = N;
to count events, such as
Signal(SIGCHLD, child_handler); children terminating.
for (i = 0; i < N; i++) {
if ((pid[i] = Fork()) == 0) {
Sleep(1); whaleshark> ./forks 14
exit(0); /* Child exits */
Handler reaped child 23240
}
} Handler reaped child 23241
while (ccount > 0) /* Parent spins */
;
} forks.c 34
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Carnegie Mellon
action.sa_handler = handler;
sigemptyset(&action.sa_mask); /* Block sigs of type being handled */
action.sa_flags = SA_RESTART; /* Restart syscalls if possible */
Sigfillset(&mask_all);
Signal(SIGCHLD, handler);
initjobs(); /* Initialize the job list */
while (1) {
if ((pid = Fork()) == 0) { /* Child */
Execve("/bin/date", argv, NULL);
}
Sigprocmask(SIG_BLOCK, &mask_all, &prev_all); /* Parent */
addjob(pid); /* Add the child to the job list */
Sigprocmask(SIG_SETMASK, &prev_all, NULL);
}
exit(0);
} procmask1.c 37
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Carnegie Mellon
Sigfillset(&mask_all);
while ((pid = waitpid(-1, NULL, 0)) > 0) { /* Reap child */
Sigprocmask(SIG_BLOCK, &mask_all, &prev_all);
deletejob(pid); /* Delete the child from the job list */
Sigprocmask(SIG_SETMASK, &prev_all, NULL);
}
if (errno != ECHILD)
Sio_error("waitpid error");
errno = olderrno;
} procmask1.c
Sigfillset(&mask_all);
Sigemptyset(&mask_one);
Sigaddset(&mask_one, SIGCHLD);
Signal(SIGCHLD, handler);
initjobs(); /* Initialize the job list */
while (1) {
Sigprocmask(SIG_BLOCK, &mask_one, &prev_one); /* Block SIGCHLD */
if ((pid = Fork()) == 0) { /* Child process */
Sigprocmask(SIG_SETMASK, &prev_one, NULL); /* Unblock SIGCHLD */
Execve("/bin/date", argv, NULL);
}
Sigprocmask(SIG_BLOCK, &mask_all, NULL); /* Parent process */
addjob(pid); /* Add the child to the job list */
Sigprocmask(SIG_SETMASK, &prev_one, NULL); /* Unblock SIGCHLD */
}
exit(0);
} procmask2.c
39
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Carnegie Mellon
void sigchld_handler(int s)
{
int olderrno = errno;
pid = Waitpid(-1, NULL, 0); /* Main is waiting for nonzero pid */
errno = olderrno;
}
void sigint_handler(int s)
{
}
waitforsignal.c
while (1) {
Sigprocmask(SIG_BLOCK, &mask, &prev); /* Block SIGCHLD */
if (Fork() == 0) /* Child */
exit(0);
/* Parent */
pid = 0;
Sigprocmask(SIG_SETMASK, &prev, NULL); /* Unblock SIGCHLD */
Solution: sigsuspend
while (1) {
Sigprocmask(SIG_BLOCK, &mask, &prev); /* Block SIGCHLD */
if (Fork() == 0) /* Child */
exit(0);
Today
Shells
Signals
Nonlocal jumps
Consult your textbook and additional slides
Summary
Signals provide process-level exception handling
Can generate from user programs
Can define effect by declaring signal handler
Be very careful when writing signal handlers
Additional slides
int setjmp(jmp_buf j)
Must be called before longjmp
Identifies a return site for a subsequent longjmp
Called once, returns one or more times
Implementation:
Remember where you are by storing the current register context,
stack pointer, and PC value in jmp_buf
Return 0
setjmp/longjmp (cont)
void longjmp(jmp_buf j, int i)
Meaning:
return from the setjmp remembered by jump buffer j again ...
… this time returning i instead of 0
Called after setjmp
Called once, but never returns
longjmp Implementation:
Restore register context (stack pointer, base pointer, PC value) from jump
buffer j
Set %eax (the return value) to i
Jump to the location indicated by the PC stored in jump buf j
setjmp/longjmp Example
Goal: return directly to original caller from a deeply-
nested function
/* Deeply nested function foo */
void foo(void)
{
if (error1)
longjmp(buf, 1);
bar();
}
void bar(void)
{
if (error2)
longjmp(buf, 2);
}
jmp_buf buf;
int main()
{
switch(setjmp(buf)) {
case 0:
foo();
break;
case 1:
printf("Detected an error1 condition in foo\n");
break;
case 2:
printf("Detected an error2 condition in foo\n");
break;
default:
printf("Unknown error condition in foo\n");
}
exit(0);
}
P1() P2
{ env
P2(); P3(); At setjmp
}
P2() P1
{
if (setjmp(env)) { env
/* Long Jump to here */ X P2
}
} P2 returns P1
P3() env
{ X P3
longjmp(env, 1);
} At longjmp
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition 53
Carnegie Mellon
sigjmp_buf buf;
greatwhite> ./restart
void handler(int sig) starting
{ processing...
siglongjmp(buf, 1); processing...
} processing...
restarting
int main() Ctrl-c
{ processing...
if (!sigsetjmp(buf, 1)) { processing...
Signal(SIGINT, handler); restarting
Sio_puts("starting\n"); processing... Ctrl-c
} processing...
else processing...
Sio_puts("restarting\n");
while(1) {
Sleep(1);
Sio_puts("processing...\n");
}
exit(0); /* Control never reaches here */
} restart.c 54
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition