Sitemap

Understanding Process in Linux

4 min readFeb 1, 2025

--

A process is an instance of a program that is running on the system.
It consumes memory for it’s program code, data and has resources allocated such as memory, file descriptors, and CPU time.

A process goes through various states during it’s lifecycle.

  • Running/ Runnable : Either queued up to run or in the process of running.
  • Blocked : There are two types of block, the Uninterruptible sleep, where the process doesn’t respond to signals and Interruptable sleep, where an event / interrupt can change the state of process.
  • Terminated :
    Stopped : A process in this state will continue to exist until it is killed or resumed with SIGCONT.
    Zombie / Defunct : Process has terminated and waiting for parent process to clear it off process table.

To manage these states and resources, process has a structure known as Process Control Block that holds information such as Process state, ID, CPU schedule info, memory management, accounting info etc about the process.In Linux, it is represented by task_struct which is pointed by the thread_info present in kernel stack of every process.

Let’s have a look into what goes behind the scenes during a process creation :

fork() syscall is the most widely used method to create a new process in Linux. The differentiation between parent and child process is via checking the RAX register, parent process will contain child’s pid and the child process has RAX set to 0. But before the RAX register is set and the fork() call returns, here is what happens :

  • fork internally calls clone syscall and passes SIGCHLD as the only flag.
  • Any signals received by parent process is delivered before fork happens.
  • task_struct for the new process is created using parent process’s properties via copy_process function.
  • copy_process in turn calls dup_task_struct which creates a new kernel stack and copies parent process’s task_struct to child’s task_struct.
  • Based on the flags passed to clone, various other data such as file descriptor table, signals, signal handlers, namespaces, memory mappings are copied from parent to child.
  • Parent process’s CPU registers are copied too via copy_thread. However, RAX is set to zero in child process.
  • Child process then receives a PID via alloc_pid.
  • The child process all this while is in TASK_UNINTERRUPTABLE state. wake_up_new_task is used to start the child process calling the scheduler and put it to runqueue.
  • Pid of child process process is then returned (RAX set) at parent process completing the fork() syscall.

The parent and child process are essentially the same(based on flags passed to clone). In modern kernels, a separate memory for child process is not yet created. It is allocated only when child process makes changes to a resource. This is known as Copy On Write. It is an optimisation mechanism as fork is usually followed by exec syscall where the entire memory is replaced anyway, which means creating a separate memory for child in the begging is an overhead.

Get Priii’s stories in your inbox

Join Medium for free to get updates from this writer.

How does a process look in memory?
The virtual address space of a process is made of several segments : code,static data, stack, heap.

  • . text : stores code segment and is sharable but read only.
  • data section: section that stores global and static variables
    .data : global and static variables that are initialised by the programmer.
    Can be further categorized to read -only and read — write. For example, a global pointer : const char* str = “hello world”; “hello world ” is stored in read-only and str in read-write data section.
    bss : un initialized global and static variables (ints are initialized to zero and pointers to null)
  • stack : a LIFO structure to store local variables and parameters passed to functions.
  • heap : dynamically allocated memory during program execution.

Note : When heap pointer meets the stack pointer, available memory space is said to be exhausted and a SEGV occurs.

While the above sections are visible to user, each process has memory dedicated to kernel to perform kernel-mode operations.

When the loader runs, it creates the memory image shown as above. Loader copies chunks of the executable into the code and data segments (with help of headers in binary) and then jumps to the program’s entry point , _start symbol.

Process vs Threads

A thread, is a path of execution within a process.

Press enter or click to view image in full size

Process is isolated, means it does not share the memory with any other process. Threads in a process are not isolated, it shares code, data, and resources with other threads in same process.
However, a thread has it’s own stack, CPU registers,Thread Control Block and signal mask.
Process switching requires the involvement of OS involvement, thread might not require.
Process communicate with each other through Inter Process Communication methods like pipe, shared memory, sockets, message queues etc while threads in same process can communicate as they share memory.

Note : Unless you “pin” a thread to a specific core, it’s up to the operating system which core that runs your thread, OS could even even change the core multiple times during the time a thread is active.

System Calls for Process in Linux
Kernel offers various system calls with respect to process like

  • fork : Creates a new process by duplicating the calling process.
  • exec : Replaces the current process memory with a new program.
  • wait : Causes a parent process to wait for the child to finish.
  • kill : Sends signals to process / process group.

Read more about fork and it’s family here : fork vs vfork vs rfork.

--

--

Priii
Priii

Written by Priii

Product Security Engineer | Documenting and sharing my learnings here!