Lec 2-Processes

Lee Nam Gil·2025년 3월 24일

Operating Systems

목록 보기
3/18

Processes

Running Dynamic Code

Basic function of OS is to execute and manage code dynamically

  • Command line terminal
  • Icon double click
  • Jobs/tasks run as part of a batch system

Programs

  • An executable file in long-term storage (e.g. chrome.exe)

Processes

  • The basic unit of a program in execution
  • The running instantiation of a program, stored in RAM
  • One-many relationship = One programs can call many processes

PCB: Process Control Block

PCB: Process Control Block = TCB: Task(or Thread) Control Block

  • Kernel data structure representing a process
  • The loader create PCB for each process
  • The kernel must manage new process
  • Has at least one thread
  • Keeps track of the memory used by the process
    = Processes have address space
    • Control flow = Address space = Thread
    • Change of IP = Change of Control flow
  • Keeps runtime state of the process
    • CPU register vals
    • EIP

Process States

  • new: Being created
  • running: Being executed
  • waiting: Waiting for some event to occur
  • ready: Waiting to be assigned to a processor
  • terminated: Finished execution

    IO is slow
    \rightarrow Though scheduling is done, IO can't be executed
    \rightarrow By giving it waiting state, Do not include in the schedule target

Parents and Children

  • All processes have parents
    = Processes are made by process
  • If a process spawns other processes, they become it's children
  • If a parent exits before its children, the children become orphans
  • If a child exits before the parent calls wait(), the children becomes a zombie
    • Zombie processes return compute resources, but do not return PIDs so that PID remians in the Process Table
  • e.g.) Parent exit()
    \rightarrow Parent can't wait() for the child
    \rightarrow Child becomes orphan
    \rightarrow Child becomes zombie

Process Tree

  • init(systemd in LINUX) is a special process started by the kernel (pid=1)
  • e.g.) Let process A is a parent of the process B
    1. A is terminated
    2. B becomes orphan
    3. B becomes zombie
    4. B becomes a child of the init
    5. B is terminated

Additional Execution Context

  • File descriptors
    • stdin, stdout, stderr, Sockets, Pipes
  • Permissions
    • User and group and others
    • Access to specific APIs
  • Shared Resources
    • Shared memory

Exapmle: UNIX

UNIX Process Management

fork()

  • Create a copy of the current process (NO arguments)
  • Return 2 same processes
    • Parent has non-zero pid (the child's pid)
    • Child has pid 0
  • Implementation
    1. Create and initialize PCB in the kernel
    2. Create a new address space
    3. Initialize the address space with a copy of the address space of the parent
      • Modern OS does not copy directly,
        Copy when exec() is not in the code
    4. Inherit the execution context of the parent
    5. Inform the scheduler that the new process is ready

exec(Program path)

  • Change the program(= code data) being run by the current process
  • Implementation
    1. Load the new program into the current address space
    2. Copy command line arguments into memory
    3. Initialize the hardware context to start execution
      • EIP = Entry point in the ELF header
        • address of main()
      • ESP = A newly allocated stack

wait(PID)

  • Wait for a process to finish
  • If wait() doesn't exist, then no guarantee which one will be executed first

signal()

  • Send a notification to another process

abort(PID) can be used for parent process to immediately end a child process


Context Switching

Context switching

  • Save state of a process before a switching
  • Restore original process state when switching back

Process Stack

Each process has a stack in memory that stores

  • Local variables
  • Arguments to functions
  • Return addresses from functions

On x86

  • Stack grows downwards
  • ESP points to the bottom of the stack
  • EBP points to the base of the current frame
  • Instructions push, pop, call, ret, int, iret modify the stack

Ex)

int bar(int a, int b) {
  int r = rand();
  return a + b - r;
}

int foo(int a) {
  int x, y;
  x = a * 2;
  y = a - 7;
  return bar(x, y);
}

int main(void) {
  …
  foo(12);
  …
}

In AT&T syntax

  • Registers are prefixed with %,
    • %eax
  • The left is source and the right is destination
    • mov %eax, %ebx (eax -> ebx)

In Intel syntax

  • The left is destination and the right is source
    • mov ebx, eax (ebx <- eax)

Call foo() & Store return addr to main()

Store EBP of previous func (main())

Allocate address space of current func foo()

Store local vars and args

Call bar() & Store return addr to foo()

Store EBP of previous func foo()

Allocate address space of current func bar()

Leave = Deallocation of the current func bar()

  • mov esp, ebp
  • pop ebp
  • Return value is placed in EAX

Ret = EIP <- ESP



Stack Switching

Stack holds

  • local vars
  • args to functions
  • return addr

A process's control flow is stored on the stack

However, stack doesn't hold register vals


Switching between processes

  1. switch()
    • Process 1 calls into switch() routine
  2. push eax ~ edx
    • CPU registers are pushed onto the stack (of Process 1)
  3. mov [cur_esp], esp
    • Stack pointer is saved into memory (ESP for Procees 1)
  4. mov esp, [saved_esp]
    • Stack pointer for process 2 is loaded
  5. pop edx ~ eax
    • CPU registers are restored (of Process 2)
  6. ret
    • switch() returns back to process 2

Switches into a process by return from a function

Switches out of a process by calling into a function


About new processes

A new process doesn't have a stack

\rightarrow Pretend there was a previous call

  • Build a fake initial stack frame
    • looks exactly like the instruction just before main() called into switch()
    • When switch() return, run main() of the new process

When do we switch processes?

Voluntary yielding

  • Processes must voluntary give up control by calling an OS API
  • Problems
    • Misbehaving or buggy apps may never yield = may never context switch
    • No guarantee that apps will yield in a reasonable amount of time
    • Wasteful CPU resources (e.g.) Process is waiting on I/O)

Switch during API calls to the OS

  • When a process calls an OS API, OS has an opportunity to context switch
  • Problems
    • Misbehaving or buggy apps may never yield = may never context switch
    • Some normal apps don't use OS APIs for long periods

Switch on I/O

  • When a process is waiting on I/O, switch to another process
  • Problems
    • Some normal apps don't have any I/O for long periods

Preemptive Context Switching = Switch based on a timer interrupt

  • Use a timer interrupt to force context switching at set intervals
    = Limit the maximum running time of process
    • If it's been running for some max duration (scheduling quantum),
      the handler switches to the next process
  • Problems
    • Requires hardware support (a programmable timer)

Isolation

Process Isolation

We can execute multiple processes concurrently

Problem: How do we stop process from behaving badly?

  • Overwritting kernel memory
  • Reading/writing data from other processes

How to implement execution with limited privilege?

  • Use an interpreter or a simulator
    • Execute each program instruction in a simulator (e.g. Java)
    • If the instruction is permitted, do the instruction
  • However, they are slow
  • So, Run the unprivileged code directly on the CPU

Protected Mode

x86 CPUs support three rings with different privileges

  • Ring 0: OS kernel
  • Ring 1, 2: device drivers
  • Ring 3: userland

Real vs Protected

  • CPU starts in 16-bit real mode
    • Protected mode is disabled
  • Bootloader switches CPU to protected mode
    • Enable pmode
mov eax, cr0
or eax, 1
mov cr0, eax

Dual-Mode Operation

  • Ring 0
    • kernel/supervisor mode
    • Full privileges of the hardware
  • Ring 3
    • user mode or userland
    • Limited privileges

Privileged Instructions

  • Examples
    • sti/cli - Enable and disable interrupts
    • Modifying the CR0 registers
    • hlt - halt the CPU
  • If a user program attempts to execute a privileged instruction
    • General protection exception gets thrown by the CPU

Changing Modes

Applications often need to access the OS

  • e.g. system calls

But the OS is ring 0, apps are ring 3

How do apps get access to the OS?

  • Apps invoke system calls with an interrupt (e.g. int 0x80)
  • int causes a mode transfer from ring 3 to ring 0

Mode Transfer

Userland

  1. Application executes trap (int) instruction
    • EIP, CS, EFLAGS are pushed onto the stack
    • Mode switches from ring 3 to ring 0

Kernel mode

  1. Save the state of the current process
    • Push EAX, EBX, etc.
  2. Locate and execute the syscall handler
  3. Restore the state of process
    • Pop EAX, EBX, etc.
  4. Place the return value in EAX
  5. Use iret to return to the process
    • Switch back to the original mode

Example

  1. Software executes int 0x80
    • Push EIP, CS, EFLAGS
  2. CPU transfers execution to the OS handler
    • Look up the handler in the IVT
    • Switch from ring 3 to 0
  3. OS executes the system call
    • Save the process state
    • Use EAX to locate the system call
    • Execute the system call
    • Restore the process state
    • Put the return value in EAX
  4. Return to the process with iret
    • Pop EIP, CS and EFLAGS
    • Switches from ring 0 to 3

0개의 댓글