☠️
smadi0x86 Playground
  • 💀Welcome to smadi0x86 Playground
    • 🍷Resources
    • 🚬Projects
    • 🎓Certifications
    • 📌Pinned
    • ❓Questions
    • 📞Contact
  • 🏞️Cloud Native
    • Docker
      • Quick Reference
      • Introduction
      • Containers
      • Images
      • Storage & Volumes
      • Security
      • Cheatsheet
    • Git
    • Serverless Framework
    • YAML
  • 🔨Software Engineering
    • System Design
    • Environment Variables
    • JSON Web Tokens
  • 👾Architecture
    • C Language
      • Introduction
      • Calling Conventions
      • GCC Compilation
      • Libraries & Linking
      • I/O
      • Files
      • Pointers
      • Dynamic Memory Allocation
      • Data Types
      • Strings Manipulation
      • Bit Manipulation
      • Pre-processors
      • Macros
      • Type Qualifiers
    • C/C++ Build Systems
      • Fundamentals for Linking
      • Symbolic Linking
      • Cross-Platform Compilation
      • CMake for Building and Linking
      • Shared Libraries
      • Dynamic Linking and Dependency Management
    • Operating Systems
      • OS & Architecture
      • Processes
      • CPU Scheduling
      • Memory Management
  • 🛩️Cyber Warfare
    • Flight Physics
    • Communication
      • PWM & PPM
      • MAVLink
  • 🏴‍☠️Offensive Security
    • Active Directory
      • Introduction
    • Web Attacks
      • Server Side
        • OS Command Injection
        • Information Disclosure
        • Directory Traversal
        • Business Logic
        • Authentication
        • File Upload
        • SSRF
      • Client Side
        • CSRF
        • XSS
    • Recon
      • Active
        • Host discovery
        • Nmap
        • Mass Scan
      • Passive
        • Metadata
      • Web Applications
        • Discovery
        • Subdomains & Directories
        • SSL Certs
        • CMS
        • WAF Detection
      • Firewall Evasion
  • Binary Exploitation
    • Stack Smashing
      • x86
      • x86_64
    • pwntools
      • Processes and Communication
      • Logging and Context
      • Cyclic
      • Packing
      • ELF
      • ROP
  • 😈Advanced Persistent Threat
    • C2
      • Sliver
    • Malware
      • Windows Internals
        • PEB
      • Academy
        • Basics
      • Sektor7
        • Essentials
  • 💌Certifications
    • AWS Certified Cloud Practitioner (CLF-C01)
      • Cloud Foundations
      • Domain 1: Cloud Concepts
      • Domain 2: Security and Compliance
      • Domain 3: Technology
      • Domain 4: Billing and Pricing
    • AWS Certified Solutions Architect - Associate (SAA-C03)
      • Foundation
    • Certified Kubernetes Administrator (CKA)
      • Core Concepts
      • Scheduling
      • Logging & Monitoring
      • Application Lifecycle Management
      • Cluster Maintenance
      • Security
      • Storage
      • Networking
      • Design Kubernetes Cluster
      • Kubernetes The Kubeadm Way
      • Troubleshooting
      • JSONPATH
      • Lightning Lab
      • Mock Exams
      • Killer Shell
    • Certified Kubernetes Security (CKS)
      • Foundation
      • Cluster Setup
      • Cluster Hardening
      • Supply Chain Security
      • Runtime Security
      • System Hardening
      • Killer Shell
    • (KGAC-101) Kong Gateway Foundations
      • Introduction to APIs and API Management
      • Introduction to Kong Gateway
      • Getting Started with Kong Enterprise
      • Getting Started with Kong Konnect
      • Introduction to Kong Plugins
  • 📜Blog Posts
    • Modern Solutions For Preventing Ransomware Attacks
Powered by GitBook
On this page
  • What's in a process?
  • Process Execution State
  • Process Data Structures
  • Process Control Block (PCB)
  • Process State Queues
  • Context Switch
  • Creating a Process
  • What is happening on the Fork
  • Example of creating a process
  • Example of killing a process
  • Cooperating Processes
  • Producers and Consumers
  • Process Management
  1. Architecture
  2. Operating Systems

Processes

PreviousOS & ArchitectureNextCPU Scheduling

Last updated 10 months ago

What's in a process?

  • A process is a dynamic execution context of an executing program.

  • Several processes may run from the same program, but each has its own state with a different PID.

  • A process executes one instruction at a time sequentially and the EIP is pointing to next instruction.

A process consists of at least:

  • Code running the program

  • Static data for the running program

  • Space of dynamic data (Heap with a HP)

  • Instruction pointer indicating next instruction

  • Execution of stack with the program's call chain (Stack with SP)

  • Values of CPU registers

  • A set of OS resources in use such as open files

  • Process execution state (ready, running, waiting, terminating)

Process Execution State

Execution state of a process indicates what it is doing.

  • new: OS is setting up the process state

  • running: Executing instructions on the CPU

  • waiting: Ready to run, but waiting for the CPU

  • terminated: OS is destroying this process

As the program executes, it moves from state to state, as a result of the program actions (syscalls), OS actions (scheduling), and external actions (interrupts).

The OS manages multiple active processes using state queues.

Process Data Structures

Process Control Block (PCB)

PCB OS data structure to keep track of all processes.

  • The PCB tracks the execution state and location of each process

  • The OS allocates a new PCB on the creation of each process and places it on a state queue

  • The OS deallocates the PCB when the process terminates

The PCB contains:

  • Process state (running, waiting, etc.)

  • Process number

  • Program Counter

  • Stack Pointer

  • General Purpose Registers

  • Memory Management Information

  • Username of owner

  • List of open files

  • Queue pointers for state queues

  • Scheduling information (e.g., priority)

  • I/O status

The difference between PCB and PEB:

The Process Environment Block (PEB) contains data that is relevant to the process itself, and hence can be read by applications.

The process control block (PCB) contains data that is only useful to the kernel, such as the preferred CPU for this process.

Process State Queues

  • The OS maintains the PCBs of all the processes in state queues.

  • The OS places the PCBs of all the processes in the same execution state in the same queue.

  • When the OS changes the state of a process, the PCB is unlinked from its current queue and moved to its new state queue.

  • The OS can use different policies to manage each queue.

  • Each I/O device has its own wait queue.

Context Switch

  • Starting and stopping processes is called a context switch, and is a relatively expensive operation.

  • The OS starts executing a ready process by loading hardware registers (EIP, SP, etc...) from its PCB.

  • While a process is running, the CPU modifies the Program Counter (PC), Stack Pointer (SP), registers, etc...

  • When the OS stops a process, it saves the current values of the registers, (PC, SP, etc.) into its PCB.

  • This process of switching the CPU from one process to another (stopping one and starting the next) is the context switch.

    • Time sharing systems may do 100 to 1000 context switches a second.

    • The cost of a context switch and the time between switches are closely related.

Creating a Process

  • One process can create other processes to do work.

    • The creator is called the parent and the new process is the child.

    • The parent defines (or donates) resources and privileges to its children.

    • A parent can either wait for the child to complete, or continue in parallel.

  • In Unix, the fork system call called is used to create child processes.

    • Fork copies variables and registers from the parent to the child.

    • The only difference between the child and the parent is the value returned by fork.

      • In the parent process, fork returns the process id of the child.

      • In the child process, the return value is 0.

    • The parent can wait for the child to terminate by executing the wait system call or continue execution.

    • The child often starts a new and different program within itself, via a call to exec system call.

What is happening on the Fork

When a parent is killed, its child processes are called orphans and they go under the init (pid1) process.

Normally when a child process is created, the parent process suspends until child is finished and returns a value, but if nothing is waiting on the child and parent doesn't suspend and wait for the child return value, the child process exits and becomes a zombie process

Example of creating a process

  • When you log in to a machine running Unix, you create a shell process.

  • Every command you type into the shell is a child of your shell process and is an implicit fork and exec pair.

  • For example, you type emacs, the OS forks a new process and then exec (executes) emacs.

  • If you type an & after the command, unix will run the process in parallel with your shell, otherwise, your next shell command must wait until the first one completes.

#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>

void main() {
 int parentID = getpid(); /* ID of this process */
 
 char prgname[1024];
 
 gets(prgname); /* read the name of program we want to start */
 
 int cid = fork();
 
 if(cid == 0) { /* I'm the child process */
 
 execlp(prgname, prgname, 0); /* Load the program */
 
 /* If the program named prgname can be started, we never get
 to this line, because the child program is replaced by prgname */
 printf("I didn't find program %s\n", prgname);
 } else { /* I'm the parent process */
 
 sleep (1); /* Give my child time to start. */
 waitpid(cid, 0, 0); /* Wait for my child to terminate. */
 printf("Program %s finished\n", prgname);

Function explanations:

  • fork(): Forks a new child process that is a copy of the parent.

  • execlp(): Replaces the program of the current process with the named program.

  • sleep(): Suspends execution for at least the specified time.

  • waitpid(): Waits for the named process to finish execution.

  • gets(): Reads a line from a file.

  • getpid(): Gets the process ID.

Functions sleep() and wait() are only useful in old operating systems with a single core, if you want concurrency just call fork() alone.

On process termination, the OS reclaims all resources assigned to the process, in unix-like systems a process can terminate itself using exit or kill system call.

Example of killing a process

#include <signal.h>
#include <unistd.h>
#include <stdio.h>

void main() {
 int parentID = getpid(); /* ID of this process */
 int cid = fork();
 if(cid == 0) { /* I'm the child process */
 
 sleep (5); /* I'll exit myself after 5 seconds. */
 
 printf ( "Quitting child\n" );
 exit (0);
 printf ( "Error! After exit call!"); /* should never get here */
 } else { /* I'm the parent process */
 
 printf ( "Type any character to kill the child.\n" );
 char answer[10];
 gets (answer);
 
 if ( !kill(cid, SIGKILL) ) {
 printf("Killed the child.\n");

Cooperating Processes

Any two process are either independent or cooperating.

  • Cooperating processes work with each other to accomplish a single task.

  • Cooperating processes can

    • Improve performance by overlapping activities or performing work in parallel.

    • Enable an application to achieve a better program structure as a set of cooperating processes, where each is smaller than a single monolithic program.

    • Easily share information between tasks.

Distributed and parallel processing is the wave of the future. To program these machines, we must cooperate and coordinate between separate processes.

Producers and Consumers

Producer is a process which is able to produce data/item.

Consumer is a Process that is able to consume the data/item produced by the Producer.

Both Producer and Consumer share a common memory buffer. This buffer is a space of a certain size in the memory of the system which is used for storage.

Producers and consumers can communicate using message passing or shared memory.

Communication using message passing

Distributed systems typically communicate using message passing

  • Each process needs to be able to name the other process.

  • The consumer is assumed to have an infinite buffer size.

  • A bounded buffer would require the tests in the previous slide, and communication of the in and out variables (in from producer to consumer, out from consumer to producer).

  • OS keeps track of messages (copies them, notifies receiving process, etc...).

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>

#define QUEUE_NAME  "/test_queue"
#define MAX_SIZE    1024
#define MSG_STOP    "exit"

// Producer function
void producer() {
    mqd_t mq;
    char buffer[MAX_SIZE];

    // Open the message queue for writing, create if it doesn't exist
    mq = mq_open(QUEUE_NAME, O_CREAT | O_WRONLY, 0644, NULL);
    if (mq == (mqd_t)-1) {
        perror("mq_open");
        exit(1);
    }

    printf("Producer started...\n");

    // Send messages to the queue
    for (int i = 1; i <= 5; i++) {
        snprintf(buffer, MAX_SIZE, "Message %d", i);
        if (mq_send(mq, buffer, strlen(buffer) + 1, 0) == -1) {
            perror("mq_send");
            exit(1);
        }
        printf("Sent: %s\n", buffer);
        sleep(1);  // Simulate some work
    }

    // Send stop message
    if (mq_send(mq, MSG_STOP, strlen(MSG_STOP) + 1, 0) == -1) {
        perror("mq_send");
        exit(1);
    }

    // Close the message queue
    if (mq_close(mq) == -1) {
        perror("mq_close");
        exit(1);
    }
}

// Consumer function
void consumer() {
    mqd_t mq;
    char buffer[MAX_SIZE];
    ssize_t bytes_read;

    // Open the message queue for reading
    mq = mq_open(QUEUE_NAME, O_RDONLY);
    if (mq == (mqd_t)-1) {
        perror("mq_open");
        exit(1);
    }

    printf("Consumer started...\n");

    while (1) {
        // Receive messages from the queue
        bytes_read = mq_receive(mq, buffer, MAX_SIZE, NULL);
        if (bytes_read == -1) {
            perror("mq_receive");
            exit(1);
        }

        printf("Received: %s\n", buffer);

        // Check for stop message
        if (strcmp(buffer, MSG_STOP) == 0) {
            break;
        }
    }

    // Close the message queue
    if (mq_close(mq) == -1) {
        perror("mq_close");
        exit(1);
    }

    // Unlink (delete) the message queue
    if (mq_unlink(QUEUE_NAME) == -1) {
        perror("mq_unlink");
        exit(1);
    }
}

int main() {
    pid_t pid = fork();

    if (pid == -1) {
        perror("fork");
        exit(1);
    }

    if (pid == 0) {
        // Child process: run the consumer
        consumer();
    } else {
        // Parent process: run the producer
        producer();
        wait(NULL);  // Wait for the child process to finish
    }

    return 0;
}

Communication using shared memory

Establish a mapping between the process's address space to a named memory object that may be shared across processes.

  • The mmap() system call performs this function.

  • Fork processes that need to share the data structure.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/wait.h>

#define SHM_KEY 1234
#define SHM_SIZE 7

void initialize_shared_memory(int *shm) {
    for (int i = 0; i < SHM_SIZE - 2; i++) {
        shm[i] = 0;
    }
    shm[5] = 0;  // Write index
    shm[6] = 0;  // Read index
}

void producer(int *shm) {
    srand(time(NULL));

    while (1) {
        sleep(2);

        int value = rand() % 100;
        int write_index = shm[5];

        shm[write_index] = value;
        printf("Produced: %d at index %d\n", value, write_index);

        shm[5] = (write_index + 1) % 5;
    }
}

void consumer(int *shm) {
    while (1) {
        sleep(3);

        int read_index = shm[6];
        int value = shm[read_index];

        printf("Consumed: %d from index %d\n", value, read_index);

        shm[6] = (read_index + 1) % 5;
    }
}

int main() {
    int shmid;
    int *shm;

    // Create the shared memory segment
    shmid = shmget(SHM_KEY, SHM_SIZE * sizeof(int), IPC_CREAT | 0666);
    if (shmid < 0) {
        perror("shmget");
        exit(1);
    }

    // Attach the shared memory segment
    shm = (int *)shmat(shmid, NULL, 0);
    if (shm == (int *)-1) {
        perror("shmat");
        exit(1);
    }

    // Initialize shared memory
    initialize_shared_memory(shm);

    pid_t pid = fork();

    if (pid < 0) {
        perror("fork");
        exit(1);
    } else if (pid == 0) {
        // Child process: Consumer
        consumer(shm);
    } else {
        // Parent process: Producer
        producer(shm);

        // Wait for the child process to complete
        wait(NULL);
    }

    // Detach from the shared memory
    shmdt(shm);

    // Destroy the shared memory segment
    shmctl(shmid, IPC_RMID, NULL);

    return 0;
}

Process Management

A process is the unit of execution.

  • Processes are represented as Process Control Blocks in the OS

    • PCBs contain process state, scheduling and memory management information, etc...

  • A process is either New, Ready, Waiting, Running, or Terminated.

  • On a uniprocessor, there is at most one running process at a time.

  • The program currently executing on the CPU is changed by performing a context switch.

  • Processes communicate either with message passing or shared memory.

👾
https://stackoverflow.com/questions/68573063/can-someone-explain-what-process-enviornement-block-peb-pcb-process-control