Overview: Process Groups & Sessions

 

34.1 — Overview: Process Groups & Sessions
Chapter 34 · The Linux Programming Interface · EmbeddedPathashala
Understanding the two-level process hierarchy that powers shell job control
Key Terms in This Section

Process Group PGID Session SID Session Leader Process Group Leader Controlling Terminal Foreground Process Group Background Process Group Controlling Process Job Control SIGINT / SIGQUIT / SIGTSTP

What is a Process Group?

A process group is simply a collection of one or more processes that share the same Process Group ID (PGID). Think of it as a team of processes that were created together to do a job.

  • Every process belongs to exactly one process group.
  • The process group leader is the process that created the group. Its PID becomes the PGID of the entire group.
  • When you fork a child, the child inherits the parent’s PGID automatically.
  • A process group has a lifetime: it starts when the leader creates it, and ends when the last member leaves (exits or joins another group).

The PGID of any process is accessible via /proc/PID/stat on Linux.

What is a Session?

A session is a collection of process groups. All the process groups you create during a single login belong to one session.

  • Every process has a Session ID (SID) inherited from its parent.
  • The session leader is the process that created the session (usually the login shell). Its PID equals the SID.
  • All processes in a session share a single controlling terminal.

The Controlling Terminal

When the session leader opens a terminal for the first time, that terminal becomes the controlling terminal for the session, and the session leader becomes the controlling process.

  • A terminal can only be the controlling terminal for one session at a time.
  • At any moment, one process group in the session is the foreground process group; all others are background process groups.
  • Only the foreground group can read from the terminal.
  • Typing terminal characters sends signals to the entire foreground group:
Key Signal Default Action
Ctrl+C SIGINT Terminate the process
Ctrl+\ SIGQUIT Terminate + core dump
Ctrl+Z SIGTSTP Stop (suspend) the process

If the controlling process dies and the connection is lost, the kernel sends SIGHUP to the controlling process to notify it.

Visual Diagram: Sessions, Groups & Processes

SESSION 400 (login shell is session leader)
Process Group 400
(Shell)
←→ Process Group 658
(Background Job)
←→ Process Group 660
(FOREGROUND Job) ★
bash
PID=400
find PID=658
wc PID=659
sort PID=660
uniq PID=661
Controlling Terminal
Foreground PGID = 660 | Controlling SID = 400

Based on Figure 34-1 from TLPI. ★ Only PG 660 can read from the terminal.

Shell Job Control — The Big Picture

Shell job control is the main reason these abstractions exist. Here is what happens during an interactive login session:

  1. You log in. The login shell becomes the session leader, controlling process, and sole member of its own process group.
  2. Every command or pipeline you type creates a new process group.
  3. If you end the command with &, it becomes a background process group. Otherwise it becomes the foreground process group.
  4. All processes created during the login belong to the same session.
  5. In a graphical environment, each terminal window has its own separate session.
💡 Tip — Checking from /proc: You can verify all this yourself: cat /proc/$$/stat The 5th field is PGID and the 6th field is the session ID.

Code Example 1 — Reading PID, PGID, SID of a Process

This program prints the PID, process group ID, and session ID of itself. Run it from a shell to see how a child inherits the parent’s PGID and SID.

/* show_ids.c
 * Compile: gcc -o show_ids show_ids.c
 * Run: ./show_ids
 */
#include <stdio.h>
#include <unistd.h>

int main(void)
{
    printf("=== Process Identity Information ===\n");
    printf("PID  (Process ID)       : %ld\n", (long)getpid());
    printf("PPID (Parent PID)       : %ld\n", (long)getppid());
    printf("PGID (Process Group ID) : %ld\n", (long)getpgrp());
    printf("SID  (Session ID)       : %ld\n", (long)getsid(0));

    /* Fork a child — child inherits PGID and SID */
    pid_t child = fork();
    if (child == 0) {
        printf("\n--- Child Process ---\n");
        printf("Child PID  : %ld\n", (long)getpid());
        printf("Child PGID : %ld  (same as parent)\n", (long)getpgrp());
        printf("Child SID  : %ld  (same as parent)\n", (long)getsid(0));
        return 0;
    }
    /* Parent waits */
    wait(NULL);
    return 0;
}

/*
 * Expected output (PIDs will differ on your system):
 *
 * === Process Identity Information ===
 * PID  (Process ID)       : 12345
 * PPID (Parent PID)       : 1200        <- shell
 * PGID (Process Group ID) : 12345       <- process is its own group leader
 * SID  (Session ID)       : 1200        <- shell's PID is session ID
 *
 * --- Child Process ---
 * Child PID  : 12346
 * Child PGID : 12345   (same as parent)
 * Child SID  : 1200    (same as parent)
 */

Code Example 2 — Verifying /proc/PID/stat Fields

/* read_proc_stat.c
 * Reads process group ID and session ID directly from /proc
 * Compile: gcc -o read_proc_stat read_proc_stat.c
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
    char path[64];
    FILE *fp;
    int pid, ppid, pgrp, session;
    char comm[256], state;

    /* Build the path to our own stat file */
    snprintf(path, sizeof(path), "/proc/%d/stat", (int)getpid());

    fp = fopen(path, "r");
    if (fp == NULL) {
        perror("fopen /proc/PID/stat");
        return 1;
    }

    /* Format: pid (comm) state ppid pgrp session ... */
    fscanf(fp, "%d %s %c %d %d %d",
           &pid, comm, &state, &ppid, &pgrp, &session);
    fclose(fp);

    printf("From /proc/%d/stat:\n", pid);
    printf("  PID     = %d\n", pid);
    printf("  Command = %s\n", comm);
    printf("  State   = %c\n", state);
    printf("  PPID    = %d\n", ppid);
    printf("  PGRP    = %d  (process group ID)\n", pgrp);
    printf("  SESSION = %d  (session ID)\n", session);

    /* Cross-check with API calls */
    printf("\nCross-check with API:\n");
    printf("  getpgrp() = %d\n", (int)getpgrp());
    printf("  getsid(0) = %d\n", (int)getsid(0));

    return 0;
}
/*
 * Expected output:
 * From /proc/12345/stat:
 *   PID     = 12345
 *   Command = (read_proc_sta)
 *   State   = S
 *   PPID    = 1200
 *   PGRP    = 12345
 *   SESSION = 1200
 *
 * Cross-check with API:
 *   getpgrp() = 12345
 *   getsid(0) = 1200
 */

Interview Questions — Section 34.1

Q1. What is a process group and what is its purpose?
A process group is a collection of one or more processes sharing the same PGID. Its main purpose is to allow the shell and kernel to manage a set of related processes (a job) as a single unit — for example, sending a signal like SIGINT to all processes in a pipeline at once.
Q2. What is the difference between a session and a process group?
A process group is a collection of related processes (usually one pipeline or command). A session is a higher-level collection of process groups. All process groups created during a login belong to one session. A session has one controlling terminal; a process group does not have its own terminal.
Q3. Who is the session leader and how is the session ID determined?
The session leader is the process that called setsid() to create the session. The session ID equals the PID of the session leader. For a login session, the shell is the session leader, so the session ID is the shell’s PID.
Q4. What signals are sent to the foreground process group and which key generates each?
Ctrl+C generates SIGINT (terminate), Ctrl+\ generates SIGQUIT (terminate with core dump), and Ctrl+Z generates SIGTSTP (stop/suspend). All three go to every process in the current foreground process group.
Q5. What happens when a background process tries to read from the terminal?
The kernel sends SIGTTIN to the background process. The default action of SIGTTIN is to stop the process. It will remain stopped until brought to the foreground and the read is retried.
Q6. What is the controlling process and why is it significant?
The controlling process is the session leader that opened the controlling terminal. Its significance is that the kernel sends it SIGHUP when a terminal disconnect occurs (e.g., closing a terminal window). The default action of SIGHUP is to terminate the process.

Leave a Reply

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