Upload
others
View
3
Download
0
Embed Size (px)
Citation preview
Monolithic Kernels and the Unix API
1
homework
xv6 introduction: due Friday
2
anonymous feedback
“It would be really helpful if homework directions were less vague.Things don’t have to be hard and confusing just for the sake of it.
it probably is, but I can only make (probably bad) guesses about howit’s vague
trying to maintain balance:giving complete homework requirements
(without saying “modify line X of file Y ”)not having walls of text that no one readsnot copying all the lecture material, etc. into homework writeup
3
homework steps
system call implementation: sys_writecounthint in writeup: imitate sys_uptimeneed a counter for number of writes
add writecount to several tables/lists(list of handlers, list of library functions to create, etc.)recommendation: imitate how other system calls are listed
create a userspace program that calls writecountrecommendation: copy from given programs
4
note on locks
some existing code uses acquire/release
you do not have to do this
only for multiprocessor support
…but, copying what’s done for ticks would be correct
5
xv6 context switch and savinguser mode kernel mode
running A
running B
start trap handlersave A’s user regsto kernel stack
swtch() — switch kernel stacks/kernel registers
exit trap handlerrestore B’s user regsfrom kernel stack
6
where things go in context switch
saved user registerstrap return addr.…caller-saved registersswtch argumentsswtch return addr.saved ebpsaved ebxsaved esisaved edi
‘from’ kernel stack
last %esp valuefor ‘from’ process(saved by swtch)
main’s return addr.main’s vars…
‘from’ user stack
%esp valuejust before exception
saved user registerstrap return addr.…caller-saved registersswtch argumentsswtch return addr.saved ebpsaved ebxsaved esisaved edi
‘to’ kernel stack
first %esp valuefor ‘to’ process
(argument to swtch)
main’s return addr.main’s vars…
‘to’ user stack
%esp value afterreturn-from-exception
7
where things go in context switch
saved user registerstrap return addr.…caller-saved registersswtch argumentsswtch return addr.saved ebpsaved ebxsaved esisaved edi
‘from’ kernel stack
last %esp valuefor ‘from’ process(saved by swtch)
main’s return addr.main’s vars…
‘from’ user stack
%esp valuejust before exception
saved user registerstrap return addr.…caller-saved registersswtch argumentsswtch return addr.saved ebpsaved ebxsaved esisaved edi
‘to’ kernel stack
first %esp valuefor ‘to’ process
(argument to swtch)
main’s return addr.main’s vars…
‘to’ user stack
%esp value afterreturn-from-exception
7
write syscall in xv6: interrupt table setup
...lidt(idt, sizeof(idt));...SETGATE(idt[T_SYSCALL], 1, SEG_KCODE<<3, vectors[T_SYSCALL], DPL_USER);...
trap.c (run on boot)
lidt —function (in x86.h) wrapping lidt instruction
sets the interrupt descriptor tabletable of handler functions for each interrupt type
(from mmu.h):// Set up a normal interrupt/trap gate descriptor.// - istrap: 1 for a trap gate, 0 for an interrupt gate.// interrupt gate clears FL_IF, trap gate leaves FL_IF alone// - sel: Code segment selector for interrupt/trap handler// - off: Offset in code segment for interrupt/trap handler// - dpl: Descriptor Privilege Level -// the privilege level required for software to invoke// this interrupt/trap gate explicitly using an int instruction.#define SETGATE(gate, istrap, sel, off, d) \
set the T_SYSCALL (= 0x40) interrupt tobe callable from user mode via int instruction(otherwise: triggers fault like privileged instruction)
set it to use the kernel “code segment”meaning: run in kernel mode(yes, code segments specifies more than that — nothing we care about)
vectors[T_SYSCALL] — OS function for processor to runset to pointer to assembly function vector64
trap returns to alltrapsalltraps restores registers from tf, then returns to user-mode
vector64:pushl $0pushl $64jmp alltraps
...
vectors.Salltraps:
...call trap...iret
trapasm.Svoidtrap(struct trapframe *tf){...
trap.c
8
interrupt descriptor table
x86’s interrupt descriptor table has an entry for each kind ofexception
segmentation faulttimer expired (“your program ran too long”)divide-by-zerosystem calls…
xv6 sets all the table entries
…and they always call the trap() function
9
process control block
some data structure needed to represent a process
called Process Control Block
xv6: struct proc
10
process control block
some data structure needed to represent a process
called Process Control Block
xv6: struct proc
10
xv6: struct proc
struct proc {uint sz; // Size of process memory (bytes)pde_t* pgdir; // Page tablechar *kstack; // Bottom of kernel stack for this processenum procstate state; // Process stateint pid; // Process IDstruct proc *parent; // Parent processstruct trapframe *tf; // Trap frame for current syscallstruct context *context; // swtch() here to run processvoid *chan; // If non-zero, sleeping on chanint killed; // If non-zero, have been killedstruct file *ofile[NOFILE]; // Open filesstruct inode *cwd; // Current directorychar name[16]; // Process name (debugging)
};
current registers/PC of process (user and kernel)stored on (pointer to) its kernel stack(if not currently running)
≈ thread’s state
the kernel stack for this processevery process has one kernel stack
is process running?or waiting?or finished?if waiting,waiting for what (chan)?
enum procstate {UNUSED, EMBRYO, SLEEPING,RUNNABLE, RUNNING, ZOMBIE
};
process IDto identify process in systemn calls
information about address spacepgdir — used by processorsz — used by OS only
information about open files, etc.
11
xv6: struct proc
struct proc {uint sz; // Size of process memory (bytes)pde_t* pgdir; // Page tablechar *kstack; // Bottom of kernel stack for this processenum procstate state; // Process stateint pid; // Process IDstruct proc *parent; // Parent processstruct trapframe *tf; // Trap frame for current syscallstruct context *context; // swtch() here to run processvoid *chan; // If non-zero, sleeping on chanint killed; // If non-zero, have been killedstruct file *ofile[NOFILE]; // Open filesstruct inode *cwd; // Current directorychar name[16]; // Process name (debugging)
};
current registers/PC of process (user and kernel)stored on (pointer to) its kernel stack(if not currently running)
≈ thread’s state
the kernel stack for this processevery process has one kernel stack
is process running?or waiting?or finished?if waiting,waiting for what (chan)?
enum procstate {UNUSED, EMBRYO, SLEEPING,RUNNABLE, RUNNING, ZOMBIE
};
process IDto identify process in systemn calls
information about address spacepgdir — used by processorsz — used by OS only
information about open files, etc.
11
xv6: struct proc
struct proc {uint sz; // Size of process memory (bytes)pde_t* pgdir; // Page tablechar *kstack; // Bottom of kernel stack for this processenum procstate state; // Process stateint pid; // Process IDstruct proc *parent; // Parent processstruct trapframe *tf; // Trap frame for current syscallstruct context *context; // swtch() here to run processvoid *chan; // If non-zero, sleeping on chanint killed; // If non-zero, have been killedstruct file *ofile[NOFILE]; // Open filesstruct inode *cwd; // Current directorychar name[16]; // Process name (debugging)
};
current registers/PC of process (user and kernel)stored on (pointer to) its kernel stack(if not currently running)
≈ thread’s state
the kernel stack for this processevery process has one kernel stack
is process running?or waiting?or finished?if waiting,waiting for what (chan)?
enum procstate {UNUSED, EMBRYO, SLEEPING,RUNNABLE, RUNNING, ZOMBIE
};
process IDto identify process in systemn calls
information about address spacepgdir — used by processorsz — used by OS only
information about open files, etc.
11
xv6: struct proc
struct proc {uint sz; // Size of process memory (bytes)pde_t* pgdir; // Page tablechar *kstack; // Bottom of kernel stack for this processenum procstate state; // Process stateint pid; // Process IDstruct proc *parent; // Parent processstruct trapframe *tf; // Trap frame for current syscallstruct context *context; // swtch() here to run processvoid *chan; // If non-zero, sleeping on chanint killed; // If non-zero, have been killedstruct file *ofile[NOFILE]; // Open filesstruct inode *cwd; // Current directorychar name[16]; // Process name (debugging)
};
current registers/PC of process (user and kernel)stored on (pointer to) its kernel stack(if not currently running)
≈ thread’s state
the kernel stack for this processevery process has one kernel stack
is process running?or waiting?or finished?if waiting,waiting for what (chan)?
enum procstate {UNUSED, EMBRYO, SLEEPING,RUNNABLE, RUNNING, ZOMBIE
};
process IDto identify process in systemn calls
information about address spacepgdir — used by processorsz — used by OS only
information about open files, etc.
11
xv6: struct proc
struct proc {uint sz; // Size of process memory (bytes)pde_t* pgdir; // Page tablechar *kstack; // Bottom of kernel stack for this processenum procstate state; // Process stateint pid; // Process IDstruct proc *parent; // Parent processstruct trapframe *tf; // Trap frame for current syscallstruct context *context; // swtch() here to run processvoid *chan; // If non-zero, sleeping on chanint killed; // If non-zero, have been killedstruct file *ofile[NOFILE]; // Open filesstruct inode *cwd; // Current directorychar name[16]; // Process name (debugging)
};
current registers/PC of process (user and kernel)stored on (pointer to) its kernel stack(if not currently running)
≈ thread’s state
the kernel stack for this processevery process has one kernel stack
is process running?or waiting?or finished?if waiting,waiting for what (chan)?
enum procstate {UNUSED, EMBRYO, SLEEPING,RUNNABLE, RUNNING, ZOMBIE
};
process IDto identify process in systemn calls
information about address spacepgdir — used by processorsz — used by OS only
information about open files, etc.
11
xv6: struct proc
struct proc {uint sz; // Size of process memory (bytes)pde_t* pgdir; // Page tablechar *kstack; // Bottom of kernel stack for this processenum procstate state; // Process stateint pid; // Process IDstruct proc *parent; // Parent processstruct trapframe *tf; // Trap frame for current syscallstruct context *context; // swtch() here to run processvoid *chan; // If non-zero, sleeping on chanint killed; // If non-zero, have been killedstruct file *ofile[NOFILE]; // Open filesstruct inode *cwd; // Current directorychar name[16]; // Process name (debugging)
};
current registers/PC of process (user and kernel)stored on (pointer to) its kernel stack(if not currently running)
≈ thread’s state
the kernel stack for this processevery process has one kernel stack
is process running?or waiting?or finished?if waiting,waiting for what (chan)?
enum procstate {UNUSED, EMBRYO, SLEEPING,RUNNABLE, RUNNING, ZOMBIE
};
process IDto identify process in systemn calls
information about address spacepgdir — used by processorsz — used by OS only
information about open files, etc.
11
xv6: struct proc
struct proc {uint sz; // Size of process memory (bytes)pde_t* pgdir; // Page tablechar *kstack; // Bottom of kernel stack for this processenum procstate state; // Process stateint pid; // Process IDstruct proc *parent; // Parent processstruct trapframe *tf; // Trap frame for current syscallstruct context *context; // swtch() here to run processvoid *chan; // If non-zero, sleeping on chanint killed; // If non-zero, have been killedstruct file *ofile[NOFILE]; // Open filesstruct inode *cwd; // Current directorychar name[16]; // Process name (debugging)
};
current registers/PC of process (user and kernel)stored on (pointer to) its kernel stack(if not currently running)
≈ thread’s state
the kernel stack for this processevery process has one kernel stack
is process running?or waiting?or finished?if waiting,waiting for what (chan)?
enum procstate {UNUSED, EMBRYO, SLEEPING,RUNNABLE, RUNNING, ZOMBIE
};
process IDto identify process in systemn calls
information about address spacepgdir — used by processorsz — used by OS only
information about open files, etc.
11
process control blocks generally
contains process’s context(s) (registers, PC, …)if context is not on a CPU(in xv6: pointers to these, actual location: process’s kernel stack)
process’s status — running, waiting, etc.
information for system calls, etc.open filesmemory allocationsprocess IDsrelated processes
12
xv6 myproc
xv6 function: myproc()
retrieves pointer to currently running struct proc
13
myproc: using a global variable
struct cpu cpus[NCPU];
struct proc*myproc(void) {struct cpu *c;...c = mycpu(); /* finds entry of cpus array
using special "ID" registeras array index */
p = c−>proc;...return p;
}14
this class: focus on Unix
Unix-like OSes will be our focus
we have source code
used to from 2150, etc.?
have been around for a while
xv6 imitates Unix
15
Unix history
OpenServer6.x
UnixWare7.x
(System VR5)
HP-UX11i+
1969
1971 to 1973
1974 to 1975
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001 to 2004
2006 to 2007
2008
2005
2009
2010
2011
2012 to 2015
2016
2017
Open Source
Mixed/Shared Source
Closed Source
No future releases
HP-UX1.0 to 1.2
OpenSolaris& derivatives
(illumos, etc.)
System III
System VR1 to R2
OpenServer5.0.5 to 5.0.7
OpenServer5.0 to 5.04
SCO Unix3.2.4
SCO XenixV/386
SCO XenixV/386
SCO XenixV/286
SCO Xenix
Xenix3.0
Xenix1.0 to 2.3
PWB/Unix
AIX1.0
AIX3.0-7.2
OpenBSD2.3-6.1
OpenBSD1.0 to 2.2
SunOS1.2 to 3.0
SunOS1 to 1.1
Unix/32V
UnixVersion 1 to 4
UnixVersion 5 to 6
UnixVersion 7
Unnamed PDP-7 operating system
BSD1.0 to 2.0
BSD3.0 to 4.1
BSD 4.2
UnixVersion 8
Unix9 and 10
(last versionsfrom
Bell Labs)
NexTSTEP/OPENSTEP1.0 to 4.0
Mac OS XServer
Mac OS X,OS X,
macOS10.0 to 10.12
(Darwin1.2.1 to 17)
Minix1.x
Minix2.x
Minix3.1.0-3.4.0
Linux2.x
Linux0.95 to 1.2.x
Linux 0.0.1
BSD4.4 to
4.4 lite2
NetBSD0.8 to 1.0
NetBSD1.1 to 1.2
NetBSD 1.3
NetBSD1.3-7.1
FreeBSD1.0 to 2.2.x
386BSD
BSD NET/2
Solaris10
Solaris11.0-11.3
System VR4
Solaris2.1 to 9
BSD 4.3
SunOS4
HP-UX2.0 to 3.0
HP-UX6 to 11
System VR3
UnixWare1.x to 2.x(System V
R4.2)
BSD 4.3Tahoe
BSD 4.3Reno
FreeBSD3.0 to 3.2
FreeBSD3.3-11.x
Linux3.x
Linux4.x OpenServer
10.x
1969
1971 to 1973
1974 to 1975
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001 to 2004
2006 to 2007
2008
2005
2009
2010
2011
2012 to 2015
2016
2017
DragonFlyBSD
1.0 to 4.8
16
POSIX: standardized Unix
Portable Operating System Interface (POSIX)“standard for Unix”
current version online:http://pubs.opengroup.org/onlinepubs/9699919799/
(almost) followed by most current Unix-like OSes
…but OSes add extra features
…but doesn’t specify everything
17
what POSIX defines
POSIX specifies the library and shell interfacesource code compatibility
doesn’t care what is/is not a system call…
doesn’t specify binary formats…
idea: write applications for POSIX, recompile and run on allimplementations
this was a very important goal in the 80s/90sat the time, Linux was very immature
18
POSIX process management
essential operations
process information: getpid
process creation: fork
running programs: exec*also posix_spawn (not widely supported), …
waiting for processes to finish: waitpid (or wait)
process destruction, ‘signaling’: exit, kill
19
POSIX process management
essential operations
process information: getpid
process creation: fork
running programs: exec*also posix_spawn (not widely supported), …
waiting for processes to finish: waitpid (or wait)
process destruction, ‘signaling’: exit, kill
20
getpid
pid_t my_pid = getpid();printf("my␣pid␣is␣%ld\n", (long) my_pid);
21
process ids in ps
cr4bd@machine:~$ psPID TTY TIME CMD
14777 pts/3 00:00:00 bash14798 pts/3 00:00:00 ps
22
POSIX process management
essential operations
process information: getpid
process creation: fork
running programs: exec*also posix_spawn (not widely supported), …
waiting for processes to finish: waitpid (or wait)
process destruction, ‘signaling’: exit, kill
23
fork
pid_t fork() — copy the current process
returns twice:in parent (original process): pid of new child processin child (new process): 0
everything (but pid) duplicated in parent, child:memoryfile descriptors (later)registers
24
fork and PCBs
user regs eax=42,ecx=133, …
kernel stackuser memoryopen files fd 0: …
fd 1: …… …
parent process control block memory
user regs eax=42,ecx=133, …
kernel stackuser memoryopen files fd 0: …
fd 1: …… …
child process control blockcopycopy
25
fork and PCBs
user regs eax=42,ecx=133, …
kernel stackuser memoryopen files fd 0: …
fd 1: …… …
parent process control block memory
user regs eax=42,ecx=133, …
kernel stackuser memoryopen files fd 0: …
fd 1: …… …
child process control blockcopy
copy
25
fork and PCBs
user regs eax=42,ecx=133, …
kernel stackuser memoryopen files fd 0: …
fd 1: …… …
parent process control block memory
user regs eax=42,ecx=133, …
kernel stackuser memoryopen files fd 0: …
fd 1: …… …
child process control blockcopycopy
25
fork and PCBs
user regs eax=42,ecx=133, …
kernel stackuser memoryopen files fd 0: …
fd 1: …… …
parent process control block memory
user regs eax=42,ecx=133, …
kernel stackuser memoryopen files fd 0: …
fd 1: …… …
child process control blockcopycopy
25
fork and PCBs
user regs eax=42child (new) pid,ecx=133, …
kernel stackuser memoryopen files fd 0: …
fd 1: …… …
parent process control block memory
user regs eax=420,ecx=133, …
kernel stackuser memoryopen files fd 0: …
fd 1: …… …
child process control blockcopycopy
25
fork example#include <stdlib.h>#include <stdio.h>#include <unistd.h>#include <sys/types.h>int main(int argc, char *argv[]) {
pid_t pid = getpid();printf("Parent␣pid:␣%d\n", (int) pid);pid_t child_pid = fork();if (child_pid > 0) {
/* Parent Process */pid_t my_pid = getpid();printf("[%d]␣parent␣of␣[%d]\n", (int) my_pid, (int) child_pid);
} else if (child_pid == 0) {/* Child Process */pid_t my_pid = getpid();printf("[%d]␣child\n", (int) my_pid);
} else {perror("Fork␣failed");
}return 0;
}
getpid — returns current process pidcast in case pid_t isn’t intPOSIX doesn’t specify (some systems it is, some not…)(not necessary if you were using C++’s cout, etc.)
prints out Fork failed: error message(example error message: “Resource temporarily unavailable”)from error number stored in special global variable errno
Example output:Parent pid: 100[100] parent of [432][432] child
26
fork example#include <stdlib.h>#include <stdio.h>#include <unistd.h>#include <sys/types.h>int main(int argc, char *argv[]) {
pid_t pid = getpid();printf("Parent␣pid:␣%d\n", (int) pid);pid_t child_pid = fork();if (child_pid > 0) {
/* Parent Process */pid_t my_pid = getpid();printf("[%d]␣parent␣of␣[%d]\n", (int) my_pid, (int) child_pid);
} else if (child_pid == 0) {/* Child Process */pid_t my_pid = getpid();printf("[%d]␣child\n", (int) my_pid);
} else {perror("Fork␣failed");
}return 0;
}
getpid — returns current process pid
cast in case pid_t isn’t intPOSIX doesn’t specify (some systems it is, some not…)(not necessary if you were using C++’s cout, etc.)
prints out Fork failed: error message(example error message: “Resource temporarily unavailable”)from error number stored in special global variable errno
Example output:Parent pid: 100[100] parent of [432][432] child
26
fork example#include <stdlib.h>#include <stdio.h>#include <unistd.h>#include <sys/types.h>int main(int argc, char *argv[]) {
pid_t pid = getpid();printf("Parent␣pid:␣%d\n", (int) pid);pid_t child_pid = fork();if (child_pid > 0) {
/* Parent Process */pid_t my_pid = getpid();printf("[%d]␣parent␣of␣[%d]\n", (int) my_pid, (int) child_pid);
} else if (child_pid == 0) {/* Child Process */pid_t my_pid = getpid();printf("[%d]␣child\n", (int) my_pid);
} else {perror("Fork␣failed");
}return 0;
}
getpid — returns current process pid
cast in case pid_t isn’t intPOSIX doesn’t specify (some systems it is, some not…)(not necessary if you were using C++’s cout, etc.)
prints out Fork failed: error message(example error message: “Resource temporarily unavailable”)from error number stored in special global variable errno
Example output:Parent pid: 100[100] parent of [432][432] child
26
fork example#include <stdlib.h>#include <stdio.h>#include <unistd.h>#include <sys/types.h>int main(int argc, char *argv[]) {
pid_t pid = getpid();printf("Parent␣pid:␣%d\n", (int) pid);pid_t child_pid = fork();if (child_pid > 0) {
/* Parent Process */pid_t my_pid = getpid();printf("[%d]␣parent␣of␣[%d]\n", (int) my_pid, (int) child_pid);
} else if (child_pid == 0) {/* Child Process */pid_t my_pid = getpid();printf("[%d]␣child\n", (int) my_pid);
} else {perror("Fork␣failed");
}return 0;
}
getpid — returns current process pidcast in case pid_t isn’t intPOSIX doesn’t specify (some systems it is, some not…)(not necessary if you were using C++’s cout, etc.)
prints out Fork failed: error message(example error message: “Resource temporarily unavailable”)from error number stored in special global variable errno
Example output:Parent pid: 100[100] parent of [432][432] child
26
fork example#include <stdlib.h>#include <stdio.h>#include <unistd.h>#include <sys/types.h>int main(int argc, char *argv[]) {
pid_t pid = getpid();printf("Parent␣pid:␣%d\n", (int) pid);pid_t child_pid = fork();if (child_pid > 0) {
/* Parent Process */pid_t my_pid = getpid();printf("[%d]␣parent␣of␣[%d]\n", (int) my_pid, (int) child_pid);
} else if (child_pid == 0) {/* Child Process */pid_t my_pid = getpid();printf("[%d]␣child\n", (int) my_pid);
} else {perror("Fork␣failed");
}return 0;
}
getpid — returns current process pidcast in case pid_t isn’t intPOSIX doesn’t specify (some systems it is, some not…)(not necessary if you were using C++’s cout, etc.)
prints out Fork failed: error message(example error message: “Resource temporarily unavailable”)from error number stored in special global variable errno
Example output:Parent pid: 100[100] parent of [432][432] child
26
a fork questionint main() {
pid_t pid = fork();if (pid == 0) {
printf("In␣child\n");} else {
printf("Child␣%d\n", pid);}printf("Done!\n");
}
Exercise: Suppose the pid of the parent process is 99 and child is100. Give two possible outputs. (Assume no crashes, etc.)
parent child parent child parent
Child 100In childDone!Done!
parent child parent
In childDone!Child 100Done!
27
a fork questionint main() {
pid_t pid = fork();if (pid == 0) {
printf("In␣child\n");} else {
printf("Child␣%d\n", pid);}printf("Done!\n");
}
Exercise: Suppose the pid of the parent process is 99 and child is100. Give two possible outputs. (Assume no crashes, etc.)
parent child parent child parent
Child 100In childDone!Done!
parent child parent
In childDone!Child 100Done!
27
POSIX process management
essential operations
process information: getpid
process creation: fork
running programs: exec*also posix_spawn (not widely supported), …
waiting for processes to finish: waitpid (or wait)
process destruction, ‘signaling’: exit, kill
28
exec*
exec* — replace current program with new program* — multiple variantssame pid, new process image
int execv(const char *path, const char **argv)
path: new program to runargv: array of arguments, termianted by null pointer
29
execv example
...child_pid = fork();if (child_pid == 0) {/* child process */char *args[] = {"ls", "-l", NULL};execv("/bin/ls", args);/* execv doesn't return when it works.
So, if we got here, it failed. */perror("execv");exit(1);
} else if (child_pid > 0) {/* parent process */...
}
used to compute argv, argcfilename of program to runneed not match first argument(but probably should match it)
30
execv example
...child_pid = fork();if (child_pid == 0) {/* child process */char *args[] = {"ls", "-l", NULL};execv("/bin/ls", args);/* execv doesn't return when it works.
So, if we got here, it failed. */perror("execv");exit(1);
} else if (child_pid > 0) {/* parent process */...
}
used to compute argv, argc
filename of program to runneed not match first argument(but probably should match it)
30
execv example
...child_pid = fork();if (child_pid == 0) {/* child process */char *args[] = {"ls", "-l", NULL};execv("/bin/ls", args);/* execv doesn't return when it works.
So, if we got here, it failed. */perror("execv");exit(1);
} else if (child_pid > 0) {/* parent process */...
}
used to compute argv, argc
filename of program to runneed not match first argument(but probably should match it)
30
exec and PCBs
user regs eax=42init. val.,ecx=133init. val., …
kernel stackuser memoryopen files fd 0: (terminal …)
fd 1: …… …
the process control block memory
loaded fromexecutable file
new stack, heap, …
copy arguments
not changed!(more on this later)
old memorydiscarded
31
exec and PCBs
user regs eax=42init. val.,ecx=133init. val., …
kernel stackuser memoryopen files fd 0: (terminal …)
fd 1: …… …
the process control block memory
loaded fromexecutable file
new stack, heap, …
copy arguments
not changed!(more on this later)
old memorydiscarded
31
exec and PCBs
user regs eax=42init. val.,ecx=133init. val., …
kernel stackuser memoryopen files fd 0: (terminal …)
fd 1: …… …
the process control block memory
loaded fromexecutable file
new stack, heap, …
copy arguments
not changed!(more on this later)
old memorydiscarded
31
exec and PCBs
user regs eax=42init. val.,ecx=133init. val., …
kernel stackuser memoryopen files fd 0: (terminal …)
fd 1: …… …
the process control block memory
loaded fromexecutable file
new stack, heap, …
copy arguments
not changed!(more on this later)
old memorydiscarded
31
exec and PCBs
user regs eax=42init. val.,ecx=133init. val., …
kernel stackuser memoryopen files fd 0: (terminal …)
fd 1: …… …
the process control block memory
loaded fromexecutable file
new stack, heap, …
copy arguments
not changed!(more on this later)
old memorydiscarded
31
aside: environment variables (1)
key=value pairs associated with every process:$ printenvMODULE_VERSION_STACK=3.2.10MANPATH=:/opt/puppetlabs/puppet/share/manXDG_SESSION_ID=754HOSTNAME=labsrv01SELINUX_ROLE_REQUESTED=TERM=screenSHELL=/bin/bashHISTSIZE=1000SSH_CLIENT=128.143.67.91 58432 22SELINUX_USE_CURRENT_RANGE=QTDIR=/usr/lib64/qt-3.3OLDPWD=/zf14/cr4bdQTINC=/usr/lib64/qt-3.3/includeSSH_TTY=/dev/pts/0QT_GRAPHICSSYSTEM_CHECKED=1USER=cr4bdLS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:MODULE_VERSION=3.2.10MAIL=/var/spool/mail/cr4bdPATH=/zf14/cr4bd/.cargo/bin:/zf14/cr4bd/bin:/usr/lib64/qt-3.3/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/opt/puppetlabs/bin:/usr/cs/contrib/bin:.PWD=/zf14/cr4bdLANG=en_US.UTF-8MODULEPATH=/sw/centos/Modules/modulefiles:/sw/linux-any/Modules/modulefilesLOADEDMODULES=KDEDIRS=/usr…_=/usr/bin/printenv
32
aside: environment variables (2)
environment variable library functions:getenv("KEY") → valueputenv("KEY=value") (sets KEY to value)setenv("KEY", "value") (sets KEY to value)
int execve(char *path, char **argv, char**envp)
char *envp[] = { "KEY1=value1", "KEY2=value2", NULL };char *argv[] = { "somecommand", "some␣arg", NULL };execve("/path/to/somecommand", argv, envp);
normal exec versions — keep same environment variables
33
aside: environment variables (3)
interpretation up to programs, but common ones…
PATH=/bin:/usr/binto run a program ‘foo’, look for an executable in /bin/foo, then/usr/bin/foo
HOME=/zf14/cr4bdcurrent user’s home directory is ‘/zf14/cr4bd’
TERM=screen-256coloryour output goes to a ‘screen-256color’-style terminal
…34
why fork/exec?
could just have a function to spawn a new program
some other OSs do this (e.g. Windows)
needs to include API to set new program stateopen files, current directory, environment variables, …with fork: just use ‘normal’ API
POSIX: posix_spawn, but rarely used, often not implemented(or implemented poorly)
35
POSIX process management
essential operations
process information: getpid
process creation: fork
running programs: exec*also posix_spawn (not widely supported), …
waiting for processes to finish: waitpid (or wait)
process destruction, ‘signaling’: exit, kill
36
wait/waitpid
pid_t waitpid(pid_t pid, int *status,int options)
wait for a child process (with pid=pid) to finish
sets *status to its “status information”
pid=-1 → wait for any child process instead
options? see manual page (command man waitpid)we’ll use 0
37
exit statuses
int main() {return 0; /* or exit(0); */
}
38
waitpid example
#include <sys/wait.h>...child_pid = fork();if (child_pid > 0) {
/* Parent process */int status;waitpid(child_pid, &status, 0);
} else if (child_pid == 0) {/* Child process */...
39
the status
#include <sys/wait.h>...waitpid(child_pid, &status, 0);if (WIFEXITED(status)) {printf("main␣returned␣or␣exit␣called␣with␣%d\n",
WEXITSTATUS(status));} else if (WIFSIGNALED(status)) {printf("killed␣by␣signal␣%d␣(control-C␣causes␣signal␣%d)\n",
WTERMSIG(status), SIGINT);} else {
...}
“status code” encodes both return value and if exit was abnormalW* macros to decode it
40
the status
#include <sys/wait.h>...waitpid(child_pid, &status, 0);if (WIFEXITED(status)) {printf("main␣returned␣or␣exit␣called␣with␣%d\n",
WEXITSTATUS(status));} else if (WIFSIGNALED(status)) {printf("killed␣by␣signal␣%d␣(control-C␣causes␣signal␣%d)\n",
WTERMSIG(status), SIGINT);} else {
...}
“status code” encodes both return value and if exit was abnormalW* macros to decode it
40
aside: signals
signals are a way of communicating between processes
they are also how abnormal termination happens
wait’s status will tell you when and what signal killed a programconstants in signal.hSIGINT — control-CSIGTERM — kill command (by default)SIGSEGV — segmentation faultSIGBUS — bus errorSIGABRT — abort() library function…
41
waiting for all children
#include <sys/wait.h>...while (true) {pid_t child_pid = waitpid(−1, &status, 0);if (child_pid == (pid_t) −1) {
if (errno == ECHILD) {/* no child process to wait for */break;
} else {/* some other error */
}}/* handle child_pid exiting */
}
42
parent and child processes
every process (but process id 1) has a parent processgetppid()
this is the process that can wait for itcreates tree of processes:
43
parent and child questions…
what if parent process exits before child?child’s parent process becomes process id 1
what if parent process never waitpid()/wait()s for child?child process stays around as a “zombie”can’t reuse pid in case parent wants to use waitpid()
what if non-parent tries to waitpid() for child?waitpid fails
44
typical pattern
pid = fork();if (pid == 0) {
exec…(…);…
} else if (pid > 0) {waitpid(pid,…);…
}…
pid = fork();if (pid == 0) {
exec…(…);…
} else if (pid > 0) {waitpid(pid,…);…
}…
pid = fork();if (pid == 0) {
exec…(…);…
} else if (pid > 0) {waitpid(pid,…);…
}…
main() {…
}
45
multiple processes?
while (...) {pid = fork();if (pid == 0) {
exec ...} else if (pid > 0) {
pids.push_back(pid);}
}
/* retrieve exit statuses in order */for (pid_t pid : pids) {
waitpid(pid, ...);...
}
46
multiple processes?
while (...) {pid = fork();if (pid == 0) {
exec ...} else if (pid > 0) {
pids.push_back(pid);}
}
/* retrieve exit statuses as processes finish */while ((pid = waitpid(−1, ...)) != −1) {
handleProcessFinishing(pid);}
47
POSIX process management
essential operations
process information: getpid
process creation: fork
running programs: exec*also posix_spawn (not widely supported), …
waiting for processes to finish: waitpid (or wait)
process destruction, ‘signaling’: exit, kill
48
shell
allow user (= person at keyborad) to run applications
user’s wrapper around process-management functions
upcoming homework — make a simple shell
49
aside: shell forms
POSIX: command line you have used before
also: graphical shellse.g. OS X Finder, Windows explorer
other types of command lines?
completely different interfaces?
50
some POSIX command-line features
searching for programs (mostly not in assignment)ls -l ≈ /bin/ls -lmake ≈ /usr/bin/make
running in background (not in assignment)./someprogram &
redirection:./someprogram >output.txt./someprogram <input.txt
pipelines:./someprogram | ./somefilter
51
some POSIX command-line features
searching for programs (mostly not in assignment)ls -l ≈ /bin/ls -lmake ≈ /usr/bin/make
running in background (not in assignment)./someprogram &
redirection:./someprogram >output.txt./someprogram <input.txt
pipelines:./someprogram | ./somefilter
52
searching for programs
POSIX convention: PATH environment variableexample: /home/cr4bd/bin:/usr/bin:/binchecked in order
one way to implement: [pseudocode]for (directory in path) {
execv(directory + "/" + program_name, argv);}
53
some POSIX command-line features
searching for programs (mostly not in assignment)ls -l ≈ /bin/ls -lmake ≈ /usr/bin/make
running in background (not in assignment)./someprogram &
redirection:./someprogram >output.txt./someprogram <input.txt
pipelines:./someprogram | ./somefilter
54
running in background
$ ./long_computation >tmp.txt &[1] 4049$ ...[1]+ Done ./long_computation > tmp.txt$ cat tmp.txtthe result is ...
& — run a program in “background”
initially output PID (above: 4049)
print out after terminatedone way: use waitpid with flag to say “don’t wait”
55