zettelkasten

Search IconIcon to open search
Dark ModeDark Mode

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 pid
      • pid_t getppid(void) - get parent pid
    • user - who ran it?
    • command
  • 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 in exit)
    • 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 stack
  • int fork(void) returns 0 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

Pasted image 20230807163558.png

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 or waitpid
    • wait waits until one of children terminates
      • returns pid of terminated process
    • 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 not NULL, will point to exit status passed to exit
        • 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, or 0 if WNOHANG in option and no child
      • when returning -1, also sets errno to ECHILD
    • When options == 0, suspend execution and wait
    • What to wait
      • pid > 0, wait for specified pid
      • pid == -1, wait for all
      • (other ones see man waitpid)
    • options
      • WNOHANG - return immediate rather than suspend. If no child terminated, return 0.
      • WUNTRACED - waitpid for stopped or terminated process, not just terminated
      • WCONTINUED - waitpid for terminated or for stopped process to continue upon SIGCONT
  • checking exit status
    • WIFEXITED(status) - normal termination
      • WEXITSTATUS(status) - status for normally terminated child
    • WIFSIGNALED(status) - return if child terminated because of uncaught signal
      • WTERMSIG(status) - check signal number that caused child to terminate
    • WIFSTOPPED(status) - if stopped
      • WSTOPSIG(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)
  • argument list argv
    • convention: argv[0]==filename
  • envp - env variables
    • NAME=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 and data in data/text segments (operating system tries to be lazy i.e. not do anything until they are actually used)
  • 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:

Pasted image 20230807165748.png

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);
	}
}