Processes
#lecture note based on 15-213 Introduction to Computer Systems
History: human used to manage processes, but in 70s they wanted multiple people to connect by terminal.
Each process have their memory and registers and stuff, but they need to be separated. Some mechanism allows each process to think they have the entire system.
H2 Process & its properties
- Process - instance of a programme running
- Private address space - each process appears to have exclusive use of memory and cpu
- Private control flow - control flow seems to be exclusive to process
- process identification
- PID - unique identifier of process
pid_t getpid(void)
- get own pidpid_t getppid(void)
- get parent pid
- user - who ran it?
- command
- PID - unique identifier of process
- Process states
- Running - executing instructions or context switched out
- Blocked / sleeping - waiting for external event e.g. keyboard input
- Stopped - prevented from executing by user, so will not be scheduled
- Result of getting SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU.
- Can be resumed if they reseive SIGCONT
- Terminated / zombie - finished but waiting for parent to do something
- Returned from main
- Received fault that result in termination
- Called exit
- Termination process
- Received signal to terminate
- Returned from
main
(which is just wrapped inexit
) - Called
void exit(int status)
0
indicates successful- else something wrong
- current working directory
H3 Concurrent process
Process concurrent if their execution overlap in time, sequential otherwise
For uniprocessor, it’s just switching between processes. Not real concurrency.
H2 Kernel’s Involvement
H3 Context Switching
Processes managed by shared chunk of memory. Kernel handles context switch between processes
Kernel remembers where stack, heap, data, code are, saves register, and bring something else in to execute by loading its saved registers.
Notes:
- Multicore is just multiple contexts going on
- It used to require programmer explicitly telling kernel to take over. Now kernel can choose to delay response to syscall, page fault, etc. to switch to other context
H3 System Call
When you want something to happen outside the process. This usually needs requesting the kernel to do something. This is different depending on OS
Examples:
- Read/write
- Get time
- Allocate RAM (
sbrk
) - create new process
Linux convention: put syscall number in %rax
(with other arguments in right place, like normal call convention) and call syscall
, expect return result in %rax
.
It’s like function call, but with differences:
- it executes kernel code
- executed with different privileges
- uses
errno
- uses
%rax
for syscall number, not function address
H4 Syscall Error Handling
Usually syscall return -1
and set errno
(a global variable containing an integer error code)
In practice: Try to recover when there’s an error, not just report and crash
Error categories:
- fatal
- informative
- ignore
H2 Child Processes
H3 Fork
parent process calls to create a child process
- initially a clone, but one treated as parent and one as child (everything identical except identifier)
- Memory usually on copy-on-write to avoid copying bytes
fork
returns twice - each process has return on stackint fork(void)
returns0
to child process and child PID (positive) to parent process. If error, return something negative - this can be used to make the clones start doing different thing- parent can wait for child by
waitpid
(main returns this way) - also share open files (which is why they print to same terminal)
H4 Process graph
Edges directed. a -> b means a before b
Any topological sort is valid ordering
Usually left to right, concurrence go vertical
H4 Reaping child processes
When parent needs to find out zombie of its child that OS holds on to
- Child terminates by:
- Receive some signal to terminate
- Return from main
- Call
exit
(with some status info)
- Parent call
wait
orwaitpid
wait
waits until one of children terminates- returns
pid
of terminated process
- returns
pid_t waitpid(pid_t pid, int *status, int options)
waits for specific child (or -1 for any)- More flexible, there are other other options
status
, if notNULL
, will point to exit status passed toexit
- There are some macros to decode the exit status
- Kernel deletes zombie (OS calls
defunct
)
(If parent dies, the orphaned child get reaped by init
)
(If child never terminates but parent terminated, they stay there…)
In code:
pid_t waitpid(pid_t pid, int *statusp, int options);
- return pid of child if worked,
-1
if error, or0
ifWNOHANG
in option and no child- when returning
-1
, also setserrno
toECHILD
- when returning
- When
options == 0
, suspend execution and wait - What to wait
pid > 0
, wait for specified pidpid == -1
, wait for all- (other ones see
man waitpid
)
- options
WNOHANG
- return immediate rather than suspend. If no child terminated, return0
.WUNTRACED
- waitpid for stopped or terminated process, not just terminatedWCONTINUED
- waitpid for terminated or for stopped process to continue uponSIGCONT
- return pid of child if worked,
- checking exit status
WIFEXITED(status)
- normal terminationWEXITSTATUS(status)
- status for normally terminated child
WIFSIGNALED(status)
- return if child terminated because of uncaught signalWTERMSIG(status)
- check signal number that caused child to terminate
WIFSTOPPED(status)
- if stoppedWSTOPSIG(status)
- what signal caused the stop
WIFCONTINUED(status)
- if continued
H3 Execute (execve)
int execve(char *filename, char *argv[], char *envp[])
filename
- an object file or a script file beginning with
#!interpreter
(e.g.#!/bin/bash
)
- an object file or a script file beginning with
- argument list
argv
- convention:
argv[0]==filename
- convention:
envp
- env variablesNAME=value
format
Run some other programme by replacing code in current process. Essentially brainwash and inject some other code with argument and environment variables
- allocate virtual memory
- overwrite stack, data, text
- put
text
anddata
in data/text segments (operating system tries to be lazy i.e. not do anything until they are actually used)
- put
- retains PID, open files and signal context
- Called once and never return, except when on error
It turns the stack into something that look like:
Note: some operating system does fork
and exec
together, linux treats them separately.
H4 Example
Running /bin/ls -lt /usr/include
in child process
if ((pid = Fork()) == 0) { /* Child runs program */
if (execve(myargv[0], myargv, environ) < 0) {
printf("%s: %s\n", myargv[0], strerror(errno));
exit(1);
}
}