What is Linux Processes vs Threads-Free Linux Device Drivers Course In Hyderabad

PREV_LECNEXT_LEC

Linux Processes vs Threads
A practical, embedded-focused look at how the Linux process model really works — part of EmbeddedPathashala’s free Linux kernel development course
Key Concepts In This Lecture
Process address space Threads pthread_create IPC RTOS migration Scheduling basics

If you are taking this free Linux kernel development course because you come from an RTOS background, this lecture is the one that will save you from the single biggest embedded-Linux design mistake: treating every RTOS task as a Linux thread without thinking about it. Before we can talk about scheduling, real-time behavior, or driver design, we need a solid mental model of what a Linux process is, what a Linux thread is, and when to reach for each one. This is one of the core lectures in our free embedded Linux course, and it sets up everything we will do later with IPC, signals, and real-time scheduling.

What You Will Learn

What a process actually is What a thread actually is Why threads share memory and processes don’t Process-per-task vs thread-per-task design A minimal multi-threaded demo Common porting mistakes from RTOS designs

Prerequisites

You should be comfortable compiling and running C programs on an embedded Linux target (covered earlier in this free linux device drivers course), and you should know what a file descriptor is. No prior POSIX threading knowledge is assumed.

A Process Is an Address Space, Not Just a Program

Every running Linux program lives inside a process. A process is best understood as a container: it owns a private virtual address space, a set of open file descriptors, a user ID and group ID, and a handful of kernel bookkeeping structures. What it does not automatically own is more than one flow of execution — that is the job of threads. When you launch a plain single-threaded program, Linux creates one process containing exactly one thread, and that thread runs your main() function.

The privacy of the address space is the important part. The kernel’s memory management subsystem keeps a separate page table for every process and reprograms the CPU’s memory management unit on every context switch, so a pointer that is valid inside process A points to something completely unrelated — or nothing at all — inside process B. This is exactly why two unrelated Linux programs can never accidentally corrupt each other’s variables. That isolation is the main reason process-based designs on embedded Linux tend to be more robust against a single misbehaving component than thread-based ones.

One Process, One Thread
Process (private address space) → Main thread → Open file descriptors → Heap / stack / mapped binary

Because a process’s memory is private, processes cannot simply read each other’s variables. If two processes need to exchange data, they must go through the kernel using a form of inter-process communication (IPC) — Unix domain sockets, pipes, shared memory segments, or message queues. We will cover each of those mechanisms in a dedicated lecture later in this free Linux kernel development course.

A Thread Is a Flow of Execution Inside a Process

A thread, by contrast, does not own its own address space. Every thread created inside a process runs inside that same process’s memory, sees the same heap, and shares the same table of open file descriptors as every other thread in that process. On Linux, additional threads beyond the main one are created with the POSIX threads function pthread_create(3), which is implemented on top of the kernel’s clone() system call with flags that tell the kernel “share this address space instead of copying it.”

One Process, Three Threads
Process (shared address space) → Thread 1 Thread 2 Thread 3 → Same heap, same file descriptors

This shared memory is exactly what makes threads attractive: passing data between two threads is as simple as writing to a shared variable, and no system call is required. It is also exactly what makes threads dangerous — without deliberate locking, two threads can race on the same variable and corrupt it, and a fatal bug in any one thread (a bad pointer dereference, for example) brings down the entire process, taking every other thread with it.

Process-Per-Task or Thread-Per-Task? The RTOS Migration Question

This question comes up constantly in this free embedded systems course from developers moving an existing RTOS design onto embedded Linux. An RTOS task looks superficially like a Linux thread — both are independent flows of execution with their own stack — so it is tempting to map every RTOS task to a Linux thread one-for-one and call the porting job done. Before doing that, it is worth weighing both extremes honestly.

DesignStrengthWeakness
One process per task (many processes, IPC between them)A crash in one task cannot corrupt another task’s memory; each task can be restarted independentlyIPC has real overhead; the system must handle a neighbor task disappearing at any time
One thread per task (single process, many threads)Sharing data between tasks is cheap and simple; context switches between threads are lighter than between processesAny thread can corrupt any other thread’s data; one fatal bug kills the whole process; debugging a large multi-threaded process is hard

Neither extreme is correct for every system. The practical answer used across most production embedded Linux designs is a hybrid: group tightly-cooperating, mutually-trusting tasks into threads of one process, and put anything that should be able to fail and restart independently — or anything written by a different team — into its own process, talking to the rest of the system over a well-defined IPC channel. We will build exactly this kind of hybrid design later in the free Linux device drivers course when we cover systemd service supervision and process restart policies.

A Minimal Multi-Threaded Demo

The example below is intentionally small: a process starts two worker threads that share one counter, incrementing it under a mutex so you can see shared-memory communication working correctly. Build it on any recent Linux distribution — the pthreads API has been part of glibc for decades and needs no special kernel version.

/* ep_thread_demo.c
 * Minimal demo: two threads sharing one counter safely.
 */
#include <stdio.h>
#include <pthread.h>

static long shared_counter = 0;
static pthread_mutex_t counter_lock = PTHREAD_MUTEX_INITIALIZER;

static void *ep_worker(void *arg)
{
    int id = *(int *)arg;

    for (int i = 0; i < 100000; i++) {
        pthread_mutex_lock(&counter_lock);
        shared_counter++;
        pthread_mutex_unlock(&counter_lock);
    }

    printf("worker %d finished\n", id);
    return NULL;
}

int main(void)
{
    pthread_t t1, t2;
    int id1 = 1, id2 = 2;

    pthread_create(&t1, NULL, ep_worker, &id1);
    pthread_create(&t2, NULL, ep_worker, &id2);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    printf("final counter value: %ld\n", shared_counter);
    return 0;
}

Build and run it on your target or dev machine:

$ gcc -Wall -O2 -o ep_thread_demo ep_thread_demo.c -lpthread
$ ./ep_thread_demo
worker 1 finished
worker 2 finished
final counter value: 200000

Notice that the final value is always exactly 200000, never less. That is the mutex doing its job — remove the pthread_mutex_lock/unlock pair and rerun the program several times; you will see the final value vary unpredictably because both threads are now racing on the same memory location. That race is the concrete cost of thread-sharing that the table above referred to as a weakness.

Common Mistakes

  • Mapping every RTOS task to a thread without a plan. A single 40-thread process is exactly the design this lecture warns against — one bad pointer write ends the entire application.
  • Forgetting that threads share file descriptors. Closing a file descriptor in one thread closes it for every thread in that process, which is a common source of “it works standalone but breaks under load” bugs.
  • Assuming shared memory means synchronized memory. Sharing an address space only means threads can see the same data — it says nothing about when it is safe to read or write it.

Best Practices

  • Default to processes for anything that should be independently restartable or comes from an untrusted/unverified source.
  • Default to threads only for tightly-cooperating work inside a single component that you own end to end.
  • Keep the number of threads in one process small and purposeful rather than mapping every logical task 1:1.
  • Always protect shared data with a mutex or another explicit synchronization primitive — never assume “it usually works.”

Summary and Key Takeaways

A process is a private address space with at least one thread inside it; a thread is a flow of execution that shares its process’s memory and file descriptors with every other thread in that same process. Processes give you isolation at the cost of IPC overhead; threads give you cheap sharing at the cost of shared-fate crashes and manual synchronization. Good embedded Linux designs mix both deliberately instead of blindly mapping RTOS tasks to threads. This foundation is required before the next lectures in this free Linux kernel development course, where we build real IPC and process-supervision examples on top of it.

FAQ

Is a Linux thread the same as an RTOS task?

Not exactly. Both are independent flows of execution with their own stack, but a Linux thread also inherits process-level resources — file descriptors, signal handlers, memory mappings — that most RTOS tasks don’t have to think about, which is why direct 1:1 porting can hide bugs.

Do threads run in parallel on Linux?

Yes, on a multi-core system the kernel scheduler can run threads of the same process on different CPU cores simultaneously, which is exactly why unsynchronized shared-memory access causes real, reproducible race conditions rather than a theoretical concern.

Which is faster: a process context switch or a thread context switch?

A thread context switch is generally cheaper because the memory management unit does not need to be reprogrammed with a new page table, unlike a switch between two different processes.

Can two processes share memory without threads?

Yes, through explicit shared memory IPC (shmget/mmap-based shared regions), which we cover in a later IPC lecture in this free embedded Linux course — it gives shared-memory speed while keeping processes otherwise isolated.

What happens if one thread crashes?

The entire process terminates, taking every thread in it down, because all threads share one address space and one set of kernel resources.

Is fork() related to threads?

No — fork() creates a new process with its own separate address space; it does not create a thread. We cover fork() in detail in the next lecture of this course.

Do I need this for embedded device driver work?

Yes — most real device drivers are exercised from user-space daemons that use exactly this process/thread model, so understanding it is a prerequisite for the driver-focused lectures later in this free Linux device drivers course.

Continue the Free Linux Kernel Development Course

This lecture is part of EmbeddedPathashala’s free embedded Linux course. Explore the next lecture on creating processes with fork(), or browse the full course index to keep learning at your own pace.

PREV_LECNEXT_LEC

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *